diff --git a/.github/workflows/frontend-bundle-analysis.yml b/.github/workflows/frontend-bundle-analysis.yml
index d1378ef0..76437eae 100644
--- a/.github/workflows/frontend-bundle-analysis.yml
+++ b/.github/workflows/frontend-bundle-analysis.yml
@@ -12,7 +12,12 @@ defaults:
jobs:
analyze:
+ name: Frontend Analyze Bundle
runs-on: ubuntu-latest
+ env:
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
+ TURBO_TEAM: ${{ vars.TURBO_TEAM }}
+
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.12.0
@@ -21,6 +26,14 @@ jobs:
- uses: actions/checkout@v4
+ - name: Cache turbo build setup
+ uses: actions/cache@v3
+ with:
+ path: .turbo
+ key: ${{ runner.os }}-turbo-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-turbo-
+
- uses: pnpm/action-setup@v2.4.0
- name: Install Node.js
@@ -30,7 +43,7 @@ jobs:
cache: 'pnpm'
- name: Install Dependencies
- run: pnpm i
+ run: pnpm install
- name: Restore Next.js Build
uses: actions/cache@v3
@@ -46,7 +59,7 @@ jobs:
- name: Build Next.js App
# change this if your site requires a custom build command
- run: cd packages/frontend && ./node_modules/.bin/next build
+ run: pnpm run build:frontend
# Here's the first place where next-bundle-analysis' own script is used
# This step pulls the raw bundle stats for the current bundle
diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml
new file mode 100644
index 00000000..db0ff76a
--- /dev/null
+++ b/.github/workflows/test-suite.yml
@@ -0,0 +1,50 @@
+name: Test Suite
+
+on:
+ push:
+ branches: ["v3","v3-dev"]
+ pull_request:
+ types: [opened, synchronize]
+
+jobs:
+ test:
+ name: Build and Test
+ timeout-minutes: 15
+ runs-on: ubuntu-latest
+ # To use Remote Caching, uncomment the next lines and follow the steps below.
+ env:
+ TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
+ TURBO_TEAM: ${{ vars.TURBO_TEAM }}
+
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 2
+
+ - name: Cache turbo build setup
+ uses: actions/cache@v3
+ with:
+ path: .turbo
+ key: ${{ runner.os }}-turbo-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-turbo-
+
+ - uses: pnpm/action-setup@v2.0.1
+ with:
+ version: 6.32.2
+
+ - name: Setup Node.js environment
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ cache: 'pnpm'
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Build
+ run: pnpm build
+
+ - name: Test
+ run: pnpm test
diff --git a/README.md b/README.md
index 8912941f..4e3a9f85 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ English
-
+
CAW 是一个自托管网络应用程序,提供开箱即用的用户管理,包括后台界面以及可配置的支付计划和相关支付界面。
@@ -35,7 +35,7 @@ English
| | |
|---------------------------------------------------------------------------|---------------------------------------------------------------------------|
-| | |
+| | |
| | |
## 开始
@@ -50,6 +50,18 @@ English
| [V2](https://github.com/AprilNEA/ChatGPT-Admin-Web/tree/v2) | 弃用 | 存在设计缺陷 |
| [V1](https://github.com/AprilNEA/ChatGPT-Admin-Web/tree/main) | 不再更新 | Redis数据库,可拓展性较差 |
+[更新日志](https://manual.sku.moe/project/chatgpt-admin-web/update-log)
+
+## 技术栈
+
+| Part | Tech |
+|:---------|:-------------------|
+| Frontend | Next.js |
+| Backend | Nest.js |
+| Database | PostgreSQL & Redis |
+
+更多技术细节:[开发手册](https://manual.sku.moe/project/chatgpt-admin-web/development)。
+
## 项目动态

@@ -62,7 +74,6 @@ English
-
## 捐赠
感谢您的激励,能让该项目持续发展。
diff --git a/README_EN.md b/README_EN.md
index 0e7f3775..b1ef1cbe 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -15,7 +15,7 @@
-
+
CAW(ChatGPT-Admin-Web) is a self-hosted web application that provides out-of-the-box user management including a back-end interface as well as configurable payment plans and related payment interfaces.
@@ -51,6 +51,19 @@ For configuration and installation, see [documentation](https://manual.sku.moe/p
| [V2](https://github.com/AprilNEA/ChatGPT-Admin-Web/tree/v2) | :x: Deprecated | Flawed design |
| [V1](https://github.com/AprilNEA/ChatGPT-Admin-Web/tree/main) | :o: No longer updated | Redis database, less scalable |
+[Update Log](https://manual.sku.moe/project/chatgpt-admin-web/update-log)
+
+
+## 技术栈
+
+| Part | Tech |
+|:---------|:-------------------|
+| Frontend | Next.js |
+| Backend | Nest.js |
+| Database | PostgreSQL & Redis |
+
+You can go to [Developer Manual](https://manual.sku.moe/project/chatgpt-admin-web/development) for more technical details.
+
## Project Status

diff --git a/package.json b/package.json
index a3085c8e..8cc96b22 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,11 @@
],
"scripts": {
"build": "turbo run build",
+ "build:frontend": "turbo run build --filter frontend",
"dev": "turbo run dev --parallel",
"start": "turbo run start",
"lint": "turbo run lint",
+ "test": "vitest run",
"prettier": "prettier --write .",
"db:init": "prisma db push && prisma db seed",
"db:generate": "prisma generate"
@@ -26,10 +28,14 @@
"@trivago/prettier-plugin-sort-imports": "latest",
"@types/node": "latest",
"prettier": "latest",
+ "prisma": "5.6.0",
"ts-node": "latest",
"turbo": "latest",
"typescript": "latest",
- "prisma": "5.6.0"
+ "vite": "^5.0.7",
+ "vite-tsconfig-paths": "^4.2.2",
+ "vitest": "^1.0.2",
+ "zod": "latest"
},
"prisma": {
"schema": "prisma/schema.prisma",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index b06589de..99de9c29 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -10,12 +10,7 @@
"dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
- "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
- "test": "jest",
- "test:watch": "jest --watch",
- "test:cov": "jest --coverage",
- "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
- "test:e2e": "jest --config test/jest-e2e.json"
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
},
"dependencies": {
"@liaoliaots/nestjs-redis": "^9.0.5",
@@ -38,7 +33,8 @@
"prisma-extension-pagination": "^0.5.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
- "spark-md5": "^3.0.2"
+ "spark-md5": "^3.0.2",
+ "zod": "*"
},
"devDependencies": {
"@nestjs/cli": "^10.2.1",
diff --git a/packages/backend/src/common/config/config.service.ts b/packages/backend/src/common/config/config.service.ts
index 3fd0f4cb..d40ac619 100644
--- a/packages/backend/src/common/config/config.service.ts
+++ b/packages/backend/src/common/config/config.service.ts
@@ -101,4 +101,26 @@ export class ConfigService {
fs.writeFileSync(this.configFilePath, jsonData, 'utf8');
this.loadConfig();
}
+
+ checkEmailEnable() {
+ return (
+ this.get('email') &&
+ this.get('email').use &&
+ this.get('email').use !== 'disable'
+ );
+ }
+
+ checkSMSEnable() {
+ return (
+ this.get('sms') &&
+ this.get('sms').use &&
+ this.get('sms').use !== 'disable'
+ );
+ }
+
+ checkNotifierEnable(all = false) {
+ return all
+ ? this.checkEmailEnable() && this.checkSMSEnable()
+ : this.checkEmailEnable() || this.checkSMSEnable();
+ }
}
diff --git a/packages/backend/src/common/exceptions/biz.exception.ts b/packages/backend/src/common/exceptions/biz.exception.ts
index b47b2105..bba89036 100644
--- a/packages/backend/src/common/exceptions/biz.exception.ts
+++ b/packages/backend/src/common/exceptions/biz.exception.ts
@@ -92,11 +92,15 @@ export const ErrorCode = Object.freeze<
});
export class BizException extends HttpException {
+ code: ErrorCodeEnum;
+
constructor(code: ErrorCodeEnum) {
const [message, chMessage, statusCode] = ErrorCode[code];
super(
HttpException.createBody({ success: false, code, message, chMessage }),
statusCode,
);
+
+ this.code = code;
}
}
diff --git a/packages/backend/src/common/pipes/zod.ts b/packages/backend/src/common/pipes/zod.ts
new file mode 100644
index 00000000..7e0a74c6
--- /dev/null
+++ b/packages/backend/src/common/pipes/zod.ts
@@ -0,0 +1,19 @@
+import { ZodObject } from 'zod';
+
+import { ArgumentMetadata, PipeTransform } from '@nestjs/common';
+
+import { BizException } from '@/common/exceptions/biz.exception';
+
+import { ErrorCodeEnum } from 'shared';
+
+export class ZodValidationPipe implements PipeTransform {
+ constructor(private schema: ZodObject) {}
+
+ transform(value: unknown, metadata: ArgumentMetadata) {
+ try {
+ return this.schema.parse(value);
+ } catch (error) {
+ throw new BizException(ErrorCodeEnum.ValidationError);
+ }
+ }
+}
diff --git a/packages/backend/src/modules/auth/auth.controller.ts b/packages/backend/src/modules/auth/auth.controller.ts
index ddd2797e..e7230679 100644
--- a/packages/backend/src/modules/auth/auth.controller.ts
+++ b/packages/backend/src/modules/auth/auth.controller.ts
@@ -1,30 +1,14 @@
-import {
- Body,
- Controller,
- Get,
- Post,
- Put,
- Query,
- UsePipes,
-} from '@nestjs/common';
+import { Body, Controller, Get, Post, Put, Query } from '@nestjs/common';
import { Role } from '@prisma/client';
+import { ConfigService } from '@/common/config';
+import { BizException } from '@/common/exceptions/biz.exception';
import { Payload, Public } from '@/common/guards/auth.guard';
-import { JoiValidationPipe } from '@/common/pipes/joi';
+import { ZodValidationPipe } from '@/common/pipes/zod';
import { WechatService } from '@/modules/auth/wechat.service';
-import { forgetPasswordDto, identityDto, validateCodeDto } from 'shared';
+import { AuthDTO, ErrorCodeEnum } from 'shared';
-import {
- ForgetPasswordSchema,
- InitAdminSchema,
- InitPasswordSchema,
- PasswordLoginSchema,
- PasswordSchema,
- RequireCodeSchema,
- ValidateCodeSchema,
- bindIdentitySchema,
-} from './auth.dto';
import { AuthService } from './auth.service';
@Controller('auth')
@@ -32,27 +16,50 @@ export class AuthController {
constructor(
private readonly authService: AuthService,
private wechatService: WechatService,
+ private configService: ConfigService,
) {}
@Public()
- @UsePipes(new JoiValidationPipe(InitAdminSchema))
@Post('admin/setup')
- async initAdmin(@Body() data: { identity: string; password: string }) {
- await this.authService.initAdmin(data.identity, data.password);
+ async initAdmin(
+ @Body(new ZodValidationPipe(AuthDTO.InitAdminSchema))
+ body: AuthDTO.InitAdminDto,
+ ) {
+ await this.authService.initAdmin(body.identity, body.password);
return {
success: true,
};
}
- /* 方法一:密码登录 */
+ /* 方法一:密码登录
+ * WARN: 当邮箱和短信通知均为开启时,用户可直接通过该接口注册
+ * WARN: When both email and SMS notification are enabled,
+ * users can register directly through this interface.
+ */
@Public()
- @UsePipes(new JoiValidationPipe(PasswordLoginSchema))
@Post('password')
- async password(@Body() data: { identity: string; password: string }) {
- return {
- success: true,
- ...(await this.authService.loginPassword(data)),
- };
+ async password(
+ @Body(new ZodValidationPipe(AuthDTO.PasswordLoginSchema))
+ body: AuthDTO.PasswordLoginDto,
+ ) {
+ try {
+ return {
+ success: true,
+ ...(await this.authService.loginPassword(body)),
+ };
+ } catch (e) {
+ if (
+ e instanceof BizException &&
+ e.code === ErrorCodeEnum.UserNotExist &&
+ this.configService.checkNotifierEnable(false) === false // It won't hold as long as one is enabled
+ ) {
+ return {
+ success: true,
+ ...(await this.authService.registerPassword(body)),
+ };
+ }
+ throw e;
+ }
}
/* 方法二:验证码登录/注册 */
@@ -60,19 +67,22 @@ export class AuthController {
@Public()
@Get('validateCode')
async newValidateCode(
- @Query(new JoiValidationPipe(RequireCodeSchema)) query: identityDto,
+ @Query(new ZodValidationPipe(AuthDTO.RequireCodeSchema))
+ body: AuthDTO.RequireCodeDto,
) {
- return await this.authService.newValidateCode(query.identity);
+ return await this.authService.newValidateCode(body.identity);
}
/** 2.通过验证码登录/注册,自动识别邮箱或手机号 */
@Public()
- @UsePipes(new JoiValidationPipe(ValidateCodeSchema))
@Post('validateCode')
- async loginByCode(@Body() data: validateCodeDto) {
+ async loginByCode(
+ @Body(new ZodValidationPipe(AuthDTO.ValidateCodeSchema))
+ data: AuthDTO.ValidateCodeDto,
+ ) {
return {
success: true,
- ...(await this.authService.WithValidateCode(data.identity, data.code)),
+ ...(await this.authService.withValidateCode(data.identity, data.code)),
};
}
@@ -100,8 +110,10 @@ export class AuthController {
/* 忘记密码 */
@Public()
@Post('forgetPassword')
- @UsePipes(new JoiValidationPipe(ForgetPasswordSchema))
- async forgetPassword(@Body() data: forgetPasswordDto) {
+ async forgetPassword(
+ @Body(new ZodValidationPipe(AuthDTO.ForgetPasswordSchema))
+ data: AuthDTO.ForgetPasswordDto,
+ ) {
await this.authService.forgetPassword(
data.identity,
data.code,
@@ -116,9 +128,13 @@ export class AuthController {
@Put('changePassword')
async changePassword(
@Payload('id') userId: number,
- @Body('password', new JoiValidationPipe(PasswordSchema)) password: string,
+ @Body(new ZodValidationPipe(AuthDTO.ChangePasswordSchema))
+ data: AuthDTO.ChangePasswordDto,
) {
- await this.authService.changePassword(userId, password);
+ await this.authService.changePassword({
+ userId,
+ ...data,
+ });
return {
success: true,
};
@@ -127,8 +143,8 @@ export class AuthController {
@Put('initUsername')
async initName(
@Payload('id') userId: number,
- @Body(new JoiValidationPipe(InitPasswordSchema))
- body: { username: string },
+ @Body(new ZodValidationPipe(AuthDTO.InitUsernameSchema))
+ body: AuthDTO.InitUsernameDto,
) {
await this.authService.initUsername(userId, body.username);
return {
@@ -140,8 +156,8 @@ export class AuthController {
@Put('initPassword')
async initPassword(
@Payload('id') userId: number,
- @Body(new JoiValidationPipe(InitPasswordSchema))
- body: { password: string },
+ @Body(new ZodValidationPipe(AuthDTO.InitPasswordSchema))
+ body: AuthDTO.InitPasswordDto,
) {
await this.authService.initPassword(userId, body.password);
return {
@@ -151,10 +167,10 @@ export class AuthController {
/* 绑定账户(邮箱或者密码) */
@Put('bindIdentity')
- @UsePipes(new JoiValidationPipe(bindIdentitySchema))
async initIdentity(
@Payload('id') userId: number,
- @Body() data: { identity: string },
+ @Body(new ZodValidationPipe(AuthDTO.BindIdentitySchema))
+ data: AuthDTO.BindIdentityDto,
) {
await this.authService.bindIdentity(userId, data.identity, true);
return {
diff --git a/packages/backend/src/modules/auth/auth.dto.ts b/packages/backend/src/modules/auth/auth.dto.ts
deleted file mode 100644
index ce9e5d5a..00000000
--- a/packages/backend/src/modules/auth/auth.dto.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as Joi from 'joi';
-
-const email = Joi.string().email();
-const phone = Joi.string().pattern(/^[0-9]{11}$/);
-
-export const IdentitySchema = Joi.alternatives().try(email, phone).required();
-export const PasswordSchema = Joi.string().min(8).max(20).required();
-
-export const RequireCodeSchema = Joi.object({
- identity: IdentitySchema,
-});
-
-export const ValidateCodeSchema = Joi.object({
- identity: IdentitySchema,
- code: Joi.string()
- .pattern(/^[0-9]{6}$/)
- .required(),
-});
-
-export const InitPasswordSchema = Joi.object({
- password: PasswordSchema.required(),
-});
-
-export const ForgetPasswordSchema = Joi.object({
- identity: Joi.alternatives().try(email, phone).required(),
- code: Joi.string()
- .pattern(/^[0-9]{6}$/)
- .required(),
- newPassword: PasswordSchema.required(),
-});
-
-export const PasswordLoginSchema = Joi.object({
- identity: IdentitySchema,
- password: PasswordSchema.required(),
-});
-
-export const bindIdentitySchema = Joi.object({
- identity: IdentitySchema,
-});
-
-export const InitAdminSchema = Joi.object({
- identity: IdentitySchema,
- password: PasswordSchema.required(),
-});
diff --git a/packages/backend/src/modules/auth/auth.service.ts b/packages/backend/src/modules/auth/auth.service.ts
index d9d9691b..742febaa 100644
--- a/packages/backend/src/modules/auth/auth.service.ts
+++ b/packages/backend/src/modules/auth/auth.service.ts
@@ -69,7 +69,7 @@ export class AuthService {
let status: IAccountStatus = 'ok';
if (!user.email && !user.phone) {
status = 'bind';
- } else if (!user.password) {
+ } else if (!user.newPassword) {
status = 'password';
}
return {
@@ -88,6 +88,38 @@ export class AuthService {
}
}
+ async #register({
+ identity,
+ password,
+ }: {
+ identity: string;
+ password?: string;
+ }) {
+ const { email, phone } = getPhoneOrEmail(identity);
+
+ const existUser = await this.prisma.client.user.findMany({
+ where: {
+ OR: [{ email }, { phone }],
+ },
+ });
+
+ let user;
+ if (existUser.length != 1) {
+ // 注册用户
+ user = await this.prisma.client.user.create({
+ data: {
+ email: email,
+ phone: phone,
+ role: Role.User,
+ password: password ? hashSync(password, SALT_ROUNDS) : undefined,
+ },
+ });
+ } else {
+ user = existUser[0];
+ }
+ return this.#signWithCheck(user);
+ }
+
/* 添加验证码 */
async newValidateCode(identity: string) {
const { email, phone } = getPhoneOrEmail(identity);
@@ -127,32 +159,18 @@ export class AuthService {
}
}
+ /* Only used when email and sms service both disabled */
+ registerPassword(data: ByPassword) {
+ return this.#register(data);
+ }
+
/* 通过验证码登录/注册 */
- async WithValidateCode(identity: string, code: string) {
+ async withValidateCode(identity: string, code: string) {
const { email, phone } = getPhoneOrEmail(identity);
await this.#verifyCode(identity, code);
- const existUser = await this.prisma.client.user.findMany({
- where: {
- OR: [{ email }, { phone }],
- },
- });
-
- let user;
- if (existUser.length != 1) {
- // 注册用户
- user = await this.prisma.client.user.create({
- data: {
- email: email,
- phone: phone,
- role: Role.User,
- },
- });
- } else {
- user = existUser[0];
- }
- return this.#signWithCheck(user);
+ return this.#register({ identity });
}
/* 通过密码登录 */
@@ -165,11 +183,11 @@ export class AuthService {
},
});
if (user.length != 1) {
- throw Error('User does not exist');
+ throw new BizException(ErrorCodeEnum.UserNotExist);
}
const isPasswordCorrect = await compare(password, user[0].password);
if (!isPasswordCorrect) {
- throw Error('Password is incorrect');
+ throw new BizException(ErrorCodeEnum.PasswordError);
}
return this.#signWithCheck(user[0]);
}
@@ -215,14 +233,35 @@ export class AuthService {
}
}
- /* 修改密码 */
- async changePassword(userId: number, password: string) {
+ /* Utils to change user password
+ * old password will be checked when old password is passed
+ */
+ async changePassword({
+ userId,
+ newPassword,
+ oldPassword,
+ }: {
+ userId: number;
+ newPassword: string;
+ oldPassword?: string;
+ }) {
+ if (oldPassword) {
+ const user = await this.prisma.client.user.findUniqueOrThrow({
+ where: {
+ id: userId,
+ },
+ });
+ const isPasswordCorrect = await compare(oldPassword, user.password);
+ if (!isPasswordCorrect) {
+ throw new BizException(ErrorCodeEnum.PasswordError);
+ }
+ }
await this.prisma.client.user.update({
where: {
id: userId,
},
data: {
- password: hashSync(password, SALT_ROUNDS),
+ password: hashSync(newPassword, SALT_ROUNDS),
},
});
}
@@ -245,7 +284,7 @@ export class AuthService {
} else {
user = existUser[0];
}
- await this.changePassword(user.id, password);
+ await this.changePassword({ userId: user.id, newPassword: password });
return this.#signWithCheck(user);
}
diff --git a/packages/backend/src/modules/auth/wechat.service.ts b/packages/backend/src/modules/auth/wechat.service.ts
index 7d342837..6f23a9f3 100644
--- a/packages/backend/src/modules/auth/wechat.service.ts
+++ b/packages/backend/src/modules/auth/wechat.service.ts
@@ -7,7 +7,7 @@ import { ConfigService } from '@/common/config';
import { JwtService } from '@/libs/jwt/jwt.service';
import { ExtendedPrismaClient } from '@/processors/database/prisma.extension';
-import { ConfigType } from 'shared';
+import { ConfigType } from 'shared/';
@Injectable()
export class WechatService {
diff --git a/packages/backend/src/modules/chat/chat.service.ts b/packages/backend/src/modules/chat/chat.service.ts
index e944531f..4d60141f 100644
--- a/packages/backend/src/modules/chat/chat.service.ts
+++ b/packages/backend/src/modules/chat/chat.service.ts
@@ -278,12 +278,10 @@ ${message}
where: { id: modelId },
});
- const histories: OpenAI.ChatCompletionMessage[] = messages.map(
- ({ role, content }) => ({
- role: role.toLowerCase() as OpenAI.ChatCompletionRole,
- content,
- }),
- );
+ const histories = messages.map(({ role, content }) => ({
+ role: role.toLowerCase() as OpenAI.ChatCompletionRole,
+ content,
+ })) as OpenAI.ChatCompletionMessage[];
const stream = await this.#streamChat({
model: model,
diff --git a/packages/backend/src/modules/order/order.controller.ts b/packages/backend/src/modules/order/order.controller.ts
index e10ca4e9..c299fb48 100644
--- a/packages/backend/src/modules/order/order.controller.ts
+++ b/packages/backend/src/modules/order/order.controller.ts
@@ -15,10 +15,11 @@ import { Role } from '@prisma/client';
import { Payload, Roles } from '@/common/guards/auth.guard';
import { Public } from '@/common/guards/auth.guard';
+import { ZodValidationPipe } from '@/common/pipes/zod';
import { JWTPayload } from '@/libs/jwt/jwt.service';
import { PaymentService } from '@/libs/payment/payment.service';
-import { newOrderDto } from 'shared';
+import { OrderDTO } from 'shared';
import { OrderService } from './order.service';
@@ -31,8 +32,12 @@ export class OrderController {
/* 新建订单 */
@Post('new')
- async newOrder(@Payload('id') userId: number, @Body() data: newOrderDto) {
- const order = await this.orderService.createOrder(userId, data.productId);
+ async newOrder(
+ @Payload('id') userId: number,
+ @Body(new ZodValidationPipe(OrderDTO.NewOrderSchema))
+ body: OrderDTO.NewOrderDto,
+ ) {
+ const order = await this.orderService.createOrder(userId, body.productId);
// TODO 防止短时间内重复产生订单
const result = await this.paymentService.xhStartPay({
orderId: order.id,
diff --git a/packages/frontend/next.config.js b/packages/frontend/next.config.js
index f2fb4c03..b3e0a139 100644
--- a/packages/frontend/next.config.js
+++ b/packages/frontend/next.config.js
@@ -11,6 +11,7 @@ const nextConfig = {
env: {
NEXT_PUBLIC_TITLE: config?.title ?? 'ChatGPT Admin Web',
},
+ transpilePackages: ['shared'],
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
diff --git a/packages/frontend/src/app/(admin-end)/dashboard/layout.tsx b/packages/frontend/src/app/(admin-end)/dashboard/layout.tsx
index b5de856a..4f5298a0 100644
--- a/packages/frontend/src/app/(admin-end)/dashboard/layout.tsx
+++ b/packages/frontend/src/app/(admin-end)/dashboard/layout.tsx
@@ -1,4 +1,7 @@
+'use client';
+
import Link from 'next/link';
+import { usePathname } from 'next/navigation';
import { Button } from '@radix-ui/themes';
@@ -40,13 +43,21 @@ export default function DashboardLayout({
}: {
children: React.ReactNode;
}) {
+ const pathname = usePathname();
return (
{navs.map((nav) => (
-
+
{nav.name}
diff --git a/packages/frontend/src/app/(admin-end)/dashboard/setting/page.tsx b/packages/frontend/src/app/(admin-end)/dashboard/setting/page.tsx
index 23c27cce..5f48e884 100644
--- a/packages/frontend/src/app/(admin-end)/dashboard/setting/page.tsx
+++ b/packages/frontend/src/app/(admin-end)/dashboard/setting/page.tsx
@@ -3,7 +3,7 @@
import useSWR from 'swr';
import { Loading } from '@/components/loading';
-import { OptionListRoot, OptionNode } from '@/components/radix-ui-lib';
+import { OptionNode } from '@/components/radix-ui-lib';
import { useStore } from '@/store';
import useInstallStore from '@/store/install';
@@ -21,10 +21,10 @@ export default function AdminSettingPage() {
if (isLoading || !data) return ;
return (
-
+
{data.schema.map((item: any) => (
))}
-
+
);
}
diff --git a/packages/frontend/src/app/(admin-end)/install/page.tsx b/packages/frontend/src/app/(admin-end)/install/page.tsx
index 55dff49d..0a130a23 100644
--- a/packages/frontend/src/app/(admin-end)/install/page.tsx
+++ b/packages/frontend/src/app/(admin-end)/install/page.tsx
@@ -3,33 +3,55 @@
import { useRouter } from 'next/navigation';
import React, { useState } from 'react';
-import { Box, Button, Text, TextField } from '@radix-ui/themes';
+import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
+import {
+ Box,
+ Button,
+ Callout,
+ IconButton,
+ Popover,
+ Text,
+ TextField,
+ Tooltip,
+} from '@radix-ui/themes';
import usePreventFormSubmit from '@/hooks/use-prevent-form';
+import { InfoIcon } from '@/icons';
import { useStore } from '@/store';
+import { ErrorCodeEnum } from 'shared';
+
export default function SetupPage() {
const router = useRouter();
- const { fetcher } = useStore();
+ const { jsonFetcher } = useStore();
const [identity, setIdentity] = useState('');
const [password, setPassword] = useState('');
+ const [errorMessage, setErrorMessage] = useState('');
const [isSubmitting, handleInitAdmin] = usePreventFormSubmit();
async function initAdmin() {
- await fetcher('/auth/admin/setup', {
+ await jsonFetcher('/auth/admin/setup', {
method: 'POST',
body: JSON.stringify({ identity, password }),
- })
- .then((res) => res.json())
- .then((res) => {
- if (res.success) {
- router.push('/auth');
+ }).then((data) => {
+ if (!data.success) {
+ switch (data.code) {
+ case ErrorCodeEnum.AdminExists:
+ return setErrorMessage('管理员已存在');
+ case ErrorCodeEnum.AuthFail:
+ return setErrorMessage('用户密码错误');
+ default:
+ setErrorMessage(data.message);
+ break;
}
- });
+ } else {
+ router.push('/auth');
+ }
+ });
}
return (
-
+
- 初始化管理员
+
+ 初始化管理员
+
+
+
+
+
+
+ {errorMessage && (
+
+
+
+
+ {errorMessage}
+
+ )}
setIdentity(e.target.value)}
- placeholder="Admin Email"
+ placeholder="Admin Indentity"
/>
{props.children} ;
-}
-
/* 开关选项 */
function SwitchItem(props: {
schema: ISettingSchema;
diff --git a/packages/frontend/src/icons/index.ts b/packages/frontend/src/icons/index.ts
new file mode 100644
index 00000000..39e460d3
--- /dev/null
+++ b/packages/frontend/src/icons/index.ts
@@ -0,0 +1 @@
+export { default as InfoIcon } from './info.svg';
diff --git a/packages/frontend/src/icons/info.svg b/packages/frontend/src/icons/info.svg
new file mode 100644
index 00000000..971ebb86
--- /dev/null
+++ b/packages/frontend/src/icons/info.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/packages/frontend/src/store/shared.ts b/packages/frontend/src/store/shared.ts
index 2ac09bb6..892af12e 100644
--- a/packages/frontend/src/store/shared.ts
+++ b/packages/frontend/src/store/shared.ts
@@ -65,6 +65,11 @@ export const createSharedStore: StateCreator
= (
return res;
});
},
+ jsonFetcher(url, init) {
+ return get()
+ .fetcher(url, init)
+ .then((res) => res.json());
+ },
// Config
config: {
...DEFAULT_CONFIG,
diff --git a/packages/frontend/src/store/types.ts b/packages/frontend/src/store/types.ts
index 847af18d..e650ce2c 100644
--- a/packages/frontend/src/store/types.ts
+++ b/packages/frontend/src/store/types.ts
@@ -1,6 +1,11 @@
import { SubmitKey, Theme } from '@/store/shared';
-import { ChatMessage, ChatMessageRole, ChatSession } from 'shared';
+import {
+ BaseResponse,
+ ChatMessage,
+ ChatMessageRole,
+ ChatSession,
+} from 'shared';
export type LocalConfig = {
theme: Theme;
@@ -36,6 +41,10 @@ export interface SharedSlice {
// utils
clearData: () => void;
fetcher: (url: string, options?: RequestInit) => Promise;
+ jsonFetcher: (
+ url: string,
+ options?: RequestInit,
+ ) => Promise>;
}
export interface ChatSlice {
diff --git a/packages/frontend/src/styles/dashboard.css b/packages/frontend/src/styles/dashboard.css
index 91299f41..dcd2888f 100644
--- a/packages/frontend/src/styles/dashboard.css
+++ b/packages/frontend/src/styles/dashboard.css
@@ -23,7 +23,7 @@ section {
"content content";
height: 100%;
width: 100%;
- grid-template-rows: 80px 1fr;
+ grid-template-rows: 48px 1fr;
grid-template-columns: 250px 1fr;
}
diff --git a/packages/frontend/src/styles/module/radix-ui-lib.module.scss b/packages/frontend/src/styles/module/radix-ui-lib.module.scss
index d9bcbc38..b3e51258 100644
--- a/packages/frontend/src/styles/module/radix-ui-lib.module.scss
+++ b/packages/frontend/src/styles/module/radix-ui-lib.module.scss
@@ -3,14 +3,7 @@
//@import '@radix-ui/colors/gray-dark.css';
//@import '@radix-ui/colors/teal-dark.css';
-.option-list-root {
- background-color: var(--gray-2);
- display: flex;
- flex-direction: column;
- max-width: 100%;
- box-shadow: 0 2px 10px var(--gray-11);
- padding: 20px;
-}
+
.option-list-item {
width: 100%;
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 59187c2e..fe67cbc6 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,12 +1,19 @@
{
"name": "shared",
- "main": "dist/index.js",
- "types": "dist/index.d.ts",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
"scripts": {
- "dev": "tsc --watch",
- "build": "tsc --build"
+ "dev": "tsc --build tsconfig.json --watch",
+ "build": "tsc --build tsconfig.build.json",
+ "test": "vitest run",
+ "test:dev": "vitest watch"
},
"dependencies": {
"@prisma/client": "workspace:@nest-http/prisma@*"
+ },
+ "devDependencies": {
+ "typescript": "*",
+ "vitest": "*",
+ "zod": "*"
}
}
diff --git a/packages/shared/src/auth.ts b/packages/shared/src/auth.ts
deleted file mode 100644
index 5ccfb828..00000000
--- a/packages/shared/src/auth.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/* 账号可能存在的状态
- * bind: 需要绑定账户,通常为 OAuth
- * password: 需要设置密码
- * block: 被禁用
- * ok: 正常 */
-export type IAccountStatus = 'bind' | 'password' | 'block' | 'ok';
-
-/* 认证 DTO */
-export interface identityDto {
- identity: string;
-}
-
-/* 验证验证码 DTO */
-export interface validateCodeDto {
- identity: string;
- code: string;
-}
-
-/* 忘记密码 DTO */
-export interface forgetPasswordDto {
- identity: string;
- code: string;
- newPassword: string;
-}
diff --git a/packages/shared/src/config.ts b/packages/shared/src/config.ts
index 849d3232..db3c272d 100644
--- a/packages/shared/src/config.ts
+++ b/packages/shared/src/config.ts
@@ -31,7 +31,7 @@ export interface ConfigType {
keys: string;
};
sms: {
- use?: 'aliyun' | 'tencent' | 'uni';
+ use?: 'disable' | 'aliyun' | 'tencent' | 'uni';
uni?: {
signature: string;
templateId: string;
@@ -40,7 +40,7 @@ export interface ConfigType {
};
};
email: {
- use?: 'smtp' | 'resend' | 'mailgun' | 'elastic';
+ use?: 'disable' | 'smtp' | 'resend' | 'mailgun' | 'elastic';
domain: string;
sender?: string;
smtp: {};
diff --git a/packages/shared/src/dto/index.ts b/packages/shared/src/dto/index.ts
new file mode 100644
index 00000000..ad02c5bf
--- /dev/null
+++ b/packages/shared/src/dto/index.ts
@@ -0,0 +1,71 @@
+import z from 'zod';
+
+export namespace AuthDTO {
+ const email = z.string().email();
+ const phone = z.string().regex(/^[0-9]{11}$/);
+ export const codeSchema = z.string().regex(/^[0-9]{6}$/);
+
+ export const identitySchema = z.union([email, phone]);
+
+ export const passwordSchema = z.string().min(8).max(20);
+
+ export const RequireCodeSchema = z.object({
+ identity: identitySchema,
+ });
+ export type RequireCodeDto = z.infer;
+
+ export const ValidateCodeSchema = z.object({
+ identity: identitySchema,
+ code: codeSchema,
+ });
+ export type ValidateCodeDto = z.infer;
+
+ export const ForgetPasswordSchema = z.object({
+ identity: identitySchema,
+ code: codeSchema,
+ newPassword: passwordSchema,
+ });
+ export type ForgetPasswordDto = z.infer;
+
+ export const ChangePasswordSchema = z.object({
+ oldPassword: passwordSchema,
+ newPassword: passwordSchema,
+ });
+ export type ChangePasswordDto = z.infer;
+
+ export const PasswordLoginSchema = z.object({
+ identity: identitySchema,
+ password: passwordSchema,
+ });
+ export type PasswordLoginDto = z.infer;
+
+ export const BindIdentitySchema = z.object({
+ identity: identitySchema,
+ });
+ export type BindIdentityDto = z.infer;
+
+ export const InitAdminSchema = z.object({
+ identity: identitySchema,
+ password: passwordSchema,
+ });
+ export type InitAdminDto = z.infer;
+
+ export const InitUsernameSchema = z.object({
+ username: z.string().min(2).max(20),
+ });
+ export type InitUsernameDto = z.infer;
+
+ export const InitPasswordSchema = z.object({
+ password: passwordSchema,
+ });
+ export type InitPasswordDto = z.infer;
+}
+
+export namespace ChatDTO {}
+
+export namespace OrderDTO {
+ export const NewOrderSchema = z.object({
+ productId: z.number(),
+ });
+ export type NewOrderDto = z.infer;
+}
diff --git a/packages/shared/src/error-code.ts b/packages/shared/src/error-code.ts
deleted file mode 100644
index 17813ae3..00000000
--- a/packages/shared/src/error-code.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-export enum ErrorCodeEnum {
- /* 200 */
- OutOfQuota = 20001,
-
- /* 400 */
- // BadRequest = 40000,
- AdminExists = 40001,
- ConfigExists,
-
- /* 403 */
- AuthFail = 40300,
- PasswordError,
- CodeValidationError,
- UserNotExist,
- NameDuplicated,
- EmailDuplicated,
- PhoneDuplicated,
- BindNameExist,
- BindEmailExist,
- BindPhoneExist,
- BindPasswordExist,
-
- /* 404 */
- NotFound = 40400,
- SessionNotFound,
-
- /* 429 */
- TooManyRequests = 42900,
-
- /* 406 */
- ValidationError = 40600,
-
- /* 500 */
- ServerError = 50000,
- DatabaseError,
- RedisError,
- EmailError,
- EmailNotSetup,
- SmsError,
- SmsNotSetup,
- WechatError,
-
- UnknownError = 99999,
-}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 09daee35..f273f402 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -1,4 +1,7 @@
-export * from './auth';
+/* Data Transfer Object */
+export { AuthDTO, OrderDTO } from './dto';
+/* Types */
+export * from './types';
export * from './config';
export * from './chat';
export * from './order';
@@ -6,5 +9,61 @@ export * from './product';
export * from './types/database';
export * from './user';
export * from './install';
-export { Role } from '@prisma/client';
-export { ErrorCodeEnum } from './error-code';
+
+export type BaseResponse = BaseResponseSuccess | BaseResponseFailure;
+
+interface BaseResponseSuccess {
+ success: true;
+ data: T;
+}
+
+interface BaseResponseFailure {
+ success: false;
+ code: ErrorCodeEnum;
+ message: string;
+}
+
+export enum ErrorCodeEnum {
+ /* 200 */
+ OutOfQuota = 20001,
+
+ /* 400 */
+ // BadRequest = 40000,
+ AdminExists = 40001,
+ ConfigExists,
+
+ /* 403 */
+ AuthFail = 40300,
+ PasswordError,
+ CodeValidationError,
+ UserNotExist,
+ NameDuplicated,
+ EmailDuplicated,
+ PhoneDuplicated,
+ BindNameExist,
+ BindEmailExist,
+ BindPhoneExist,
+ BindPasswordExist,
+
+ /* 404 */
+ NotFound = 40400,
+ SessionNotFound,
+
+ /* 429 */
+ TooManyRequests = 42900,
+
+ /* 406 */
+ ValidationError = 40600,
+
+ /* 500 */
+ ServerError = 50000,
+ DatabaseError,
+ RedisError,
+ EmailError,
+ EmailNotSetup,
+ SmsError,
+ SmsNotSetup,
+ WechatError,
+
+ UnknownError = 99999,
+}
diff --git a/packages/shared/src/types/auth.ts b/packages/shared/src/types/auth.ts
new file mode 100644
index 00000000..07d68a7e
--- /dev/null
+++ b/packages/shared/src/types/auth.ts
@@ -0,0 +1,6 @@
+/* 账号可能存在的状态
+ * bind: 需要绑定账户,通常为 OAuth
+ * password: 需要设置密码
+ * block: 被禁用
+ * ok: 正常 */
+export type IAccountStatus = 'bind' | 'password' | 'block' | 'ok';
diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts
new file mode 100644
index 00000000..ad5b7af6
--- /dev/null
+++ b/packages/shared/src/types/index.ts
@@ -0,0 +1,3 @@
+export * from './auth';
+
+export type { Role } from '@prisma/client';
diff --git a/packages/shared/tsconfig.build.json b/packages/shared/tsconfig.build.json
new file mode 100644
index 00000000..1c370501
--- /dev/null
+++ b/packages/shared/tsconfig.build.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "sourceMap": false
+ },
+ "extends": "./tsconfig.json",
+ "exclude": [
+ "node_modules",
+ "dist"
+ ]
+}
diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json
index 770dcd39..e4ada9ef 100644
--- a/packages/shared/tsconfig.json
+++ b/packages/shared/tsconfig.json
@@ -22,7 +22,7 @@
"resolveJsonModule": true,
"baseUrl": "./src",
"paths": {
- "@": [
+ "@/*": [
"./*"
]
}
@@ -31,6 +31,8 @@
"src/**/*"
],
"exclude": [
- "node_modules"
+ "node_modules",
+ "dist",
+ "test"
]
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c852c386..bae2c59d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -34,6 +34,18 @@ importers:
typescript:
specifier: latest
version: 5.2.2
+ vite:
+ specifier: ^5.0.7
+ version: 5.0.7(@types/node@20.8.10)
+ vite-tsconfig-paths:
+ specifier: ^4.2.2
+ version: 4.2.2(typescript@5.2.2)(vite@5.0.7)
+ vitest:
+ specifier: ^1.0.2
+ version: 1.0.2(@types/node@20.8.10)
+ zod:
+ specifier: latest
+ version: 3.22.4
packages/backend:
dependencies:
@@ -100,6 +112,9 @@ importers:
spark-md5:
specifier: ^3.0.2
version: 3.0.2
+ zod:
+ specifier: '*'
+ version: 3.22.4
devDependencies:
'@nestjs/cli':
specifier: ^10.2.1
@@ -279,6 +294,16 @@ importers:
'@prisma/client':
specifier: workspace:@nest-http/prisma@*
version: link:../../prisma
+ devDependencies:
+ typescript:
+ specifier: '*'
+ version: 5.3.3
+ vitest:
+ specifier: '*'
+ version: 1.0.2(@types/node@20.8.10)
+ zod:
+ specifier: '*'
+ version: 3.22.4
prisma:
dependencies:
@@ -1679,6 +1704,204 @@ packages:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
+ /@esbuild/android-arm64@0.19.8:
+ resolution: {integrity: sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm@0.19.8:
+ resolution: {integrity: sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-x64@0.19.8:
+ resolution: {integrity: sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-arm64@0.19.8:
+ resolution: {integrity: sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-x64@0.19.8:
+ resolution: {integrity: sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-arm64@0.19.8:
+ resolution: {integrity: sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-x64@0.19.8:
+ resolution: {integrity: sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm64@0.19.8:
+ resolution: {integrity: sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm@0.19.8:
+ resolution: {integrity: sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ia32@0.19.8:
+ resolution: {integrity: sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-loong64@0.19.8:
+ resolution: {integrity: sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-mips64el@0.19.8:
+ resolution: {integrity: sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ppc64@0.19.8:
+ resolution: {integrity: sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-riscv64@0.19.8:
+ resolution: {integrity: sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-s390x@0.19.8:
+ resolution: {integrity: sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-x64@0.19.8:
+ resolution: {integrity: sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/netbsd-x64@0.19.8:
+ resolution: {integrity: sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/openbsd-x64@0.19.8:
+ resolution: {integrity: sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/sunos-x64@0.19.8:
+ resolution: {integrity: sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-arm64@0.19.8:
+ resolution: {integrity: sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-ia32@0.19.8:
+ resolution: {integrity: sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-x64@0.19.8:
+ resolution: {integrity: sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@eslint-community/eslint-utils@4.4.0(eslint@8.42.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2047,6 +2270,13 @@ packages:
'@sinclair/typebox': 0.27.8
dev: true
+ /@jest/schemas@29.6.3:
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@sinclair/typebox': 0.27.8
+ dev: true
+
/@jest/source-map@29.6.0:
resolution: {integrity: sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -3603,6 +3833,110 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@rollup/rollup-android-arm-eabi@4.7.0:
+ resolution: {integrity: sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw==}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-android-arm64@4.7.0:
+ resolution: {integrity: sha512-/EBw0cuJ/KVHiU2qyVYUhogXz7W2vXxBzeE9xtVIMC+RyitlY2vvaoysMUqASpkUtoNIHlnKTu/l7mXOPgnKOA==}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-arm64@4.7.0:
+ resolution: {integrity: sha512-4VXG1bgvClJdbEYYjQ85RkOtwN8sqI3uCxH0HC5w9fKdqzRzgG39K7GAehATGS8jghA7zNoS5CjSKkDEqWmNZg==}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-x64@4.7.0:
+ resolution: {integrity: sha512-/ImhO+T/RWJ96hUbxiCn2yWI0/MeQZV/aeukQQfhxiSXuZJfyqtdHPUPrc84jxCfXTxbJLmg4q+GBETeb61aNw==}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm-gnueabihf@4.7.0:
+ resolution: {integrity: sha512-zhye8POvTyUXlKbfPBVqoHy3t43gIgffY+7qBFqFxNqVtltQLtWeHNAbrMnXiLIfYmxcoL/feuLDote2tx+Qbg==}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-gnu@4.7.0:
+ resolution: {integrity: sha512-RAdr3OJnUum6Vs83cQmKjxdTg31zJnLLTkjhcFt0auxM6jw00GD6IPFF42uasYPr/wGC6TRm7FsQiJyk0qIEfg==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-musl@4.7.0:
+ resolution: {integrity: sha512-nhWwYsiJwZGq7SyR3afS3EekEOsEAlrNMpPC4ZDKn5ooYSEjDLe9W/xGvoIV8/F/+HNIY6jY8lIdXjjxfxopXw==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-riscv64-gnu@4.7.0:
+ resolution: {integrity: sha512-rlfy5RnQG1aop1BL/gjdH42M2geMUyVQqd52GJVirqYc787A/XVvl3kQ5NG/43KXgOgE9HXgCaEH05kzQ+hLoA==}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-gnu@4.7.0:
+ resolution: {integrity: sha512-cCkoGlGWfBobdDtiiypxf79q6k3/iRVGu1HVLbD92gWV5WZbmuWJCgRM4x2N6i7ljGn1cGytPn9ZAfS8UwF6vg==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-musl@4.7.0:
+ resolution: {integrity: sha512-R2oBf2p/Arc1m+tWmiWbpHBjEcJnHVnv6bsypu4tcKdrYTpDfl1UT9qTyfkIL1iiii5D4WHxUHCg5X0pzqmxFg==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-arm64-msvc@4.7.0:
+ resolution: {integrity: sha512-CPtgaQL1aaPc80m8SCVEoxFGHxKYIt3zQYC3AccL/SqqiWXblo3pgToHuBwR8eCP2Toa+X1WmTR/QKFMykws7g==}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-ia32-msvc@4.7.0:
+ resolution: {integrity: sha512-pmioUlttNh9GXF5x2CzNa7Z8kmRTyhEzzAC+2WOOapjewMbl+3tGuAnxbwc5JyG8Jsz2+hf/QD/n5VjimOZ63g==}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-x64-msvc@4.7.0:
+ resolution: {integrity: sha512-SeZzC2QhhdBQUm3U0c8+c/P6UlRyBcLL2Xp5KX7z46WXZxzR8RJSIWL9wSUeBTgxog5LTPJuPj0WOT9lvrtP7Q==}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@rushstack/eslint-patch@1.3.3:
resolution: {integrity: sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==}
dev: false
@@ -4279,6 +4613,44 @@ packages:
'@typescript-eslint/types': 5.59.11
eslint-visitor-keys: 3.4.3
+ /@vitest/expect@1.0.2:
+ resolution: {integrity: sha512-mAIo/8uddSWkjQMLFcjqZP3WmkwvvN0OtlyZIu33jFnwme3vZds8m8EDMxtj+Uzni2DwtPfHNjJcTM8zTV1f4A==}
+ dependencies:
+ '@vitest/spy': 1.0.2
+ '@vitest/utils': 1.0.2
+ chai: 4.3.10
+ dev: true
+
+ /@vitest/runner@1.0.2:
+ resolution: {integrity: sha512-ZcHJXPT2kg/9Hc4fNkCbItlsgZSs3m4vQbxB8LCSdzpbG85bExCmSvu6K9lWpMNdoKfAr1Jn0BwS9SWUcGnbTQ==}
+ dependencies:
+ '@vitest/utils': 1.0.2
+ p-limit: 5.0.0
+ pathe: 1.1.1
+ dev: true
+
+ /@vitest/snapshot@1.0.2:
+ resolution: {integrity: sha512-9ClDz2/aV5TfWA4reV7XR9p+hE0e7bifhwxlURugj3Fw0YXeTFzHmKCNEHd6wOIFMfthbGGwhlq7TOJ2jDO4/g==}
+ dependencies:
+ magic-string: 0.30.5
+ pathe: 1.1.1
+ pretty-format: 29.7.0
+ dev: true
+
+ /@vitest/spy@1.0.2:
+ resolution: {integrity: sha512-YlnHmDntp+zNV3QoTVFI5EVHV0AXpiThd7+xnDEbWnD6fw0TH/J4/+3GFPClLimR39h6nA5m0W4Bjm5Edg4A/A==}
+ dependencies:
+ tinyspy: 2.2.0
+ dev: true
+
+ /@vitest/utils@1.0.2:
+ resolution: {integrity: sha512-GPQkGHAnFAP/+seSbB9pCsj339yRrMgILoI5H2sPevTLCYgBq0VRjF8QSllmnQyvf0EontF6KUIt2t5s2SmqoQ==}
+ dependencies:
+ diff-sequences: 29.6.3
+ loupe: 2.3.7
+ pretty-format: 29.7.0
+ dev: true
+
/@webassemblyjs/ast@1.11.6:
resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
dependencies:
@@ -4427,6 +4799,11 @@ packages:
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
engines: {node: '>=0.4.0'}
+ /acorn-walk@8.3.1:
+ resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==}
+ engines: {node: '>=0.4.0'}
+ dev: true
+
/acorn@8.10.0:
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
engines: {node: '>=0.4.0'}
@@ -4682,6 +5059,10 @@ packages:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
dev: true
+ /assertion-error@1.1.0:
+ resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+ dev: true
+
/ast-types-flow@0.0.7:
resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==}
dev: false
@@ -4950,6 +5331,11 @@ packages:
streamsearch: 1.1.0
dev: false
+ /cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+ dev: true
+
/call-bind@1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
@@ -4984,6 +5370,19 @@ packages:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
dev: false
+ /chai@4.3.10:
+ resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
+ engines: {node: '>=4'}
+ dependencies:
+ assertion-error: 1.1.0
+ check-error: 1.0.3
+ deep-eql: 4.1.3
+ get-func-name: 2.0.2
+ loupe: 2.3.7
+ pathval: 1.1.1
+ type-detect: 4.0.8
+ dev: true
+
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -5024,6 +5423,12 @@ packages:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
dev: false
+ /check-error@1.0.3:
+ resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+ dependencies:
+ get-func-name: 2.0.2
+ dev: true
+
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -5036,7 +5441,7 @@ packages:
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
/chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
@@ -5426,6 +5831,13 @@ packages:
optional: true
dev: true
+ /deep-eql@4.1.3:
+ resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
+ engines: {node: '>=6'}
+ dependencies:
+ type-detect: 4.0.8
+ dev: true
+
/deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -5493,6 +5905,11 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
+ /diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dev: true
+
/diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
@@ -5712,6 +6129,36 @@ packages:
is-symbol: 1.0.4
dev: false
+ /esbuild@0.19.8:
+ resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@esbuild/android-arm': 0.19.8
+ '@esbuild/android-arm64': 0.19.8
+ '@esbuild/android-x64': 0.19.8
+ '@esbuild/darwin-arm64': 0.19.8
+ '@esbuild/darwin-x64': 0.19.8
+ '@esbuild/freebsd-arm64': 0.19.8
+ '@esbuild/freebsd-x64': 0.19.8
+ '@esbuild/linux-arm': 0.19.8
+ '@esbuild/linux-arm64': 0.19.8
+ '@esbuild/linux-ia32': 0.19.8
+ '@esbuild/linux-loong64': 0.19.8
+ '@esbuild/linux-mips64el': 0.19.8
+ '@esbuild/linux-ppc64': 0.19.8
+ '@esbuild/linux-riscv64': 0.19.8
+ '@esbuild/linux-s390x': 0.19.8
+ '@esbuild/linux-x64': 0.19.8
+ '@esbuild/netbsd-x64': 0.19.8
+ '@esbuild/openbsd-x64': 0.19.8
+ '@esbuild/sunos-x64': 0.19.8
+ '@esbuild/win32-arm64': 0.19.8
+ '@esbuild/win32-ia32': 0.19.8
+ '@esbuild/win32-x64': 0.19.8
+ dev: true
+
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
@@ -6163,6 +6610,21 @@ packages:
strip-final-newline: 2.0.0
dev: true
+ /execa@8.0.1:
+ resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+ engines: {node: '>=16.17'}
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.1.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+ dev: true
+
/exit@0.1.2:
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
engines: {node: '>= 0.8.0'}
@@ -6447,8 +6909,8 @@ packages:
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
- /fsevents@2.3.2:
- resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ /fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
@@ -6495,6 +6957,10 @@ packages:
engines: {node: 6.* || 8.* || >= 10.*}
dev: true
+ /get-func-name@2.0.2:
+ resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+ dev: true
+
/get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
dependencies:
@@ -6525,6 +6991,11 @@ packages:
engines: {node: '>=10'}
dev: true
+ /get-stream@8.0.1:
+ resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+ engines: {node: '>=16'}
+ dev: true
+
/get-symbol-description@1.0.0:
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
engines: {node: '>= 0.4'}
@@ -6635,6 +7106,10 @@ packages:
merge2: 1.4.1
slash: 3.0.0
+ /globrex@0.1.2:
+ resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+ dev: true
+
/gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
@@ -6788,6 +7263,11 @@ packages:
engines: {node: '>=10.17.0'}
dev: true
+ /human-signals@5.0.0:
+ resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+ engines: {node: '>=16.17.0'}
+ dev: true
+
/humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
dependencies:
@@ -7077,6 +7557,11 @@ packages:
engines: {node: '>=8'}
dev: true
+ /is-stream@3.0.0:
+ resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: true
+
/is-string@1.0.7:
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'}
@@ -7387,7 +7872,7 @@ packages:
micromatch: 4.0.5
walker: 1.0.8
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
dev: true
/jest-leak-detector@29.6.2:
@@ -7808,6 +8293,14 @@ packages:
engines: {node: '>=6.11.5'}
dev: true
+ /local-pkg@0.5.0:
+ resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
+ engines: {node: '>=14'}
+ dependencies:
+ mlly: 1.4.2
+ pkg-types: 1.0.3
+ dev: true
+
/locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@@ -7861,6 +8354,12 @@ packages:
js-tokens: 4.0.0
dev: false
+ /loupe@2.3.7:
+ resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+ dependencies:
+ get-func-name: 2.0.2
+ dev: true
+
/lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
dependencies:
@@ -7901,6 +8400,13 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
+ /magic-string@0.30.5:
+ resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.15
+ dev: true
+
/make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@@ -8389,6 +8895,11 @@ packages:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
+ /mimic-fn@4.0.0:
+ resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+ engines: {node: '>=12'}
+ dev: true
+
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@@ -8451,6 +8962,15 @@ packages:
hasBin: true
dev: false
+ /mlly@1.4.2:
+ resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
+ dependencies:
+ acorn: 8.10.0
+ pathe: 1.1.1
+ pkg-types: 1.0.3
+ ufo: 1.3.2
+ dev: true
+
/mnemonist@0.39.5:
resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==}
dependencies:
@@ -8485,6 +9005,12 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ /nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+ dev: true
+
/natural-compare-lite@1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
dev: true
@@ -8621,6 +9147,13 @@ packages:
path-key: 3.1.1
dev: true
+ /npm-run-path@5.1.0:
+ resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ path-key: 4.0.0
+ dev: true
+
/npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
dependencies:
@@ -8725,6 +9258,13 @@ packages:
dependencies:
mimic-fn: 2.1.0
+ /onetime@6.0.0:
+ resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ mimic-fn: 4.0.0
+ dev: true
+
/openai@4.14.0:
resolution: {integrity: sha512-pWQYkFWdeudR1yLyS/rFfIv/MTXqFEtlN4EKVm0F6KKKGjhuthznPOCC4hxfAFmjlgbpJJXhQQ/oBeB9bxFmUw==}
hasBin: true
@@ -8793,6 +9333,13 @@ packages:
dependencies:
yocto-queue: 0.1.0
+ /p-limit@5.0.0:
+ resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
+ engines: {node: '>=18'}
+ dependencies:
+ yocto-queue: 1.0.0
+ dev: true
+
/p-locate@4.1.0:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
@@ -8859,6 +9406,11 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
+ /path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+ dev: true
+
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
@@ -8881,6 +9433,14 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
+ /pathe@1.1.1:
+ resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
+ dev: true
+
+ /pathval@1.1.1:
+ resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+ dev: true
+
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@@ -8931,6 +9491,14 @@ packages:
find-up: 4.1.0
dev: true
+ /pkg-types@1.0.3:
+ resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+ dependencies:
+ jsonc-parser: 3.2.0
+ mlly: 1.4.2
+ pathe: 1.1.1
+ dev: true
+
/pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
@@ -9000,6 +9568,15 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
+ /postcss@8.4.32:
+ resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+ dev: true
+
/prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -9032,6 +9609,15 @@ packages:
react-is: 18.2.0
dev: true
+ /pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/schemas': 29.6.3
+ ansi-styles: 5.2.0
+ react-is: 18.2.0
+ dev: true
+
/prisma-extension-pagination@0.5.0(@prisma/client@prisma):
resolution: {integrity: sha512-M2vqOkaQR8U++zPbc+ti1nVArZbpRgOr/BdaUDkBk7XV7uNikjJeDvQzmUEadzkTAON3H+SWtTrafHr0P0AMzA==}
peerDependencies:
@@ -9622,6 +10208,27 @@ packages:
glob: 9.3.5
dev: true
+ /rollup@4.7.0:
+ resolution: {integrity: sha512-7Kw0dUP4BWH78zaZCqF1rPyQ8D5DSU6URG45v1dqS/faNsx9WXyess00uTOZxKr7oR/4TOjO1CPudT8L1UsEgw==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.7.0
+ '@rollup/rollup-android-arm64': 4.7.0
+ '@rollup/rollup-darwin-arm64': 4.7.0
+ '@rollup/rollup-darwin-x64': 4.7.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.7.0
+ '@rollup/rollup-linux-arm64-gnu': 4.7.0
+ '@rollup/rollup-linux-arm64-musl': 4.7.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.7.0
+ '@rollup/rollup-linux-x64-gnu': 4.7.0
+ '@rollup/rollup-linux-x64-musl': 4.7.0
+ '@rollup/rollup-win32-arm64-msvc': 4.7.0
+ '@rollup/rollup-win32-ia32-msvc': 4.7.0
+ '@rollup/rollup-win32-x64-msvc': 4.7.0
+ fsevents: 2.3.3
+ dev: true
+
/run-async@2.4.1:
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
engines: {node: '>=0.12.0'}
@@ -9768,6 +10375,10 @@ packages:
get-intrinsic: 1.2.1
object-inspect: 1.12.3
+ /siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+ dev: true
+
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -9864,10 +10475,18 @@ packages:
escape-string-regexp: 2.0.0
dev: true
+ /stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+ dev: true
+
/standard-as-callback@2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
dev: false
+ /std-env@3.6.0:
+ resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==}
+ dev: true
+
/streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -9968,10 +10587,21 @@ packages:
engines: {node: '>=6'}
dev: true
+ /strip-final-newline@3.0.0:
+ resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+ engines: {node: '>=12'}
+ dev: true
+
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
+ /strip-literal@1.3.0:
+ resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
+ dependencies:
+ acorn: 8.10.0
+ dev: true
+
/style-to-object@0.4.2:
resolution: {integrity: sha512-1JGpfPB3lo42ZX8cuPrheZbfQ6kqPPnPHlKMyeRYtfKD+0jG+QsXgXN57O/dvJlzlB2elI6dGmrPnl5VPQFPaA==}
dependencies:
@@ -10218,6 +10848,20 @@ packages:
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
dev: false
+ /tinybench@2.5.1:
+ resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==}
+ dev: true
+
+ /tinypool@0.8.1:
+ resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /tinyspy@2.2.0:
+ resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
/tmp@0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
@@ -10409,6 +11053,19 @@ packages:
resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==}
dev: false
+ /tsconfck@2.1.2(typescript@5.2.2):
+ resolution: {integrity: sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==}
+ engines: {node: ^14.13.1 || ^16 || >=18}
+ hasBin: true
+ peerDependencies:
+ typescript: ^4.3.5 || ^5.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ typescript: 5.2.2
+ dev: true
+
/tsconfig-paths-webpack-plugin@4.1.0:
resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==}
engines: {node: '>=10.13.0'}
@@ -10584,6 +11241,16 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ /typescript@5.3.3:
+ resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+ dev: true
+
+ /ufo@1.3.2:
+ resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
+ dev: true
+
/uid@2.0.2:
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
engines: {node: '>=8'}
@@ -10828,6 +11495,137 @@ packages:
d3-timer: 3.0.1
dev: false
+ /vite-node@1.0.2(@types/node@20.8.10):
+ resolution: {integrity: sha512-h7BbMJf46fLvFW/9Ygo3snkIBEHFh6fHpB4lge98H5quYrDhPFeI3S0LREz328uqPWSnii2yeJXktQ+Pmqk5BQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ dependencies:
+ cac: 6.7.14
+ debug: 4.3.4
+ pathe: 1.1.1
+ picocolors: 1.0.0
+ vite: 5.0.7(@types/node@20.8.10)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
+ /vite-tsconfig-paths@4.2.2(typescript@5.2.2)(vite@5.0.7):
+ resolution: {integrity: sha512-dq0FjyxHHDnp0uS3P12WEOX2W7NeuLzX9AWP38D7Zw2CTbFErapwQVlCiT5DMJcVWKQ1MMdTe92PZl/rBQ7qcw==}
+ peerDependencies:
+ vite: '*'
+ peerDependenciesMeta:
+ vite:
+ optional: true
+ dependencies:
+ debug: 4.3.4
+ globrex: 0.1.2
+ tsconfck: 2.1.2(typescript@5.2.2)
+ vite: 5.0.7(@types/node@20.8.10)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+ dev: true
+
+ /vite@5.0.7(@types/node@20.8.10):
+ resolution: {integrity: sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ dependencies:
+ '@types/node': 20.8.10
+ esbuild: 0.19.8
+ postcss: 8.4.32
+ rollup: 4.7.0
+ optionalDependencies:
+ fsevents: 2.3.3
+ dev: true
+
+ /vitest@1.0.2(@types/node@20.8.10):
+ resolution: {integrity: sha512-F3NVwwpXfRSDnJmyv+ALPwSRVt0zDkRRE18pwUHSUPXAlWQ47rY1dc99ziMW5bBHyqwK2ERjMisLNoef64qk9w==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/node': ^18.0.0 || >=20.0.0
+ '@vitest/browser': ^1.0.0
+ '@vitest/ui': ^1.0.0
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ dependencies:
+ '@types/node': 20.8.10
+ '@vitest/expect': 1.0.2
+ '@vitest/runner': 1.0.2
+ '@vitest/snapshot': 1.0.2
+ '@vitest/spy': 1.0.2
+ '@vitest/utils': 1.0.2
+ acorn-walk: 8.3.1
+ cac: 6.7.14
+ chai: 4.3.10
+ debug: 4.3.4
+ execa: 8.0.1
+ local-pkg: 0.5.0
+ magic-string: 0.30.5
+ pathe: 1.1.1
+ picocolors: 1.0.0
+ std-env: 3.6.0
+ strip-literal: 1.3.0
+ tinybench: 2.5.1
+ tinypool: 0.8.1
+ vite: 5.0.7(@types/node@20.8.10)
+ vite-node: 1.0.2(@types/node@20.8.10)
+ why-is-node-running: 2.2.2
+ transitivePeerDependencies:
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
/walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
dependencies:
@@ -10947,6 +11745,15 @@ packages:
dependencies:
isexe: 2.0.0
+ /why-is-node-running@2.2.2:
+ resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
+ engines: {node: '>=8'}
+ hasBin: true
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+ dev: true
+
/wide-align@1.1.5:
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
dependencies:
@@ -11039,6 +11846,14 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+ /yocto-queue@1.0.0:
+ resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
+ engines: {node: '>=12.20'}
+ dev: true
+
+ /zod@3.22.4:
+ resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
+
/zustand@4.3.6(react@18.2.0):
resolution: {integrity: sha512-6J5zDxjxLE+yukC2XZWf/IyWVKnXT9b9HUv09VJ/bwGCpKNcaTqp7Ws28Xr8jnbvnZcdRaidztAPsXFBIqufiw==}
engines: {node: '>=12.7.0'}
diff --git a/prisma/index.d.ts b/prisma/index.d.ts
new file mode 100644
index 00000000..4f1cce44
--- /dev/null
+++ b/prisma/index.d.ts
@@ -0,0 +1 @@
+export * from './client';
diff --git a/prisma/index.mjs b/prisma/index.mjs
new file mode 100644
index 00000000..83dae763
--- /dev/null
+++ b/prisma/index.mjs
@@ -0,0 +1 @@
+export * from './client'
diff --git a/test/dto/index.spec.ts b/test/dto/index.spec.ts
new file mode 100644
index 00000000..67c6dc54
--- /dev/null
+++ b/test/dto/index.spec.ts
@@ -0,0 +1,21 @@
+import { describe, expect, test } from 'vitest';
+
+import { AuthDTO } from 'shared';
+
+describe('[DTO] Auth', () => {
+ test('normal email is safe for identity', () => {
+ expect(AuthDTO.identitySchema.safeParse('example@gmail.com')).toBeSafe();
+ });
+ test('string is not safe for identity', () => {
+ expect(AuthDTO.identitySchema.safeParse('example.com')).not.toBeSafe();
+ });
+ test('chinese phone is safe for identity', () => {
+ expect(AuthDTO.identitySchema.safeParse('13800138000')).toBeSafe();
+ });
+ test('too short is safe for password', () => {
+ expect(AuthDTO.passwordSchema.safeParse('123456')).not.toBeSafe();
+ });
+ test('6-digit is safe for validate code', () => {
+ expect(AuthDTO.codeSchema.safeParse('123456')).toBeSafe();
+ });
+});
diff --git a/test/schema.ts b/test/schema.ts
new file mode 100644
index 00000000..a994b3bf
--- /dev/null
+++ b/test/schema.ts
@@ -0,0 +1,17 @@
+import { ZodError } from 'zod';
+
+export function toBeSafe(
+ received: { success: true; data: T; error?: ZodError },
+ expected?: T,
+) {
+ // @ts-ignore
+ const { isNot } = this;
+ return {
+ // 请勿根据 isNot 参数更改你的 "pass" 值,Vitest 为你做了这件事情
+ pass:
+ expected !== undefined
+ ? received.success && received.data === expected
+ : received.success,
+ message: () => `${received.data} is${isNot ? ' not' : ''} safe`,
+ };
+}
diff --git a/test/setup-file.ts b/test/setup-file.ts
new file mode 100644
index 00000000..3fc122d6
--- /dev/null
+++ b/test/setup-file.ts
@@ -0,0 +1,21 @@
+import { expect } from 'vitest';
+
+import { toBeSafe } from '@test/schema';
+
+expect.extend({ toBeSafe });
+
+// import { redisHelper } from './helper/redis-mock.helper'
+// import { prisma } from './lib/prisma'
+// import resetDb from './lib/reset-db'
+//
+// beforeAll(() => {})
+//
+// beforeEach(async () => {
+// await resetDb()
+// })
+//
+// afterAll(async () => {
+// await resetDb()
+// await prisma.$disconnect()
+// await (await redisHelper).close()
+// })
diff --git a/test/setup.ts b/test/setup.ts
new file mode 100644
index 00000000..cc40a464
--- /dev/null
+++ b/test/setup.ts
@@ -0,0 +1 @@
+module.exports = () => {};
diff --git a/test/tsconfig.json b/test/tsconfig.json
new file mode 100644
index 00000000..623dd425
--- /dev/null
+++ b/test/tsconfig.json
@@ -0,0 +1,63 @@
+{
+ "compilerOptions": {
+ "module": "CommonJS",
+ "declaration": true,
+ "strict": true,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "es2022",
+ "sourceMap": true,
+ "outDir": "./dist",
+ "noEmit": true,
+ "allowJs": true,
+ "noImplicitAny": false,
+ "incremental": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "types": [
+ "node",
+ "vitest/globals",
+ "./vitest.d.ts"
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "frontend": [
+ "../packages/frontend/src"
+ ],
+ "frontend/*": [
+ "../packages/frontend/src/*"
+ ],
+ "backend": [
+ "../packages/backend/src"
+ ],
+ "backend/*": [
+ "../packages/backend/src/*"
+ ],
+ "shared": [
+ "../packages/shared/src"
+ ],
+ "shared/*": [
+ "../packages/shared/src/*"
+ ],
+ "@test": [
+ "."
+ ],
+ "@test/*": [
+ "./*"
+ ],
+ "@prisma/client": [
+ "../prisma/client"
+ ]
+ }
+ },
+ "include": [
+ "./**/*.ts",
+ "./vitest.d.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/test/vitest.d.ts b/test/vitest.d.ts
new file mode 100644
index 00000000..074e46a4
--- /dev/null
+++ b/test/vitest.d.ts
@@ -0,0 +1,9 @@
+interface CustomMatchers {
+ toBeFoo(): R;
+}
+
+declare module 'vitest' {
+ interface Assertion extends CustomMatchers {}
+
+ interface AsymmetricMatchersContaining extends CustomMatchers {}
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..3d6b9b07
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,53 @@
+{
+ "compilerOptions": {
+ "module": "CommonJS",
+ "declaration": true,
+ "strictNullChecks": true,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "target": "es2021",
+ "sourceMap": true,
+ "outDir": "./dist",
+ "baseUrl": ".",
+ "noImplicitAny": false,
+ "incremental": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "paths": {
+ "frontend": [
+ "./packages/frontend/src"
+ ],
+ "frontend/*": [
+ "./packages/frontend/src/*"
+ ],
+ "backend": [
+ "./packages/backend/src"
+ ],
+ "backend/*": [
+ "./packages/backend/src/*"
+ ],
+ "shared": [
+ "./packages/shared/src"
+ ],
+ "shared/*": [
+ "./packages/shared/src/*"
+ ],
+ "@test": [
+ "."
+ ],
+ "@test/*": [
+ "./*"
+ ],
+ "@prisma/client": [
+ "./prisma/client"
+ ]
+ }
+ },
+ "exclude": [
+ "dist",
+ "tmp"
+ ]
+}
diff --git a/turbo.json b/turbo.json
index e4260cbe..bf6f8754 100644
--- a/turbo.json
+++ b/turbo.json
@@ -10,6 +10,15 @@
"^db:generate"
]
},
+ "frontend#build": {
+ "inputs": ["config.json"],
+ "dependsOn": [
+ "shared#build"
+ ],
+ "outputs": [
+ "packages/backend/.next/**"
+ ]
+ },
"backend#build": {
"dependsOn": [
"db:generate",
@@ -31,10 +40,8 @@
"!.next/cache/**"
]
},
- "test": {
- "dependsOn": [
- "^test"
- ]
+ "//#test": {
+ "dependsOn": []
},
"start": {
"dependsOn": [
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 00000000..6d9d1a01
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,24 @@
+import { resolve } from 'path';
+import tsconfigPath from 'vite-tsconfig-paths';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ root: './test',
+ test: {
+ include: ['**/*.spec.ts', '**/*.e2e-spec.ts'],
+
+ globals: true,
+ setupFiles: [resolve(__dirname, './test/setup-file.ts')],
+ environment: 'node',
+ includeSource: [resolve(__dirname, './test')],
+ },
+
+ plugins: [
+ tsconfigPath({
+ projects: [
+ resolve(__dirname, './test/tsconfig.json'),
+ resolve(__dirname, './tsconfig.json'),
+ ],
+ }),
+ ],
+});