This commit is contained in:
longdayi 2025-05-28 12:21:03 +08:00
parent dc85586930
commit 8956994325
17 changed files with 886 additions and 1399 deletions

View File

@ -10,7 +10,9 @@
"@hono/zod-validator": "^0.5.0", "@hono/zod-validator": "^0.5.0",
"@repo/db": "workspace:*", "@repo/db": "workspace:*",
"@repo/oidc-provider": "workspace:*", "@repo/oidc-provider": "workspace:*",
"@repo/tus": "workspace:*",
"@trpc/server": "11.1.2", "@trpc/server": "11.1.2",
"dayjs": "^1.11.12",
"hono": "^4.7.10", "hono": "^4.7.10",
"ioredis": "5.4.1", "ioredis": "5.4.1",
"jose": "^6.0.11", "jose": "^6.0.11",
@ -19,6 +21,7 @@
"node-cron": "^4.0.7", "node-cron": "^4.0.7",
"oidc-provider": "^9.1.1", "oidc-provider": "^9.1.1",
"superjson": "^2.2.2", "superjson": "^2.2.2",
"transliteration": "^2.3.5",
"valibot": "^1.1.0", "valibot": "^1.1.0",
"zod": "^3.25.23" "zod": "^3.25.23"
}, },

View File

@ -1,7 +1,7 @@
import { Server, Upload } from '@repo/tus'; import { Server, Upload } from '@repo/tus';
import { prisma } from '@repo/db'; import { prisma } from '@repo/db';
import { getFilenameWithoutExt } from '../utils/file'; import { getFilenameWithoutExt } from '../utils/file';
import { nanoid } from 'nanoid-cjs'; import { nanoid } from 'nanoid';
import { slugify } from 'transliteration'; import { slugify } from 'transliteration';
import { StorageManager } from './storage.adapter'; import { StorageManager } from './storage.adapter';

View File

@ -0,0 +1,158 @@
# OIDC Provider
OpenID Connect Provider 实现,支持标准的 OIDC 协议流程。
## 特性
- 完整的 OIDC 协议支持
- 密码认证策略
- 会话管理
- 令牌管理访问令牌、刷新令牌、ID令牌
- PKCE 支持
- 可自定义的存储适配器
## 快速开始
### 1. 安装
```bash
npm install @nice/oidc-provider
```
### 2. 配置
```typescript
import { createOIDCProvider } from '@nice/oidc-provider/middleware/hono';
import { MemoryStorageAdapter } from '@nice/oidc-provider/storage';
const config = {
issuer: 'https://your-domain.com',
signingKey: 'your-signing-key',
storage: new MemoryStorageAdapter(),
// 用户和客户端查找函数
findUser: async (userId: string) => {
// 从数据库查找用户
return await db.user.findUnique({ where: { id: userId } });
},
findClient: async (clientId: string) => {
// 从数据库查找客户端
return await db.client.findUnique({ where: { id: clientId } });
},
// 认证配置
authConfig: {
// 密码验证器
passwordValidator: async (username: string, password: string) => {
const user = await db.user.findUnique({ where: { username } });
if (user && await bcrypt.compare(password, user.hashedPassword)) {
return user.id;
}
return null;
},
// 会话配置
sessionTTL: 24 * 60 * 60, // 24小时
rememberMeMaxAge: 30 * 24 * 60 * 60, // 30天
// 页面配置
pageConfig: {
title: '用户登录',
brandName: '我的应用',
logoUrl: '/logo.png'
}
}
};
// 创建 OIDC Provider Hono 应用
const oidcApp = createOIDCProvider(config);
```
### 3. 集成到 Hono 应用
```typescript
import { Hono } from 'hono';
const app = new Hono();
// 挂载 OIDC Provider
app.route('/oidc', oidcApp);
export default app;
```
## API 端点
创建后的 OIDC Provider 将提供以下标准端点:
- `POST /login` - 用户登录
- `GET /logout` - 用户登出
- `POST /logout` - 用户登出POST 方式)
- `GET /.well-known/openid-configuration` - OIDC 发现文档
- `GET /.well-known/jwks.json` - JSON Web Key Set
- `GET /auth` - 授权端点
- `POST /token` - 令牌端点
- `GET /userinfo` - 用户信息端点
- `POST /revoke` - 令牌撤销端点
- `POST /introspect` - 令牌内省端点
## 配置选项
### OIDCProviderConfig
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `issuer` | string | ✓ | 发行者标识符 |
| `signingKey` | string | ✓ | JWT 签名密钥 |
| `storage` | StorageAdapter | ✓ | 存储适配器 |
| `findUser` | function | ✓ | 用户查找函数 |
| `findClient` | function | ✓ | 客户端查找函数 |
| `authConfig` | AuthConfig | - | 认证配置 |
| `tokenTTL` | TokenTTLConfig | - | 令牌过期时间配置 |
### AuthConfig
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `passwordValidator` | function | - | 密码验证函数 |
| `sessionTTL` | number | - | 会话过期时间(秒) |
| `rememberMeMaxAge` | number | - | 记住我最长时间(秒) |
| `pageConfig` | PageConfig | - | 登录页面配置 |
### PageConfig
| 字段 | 类型 | 描述 |
|------|------|------|
| `title` | string | 登录页面标题 |
| `brandName` | string | 品牌名称 |
| `logoUrl` | string | Logo URL |
## 存储适配器
项目提供了多种存储适配器:
- `MemoryStorageAdapter` - 内存存储(适用于开发和测试)
- `RedisStorageAdapter` - Redis 存储
- `DatabaseStorageAdapter` - 数据库存储
### 自定义存储适配器
```typescript
import { StorageAdapter } from '@nice/oidc-provider/storage';
class CustomStorageAdapter implements StorageAdapter {
// 实现所需的方法
}
```
## 安全考虑
1. **签名密钥安全**:确保 `signingKey` 足够复杂且妥善保管
2. **HTTPS**:生产环境必须使用 HTTPS
3. **客户端验证**:实现严格的客户端验证逻辑
4. **密码策略**:在 `passwordValidator` 中实现适当的密码策略
## 许可证
MIT

View File

@ -1,157 +0,0 @@
import type { Context } from 'hono';
import type { AuthorizationRequest, OIDCProviderConfig } from '../types';
import { PasswordAuthStrategy, type AuthenticationResult, type PasswordAuthConfig } from './strategies/password-auth-strategy';
/**
*
*/
export type PasswordValidator = (username: string, password: string) => Promise<string | null>;
/**
*
*/
export interface AuthManagerConfig {
/** 会话TTL */
sessionTTL?: number;
/** 记住我最大存活时间(秒) */
rememberMeMaxAge?: number;
/** 页面配置 */
pageConfig?: {
title?: string;
brandName?: string;
logoUrl?: string;
};
}
/**
*
*
*/
export class AuthManager {
private readonly passwordAuth: PasswordAuthStrategy;
constructor(
oidcConfig: OIDCProviderConfig,
passwordValidator: PasswordValidator,
config: AuthManagerConfig = {}
) {
const passwordConfig: PasswordAuthConfig = {
sessionTTL: config.sessionTTL || 24 * 60 * 60, // 默认24小时
rememberMeMaxAge: config.rememberMeMaxAge || 30 * 24 * 60 * 60, // 默认30天
pageConfig: config.pageConfig || {}
};
this.passwordAuth = new PasswordAuthStrategy(
oidcConfig,
passwordValidator,
passwordConfig
);
}
/**
*
*/
async getCurrentUser(c: Context): Promise<string | null> {
return await this.passwordAuth.getCurrentUser(c);
}
/**
*
*/
async handleAuthenticationRequired(
c: Context,
authRequest: AuthorizationRequest
): Promise<Response> {
return await this.passwordAuth.handleAuthenticationRequired(c, authRequest);
}
/**
*
*/
async authenticate(c: Context): Promise<AuthenticationResult> {
return await this.passwordAuth.authenticate(c);
}
/**
*
*/
async logout(c: Context): Promise<Response> {
return await this.passwordAuth.logout(c);
}
/**
*
*/
async handleLogin(c: Context, authRequest: AuthorizationRequest): Promise<Response> {
try {
// 执行认证
const authResult = await this.passwordAuth.authenticate(c);
if (authResult.success) {
// 认证成功,处理后续操作
return await this.handleAuthenticationSuccess(c, authResult);
} else {
// 认证失败,返回错误页面
return await this.handleAuthenticationFailure(c, authResult, authRequest);
}
} catch (error) {
console.error('登录流程处理失败:', error);
// 返回通用错误页面
return await this.handleAuthenticationFailure(
c,
{ success: false, error: '服务器内部错误' },
authRequest
);
}
}
/**
*
*/
async checkAuthenticationStatus(
c: Context,
authRequest: AuthorizationRequest
): Promise<{ authenticated: boolean; userId?: string; response?: Response }> {
try {
const userId = await this.getCurrentUser(c);
if (userId) {
return {
authenticated: true,
userId
};
} else {
const response = await this.handleAuthenticationRequired(c, authRequest);
return {
authenticated: false,
response
};
}
} catch (error) {
console.error('检查认证状态失败:', error);
throw error;
}
}
/**
*
*/
private async handleAuthenticationSuccess(
c: Context,
result: AuthenticationResult
): Promise<Response> {
return await this.passwordAuth.handleAuthenticationSuccess(c, result);
}
/**
*
*/
private async handleAuthenticationFailure(
c: Context,
result: AuthenticationResult,
authRequest: AuthorizationRequest
): Promise<Response> {
return await this.passwordAuth.handleAuthenticationFailure(c, result, authRequest);
}
}

View File

@ -1,13 +1,13 @@
// 核心管理器
export { AuthManager, type AuthManagerConfig, type PasswordValidator } from './auth-manager';
// 密码认证 // 密码认证
export { PasswordAuthStrategy, type AuthenticationResult, type PasswordAuthConfig } from './strategies/password-auth-strategy'; export { PasswordAuth, type AuthenticationResult, type PasswordAuthConfig } from './password-auth';
// 类型定义(从 types 模块重新导出)
export type { PasswordValidator } from '../types';
// 工具类 // 工具类
export { CookieUtils, type CookieConfig } from './utils/cookie-utils'; export { CookieUtils, type CookieConfig } from './utils/cookie-utils';
export { HtmlTemplates, type PageConfig } from './utils/html-templates'; export { HtmlTemplates, type PageConfig } from './utils/html-templates';
// 原有组件(向后兼容) // 存储管理器
export { TokenManager } from './token-manager'; export { TokenManager } from './token-manager';
export { SessionManager } from './session-manager'; export { SessionManager } from './session-manager';

View File

@ -1,8 +1,8 @@
import type { Context } from 'hono'; import type { Context } from 'hono';
import type { AuthorizationRequest, LoginCredentials, PasswordValidator, OIDCProviderConfig } from '../../types'; import { SessionManager } from './session-manager';
import { SessionManager } from '../session-manager'; import { CookieUtils } from './utils/cookie-utils';
import { CookieUtils } from '../utils/cookie-utils'; import { HtmlTemplates, type PageConfig } from './utils/html-templates';
import { HtmlTemplates, type PageConfig } from '../utils/html-templates'; import { OIDCProviderConfig, PasswordValidator, AuthorizationRequest, LoginCredentials } from '../types';
/** /**
* *
@ -31,14 +31,18 @@ export interface PasswordAuthConfig {
} }
/** /**
* *
* *
*/ */
export class PasswordAuthStrategy { export class PasswordAuth {
readonly name = 'password'; readonly name = 'password';
private readonly sessionManager: SessionManager; private readonly sessionManager: SessionManager;
private readonly config: PasswordAuthConfig; private readonly config: {
sessionTTL: number;
rememberMeMaxAge: number;
pageConfig?: PageConfig;
};
constructor( constructor(
private readonly oidcConfig: OIDCProviderConfig, private readonly oidcConfig: OIDCProviderConfig,
@ -46,8 +50,8 @@ export class PasswordAuthStrategy {
config: PasswordAuthConfig = {} config: PasswordAuthConfig = {}
) { ) {
this.config = { this.config = {
sessionTTL: 24 * 60 * 60, // 默认24小时 sessionTTL: 24 * 60 * 60, // 24小时
rememberMeMaxAge: 30 * 24 * 60 * 60, // 默认30天 rememberMeMaxAge: 30 * 24 * 60 * 60, // 30天
...config ...config
}; };
this.sessionManager = new SessionManager(oidcConfig.storage, this.config.sessionTTL); this.sessionManager = new SessionManager(oidcConfig.storage, this.config.sessionTTL);
@ -58,9 +62,7 @@ export class PasswordAuthStrategy {
*/ */
async getCurrentUser(c: Context): Promise<string | null> { async getCurrentUser(c: Context): Promise<string | null> {
const sessionId = CookieUtils.getSessionIdFromCookie(c); const sessionId = CookieUtils.getSessionIdFromCookie(c);
if (!sessionId) { if (!sessionId) return null;
return null;
}
try { try {
const session = await this.sessionManager.getSession(sessionId); const session = await this.sessionManager.getSession(sessionId);
@ -76,7 +78,6 @@ export class PasswordAuthStrategy {
*/ */
async handleAuthenticationRequired(c: Context, authRequest: AuthorizationRequest): Promise<Response> { async handleAuthenticationRequired(c: Context, authRequest: AuthorizationRequest): Promise<Response> {
const loginHtml = HtmlTemplates.generateLoginPage(authRequest, this.config.pageConfig); const loginHtml = HtmlTemplates.generateLoginPage(authRequest, this.config.pageConfig);
c.header('Content-Type', 'text/html; charset=utf-8'); c.header('Content-Type', 'text/html; charset=utf-8');
return c.body(loginHtml); return c.body(loginHtml);
} }
@ -86,7 +87,6 @@ export class PasswordAuthStrategy {
*/ */
async authenticate(c: Context): Promise<AuthenticationResult> { async authenticate(c: Context): Promise<AuthenticationResult> {
try { try {
// 解析表单数据
const formData = await c.req.formData(); const formData = await c.req.formData();
const credentials = this.parseCredentials(formData); const credentials = this.parseCredentials(formData);
const authParams = this.extractAuthParams(formData); const authParams = this.extractAuthParams(formData);
@ -94,41 +94,27 @@ export class PasswordAuthStrategy {
// 验证输入 // 验证输入
const validationError = this.validateCredentials(credentials); const validationError = this.validateCredentials(credentials);
if (validationError) { if (validationError) {
return { return { success: false, error: validationError };
success: false,
error: validationError
};
} }
// 验证用户密码 // 验证用户密码
const userId = await this.passwordValidator(credentials.username, credentials.password); const userId = await this.passwordValidator(credentials.username, credentials.password);
if (!userId) { if (!userId) {
return { return { success: false, error: '用户名或密码错误' };
success: false,
error: '用户名或密码错误'
};
} }
// 检查用户是否存在 // 检查用户是否存在
const user = await this.oidcConfig.findUser(userId); const user = await this.oidcConfig.findUser(userId);
if (!user) { if (!user) {
return { return { success: false, error: '用户不存在' };
success: false,
error: '用户不存在'
};
} }
// 创建会话 // 创建会话
const session = await this.sessionManager.createSession( const session = await this.sessionManager.createSession(userId, authParams.client_id, authParams);
userId,
authParams.client_id,
authParams
);
// 设置Cookie
const maxAge = credentials.remember_me const maxAge = credentials.remember_me
? this.config.rememberMeMaxAge! ? this.config.rememberMeMaxAge
: this.config.sessionTTL!; : this.config.sessionTTL;
return { return {
success: true, success: true,
@ -140,13 +126,9 @@ export class PasswordAuthStrategy {
rememberMe: credentials.remember_me rememberMe: credentials.remember_me
} }
}; };
} catch (error) { } catch (error) {
console.error('认证处理失败:', error); console.error('认证处理失败:', error);
return { return { success: false, error: '服务器内部错误,请稍后重试' };
success: false,
error: '服务器内部错误,请稍后重试'
};
} }
} }
@ -164,10 +146,8 @@ export class PasswordAuthStrategy {
} }
} }
// 清除Cookie
CookieUtils.clearSessionCookie(c); CookieUtils.clearSessionCookie(c);
// 处理重定向
const postLogoutRedirectUri = c.req.query('post_logout_redirect_uri'); const postLogoutRedirectUri = c.req.query('post_logout_redirect_uri');
const state = c.req.query('state'); const state = c.req.query('state');
@ -185,20 +165,14 @@ export class PasswordAuthStrategy {
/** /**
* *
*/ */
async handleAuthenticationSuccess( async handleAuthenticationSuccess(c: Context, result: AuthenticationResult): Promise<Response> {
c: Context,
result: AuthenticationResult
): Promise<Response> {
if (!result.success || !result.metadata) { if (!result.success || !result.metadata) {
throw new Error('认证结果无效'); throw new Error('认证结果无效');
} }
const { sessionId, maxAge, authParams } = result.metadata; const { sessionId, maxAge, authParams } = result.metadata;
// 设置会话Cookie
CookieUtils.setSessionCookie(c, sessionId, maxAge); CookieUtils.setSessionCookie(c, sessionId, maxAge);
// 重定向到授权端点
const authUrl = this.buildAuthorizationUrl(authParams); const authUrl = this.buildAuthorizationUrl(authParams);
return c.redirect(authUrl); return c.redirect(authUrl);
} }
@ -211,12 +185,7 @@ export class PasswordAuthStrategy {
result: AuthenticationResult, result: AuthenticationResult,
authRequest: AuthorizationRequest authRequest: AuthorizationRequest
): Promise<Response> { ): Promise<Response> {
const errorHtml = HtmlTemplates.generateLoginPage( const errorHtml = HtmlTemplates.generateLoginPage(authRequest, this.config.pageConfig, result.error);
authRequest,
this.config.pageConfig,
result.error
);
c.header('Content-Type', 'text/html; charset=utf-8'); c.header('Content-Type', 'text/html; charset=utf-8');
return c.body(errorHtml); return c.body(errorHtml);
} }
@ -236,22 +205,10 @@ export class PasswordAuthStrategy {
* *
*/ */
private validateCredentials(credentials: LoginCredentials): string | null { private validateCredentials(credentials: LoginCredentials): string | null {
if (!credentials.username.trim()) { if (!credentials.username.trim()) return '请输入用户名';
return '请输入用户名'; if (!credentials.password) return '请输入密码';
} if (credentials.username.length > 100) return '用户名过长';
if (credentials.password.length > 200) return '密码过长';
if (!credentials.password) {
return '请输入密码';
}
if (credentials.username.length > 100) {
return '用户名过长';
}
if (credentials.password.length > 200) {
return '密码过长';
}
return null; return null;
} }
@ -259,9 +216,7 @@ export class PasswordAuthStrategy {
* URL * URL
*/ */
private buildPostLogoutRedirectUrl(redirectUri: string, state?: string): string { private buildPostLogoutRedirectUrl(redirectUri: string, state?: string): string {
if (!state) { if (!state) return redirectUri;
return redirectUri;
}
const url = new URL(redirectUri); const url = new URL(redirectUri);
url.searchParams.set('state', state); url.searchParams.set('state', state);
@ -271,7 +226,7 @@ export class PasswordAuthStrategy {
/** /**
* URL * URL
*/ */
protected buildAuthorizationUrl(authParams: AuthorizationRequest): string { private buildAuthorizationUrl(authParams: AuthorizationRequest): string {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set('response_type', authParams.response_type); params.set('response_type', authParams.response_type);
params.set('client_id', authParams.client_id); params.set('client_id', authParams.client_id);
@ -289,7 +244,7 @@ export class PasswordAuthStrategy {
/** /**
* *
*/ */
protected extractAuthParams(formData: FormData): AuthorizationRequest { private extractAuthParams(formData: FormData): AuthorizationRequest {
return { return {
response_type: formData.get('response_type')?.toString() || '', response_type: formData.get('response_type')?.toString() || '',
client_id: formData.get('client_id')?.toString() || '', client_id: formData.get('client_id')?.toString() || '',

View File

@ -1,5 +1,5 @@
import type { StorageAdapter } from '../storage';
import type { AuthorizationRequest } from '../types'; import type { AuthorizationRequest } from '../types';
import type { StorageAdapter } from '../storage';
/** /**
* *
@ -19,7 +19,7 @@ interface SessionData {
export class SessionManager { export class SessionManager {
constructor(private storage: StorageAdapter, private sessionTTL: number = 3600) { } constructor(private storage: StorageAdapter, private sessionTTL: number = 3600) { }
private getSessionKey(sessionId: string): string { private getKey(sessionId: string): string {
return `session:${sessionId}`; return `session:${sessionId}`;
} }
@ -59,17 +59,17 @@ export class SessionManager {
} }
async storeSession(sessionId: string, data: any): Promise<void> { async storeSession(sessionId: string, data: any): Promise<void> {
const key = this.getSessionKey(sessionId); const key = this.getKey(sessionId);
await this.storage.set(key, data, this.sessionTTL); await this.storage.set(key, data, this.sessionTTL);
} }
async getSession(sessionId: string): Promise<any> { async getSession(sessionId: string): Promise<any> {
const key = this.getSessionKey(sessionId); const key = this.getKey(sessionId);
return await this.storage.get(key); return await this.storage.get(key);
} }
async deleteSession(sessionId: string): Promise<void> { async deleteSession(sessionId: string): Promise<void> {
const key = this.getSessionKey(sessionId); const key = this.getKey(sessionId);
await this.storage.delete(key); await this.storage.delete(key);
} }

View File

@ -13,7 +13,14 @@ import type { StorageAdapter } from '../storage';
export class TokenManager { export class TokenManager {
constructor(private storage: StorageAdapter) { } constructor(private storage: StorageAdapter) { }
// 生成存储键 /**
* TTL
*/
private calculateTTL(expiresAt: Date): number {
return Math.max(Math.floor((expiresAt.getTime() - Date.now()) / 1000), 1);
}
// 生成令牌存储键
private getTokenKey(type: string, token: string): string { private getTokenKey(type: string, token: string): string {
return `${type}:${token}`; return `${type}:${token}`;
} }
@ -25,27 +32,13 @@ export class TokenManager {
// 授权码管理 // 授权码管理
async storeAuthorizationCode(authCode: AuthorizationCode): Promise<void> { async storeAuthorizationCode(authCode: AuthorizationCode): Promise<void> {
const key = this.getTokenKey('auth_code', authCode.code); const key = this.getTokenKey('auth_code', authCode.code);
const ttl = Math.floor((authCode.expires_at.getTime() - Date.now()) / 1000); const ttl = this.calculateTTL(authCode.expires_at);
await this.storage.set(key, authCode, ttl);
const data = {
...authCode,
expires_at: authCode.expires_at.toISOString(),
created_at: authCode.created_at.toISOString(),
};
await this.storage.set(key, data, Math.max(ttl, 1));
} }
async getAuthorizationCode(code: string): Promise<AuthorizationCode | null> { async getAuthorizationCode(code: string): Promise<AuthorizationCode | null> {
const key = this.getTokenKey('auth_code', code); const key = this.getTokenKey('auth_code', code);
const data = await this.storage.get(key); return await this.storage.get<AuthorizationCode>(key);
if (!data) return null;
return {
...data,
expires_at: new Date(data.expires_at),
created_at: new Date(data.created_at),
};
} }
async deleteAuthorizationCode(code: string): Promise<void> { async deleteAuthorizationCode(code: string): Promise<void> {
@ -57,33 +50,20 @@ export class TokenManager {
async storeAccessToken(token: AccessToken): Promise<void> { async storeAccessToken(token: AccessToken): Promise<void> {
const key = this.getTokenKey('access_token', token.token); const key = this.getTokenKey('access_token', token.token);
const userClientKey = this.getUserClientKey('access_tokens', token.user_id, token.client_id); const userClientKey = this.getUserClientKey('access_tokens', token.user_id, token.client_id);
const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000); const ttl = this.calculateTTL(token.expires_at);
const data = {
...token,
expires_at: token.expires_at.toISOString(),
created_at: token.created_at.toISOString(),
};
// 存储令牌数据 // 存储令牌数据
await this.storage.set(key, data, Math.max(ttl, 1)); await this.storage.set(key, token, ttl);
// 存储用户-客户端索引 // 存储用户-客户端索引
const existingTokens = await this.storage.get<string[]>(userClientKey) || []; const existingTokens = await this.storage.get<string[]>(userClientKey) || [];
existingTokens.push(token.token); existingTokens.push(token.token);
await this.storage.set(userClientKey, existingTokens, Math.max(ttl, 1)); await this.storage.set(userClientKey, existingTokens, ttl);
} }
async getAccessToken(token: string): Promise<AccessToken | null> { async getAccessToken(token: string): Promise<AccessToken | null> {
const key = this.getTokenKey('access_token', token); const key = this.getTokenKey('access_token', token);
const data = await this.storage.get(key); return await this.storage.get<AccessToken>(key);
if (!data) return null;
return {
...data,
expires_at: new Date(data.expires_at),
created_at: new Date(data.created_at),
};
} }
async deleteAccessToken(token: string): Promise<void> { async deleteAccessToken(token: string): Promise<void> {
@ -123,33 +103,20 @@ export class TokenManager {
async storeRefreshToken(token: RefreshToken): Promise<void> { async storeRefreshToken(token: RefreshToken): Promise<void> {
const key = this.getTokenKey('refresh_token', token.token); const key = this.getTokenKey('refresh_token', token.token);
const userClientKey = this.getUserClientKey('refresh_tokens', token.user_id, token.client_id); const userClientKey = this.getUserClientKey('refresh_tokens', token.user_id, token.client_id);
const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000); const ttl = this.calculateTTL(token.expires_at);
const data = {
...token,
expires_at: token.expires_at.toISOString(),
created_at: token.created_at.toISOString(),
};
// 存储令牌数据 // 存储令牌数据
await this.storage.set(key, data, Math.max(ttl, 1)); await this.storage.set(key, token, ttl);
// 存储用户-客户端索引 // 存储用户-客户端索引
const existingTokens = await this.storage.get<string[]>(userClientKey) || []; const existingTokens = await this.storage.get<string[]>(userClientKey) || [];
existingTokens.push(token.token); existingTokens.push(token.token);
await this.storage.set(userClientKey, existingTokens, Math.max(ttl, 1)); await this.storage.set(userClientKey, existingTokens, ttl);
} }
async getRefreshToken(token: string): Promise<RefreshToken | null> { async getRefreshToken(token: string): Promise<RefreshToken | null> {
const key = this.getTokenKey('refresh_token', token); const key = this.getTokenKey('refresh_token', token);
const data = await this.storage.get(key); return await this.storage.get<RefreshToken>(key);
if (!data) return null;
return {
...data,
expires_at: new Date(data.expires_at),
created_at: new Date(data.created_at),
};
} }
async deleteRefreshToken(token: string): Promise<void> { async deleteRefreshToken(token: string): Promise<void> {
@ -175,27 +142,13 @@ export class TokenManager {
// ID令牌管理 // ID令牌管理
async storeIDToken(token: IDToken): Promise<void> { async storeIDToken(token: IDToken): Promise<void> {
const key = this.getTokenKey('id_token', token.token); const key = this.getTokenKey('id_token', token.token);
const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000); const ttl = this.calculateTTL(token.expires_at);
await this.storage.set(key, token, ttl);
const data = {
...token,
expires_at: token.expires_at.toISOString(),
created_at: token.created_at.toISOString(),
};
await this.storage.set(key, data, Math.max(ttl, 1));
} }
async getIDToken(token: string): Promise<IDToken | null> { async getIDToken(token: string): Promise<IDToken | null> {
const key = this.getTokenKey('id_token', token); const key = this.getTokenKey('id_token', token);
const data = await this.storage.get(key); return await this.storage.get<IDToken>(key);
if (!data) return null;
return {
...data,
expires_at: new Date(data.expires_at),
created_at: new Date(data.created_at),
};
} }
async deleteIDToken(token: string): Promise<void> { async deleteIDToken(token: string): Promise<void> {

View File

@ -0,0 +1,109 @@
import type { OIDCError } from '../types';
/**
* OIDC错误处理工厂类
* OIDC错误响应
*/
export class OIDCErrorFactory {
/**
* URI和state
*/
static createAuthError(error: string, description: string, state?: string) {
return {
success: false as const,
error: { error, error_description: description, state },
redirectUri: undefined as string | undefined,
};
}
/**
*
*/
static createTokenError(error: string, description: string) {
return {
success: false as const,
error: { error, error_description: description },
};
}
/**
* API响应
*/
static createSimpleError(error: string, description: string): OIDCError {
return { error, error_description: description };
}
/**
*
*/
static serverError(state?: string) {
return this.createAuthError('server_error', 'Internal server error', state);
}
/**
*
*/
static invalidToken(description = 'Invalid token') {
return this.createSimpleError('invalid_token', description);
}
/**
*
*/
static invalidRequest(description: string) {
return this.createSimpleError('invalid_request', description);
}
/**
*
*/
static invalidClient(description: string) {
return this.createTokenError('invalid_client', description);
}
/**
*
*/
static invalidGrant(description: string) {
return this.createTokenError('invalid_grant', description);
}
/**
*
*/
static unsupportedGrantType(description = 'Grant type not supported') {
return this.createTokenError('unsupported_grant_type', description);
}
/**
*
*/
static invalidScope(description: string) {
return this.createTokenError('invalid_scope', description);
}
/**
*
*/
static loginRequired(description = 'User authentication is required', state?: string) {
return this.createAuthError('login_required', description, state);
}
/**
* PKCE相关错误
*/
static pkceError(description: string, state?: string) {
return this.createAuthError('invalid_request', description, state);
}
/**
* URL参数
*/
static buildErrorResponse(error: OIDCError): URLSearchParams {
const params = new URLSearchParams();
Object.entries(error).forEach(([key, value]) => {
if (value != null) params.set(key, String(value));
});
return params;
}
}

View File

@ -0,0 +1 @@
export { OIDCErrorFactory } from './error-factory';

View File

@ -1,7 +1,7 @@
// 核心类 // 重新导出核心组件
export { OIDCProvider } from './provider'; export { OIDCProvider } from './provider';
// 类型定义 // 重新导出类型定义
export type { export type {
OIDCProviderConfig, OIDCProviderConfig,
OIDCClient, OIDCClient,
@ -20,41 +20,29 @@ export type {
PasswordValidator, PasswordValidator,
} from './types'; } from './types';
// 存储适配器接口和示例实现 // 重新导出存储适配器
export type { StorageAdapter } from './storage'; export type { StorageAdapter } from './storage/adapter';
export { RedisStorageAdapter } from './storage'; export { RedisStorageAdapter } from './storage/redis-adapter';
// Hono中间件 // 重新导出JWT工具
export {
createOIDCProvider,
oidcProvider,
getOIDCProvider
} from './middleware/hono';
// 导出中间件配置类型
export type {
OIDCHonoOptions
} from './middleware/hono';
// 工具类
export { JWTUtils } from './utils/jwt'; export { JWTUtils } from './utils/jwt';
export { PKCEUtils } from './utils/pkce';
export { ValidationUtils } from './utils/validation';
// 认证模块 // 重新导出验证工具
export { ValidationUtils } from './utils/validation';
export { PKCEUtils } from './utils/pkce';
// 重新导出认证相关
export { export {
AuthManager, PasswordAuth,
PasswordAuthStrategy, type AuthenticationResult,
type PasswordAuthConfig,
TokenManager,
SessionManager,
CookieUtils, CookieUtils,
HtmlTemplates, HtmlTemplates,
SessionManager, type PageConfig,
TokenManager type CookieConfig,
} from './auth'; } from './auth';
export type { // 重新导出中间件
AuthManagerConfig, export { createOIDCProvider, oidcProvider, getOIDCProvider } from './middleware/hono';
PasswordAuthConfig,
AuthenticationResult,
CookieConfig,
PageConfig
} from './auth';

View File

@ -1,313 +1,62 @@
import { Hono } from 'hono'; import { Hono } from 'hono';
import type { Context, Next } from 'hono'; import type { Context, Next } from 'hono';
import { OIDCProvider } from '../provider'; import { OIDCProvider } from '../provider';
import type { OIDCProviderConfig, AuthorizationRequest, TokenRequest } from '../types'; import type { OIDCProviderConfig } from '../types';
import { AuthManager, type PasswordValidator } from '../auth';
/**
* OIDC Provider配置选项
*/
export interface OIDCHonoOptions {
/** OIDC Provider配置 */
config: OIDCProviderConfig;
/** 密码验证器 - 用于验证用户名和密码 */
passwordValidator: PasswordValidator;
/** 认证配置选项 */
authConfig?: {
/** 会话TTL */
sessionTTL?: number;
/** 登录页面标题 */
loginPageTitle?: string;
/** 品牌名称 */
brandName?: string;
/** 品牌Logo URL */
logoUrl?: string;
};
}
/** /**
* OIDC Provider Hono应用 * OIDC Provider Hono应用
*/ */
export function createOIDCProvider(options: OIDCHonoOptions): Hono { export function createOIDCProvider(config: OIDCProviderConfig): Hono {
const { config, passwordValidator, authConfig = {} } = options;
// 创建认证管理器
const authManager = new AuthManager(
config,
passwordValidator,
{
sessionTTL: authConfig.sessionTTL,
pageConfig: {
title: authConfig.loginPageTitle,
brandName: authConfig.brandName,
logoUrl: authConfig.logoUrl
}
}
);
const app = new Hono(); const app = new Hono();
const provider = new OIDCProvider(config); const provider = new OIDCProvider(config);
// 登录端点 // 登录端点
app.post('/login', async (c: Context) => { app.post('/login', async (c: Context) => {
// 从表单中提取授权参数 return await provider.handleLogin(c);
const formData = await c.req.formData();
const authRequest = {
response_type: formData.get('response_type')?.toString() || '',
client_id: formData.get('client_id')?.toString() || '',
redirect_uri: formData.get('redirect_uri')?.toString() || '',
scope: formData.get('scope')?.toString() || '',
state: formData.get('state')?.toString(),
nonce: formData.get('nonce')?.toString(),
code_challenge: formData.get('code_challenge')?.toString(),
code_challenge_method: formData.get('code_challenge_method')?.toString() as 'plain' | 'S256' | undefined,
};
return await authManager.handleLogin(c, authRequest);
}); });
// 登出端点 // 登出端点
app.get('/logout', async (c: Context) => { app.get('/logout', async (c: Context) => {
return await authManager.logout(c); return await provider.handleLogout(c);
}); });
app.post('/logout', async (c: Context) => { app.post('/logout', async (c: Context) => {
return await authManager.logout(c); return await provider.handleLogout(c);
}); });
// 发现文档端点 // 发现文档端点 - 直接调用核心方法
app.get('/.well-known/openid-configuration', async (c: Context) => { app.get('/.well-known/openid-configuration', (c: Context) => {
const discovery = provider.getDiscoveryDocument(); return c.json(provider.getDiscoveryDocument());
return c.json(discovery);
}); });
// JWKS端点 // JWKS端点 - 直接调用核心方法
app.get('/.well-known/jwks.json', async (c: Context) => { app.get('/.well-known/jwks.json', async (c: Context) => {
const jwks = await provider.getJWKS(); return c.json(await provider.getJWKS());
return c.json(jwks);
}); });
// 授权端点 - 使用认证管理器 // 授权端点
app.get('/auth', async (c: Context) => { app.get('/auth', async (c: Context) => {
const query = c.req.query(); return await provider.handleAuthorization(c);
const authRequest: AuthorizationRequest = {
response_type: query.response_type || '',
client_id: query.client_id || '',
redirect_uri: query.redirect_uri || '',
scope: query.scope || '',
state: query.state,
nonce: query.nonce,
code_challenge: query.code_challenge,
code_challenge_method: query.code_challenge_method as 'plain' | 'S256' | undefined,
prompt: query.prompt,
max_age: query.max_age ? parseInt(query.max_age) : undefined,
id_token_hint: query.id_token_hint,
login_hint: query.login_hint,
acr_values: query.acr_values,
};
// 检查用户认证状态
const userId = await authManager.getCurrentUser(c);
if (!userId) {
// 用户未认证,显示登录页面
return await authManager.handleAuthenticationRequired(c, authRequest);
}
// 用户已认证,处理授权请求
const result = await provider.handleAuthorizationRequest(authRequest, userId);
if (!result.success) {
const error = result.error;
const errorParams = new URLSearchParams({
error: error.error,
...(error.error_description && { error_description: error.error_description }),
...(error.error_uri && { error_uri: error.error_uri }),
...(error.state && { state: error.state }),
});
const redirectUri = result.redirectUri || query.redirect_uri;
if (redirectUri) {
return c.redirect(`${redirectUri}?${errorParams.toString()}`);
} else {
c.status(400);
return c.json({
error: error.error,
error_description: error.error_description,
});
}
}
// 成功生成授权码,重定向回客户端
const params = new URLSearchParams({
code: result.code,
...(query.state && { state: query.state }),
});
return c.redirect(`${result.redirectUri}?${params.toString()}`);
}); });
// 令牌端点 // 令牌端点
app.post('/token', async (c: Context) => { app.post('/token', async (c: Context) => {
const body = await c.req.formData(); return await provider.handleToken(c);
// 将可选字段的类型处理为可选的而不是undefined
const clientId = body.get('client_id')?.toString();
const tokenRequest: TokenRequest = {
grant_type: body.get('grant_type')?.toString() || '',
client_id: clientId || '',
};
// 可选参数,只有存在时才添加
const code = body.get('code')?.toString();
if (code) tokenRequest.code = code;
const redirectUri = body.get('redirect_uri')?.toString();
if (redirectUri) tokenRequest.redirect_uri = redirectUri;
const clientSecret = body.get('client_secret')?.toString();
if (clientSecret) tokenRequest.client_secret = clientSecret;
const refreshToken = body.get('refresh_token')?.toString();
if (refreshToken) tokenRequest.refresh_token = refreshToken;
const codeVerifier = body.get('code_verifier')?.toString();
if (codeVerifier) tokenRequest.code_verifier = codeVerifier;
const scope = body.get('scope')?.toString();
if (scope) tokenRequest.scope = scope;
// 客户端认证
const authHeader = c.req.header('Authorization');
if (authHeader?.startsWith('Basic ')) {
const decoded = atob(authHeader.substring(6));
const [headerClientId, headerClientSecret] = decoded.split(':');
if (headerClientId) {
tokenRequest.client_id = headerClientId;
}
if (headerClientSecret) {
tokenRequest.client_secret = headerClientSecret;
}
}
// 请求令牌
const result = await provider.handleTokenRequest(tokenRequest);
if (!result.success) {
c.status(400);
return c.json({
error: result.error.error,
error_description: result.error.error_description,
});
}
return c.json(result.response);
}); });
// 用户信息端点 // 用户信息端点
app.get('/userinfo', async (c: Context) => { app.get('/userinfo', async (c: Context) => {
const authHeader = c.req.header('Authorization'); return await provider.handleUserInfo(c);
if (!authHeader || !authHeader.startsWith('Bearer ')) {
c.status(401);
c.header('WWW-Authenticate', 'Bearer');
return c.json({
error: 'invalid_token',
error_description: '无效的访问令牌',
});
}
const accessToken = authHeader.substring(7);
const result = await provider.getUserInfo(accessToken);
if (!result.success) {
c.status(401);
c.header('WWW-Authenticate', `Bearer error="${result.error.error}"`);
return c.json({
error: result.error.error,
error_description: result.error.error_description,
});
}
return c.json(result.user);
}); });
// 令牌撤销端点 // 令牌撤销端点
app.post('/revoke', async (c: Context) => { app.post('/revoke', async (c: Context) => {
const body = await c.req.formData(); return await provider.handleRevoke(c);
const token = body.get('token')?.toString() || '';
const tokenTypeHint = body.get('token_type_hint')?.toString();
const clientId = body.get('client_id')?.toString();
const clientSecret = body.get('client_secret')?.toString();
if (!token) {
c.status(400);
return c.json({
error: 'invalid_request',
error_description: '缺少token参数',
});
}
// 客户端认证
let authClientId = clientId;
let authClientSecret = clientSecret;
const authHeader = c.req.header('Authorization');
if (authHeader?.startsWith('Basic ')) {
const decoded = atob(authHeader.substring(6));
const [id, secret] = decoded.split(':');
authClientId = id;
authClientSecret = secret;
}
// 撤销令牌
const result = await provider.revokeToken(token, tokenTypeHint);
if (!result.success && result.error) {
c.status(400);
return c.json({
error: result.error.error,
error_description: result.error.error_description,
});
}
// 撤销成功
c.status(200);
return c.body(null);
}); });
// 令牌内省端点 // 令牌内省端点
app.post('/introspect', async (c: Context) => { app.post('/introspect', async (c: Context) => {
const body = await c.req.formData(); return await provider.handleIntrospect(c);
const token = body.get('token')?.toString() || '';
const tokenTypeHint = body.get('token_type_hint')?.toString();
const clientId = body.get('client_id')?.toString();
const clientSecret = body.get('client_secret')?.toString();
if (!token) {
c.status(400);
return c.json({
error: 'invalid_request',
error_description: '缺少token参数',
});
}
// 客户端认证
let authClientId = clientId;
let authClientSecret = clientSecret;
const authHeader = c.req.header('Authorization');
if (authHeader?.startsWith('Basic ')) {
const decoded = atob(authHeader.substring(6));
const [id, secret] = decoded.split(':');
authClientId = id;
authClientSecret = secret;
}
// 内省令牌
const result = await provider.introspectToken(token);
// 返回内省结果
return c.json(result);
}); });
// 返回应用实例 // 返回应用实例

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,24 @@ export interface OIDCProviderConfig {
findUser: (userId: string) => Promise<OIDCUser | null>; findUser: (userId: string) => Promise<OIDCUser | null>;
/** 获取客户端的回调函数 */ /** 获取客户端的回调函数 */
findClient: (clientId: string) => Promise<OIDCClient | null>; findClient: (clientId: string) => Promise<OIDCClient | null>;
/** 认证配置选项 - 必须配置 */
authConfig: {
/** 密码验证器 - 用于验证用户名和密码,必须配置 */
passwordValidator: PasswordValidator;
/** 会话TTL */
sessionTTL?: number;
/** 页面配置 */
pageConfig?: {
/** 登录页面标题 */
title?: string;
/** 品牌名称 */
brandName?: string;
/** 品牌Logo URL */
logoUrl?: string;
};
/** 记住我功能的最大时长(秒) */
rememberMeMaxAge?: number;
};
/** 令牌过期时间配置 */ /** 令牌过期时间配置 */
tokenTTL?: { tokenTTL?: {
accessToken?: number; // 默认 3600 秒 accessToken?: number; // 默认 3600 秒

View File

@ -1,10 +1,10 @@
{ {
"name": "@repo/tus", "name": "@repo/tus",
"version": "1.0.0", "version": "1.0.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"private": true, "private": true,
"exports": {
".": "./src/index.ts"
},
"scripts": { "scripts": {
"build": "tsup", "build": "tsup",
"dev": "tsup --watch", "dev": "tsup --watch",

View File

@ -1,10 +0,0 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
splitting: false,
sourcemap: true,
clean: false,
dts: true
});

View File

@ -47,9 +47,15 @@ importers:
'@repo/oidc-provider': '@repo/oidc-provider':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/oidc-provider version: link:../../packages/oidc-provider
'@repo/tus':
specifier: workspace:*
version: link:../../packages/tus
'@trpc/server': '@trpc/server':
specifier: 11.1.2 specifier: 11.1.2
version: 11.1.2(typescript@5.8.3) version: 11.1.2(typescript@5.8.3)
dayjs:
specifier: ^1.11.12
version: 1.11.13
hono: hono:
specifier: ^4.7.10 specifier: ^4.7.10
version: 4.7.10 version: 4.7.10
@ -65,9 +71,6 @@ importers:
nanoid: nanoid:
specifier: ^5.1.5 specifier: ^5.1.5
version: 5.1.5 version: 5.1.5
nanoid-cjs:
specifier: ^0.0.7
version: 0.0.7
node-cron: node-cron:
specifier: ^4.0.7 specifier: ^4.0.7
version: 4.0.7 version: 4.0.7
@ -77,6 +80,9 @@ importers:
superjson: superjson:
specifier: ^2.2.2 specifier: ^2.2.2
version: 2.2.2 version: 2.2.2
transliteration:
specifier: ^2.3.5
version: 2.3.5
valibot: valibot:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0(typescript@5.8.3) version: 1.1.0(typescript@5.8.3)
@ -98,7 +104,7 @@ importers:
version: 7.1.1 version: 7.1.1
vitest: vitest:
specifier: ^3.1.4 specifier: ^3.1.4
version: 3.1.4(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) version: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
apps/web: apps/web:
dependencies: dependencies:
@ -1241,72 +1247,85 @@ packages:
resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.1.0': '@img/sharp-libvips-linux-arm@1.1.0':
resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.1.0': '@img/sharp-libvips-linux-ppc64@1.1.0':
resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.1.0': '@img/sharp-libvips-linux-s390x@1.1.0':
resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.1.0': '@img/sharp-libvips-linux-x64@1.1.0':
resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.1.0': '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.1.0': '@img/sharp-libvips-linuxmusl-x64@1.1.0':
resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.34.2': '@img/sharp-linux-arm64@0.34.2':
resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.2': '@img/sharp-linux-arm@0.34.2':
resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.2': '@img/sharp-linux-s390x@0.34.2':
resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.2': '@img/sharp-linux-x64@0.34.2':
resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.2': '@img/sharp-linuxmusl-arm64@0.34.2':
resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.2': '@img/sharp-linuxmusl-x64@0.34.2':
resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.34.2': '@img/sharp-wasm32@0.34.2':
resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==}
@ -1394,24 +1413,28 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@15.3.2': '@next/swc-linux-arm64-musl@15.3.2':
resolution: {integrity: sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==} resolution: {integrity: sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@15.3.2': '@next/swc-linux-x64-gnu@15.3.2':
resolution: {integrity: sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==} resolution: {integrity: sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@15.3.2': '@next/swc-linux-x64-musl@15.3.2':
resolution: {integrity: sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==} resolution: {integrity: sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@15.3.2': '@next/swc-win32-arm64-msvc@15.3.2':
resolution: {integrity: sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==} resolution: {integrity: sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==}
@ -1851,56 +1874,67 @@ packages:
resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==} resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.41.0': '@rollup/rollup-linux-arm-musleabihf@4.41.0':
resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==} resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.41.0': '@rollup/rollup-linux-arm64-gnu@4.41.0':
resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==} resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.41.0': '@rollup/rollup-linux-arm64-musl@4.41.0':
resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==} resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loongarch64-gnu@4.41.0': '@rollup/rollup-linux-loongarch64-gnu@4.41.0':
resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==} resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-powerpc64le-gnu@4.41.0': '@rollup/rollup-linux-powerpc64le-gnu@4.41.0':
resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==} resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.41.0': '@rollup/rollup-linux-riscv64-gnu@4.41.0':
resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==} resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.41.0': '@rollup/rollup-linux-riscv64-musl@4.41.0':
resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==} resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.41.0': '@rollup/rollup-linux-s390x-gnu@4.41.0':
resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==} resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.41.0': '@rollup/rollup-linux-x64-gnu@4.41.0':
resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==} resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.41.0': '@rollup/rollup-linux-x64-musl@4.41.0':
resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==} resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.41.0': '@rollup/rollup-win32-arm64-msvc@4.41.0':
resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==} resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==}
@ -2237,24 +2271,28 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@swc/core-linux-arm64-musl@1.11.29': '@swc/core-linux-arm64-musl@1.11.29':
resolution: {integrity: sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==} resolution: {integrity: sha512-PwjB10BC0N+Ce7RU/L23eYch6lXFHz7r3NFavIcwDNa/AAqywfxyxh13OeRy+P0cg7NDpWEETWspXeI4Ek8otw==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@swc/core-linux-x64-gnu@1.11.29': '@swc/core-linux-x64-gnu@1.11.29':
resolution: {integrity: sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==} resolution: {integrity: sha512-i62vBVoPaVe9A3mc6gJG07n0/e7FVeAvdD9uzZTtGLiuIfVfIBta8EMquzvf+POLycSk79Z6lRhGPZPJPYiQaA==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@swc/core-linux-x64-musl@1.11.29': '@swc/core-linux-x64-musl@1.11.29':
resolution: {integrity: sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==} resolution: {integrity: sha512-YER0XU1xqFdK0hKkfSVX1YIyCvMDI7K07GIpefPvcfyNGs38AXKhb2byySDjbVxkdl4dycaxxhRyhQ2gKSlsFQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@swc/core-win32-arm64-msvc@1.11.29': '@swc/core-win32-arm64-msvc@1.11.29':
resolution: {integrity: sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==} resolution: {integrity: sha512-po+WHw+k9g6FAg5IJ+sMwtA/fIUL3zPQ4m/uJgONBATCVnDDkyW6dBA49uHNVtSEvjvhuD8DVWdFP847YTcITw==}
@ -2333,24 +2371,28 @@ packages:
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.7': '@tailwindcss/oxide-linux-arm64-musl@4.1.7':
resolution: {integrity: sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==} resolution: {integrity: sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.7': '@tailwindcss/oxide-linux-x64-gnu@4.1.7':
resolution: {integrity: sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==} resolution: {integrity: sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.7': '@tailwindcss/oxide-linux-x64-musl@4.1.7':
resolution: {integrity: sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==} resolution: {integrity: sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.7': '@tailwindcss/oxide-wasm32-wasi@4.1.7':
resolution: {integrity: sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==} resolution: {integrity: sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==}
@ -2856,9 +2898,6 @@ packages:
buffer-crc32@0.2.13: buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
buffer@5.7.1: buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
@ -3023,9 +3062,6 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'} engines: {node: '>=12.5.0'}
combine-errors@3.0.3:
resolution: {integrity: sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==}
combined-stream@1.0.8: combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -3139,9 +3175,6 @@ packages:
csstype@3.1.3: csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
custom-error-instance@2.1.1:
resolution: {integrity: sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==}
data-uri-to-buffer@6.0.2: data-uri-to-buffer@6.0.2:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@ -3291,10 +3324,6 @@ packages:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'} engines: {node: '>=12'}
dotenv@16.5.0:
resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
engines: {node: '>=12'}
dunder-proto@1.0.1: dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -3522,6 +3551,10 @@ packages:
fast-safe-stringify@2.1.1: fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
fast-xml-parser@4.4.1:
resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==}
hasBin: true
fast-xml-parser@4.5.3: fast-xml-parser@4.5.3:
resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
hasBin: true hasBin: true
@ -4082,9 +4115,6 @@ packages:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'} engines: {node: '>=10'}
js-base64@3.7.7:
resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==}
js-tokens@4.0.0: js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -4185,24 +4215,28 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.1: lightningcss-linux-arm64-musl@1.30.1:
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.1: lightningcss-linux-x64-gnu@1.30.1:
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.1: lightningcss-linux-x64-musl@1.30.1:
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.1: lightningcss-win32-arm64-msvc@1.30.1:
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
@ -4235,24 +4269,6 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'} engines: {node: '>=10'}
lodash._baseiteratee@4.7.0:
resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==}
lodash._basetostring@4.12.0:
resolution: {integrity: sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==}
lodash._baseuniq@4.6.0:
resolution: {integrity: sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==}
lodash._createset@4.0.3:
resolution: {integrity: sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==}
lodash._root@3.0.1:
resolution: {integrity: sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==}
lodash._stringtopath@4.8.0:
resolution: {integrity: sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==}
lodash.camelcase@4.3.0: lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
@ -4275,9 +4291,6 @@ packages:
lodash.throttle@4.1.1: lodash.throttle@4.1.1:
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
lodash.uniqby@4.5.0:
resolution: {integrity: sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==}
lodash@4.17.21: lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
@ -4434,9 +4447,6 @@ packages:
mz@2.7.0: mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
nanoid-cjs@0.0.7:
resolution: {integrity: sha512-z72crZ0JcTb5s40Pm9Vk99qfEw9Oe1qyVjK/kpelCKyZDH8YTX4HejSfp54PMJT8F5rmsiBpG6wfVAGAhLEFhA==}
nanoid@3.3.11: nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -4768,9 +4778,6 @@ packages:
prop-types@15.8.1: prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
proper-lockfile@4.1.2:
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
proxy-agent@6.5.0: proxy-agent@6.5.0:
resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
@ -4790,9 +4797,6 @@ packages:
resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
engines: {node: '>=6'} engines: {node: '>=6'}
querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@ -4889,9 +4893,6 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
resolve-from@4.0.0: resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -4916,10 +4917,6 @@ packages:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'} engines: {node: '>=8'}
retry@0.12.0:
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
engines: {node: '>= 4'}
reusify@1.1.0: reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'} engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@ -5460,10 +5457,6 @@ packages:
resolution: {integrity: sha512-iHuaNcq5GZZnr3XDZNuu2LSyCzAOPwDuo5Qt+q64DfsTP1i3T2bKfxJhni2ZQxsvAoxRbuUK5QetJki4qc5aYA==} resolution: {integrity: sha512-iHuaNcq5GZZnr3XDZNuu2LSyCzAOPwDuo5Qt+q64DfsTP1i3T2bKfxJhni2ZQxsvAoxRbuUK5QetJki4qc5aYA==}
hasBin: true hasBin: true
tus-js-client@4.3.1:
resolution: {integrity: sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg==}
engines: {node: '>=18'}
tw-animate-css@1.3.0: tw-animate-css@1.3.0:
resolution: {integrity: sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==} resolution: {integrity: sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==}
@ -5571,9 +5564,6 @@ packages:
uri-js@4.4.1: uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
use-callback-ref@1.3.3: use-callback-ref@1.3.3:
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -8411,8 +8401,6 @@ snapshots:
buffer-crc32@0.2.13: {} buffer-crc32@0.2.13: {}
buffer-from@1.1.2: {}
buffer@5.7.1: buffer@5.7.1:
dependencies: dependencies:
base64-js: 1.5.1 base64-js: 1.5.1
@ -8598,11 +8586,6 @@ snapshots:
color-string: 1.9.1 color-string: 1.9.1
optional: true optional: true
combine-errors@3.0.3:
dependencies:
custom-error-instance: 2.1.1
lodash.uniqby: 4.5.0
combined-stream@1.0.8: combined-stream@1.0.8:
dependencies: dependencies:
delayed-stream: 1.0.0 delayed-stream: 1.0.0
@ -8716,8 +8699,6 @@ snapshots:
csstype@3.1.3: {} csstype@3.1.3: {}
custom-error-instance@2.1.1: {}
data-uri-to-buffer@6.0.2: {} data-uri-to-buffer@6.0.2: {}
data-view-buffer@1.0.2: data-view-buffer@1.0.2:
@ -8859,8 +8840,6 @@ snapshots:
dotenv@16.4.5: {} dotenv@16.4.5: {}
dotenv@16.5.0: {}
dunder-proto@1.0.1: dunder-proto@1.0.1:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
@ -9273,6 +9252,10 @@ snapshots:
fast-safe-stringify@2.1.1: {} fast-safe-stringify@2.1.1: {}
fast-xml-parser@4.4.1:
dependencies:
strnum: 1.1.2
fast-xml-parser@4.5.3: fast-xml-parser@4.5.3:
dependencies: dependencies:
strnum: 1.1.2 strnum: 1.1.2
@ -9883,8 +9866,6 @@ snapshots:
joycon@3.1.1: {} joycon@3.1.1: {}
js-base64@3.7.7: {}
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
js-yaml@4.1.0: js-yaml@4.1.0:
@ -10022,25 +10003,6 @@ snapshots:
dependencies: dependencies:
p-locate: 5.0.0 p-locate: 5.0.0
lodash._baseiteratee@4.7.0:
dependencies:
lodash._stringtopath: 4.8.0
lodash._basetostring@4.12.0: {}
lodash._baseuniq@4.6.0:
dependencies:
lodash._createset: 4.0.3
lodash._root: 3.0.1
lodash._createset@4.0.3: {}
lodash._root@3.0.1: {}
lodash._stringtopath@4.8.0:
dependencies:
lodash._basetostring: 4.12.0
lodash.camelcase@4.3.0: {} lodash.camelcase@4.3.0: {}
lodash.defaults@4.2.0: {} lodash.defaults@4.2.0: {}
@ -10055,11 +10017,6 @@ snapshots:
lodash.throttle@4.1.1: {} lodash.throttle@4.1.1: {}
lodash.uniqby@4.5.0:
dependencies:
lodash._baseiteratee: 4.7.0
lodash._baseuniq: 4.6.0
lodash@4.17.21: {} lodash@4.17.21: {}
log-symbols@3.0.0: log-symbols@3.0.0:
@ -10207,10 +10164,6 @@ snapshots:
object-assign: 4.1.1 object-assign: 4.1.1
thenify-all: 1.6.0 thenify-all: 1.6.0
nanoid-cjs@0.0.7:
dependencies:
nanoid: 5.1.5
nanoid@3.3.11: {} nanoid@3.3.11: {}
nanoid@5.1.5: {} nanoid@5.1.5: {}
@ -10562,12 +10515,6 @@ snapshots:
object-assign: 4.1.1 object-assign: 4.1.1
react-is: 16.13.1 react-is: 16.13.1
proper-lockfile@4.1.2:
dependencies:
graceful-fs: 4.2.11
retry: 0.12.0
signal-exit: 3.0.7
proxy-agent@6.5.0: proxy-agent@6.5.0:
dependencies: dependencies:
agent-base: 7.1.3 agent-base: 7.1.3
@ -10596,8 +10543,6 @@ snapshots:
split-on-first: 1.1.0 split-on-first: 1.1.0
strict-uri-encode: 2.0.0 strict-uri-encode: 2.0.0
querystringify@2.2.0: {}
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
quick-lru@7.0.1: {} quick-lru@7.0.1: {}
@ -10701,8 +10646,6 @@ snapshots:
require-directory@2.1.1: {} require-directory@2.1.1: {}
requires-port@1.0.0: {}
resolve-from@4.0.0: {} resolve-from@4.0.0: {}
resolve-from@5.0.0: {} resolve-from@5.0.0: {}
@ -10726,8 +10669,6 @@ snapshots:
onetime: 5.1.2 onetime: 5.1.2
signal-exit: 3.0.7 signal-exit: 3.0.7
retry@0.12.0: {}
reusify@1.1.0: {} reusify@1.1.0: {}
rimraf@3.0.2: rimraf@3.0.2:
@ -11358,16 +11299,6 @@ snapshots:
turbo-windows-64: 2.5.3 turbo-windows-64: 2.5.3
turbo-windows-arm64: 2.5.3 turbo-windows-arm64: 2.5.3
tus-js-client@4.3.1:
dependencies:
buffer-from: 1.1.2
combine-errors: 3.0.3
is-stream: 2.0.1
js-base64: 3.7.7
lodash.throttle: 4.1.1
proper-lockfile: 4.1.2
url-parse: 1.5.10
tw-animate-css@1.3.0: {} tw-animate-css@1.3.0: {}
type-check@0.4.0: type-check@0.4.0:
@ -11478,11 +11409,6 @@ snapshots:
dependencies: dependencies:
punycode: 2.3.1 punycode: 2.3.1
url-parse@1.5.10:
dependencies:
querystringify: 2.2.0
requires-port: 1.0.0
use-callback-ref@1.3.3(@types/react@19.1.5)(react@19.1.0): use-callback-ref@1.3.3(@types/react@19.1.5)(react@19.1.0):
dependencies: dependencies:
react: 19.1.0 react: 19.1.0
@ -11561,7 +11487,7 @@ snapshots:
tsx: 4.19.4 tsx: 4.19.4
yaml: 2.8.0 yaml: 2.8.0
vitest@3.1.4(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0): vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0):
dependencies: dependencies:
'@vitest/expect': 3.1.4 '@vitest/expect': 3.1.4
'@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)) '@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0))
@ -11585,6 +11511,7 @@ snapshots:
vite-node: 3.1.4(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) vite-node: 3.1.4(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/debug': 4.1.12
'@types/node': 22.15.21 '@types/node': 22.15.21
transitivePeerDependencies: transitivePeerDependencies:
- jiti - jiti