05281221
This commit is contained in:
parent
dc85586930
commit
8956994325
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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';
|
|
@ -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() || '',
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export { OIDCErrorFactory } from './error-factory';
|
|
@ -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';
|
|
|
@ -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
|
@ -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 秒
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
|
||||||
});
|
|
193
pnpm-lock.yaml
193
pnpm-lock.yaml
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue