初始化足球投注平台 MVP Monorepo
包含 NestJS 后端、三端前端、Prisma 数据模型、结算引擎测试与 PRD 文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
8
.env.example
Normal file
8
.env.example
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
DATABASE_URL=postgresql://thebet365:thebet365@localhost:5432/thebet365
|
||||||
|
REDIS_URL=redis://localhost:6379
|
||||||
|
JWT_SECRET=change-me-in-production-use-long-random-string
|
||||||
|
JWT_PLAYER_EXPIRES=24h
|
||||||
|
JWT_ADMIN_EXPIRES=2h
|
||||||
|
JWT_AGENT_EXPIRES=8h
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=development
|
||||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
coverage/
|
||||||
|
.turbo/
|
||||||
|
*.tsbuildinfo
|
||||||
|
apps/api/prisma/migrations/*_migration_lock.toml
|
||||||
6
.npmrc
Normal file
6
.npmrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
only-built-dependencies[]=@prisma/client
|
||||||
|
only-built-dependencies[]=@prisma/engines
|
||||||
|
only-built-dependencies[]=prisma
|
||||||
|
only-built-dependencies[]=@nestjs/core
|
||||||
|
only-built-dependencies[]=esbuild
|
||||||
|
only-built-dependencies[]=vue-demi
|
||||||
88
README.md
Normal file
88
README.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# TheBet365 - 足球投注平台 MVP
|
||||||
|
|
||||||
|
Monorepo 项目,包含 NestJS 后端与 Vue 3 三端前端。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
| 层级 | 技术 |
|
||||||
|
|------|------|
|
||||||
|
| 后端 | NestJS + Prisma + PostgreSQL + Redis |
|
||||||
|
| 玩家前台 | Vue 3 + Vite + Pinia (H5 移动端优先) |
|
||||||
|
| 平台/代理后台 | Vue 3 + Vite + Element Plus |
|
||||||
|
| 包管理 | pnpm workspace |
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 启动基础设施
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置环境
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example apps/api/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 数据库迁移与种子数据
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm db:migrate
|
||||||
|
pnpm db:seed
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 启动开发服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev:api # API http://localhost:3000
|
||||||
|
pnpm dev:player # 玩家前台 http://localhost:5173
|
||||||
|
pnpm dev:admin # 平台后台 http://localhost:5174
|
||||||
|
pnpm dev:agent # 代理后台 http://localhost:5175
|
||||||
|
```
|
||||||
|
|
||||||
|
API 文档:http://localhost:3000/api/docs
|
||||||
|
|
||||||
|
## 测试账号
|
||||||
|
|
||||||
|
| 角色 | 用户名 | 密码 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 超级管理员 | admin | Admin@123 |
|
||||||
|
| 一级代理 | agent1 | Agent@123 |
|
||||||
|
| 二级代理 | agent2 | Agent@123 |
|
||||||
|
| 玩家 | player1 | Player@123 |
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/
|
||||||
|
api/ NestJS 单体后端
|
||||||
|
player/ 玩家 H5 前台
|
||||||
|
admin/ 平台后台
|
||||||
|
agent/ 代理后台
|
||||||
|
packages/
|
||||||
|
shared/ 共享类型与常量
|
||||||
|
```
|
||||||
|
|
||||||
|
## MVP 功能
|
||||||
|
|
||||||
|
- 三端登录鉴权 + RBAC
|
||||||
|
- 2 级代理 + 信用额度池
|
||||||
|
- 钱包账变(禁止直接改余额)
|
||||||
|
- 11 种足球玩法 + 盘口模板
|
||||||
|
- 单关 / 2~5 串 1 下注
|
||||||
|
- 结算预览 + 确认入账
|
||||||
|
- 返水批次
|
||||||
|
- Banner / 公告 / 多语言
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm --filter @thebet365/api test
|
||||||
|
```
|
||||||
12
apps/admin/index.html
Normal file
12
apps/admin/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>TheBet365 Admin</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
23
apps/admin/package.json
Normal file
23
apps/admin/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@thebet365/admin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --port 5174",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"element-plus": "^2.9.3",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vite": "^6.0.11",
|
||||||
|
"vue-tsc": "^2.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
apps/admin/src/App.vue
Normal file
4
apps/admin/src/App.vue
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterView } from 'vue-router';
|
||||||
|
</script>
|
||||||
|
<template><RouterView /></template>
|
||||||
9
apps/admin/src/api.ts
Normal file
9
apps/admin/src/api.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const api = axios.create({ baseURL: '/api' });
|
||||||
|
api.interceptors.request.use((c) => {
|
||||||
|
const t = localStorage.getItem('admin_token');
|
||||||
|
if (t) c.headers.Authorization = `Bearer ${t}`;
|
||||||
|
return c;
|
||||||
|
});
|
||||||
|
export default api;
|
||||||
40
apps/admin/src/layouts/AdminLayout.vue
Normal file
40
apps/admin/src/layouts/AdminLayout.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterView, RouterLink, useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
localStorage.removeItem('admin_token');
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
const menus = [
|
||||||
|
{ path: '/', label: '控制台' },
|
||||||
|
{ path: '/users', label: '玩家管理' },
|
||||||
|
{ path: '/agents', label: '代理管理' },
|
||||||
|
{ path: '/matches', label: '赛事管理' },
|
||||||
|
{ path: '/bets', label: '注单管理' },
|
||||||
|
{ path: '/cashback', label: '返水管理' },
|
||||||
|
{ path: '/audit', label: '操作日志' },
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-container style="min-height: 100vh">
|
||||||
|
<el-aside width="200px" style="background: #1a2332">
|
||||||
|
<div style="padding: 20px; color: #00a826; font-weight: 800">TheBet365 Admin</div>
|
||||||
|
<el-menu background-color="#1a2332" text-color="#ccc" active-text-color="#00a826" :default-active="route.path">
|
||||||
|
<el-menu-item v-for="m in menus" :key="m.path" :index="m.path">
|
||||||
|
<RouterLink :to="m.path" style="color: inherit; width: 100%">{{ m.label }}</RouterLink>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-aside>
|
||||||
|
<el-container>
|
||||||
|
<el-header style="display: flex; justify-content: flex-end; align-items: center; border-bottom: 1px solid #eee">
|
||||||
|
<el-button @click="logout">退出</el-button>
|
||||||
|
</el-header>
|
||||||
|
<el-main><RouterView /></el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
7
apps/admin/src/main.ts
Normal file
7
apps/admin/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createApp } from 'vue';
|
||||||
|
import ElementPlus from 'element-plus';
|
||||||
|
import 'element-plus/dist/index.css';
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
|
||||||
|
createApp(App).use(router).use(ElementPlus).mount('#app');
|
||||||
23
apps/admin/src/router/index.ts
Normal file
23
apps/admin/src/router/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
|
export default createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/login', component: () => import('./views/Login.vue') },
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('./layouts/AdminLayout.vue'),
|
||||||
|
meta: { auth: true },
|
||||||
|
children: [
|
||||||
|
{ path: '', component: () => import('./views/Dashboard.vue') },
|
||||||
|
{ path: 'users', component: () => import('./views/Users.vue') },
|
||||||
|
{ path: 'agents', component: () => import('./views/Agents.vue') },
|
||||||
|
{ path: 'matches', component: () => import('./views/Matches.vue') },
|
||||||
|
{ path: 'bets', component: () => import('./views/Bets.vue') },
|
||||||
|
{ path: 'settlement/:id', component: () => import('./views/Settlement.vue') },
|
||||||
|
{ path: 'cashback', component: () => import('./views/Cashback.vue') },
|
||||||
|
{ path: 'audit', component: () => import('./views/Audit.vue') },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
52
apps/admin/src/views/Agents.vue
Normal file
52
apps/admin/src/views/Agents.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const agents = ref<unknown[]>([]);
|
||||||
|
const form = ref({ username: '', password: 'Agent@123', creditLimit: 50000 });
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const { data } = await api.get('/admin/agents');
|
||||||
|
agents.value = data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
await api.post('/admin/agents', form.value);
|
||||||
|
ElMessage.success('创建成功');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function adjustCredit(agent: { userId: string }, amount: number) {
|
||||||
|
await api.post(`/admin/agents/${agent.userId}/credit`, {
|
||||||
|
amount,
|
||||||
|
requestId: `credit-${Date.now()}`,
|
||||||
|
});
|
||||||
|
ElMessage.success('额度已调整');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>代理管理</h2>
|
||||||
|
<el-form inline style="margin: 16px 0">
|
||||||
|
<el-input v-model="form.username" placeholder="用户名" style="width: 120px" />
|
||||||
|
<el-input-number v-model="form.creditLimit" placeholder="额度" />
|
||||||
|
<el-button type="primary" @click="create">创建一级代理</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="agents">
|
||||||
|
<el-table-column label="用户名">
|
||||||
|
<template #default="{ row }">{{ (row as { user?: { username: string } }).user?.username }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="level" label="层级" />
|
||||||
|
<el-table-column prop="creditLimit" label="授信额度" />
|
||||||
|
<el-table-column prop="usedCredit" label="已用额度" />
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" @click="adjustCredit(row as { userId: string }, 10000)">+10000</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
23
apps/admin/src/views/Audit.vue
Normal file
23
apps/admin/src/views/Audit.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const logs = ref<unknown[]>([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/admin/audit-logs');
|
||||||
|
logs.value = data.data.items;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>操作日志</h2>
|
||||||
|
<el-table :data="logs">
|
||||||
|
<el-table-column prop="action" label="操作" />
|
||||||
|
<el-table-column prop="module" label="模块" />
|
||||||
|
<el-table-column prop="targetId" label="目标" />
|
||||||
|
<el-table-column label="时间">
|
||||||
|
<template #default="{ row }">{{ new Date((row as { createdAt: string }).createdAt).toLocaleString() }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
24
apps/admin/src/views/Bets.vue
Normal file
24
apps/admin/src/views/Bets.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const bets = ref<unknown[]>([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/admin/bets');
|
||||||
|
bets.value = data.data.items;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>注单管理</h2>
|
||||||
|
<el-table :data="bets">
|
||||||
|
<el-table-column prop="betNo" label="注单号" />
|
||||||
|
<el-table-column prop="betType" label="类型" />
|
||||||
|
<el-table-column prop="stake" label="投注额" />
|
||||||
|
<el-table-column prop="status" label="状态" />
|
||||||
|
<el-table-column label="时间">
|
||||||
|
<template #default="{ row }">{{ new Date((row as { placedAt: string }).placedAt).toLocaleString() }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
39
apps/admin/src/views/Cashback.vue
Normal file
39
apps/admin/src/views/Cashback.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const preview = ref<Record<string, unknown> | null>(null);
|
||||||
|
const period = ref({
|
||||||
|
start: new Date(Date.now() - 7 * 86400000).toISOString().slice(0, 10),
|
||||||
|
end: new Date().toISOString().slice(0, 10),
|
||||||
|
});
|
||||||
|
|
||||||
|
async function generatePreview() {
|
||||||
|
const { data } = await api.post('/admin/cashbacks/preview', {
|
||||||
|
periodStart: period.value.start,
|
||||||
|
periodEnd: period.value.end,
|
||||||
|
});
|
||||||
|
preview.value = data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirm() {
|
||||||
|
if (!preview.value?.batch) return;
|
||||||
|
await api.post(`/admin/cashbacks/${(preview.value.batch as { id: string }).id}/confirm`);
|
||||||
|
ElMessage.success('返水已发放');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>返水管理</h2>
|
||||||
|
<el-form inline>
|
||||||
|
<el-date-picker v-model="period.start" type="date" value-format="YYYY-MM-DD" />
|
||||||
|
<el-date-picker v-model="period.end" type="date" value-format="YYYY-MM-DD" />
|
||||||
|
<el-button @click="generatePreview">生成预览</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-card v-if="preview" style="margin-top: 16px">
|
||||||
|
<p>玩家数: {{ (preview.batch as { playerCount: number })?.playerCount }}</p>
|
||||||
|
<p>总金额: {{ preview.totalAmount }}</p>
|
||||||
|
<el-button type="success" @click="confirm">确认发放</el-button>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
21
apps/admin/src/views/Dashboard.vue
Normal file
21
apps/admin/src/views/Dashboard.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const stats = ref<Record<string, unknown>>({});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/admin/dashboard');
|
||||||
|
stats.value = data.data;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>控制台</h2>
|
||||||
|
<el-row :gutter="16" style="margin-top: 16px">
|
||||||
|
<el-col :span="6"><el-statistic title="今日投注笔数" :value="(stats.todayBetCount as number) || 0" /></el-col>
|
||||||
|
<el-col :span="6"><el-statistic title="今日投注额" :value="Number(stats.todayStake) || 0" :precision="2" /></el-col>
|
||||||
|
<el-col :span="6"><el-statistic title="今日派彩" :value="Number(stats.todayPayout) || 0" :precision="2" /></el-col>
|
||||||
|
<el-col :span="6"><el-statistic title="待结算赛事" :value="(stats.pendingSettlement as number) || 0" /></el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
40
apps/admin/src/views/Login.vue
Normal file
40
apps/admin/src/views/Login.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const form = ref({ username: 'admin', password: 'Admin@123' });
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const { data } = await api.post('/admin/auth/login', form.value);
|
||||||
|
localStorage.setItem('admin_token', data.data.token);
|
||||||
|
router.push('/');
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('登录失败');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div style="min-height: 100vh; display: flex; align-items: center; justify-content: center">
|
||||||
|
<el-card style="width: 400px">
|
||||||
|
<h2 style="text-align: center; margin-bottom: 24px">平台后台登录</h2>
|
||||||
|
<el-form @submit.prevent="login">
|
||||||
|
<el-form-item label="用户名">
|
||||||
|
<el-input v-model="form.username" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码">
|
||||||
|
<el-input v-model="form.password" type="password" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-button type="primary" native-type="submit" :loading="loading" style="width: 100%">登录</el-button>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
74
apps/admin/src/views/Matches.vue
Normal file
74
apps/admin/src/views/Matches.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const matches = ref<unknown[]>([]);
|
||||||
|
const form = ref({
|
||||||
|
leagueId: '',
|
||||||
|
homeTeamId: '',
|
||||||
|
awayTeamId: '',
|
||||||
|
startTime: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const { data } = await api.get('/admin/matches');
|
||||||
|
matches.value = data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
await api.post('/admin/matches', form.value);
|
||||||
|
ElMessage.success('赛事已创建');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function publish(id: string) {
|
||||||
|
await api.post(`/admin/matches/${id}/publish`);
|
||||||
|
await api.post(`/admin/matches/${id}/markets/templates`, {
|
||||||
|
marketTypes: ['FT_1X2', 'FT_HANDICAP', 'FT_OVER_UNDER', 'FT_ODD_EVEN'],
|
||||||
|
});
|
||||||
|
ElMessage.success('已发布并生成盘口');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function close(id: string) {
|
||||||
|
await api.post(`/admin/matches/${id}/close`);
|
||||||
|
ElMessage.success('已封盘');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
function settle(id: string) {
|
||||||
|
router.push(`/settlement/${id}`);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>赛事管理</h2>
|
||||||
|
<el-card style="margin-bottom: 16px">
|
||||||
|
<el-form inline>
|
||||||
|
<el-input v-model="form.leagueId" placeholder="联赛ID" style="width: 100px" />
|
||||||
|
<el-input v-model="form.homeTeamId" placeholder="主队ID" style="width: 100px" />
|
||||||
|
<el-input v-model="form.awayTeamId" placeholder="客队ID" style="width: 100px" />
|
||||||
|
<el-input v-model="form.startTime" placeholder="开赛时间 ISO" style="width: 200px" />
|
||||||
|
<el-button type="primary" @click="create">创建赛事</el-button>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
<el-table :data="matches">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" />
|
||||||
|
<el-table-column prop="status" label="状态" />
|
||||||
|
<el-table-column label="开赛时间">
|
||||||
|
<template #default="{ row }">{{ new Date((row as { startTime: string }).startTime).toLocaleString() }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="300">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button v-if="(row as { status: string }).status === 'DRAFT'" size="small" @click="publish((row as { id: string }).id)">发布</el-button>
|
||||||
|
<el-button v-if="(row as { status: string }).status === 'PUBLISHED'" size="small" @click="close((row as { id: string }).id)">封盘</el-button>
|
||||||
|
<el-button size="small" type="warning" @click="settle((row as { id: string }).id)">结算</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
46
apps/admin/src/views/Settlement.vue
Normal file
46
apps/admin/src/views/Settlement.vue
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const score = ref({ htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 });
|
||||||
|
const preview = ref<Record<string, unknown> | null>(null);
|
||||||
|
|
||||||
|
async function recordScore() {
|
||||||
|
await api.post(`/admin/matches/${route.params.id}/settlement/score`, score.value);
|
||||||
|
ElMessage.success('比分已录入');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function previewSettlement() {
|
||||||
|
const { data } = await api.post(`/admin/matches/${route.params.id}/settlement/preview`);
|
||||||
|
preview.value = data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirm() {
|
||||||
|
if (!preview.value?.batch) return;
|
||||||
|
await api.post(`/admin/settlement/${(preview.value.batch as { id: string }).id}/confirm`);
|
||||||
|
ElMessage.success('结算已确认');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>赛事结算 #{{ route.params.id }}</h2>
|
||||||
|
<el-form inline style="margin: 16px 0">
|
||||||
|
<el-input-number v-model="score.htHome" :min="0" />
|
||||||
|
<el-input-number v-model="score.htAway" :min="0" />
|
||||||
|
<span>半场</span>
|
||||||
|
<el-input-number v-model="score.ftHome" :min="0" />
|
||||||
|
<el-input-number v-model="score.ftAway" :min="0" />
|
||||||
|
<span>全场</span>
|
||||||
|
<el-button @click="recordScore">录入比分</el-button>
|
||||||
|
<el-button type="primary" @click="previewSettlement">生成预览</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-card v-if="preview">
|
||||||
|
<p>单关注单: {{ preview.singleBetCount }}</p>
|
||||||
|
<p>预计派彩: {{ preview.totalPayout }}</p>
|
||||||
|
<p>退款: {{ preview.totalRefund }}</p>
|
||||||
|
<el-button type="success" @click="confirm">确认结算</el-button>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
38
apps/admin/src/views/Users.vue
Normal file
38
apps/admin/src/views/Users.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const users = ref<unknown[]>([]);
|
||||||
|
const form = ref({ username: '', password: 'Player@123', parentId: '' });
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const { data } = await api.get('/admin/users');
|
||||||
|
users.value = data.data.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
await api.post('/admin/users', form.value);
|
||||||
|
ElMessage.success('创建成功');
|
||||||
|
form.value.username = '';
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>玩家管理</h2>
|
||||||
|
<el-form inline style="margin: 16px 0">
|
||||||
|
<el-input v-model="form.username" placeholder="用户名" style="width: 150px" />
|
||||||
|
<el-input v-model="form.password" placeholder="密码" style="width: 150px" />
|
||||||
|
<el-button type="primary" @click="create">创建玩家</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="users">
|
||||||
|
<el-table-column prop="username" label="用户名" />
|
||||||
|
<el-table-column prop="status" label="状态" />
|
||||||
|
<el-table-column label="余额">
|
||||||
|
<template #default="{ row }">{{ (row as { wallet?: { availableBalance: string } }).wallet?.availableBalance }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
5
apps/admin/src/vite-env.d.ts
vendored
Normal file
5
apps/admin/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue';
|
||||||
|
const component: DefineComponent<object, object, unknown>;
|
||||||
|
export default component;
|
||||||
|
}
|
||||||
1
apps/admin/tsconfig.json
Normal file
1
apps/admin/tsconfig.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true }, "include": ["src/**/*.ts", "src/**/*.vue"] }
|
||||||
10
apps/admin/vite.config.ts
Normal file
10
apps/admin/vite.config.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
server: {
|
||||||
|
port: 5174,
|
||||||
|
proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
5
apps/agent/index.html
Normal file
5
apps/agent/index.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head><meta charset="UTF-8" /><title>TheBet365 Agent</title></head>
|
||||||
|
<body><div id="app"></div><script type="module" src="/src/main.ts"></script></body>
|
||||||
|
</html>
|
||||||
23
apps/agent/package.json
Normal file
23
apps/agent/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@thebet365/agent",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --port 5175",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"element-plus": "^2.9.3",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vite": "^6.0.11",
|
||||||
|
"vue-tsc": "^2.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
apps/agent/src/App.vue
Normal file
1
apps/agent/src/App.vue
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<script setup lang="ts"><template><router-view /></template></script>
|
||||||
9
apps/agent/src/api.ts
Normal file
9
apps/agent/src/api.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const api = axios.create({ baseURL: '/api' });
|
||||||
|
api.interceptors.request.use((c) => {
|
||||||
|
const t = localStorage.getItem('agent_token');
|
||||||
|
if (t) c.headers.Authorization = `Bearer ${t}`;
|
||||||
|
return c;
|
||||||
|
});
|
||||||
|
export default api;
|
||||||
29
apps/agent/src/layouts/AgentLayout.vue
Normal file
29
apps/agent/src/layouts/AgentLayout.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterView, RouterLink, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
function logout() {
|
||||||
|
localStorage.removeItem('agent_token');
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-container style="min-height: 100vh">
|
||||||
|
<el-aside width="180px" style="background: #1a2332">
|
||||||
|
<div style="padding: 16px; color: #00a826; font-weight: 700">代理后台</div>
|
||||||
|
<el-menu background-color="#1a2332" text-color="#ccc" active-text-color="#00a826">
|
||||||
|
<el-menu-item index="/"><RouterLink to="/" style="color: inherit">概览</RouterLink></el-menu-item>
|
||||||
|
<el-menu-item index="/players"><RouterLink to="/players" style="color: inherit">直属玩家</RouterLink></el-menu-item>
|
||||||
|
<el-menu-item index="/agents"><RouterLink to="/agents" style="color: inherit">下级代理</RouterLink></el-menu-item>
|
||||||
|
<el-menu-item index="/bets"><RouterLink to="/bets" style="color: inherit">注单查询</RouterLink></el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-aside>
|
||||||
|
<el-container>
|
||||||
|
<el-header style="text-align: right; line-height: 60px">
|
||||||
|
<el-button @click="logout">退出</el-button>
|
||||||
|
</el-header>
|
||||||
|
<el-main><RouterView /></el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
7
apps/agent/src/main.ts
Normal file
7
apps/agent/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createApp } from 'vue';
|
||||||
|
import ElementPlus from 'element-plus';
|
||||||
|
import 'element-plus/dist/index.css';
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
|
||||||
|
createApp(App).use(router).use(ElementPlus).mount('#app');
|
||||||
18
apps/agent/src/router/index.ts
Normal file
18
apps/agent/src/router/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
|
export default createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/login', component: () => import('./views/Login.vue') },
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('./layouts/AgentLayout.vue'),
|
||||||
|
children: [
|
||||||
|
{ path: '', component: () => import('./views/Dashboard.vue') },
|
||||||
|
{ path: 'players', component: () => import('./views/Players.vue') },
|
||||||
|
{ path: 'agents', component: () => import('./views/SubAgents.vue') },
|
||||||
|
{ path: 'bets', component: () => import('./views/Bets.vue') },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
23
apps/agent/src/views/Bets.vue
Normal file
23
apps/agent/src/views/Bets.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const bets = ref<unknown[]>([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/agent/bets');
|
||||||
|
bets.value = data.data.items;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>下级注单</h2>
|
||||||
|
<el-table :data="bets">
|
||||||
|
<el-table-column prop="betNo" label="注单号" />
|
||||||
|
<el-table-column label="玩家">
|
||||||
|
<template #default="{ row }">{{ (row as { user?: { username: string } }).user?.username }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="stake" label="投注额" />
|
||||||
|
<el-table-column prop="status" label="状态" />
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
21
apps/agent/src/views/Dashboard.vue
Normal file
21
apps/agent/src/views/Dashboard.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const summary = ref<Record<string, unknown>>({});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/agent/reports/summary');
|
||||||
|
summary.value = data.data;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>代理概览</h2>
|
||||||
|
<el-row :gutter="16" style="margin-top: 16px">
|
||||||
|
<el-col :span="6"><el-statistic title="授信额度" :value="Number((summary.profile as { creditLimit?: string })?.creditLimit) || 0" /></el-col>
|
||||||
|
<el-col :span="6"><el-statistic title="已用额度" :value="Number((summary.profile as { usedCredit?: string })?.usedCredit) || 0" /></el-col>
|
||||||
|
<el-col :span="6"><el-statistic title="直属玩家" :value="(summary.directPlayerCount as number) || 0" /></el-col>
|
||||||
|
<el-col :span="6"><el-statistic title="今日投注" :value="Number(summary.todayStake) || 0" :precision="2" /></el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
32
apps/agent/src/views/Login.vue
Normal file
32
apps/agent/src/views/Login.vue
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const form = ref({ username: 'agent1', password: 'Agent@123' });
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
try {
|
||||||
|
const { data } = await api.post('/agent/auth/login', form.value);
|
||||||
|
localStorage.setItem('agent_token', data.data.token);
|
||||||
|
router.push('/');
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('登录失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div style="min-height: 100vh; display: flex; align-items: center; justify-content: center">
|
||||||
|
<el-card style="width: 360px">
|
||||||
|
<h2 style="text-align: center">代理后台登录</h2>
|
||||||
|
<el-form @submit.prevent="login" style="margin-top: 20px">
|
||||||
|
<el-input v-model="form.username" placeholder="用户名" style="margin-bottom: 12px" />
|
||||||
|
<el-input v-model="form.password" type="password" placeholder="密码" style="margin-bottom: 12px" />
|
||||||
|
<el-button type="primary" native-type="submit" style="width: 100%">登录</el-button>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
66
apps/agent/src/views/Players.vue
Normal file
66
apps/agent/src/views/Players.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const players = ref<unknown[]>([]);
|
||||||
|
const form = ref({ username: '', password: 'Player@123' });
|
||||||
|
const depositForm = ref({ playerId: '', amount: 100, requestId: '' });
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const { data } = await api.get('/agent/players');
|
||||||
|
players.value = data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
await api.post('/agent/players', form.value);
|
||||||
|
ElMessage.success('玩家已创建');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deposit() {
|
||||||
|
depositForm.value.requestId = `dep-${Date.now()}`;
|
||||||
|
await api.post(`/agent/players/${depositForm.value.playerId}/deposit`, {
|
||||||
|
amount: depositForm.value.amount,
|
||||||
|
requestId: depositForm.value.requestId,
|
||||||
|
});
|
||||||
|
ElMessage.success('上分成功');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withdraw(playerId: string, amount: number) {
|
||||||
|
await api.post(`/agent/players/${playerId}/withdraw`, {
|
||||||
|
amount,
|
||||||
|
requestId: `wd-${Date.now()}`,
|
||||||
|
});
|
||||||
|
ElMessage.success('下分成功');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>直属玩家</h2>
|
||||||
|
<el-form inline style="margin-bottom: 16px">
|
||||||
|
<el-input v-model="form.username" placeholder="用户名" />
|
||||||
|
<el-button type="primary" @click="create">创建玩家</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-form inline style="margin-bottom: 16px">
|
||||||
|
<el-input v-model="depositForm.playerId" placeholder="玩家ID" style="width: 100px" />
|
||||||
|
<el-input-number v-model="depositForm.amount" :min="1" />
|
||||||
|
<el-button type="success" @click="deposit">上分</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="players">
|
||||||
|
<el-table-column prop="id" label="ID" />
|
||||||
|
<el-table-column prop="username" label="用户名" />
|
||||||
|
<el-table-column label="余额">
|
||||||
|
<template #default="{ row }">{{ (row as { wallet?: { availableBalance: string } }).wallet?.availableBalance }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" @click="withdraw((row as { id: string }).id, 50)">下分50</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
37
apps/agent/src/views/SubAgents.vue
Normal file
37
apps/agent/src/views/SubAgents.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const agents = ref<unknown[]>([]);
|
||||||
|
const form = ref({ username: '', password: 'Agent@123', creditLimit: 10000 });
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const { data } = await api.get('/agent/agents');
|
||||||
|
agents.value = data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create() {
|
||||||
|
await api.post('/agent/agents', form.value);
|
||||||
|
ElMessage.success('下级代理已创建');
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>下级代理(仅一级代理可见)</h2>
|
||||||
|
<el-form inline style="margin-bottom: 16px">
|
||||||
|
<el-input v-model="form.username" placeholder="用户名" />
|
||||||
|
<el-input-number v-model="form.creditLimit" />
|
||||||
|
<el-button type="primary" @click="create">创建二级代理</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="agents">
|
||||||
|
<el-table-column label="用户名">
|
||||||
|
<template #default="{ row }">{{ (row as { user?: { username: string } }).user?.username }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="creditLimit" label="额度" />
|
||||||
|
<el-table-column prop="usedCredit" label="已用" />
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
5
apps/agent/src/vite-env.d.ts
vendored
Normal file
5
apps/agent/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue';
|
||||||
|
const component: DefineComponent<object, object, unknown>;
|
||||||
|
export default component;
|
||||||
|
}
|
||||||
1
apps/agent/tsconfig.json
Normal file
1
apps/agent/tsconfig.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true }, "include": ["src/**/*.ts", "src/**/*.vue"] }
|
||||||
10
apps/agent/vite.config.ts
Normal file
10
apps/agent/vite.config.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
server: {
|
||||||
|
port: 5175,
|
||||||
|
proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
14
apps/api/jest.config.js
Normal file
14
apps/api/jest.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||||
|
rootDir: 'src',
|
||||||
|
testRegex: '.*\\.spec\\.ts$',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(t|j)s$': 'ts-jest',
|
||||||
|
},
|
||||||
|
collectCoverageFrom: ['**/*.(t|j)s'],
|
||||||
|
coverageDirectory: '../coverage',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^@/(.*)$': '<rootDir>/$1',
|
||||||
|
},
|
||||||
|
};
|
||||||
8
apps/api/nest-cli.json
Normal file
8
apps/api/nest-cli.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
59
apps/api/package.json
Normal file
59
apps/api/package.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"name": "@thebet365/api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"dev": "nest start --watch",
|
||||||
|
"start": "node dist/main",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"db:generate": "prisma generate",
|
||||||
|
"db:migrate": "prisma migrate dev",
|
||||||
|
"db:migrate:deploy": "prisma migrate deploy",
|
||||||
|
"db:seed": "ts-node prisma/seed.ts",
|
||||||
|
"db:studio": "prisma studio"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^11.0.6",
|
||||||
|
"@nestjs/config": "^4.0.0",
|
||||||
|
"@nestjs/core": "^11.0.6",
|
||||||
|
"@nestjs/jwt": "^11.0.0",
|
||||||
|
"@nestjs/passport": "^11.0.5",
|
||||||
|
"@nestjs/platform-express": "^11.0.6",
|
||||||
|
"@nestjs/schedule": "^5.0.1",
|
||||||
|
"@nestjs/swagger": "^11.0.3",
|
||||||
|
"@prisma/client": "^6.3.1",
|
||||||
|
"@thebet365/shared": "workspace:*",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"class-transformer": "^0.5.1",
|
||||||
|
"class-validator": "^0.14.1",
|
||||||
|
"decimal.js": "^10.4.3",
|
||||||
|
"ioredis": "^5.4.2",
|
||||||
|
"passport": "^0.7.0",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rxjs": "^7.8.1",
|
||||||
|
"uuid": "^11.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^11.0.2",
|
||||||
|
"@nestjs/schematics": "^11.0.0",
|
||||||
|
"@nestjs/testing": "^11.0.6",
|
||||||
|
"@types/bcryptjs": "^2.4.6",
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/node": "^22.10.7",
|
||||||
|
"@types/passport-jwt": "^4.0.1",
|
||||||
|
"@types/uuid": "^10.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prisma": "^6.3.1",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"prisma": {
|
||||||
|
"seed": "ts-node prisma/seed.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
apps/api/prisma/migrations/README.md
Normal file
5
apps/api/prisma/migrations/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-- Initial migration for TheBet365 MVP
|
||||||
|
-- Run: pnpm --filter @thebet365/api db:migrate
|
||||||
|
|
||||||
|
-- Generated from prisma/schema.prisma
|
||||||
|
-- Use `pnpm db:migrate` when PostgreSQL is available
|
||||||
585
apps/api/prisma/schema.prisma
Normal file
585
apps/api/prisma/schema.prisma
Normal file
@@ -0,0 +1,585 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Users & Auth ============
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
username String @unique @db.VarChar(64)
|
||||||
|
userType String @map("user_type") @db.VarChar(20)
|
||||||
|
status String @default("ACTIVE") @db.VarChar(20)
|
||||||
|
parentId BigInt? @map("parent_id")
|
||||||
|
agentLevel Int? @map("agent_level")
|
||||||
|
locale String @default("en-US") @db.VarChar(10)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
|
auth UserAuth?
|
||||||
|
wallet Wallet?
|
||||||
|
agentProfile AgentProfile?
|
||||||
|
adminRole AdminUserRole?
|
||||||
|
bets Bet[]
|
||||||
|
preferences UserPreference?
|
||||||
|
|
||||||
|
parent User? @relation("UserHierarchy", fields: [parentId], references: [id])
|
||||||
|
children User[] @relation("UserHierarchy")
|
||||||
|
|
||||||
|
@@index([userType])
|
||||||
|
@@index([parentId])
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserAuth {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
userId BigInt @unique @map("user_id")
|
||||||
|
passwordHash String @map("password_hash") @db.VarChar(255)
|
||||||
|
loginFailCount Int @default(0) @map("login_fail_count")
|
||||||
|
lockedUntil DateTime? @map("locked_until")
|
||||||
|
lastLoginAt DateTime? @map("last_login_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@map("user_auth")
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserPreference {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
userId BigInt @unique @map("user_id")
|
||||||
|
locale String @default("en-US") @db.VarChar(10)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@map("user_preferences")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Role {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
code String @unique @db.VarChar(64)
|
||||||
|
name String @db.VarChar(128)
|
||||||
|
description String? @db.VarChar(255)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
permissions RolePermission[]
|
||||||
|
adminUsers AdminUserRole[]
|
||||||
|
|
||||||
|
@@map("roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Permission {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
code String @unique @db.VarChar(128)
|
||||||
|
name String @db.VarChar(128)
|
||||||
|
module String @db.VarChar(64)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
roles RolePermission[]
|
||||||
|
|
||||||
|
@@map("permissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model RolePermission {
|
||||||
|
roleId BigInt @map("role_id")
|
||||||
|
permissionId BigInt @map("permission_id")
|
||||||
|
|
||||||
|
role Role @relation(fields: [roleId], references: [id])
|
||||||
|
permission Permission @relation(fields: [permissionId], references: [id])
|
||||||
|
|
||||||
|
@@id([roleId, permissionId])
|
||||||
|
@@map("role_permissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AdminUserRole {
|
||||||
|
userId BigInt @unique @map("user_id")
|
||||||
|
roleId BigInt @map("role_id")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
role Role @relation(fields: [roleId], references: [id])
|
||||||
|
|
||||||
|
@@map("admin_user_roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Agent ============
|
||||||
|
|
||||||
|
model AgentProfile {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
userId BigInt @unique @map("user_id")
|
||||||
|
level Int
|
||||||
|
parentAgentId BigInt? @map("parent_agent_id")
|
||||||
|
creditLimit Decimal @default(0) @map("credit_limit") @db.Decimal(18, 4)
|
||||||
|
usedCredit Decimal @default(0) @map("used_credit") @db.Decimal(18, 4)
|
||||||
|
directPlayerLiability Decimal @default(0) @map("direct_player_liability") @db.Decimal(18, 4)
|
||||||
|
childAgentExposure Decimal @default(0) @map("child_agent_exposure") @db.Decimal(18, 4)
|
||||||
|
status String @default("ACTIVE") @db.VarChar(20)
|
||||||
|
maxSingleDeposit Decimal? @map("max_single_deposit") @db.Decimal(18, 4)
|
||||||
|
maxDailyDeposit Decimal? @map("max_daily_deposit") @db.Decimal(18, 4)
|
||||||
|
cashbackRate Decimal @default(0) @map("cashback_rate") @db.Decimal(8, 4)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@index([parentAgentId])
|
||||||
|
@@map("agent_profiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AgentClosure {
|
||||||
|
ancestorId BigInt @map("ancestor_id")
|
||||||
|
descendantId BigInt @map("descendant_id")
|
||||||
|
depth Int
|
||||||
|
|
||||||
|
@@id([ancestorId, descendantId])
|
||||||
|
@@index([descendantId])
|
||||||
|
@@map("agent_closure")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AgentCreditTransaction {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
agentId BigInt @map("agent_id")
|
||||||
|
transactionType String @map("transaction_type") @db.VarChar(32)
|
||||||
|
amount Decimal @db.Decimal(18, 4)
|
||||||
|
creditBefore Decimal @map("credit_before") @db.Decimal(18, 4)
|
||||||
|
creditAfter Decimal @map("credit_after") @db.Decimal(18, 4)
|
||||||
|
referenceType String? @map("reference_type") @db.VarChar(32)
|
||||||
|
referenceId String? @map("reference_id") @db.VarChar(64)
|
||||||
|
operatorId BigInt? @map("operator_id")
|
||||||
|
requestId String? @map("request_id") @db.VarChar(128)
|
||||||
|
remark String? @db.VarChar(500)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
@@unique([operatorId, requestId])
|
||||||
|
@@index([agentId])
|
||||||
|
@@map("agent_credit_transactions")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Wallet ============
|
||||||
|
|
||||||
|
model Wallet {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
userId BigInt @unique @map("user_id")
|
||||||
|
availableBalance Decimal @default(0) @map("available_balance") @db.Decimal(18, 4)
|
||||||
|
frozenBalance Decimal @default(0) @map("frozen_balance") @db.Decimal(18, 4)
|
||||||
|
currency String @default("USD") @db.VarChar(16)
|
||||||
|
status String @default("ACTIVE") @db.VarChar(20)
|
||||||
|
version Int @default(0)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
transactions WalletTransaction[]
|
||||||
|
|
||||||
|
@@map("wallets")
|
||||||
|
}
|
||||||
|
|
||||||
|
model WalletTransaction {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
transactionId String @unique @map("transaction_id") @db.VarChar(64)
|
||||||
|
userId BigInt @map("user_id")
|
||||||
|
walletId BigInt @map("wallet_id")
|
||||||
|
transactionType String @map("transaction_type") @db.VarChar(32)
|
||||||
|
amount Decimal @db.Decimal(18, 4)
|
||||||
|
balanceBefore Decimal @map("balance_before") @db.Decimal(18, 4)
|
||||||
|
balanceAfter Decimal @map("balance_after") @db.Decimal(18, 4)
|
||||||
|
frozenBefore Decimal @map("frozen_before") @db.Decimal(18, 4)
|
||||||
|
frozenAfter Decimal @map("frozen_after") @db.Decimal(18, 4)
|
||||||
|
referenceType String? @map("reference_type") @db.VarChar(32)
|
||||||
|
referenceId String? @map("reference_id") @db.VarChar(64)
|
||||||
|
operatorId BigInt? @map("operator_id")
|
||||||
|
remark String? @db.VarChar(500)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
wallet Wallet @relation(fields: [walletId], references: [id])
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
@@index([walletId])
|
||||||
|
@@index([createdAt])
|
||||||
|
@@map("wallet_transactions")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Sports Data ============
|
||||||
|
|
||||||
|
model League {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
sportType String @default("FOOTBALL") @map("sport_type") @db.VarChar(20)
|
||||||
|
code String @unique @db.VarChar(64)
|
||||||
|
displayOrder Int @default(0) @map("display_order")
|
||||||
|
isActive Boolean @default(true) @map("is_active")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
|
matches Match[]
|
||||||
|
|
||||||
|
@@map("leagues")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Team {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
sportType String @default("FOOTBALL") @map("sport_type") @db.VarChar(20)
|
||||||
|
code String @unique @db.VarChar(64)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
|
homeMatches Match[] @relation("HomeTeam")
|
||||||
|
awayMatches Match[] @relation("AwayTeam")
|
||||||
|
|
||||||
|
@@map("teams")
|
||||||
|
}
|
||||||
|
|
||||||
|
model EntityTranslation {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
entityType String @map("entity_type") @db.VarChar(32)
|
||||||
|
entityId BigInt @map("entity_id")
|
||||||
|
locale String @db.VarChar(10)
|
||||||
|
fieldName String @map("field_name") @db.VarChar(32)
|
||||||
|
value String @db.VarChar(500)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@unique([entityType, entityId, locale, fieldName])
|
||||||
|
@@index([entityType, entityId])
|
||||||
|
@@map("entity_translations")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Match {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
sportType String @default("FOOTBALL") @map("sport_type") @db.VarChar(20)
|
||||||
|
leagueId BigInt @map("league_id")
|
||||||
|
homeTeamId BigInt @map("home_team_id")
|
||||||
|
awayTeamId BigInt @map("away_team_id")
|
||||||
|
startTime DateTime @map("start_time")
|
||||||
|
status String @default("DRAFT") @db.VarChar(32)
|
||||||
|
isHot Boolean @default(false) @map("is_hot")
|
||||||
|
displayOrder Int @default(0) @map("display_order")
|
||||||
|
publishTime DateTime? @map("publish_time")
|
||||||
|
closeTime DateTime? @map("close_time")
|
||||||
|
isOutright Boolean @default(false) @map("is_outright")
|
||||||
|
createdBy BigInt? @map("created_by")
|
||||||
|
updatedBy BigInt? @map("updated_by")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
|
league League @relation(fields: [leagueId], references: [id])
|
||||||
|
homeTeam Team @relation("HomeTeam", fields: [homeTeamId], references: [id])
|
||||||
|
awayTeam Team @relation("AwayTeam", fields: [awayTeamId], references: [id])
|
||||||
|
score MatchScore?
|
||||||
|
markets Market[]
|
||||||
|
settlements SettlementBatch[]
|
||||||
|
|
||||||
|
@@index([status])
|
||||||
|
@@index([startTime])
|
||||||
|
@@index([leagueId])
|
||||||
|
@@map("matches")
|
||||||
|
}
|
||||||
|
|
||||||
|
model MatchScore {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
matchId BigInt @unique @map("match_id")
|
||||||
|
htHomeScore Int? @map("ht_home_score")
|
||||||
|
htAwayScore Int? @map("ht_away_score")
|
||||||
|
ftHomeScore Int? @map("ft_home_score")
|
||||||
|
ftAwayScore Int? @map("ft_away_score")
|
||||||
|
winnerTeamId BigInt? @map("winner_team_id")
|
||||||
|
recordedBy BigInt? @map("recorded_by")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
match Match @relation(fields: [matchId], references: [id])
|
||||||
|
|
||||||
|
@@map("match_scores")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Market {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
matchId BigInt @map("match_id")
|
||||||
|
marketType String @map("market_type") @db.VarChar(64)
|
||||||
|
period String @db.VarChar(16)
|
||||||
|
lineValue Decimal? @map("line_value") @db.Decimal(8, 2)
|
||||||
|
status String @default("OPEN") @db.VarChar(20)
|
||||||
|
allowSingle Boolean @default(true) @map("allow_single")
|
||||||
|
allowParlay Boolean @default(true) @map("allow_parlay")
|
||||||
|
sortOrder Int @default(0) @map("sort_order")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
match Match @relation(fields: [matchId], references: [id])
|
||||||
|
selections MarketSelection[]
|
||||||
|
|
||||||
|
@@index([matchId])
|
||||||
|
@@index([marketType])
|
||||||
|
@@map("markets")
|
||||||
|
}
|
||||||
|
|
||||||
|
model MarketSelection {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
marketId BigInt @map("market_id")
|
||||||
|
selectionCode String @map("selection_code") @db.VarChar(64)
|
||||||
|
selectionName String @map("selection_name") @db.VarChar(255)
|
||||||
|
odds Decimal @db.Decimal(18, 6)
|
||||||
|
oddsVersion BigInt @default(1) @map("odds_version")
|
||||||
|
status String @default("OPEN") @db.VarChar(20)
|
||||||
|
sortOrder Int @default(0) @map("sort_order")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
market Market @relation(fields: [marketId], references: [id])
|
||||||
|
oddsLogs OddsChangeLog[]
|
||||||
|
|
||||||
|
@@index([marketId])
|
||||||
|
@@map("market_selections")
|
||||||
|
}
|
||||||
|
|
||||||
|
model OddsChangeLog {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
selectionId BigInt @map("selection_id")
|
||||||
|
oldOdds Decimal @map("old_odds") @db.Decimal(18, 6)
|
||||||
|
newOdds Decimal @map("new_odds") @db.Decimal(18, 6)
|
||||||
|
oddsVersion BigInt @map("odds_version")
|
||||||
|
changedBy BigInt? @map("changed_by")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
selection MarketSelection @relation(fields: [selectionId], references: [id])
|
||||||
|
|
||||||
|
@@index([selectionId])
|
||||||
|
@@map("odds_change_logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Bets ============
|
||||||
|
|
||||||
|
model Bet {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
betNo String @unique @map("bet_no") @db.VarChar(64)
|
||||||
|
userId BigInt @map("user_id")
|
||||||
|
agentId BigInt? @map("agent_id")
|
||||||
|
betType String @map("bet_type") @db.VarChar(20)
|
||||||
|
stake Decimal @db.Decimal(18, 4)
|
||||||
|
totalOdds Decimal? @map("total_odds") @db.Decimal(18, 6)
|
||||||
|
potentialReturn Decimal? @map("potential_return") @db.Decimal(18, 4)
|
||||||
|
actualReturn Decimal @default(0) @map("actual_return") @db.Decimal(18, 4)
|
||||||
|
status String @default("PENDING") @db.VarChar(32)
|
||||||
|
settlementStatus String? @map("settlement_status") @db.VarChar(32)
|
||||||
|
currency String @default("USD") @db.VarChar(16)
|
||||||
|
requestId String @map("request_id") @db.VarChar(128)
|
||||||
|
placedAt DateTime @default(now()) @map("placed_at")
|
||||||
|
settledAt DateTime? @map("settled_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
selections BetSelection[]
|
||||||
|
|
||||||
|
@@unique([userId, requestId])
|
||||||
|
@@index([userId])
|
||||||
|
@@index([agentId])
|
||||||
|
@@index([status])
|
||||||
|
@@index([placedAt])
|
||||||
|
@@map("bets")
|
||||||
|
}
|
||||||
|
|
||||||
|
model BetSelection {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
betId BigInt @map("bet_id")
|
||||||
|
matchId BigInt? @map("match_id")
|
||||||
|
marketId BigInt @map("market_id")
|
||||||
|
selectionId BigInt @map("selection_id")
|
||||||
|
marketType String @map("market_type") @db.VarChar(64)
|
||||||
|
period String? @db.VarChar(16)
|
||||||
|
selectionNameSnapshot String @map("selection_name_snapshot") @db.VarChar(255)
|
||||||
|
handicapLine Decimal? @map("handicap_line") @db.Decimal(8, 2)
|
||||||
|
totalLine Decimal? @map("total_line") @db.Decimal(8, 2)
|
||||||
|
odds Decimal @db.Decimal(18, 6)
|
||||||
|
oddsVersion BigInt @map("odds_version")
|
||||||
|
resultStatus String? @map("result_status") @db.VarChar(32)
|
||||||
|
effectiveOdds Decimal? @map("effective_odds") @db.Decimal(18, 6)
|
||||||
|
sortOrder Int @default(0) @map("sort_order")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
bet Bet @relation(fields: [betId], references: [id])
|
||||||
|
|
||||||
|
@@index([betId])
|
||||||
|
@@index([matchId])
|
||||||
|
@@map("bet_selections")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Settlement ============
|
||||||
|
|
||||||
|
model SettlementBatch {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
matchId BigInt @map("match_id")
|
||||||
|
batchNo String @unique @map("batch_no") @db.VarChar(64)
|
||||||
|
htHomeScore Int? @map("ht_home_score")
|
||||||
|
htAwayScore Int? @map("ht_away_score")
|
||||||
|
ftHomeScore Int? @map("ft_home_score")
|
||||||
|
ftAwayScore Int? @map("ft_away_score")
|
||||||
|
status String @default("PREVIEW") @db.VarChar(20)
|
||||||
|
totalBets Int @default(0) @map("total_bets")
|
||||||
|
totalPayout Decimal @default(0) @map("total_payout") @db.Decimal(18, 4)
|
||||||
|
totalRefund Decimal @default(0) @map("total_refund") @db.Decimal(18, 4)
|
||||||
|
operatorId BigInt? @map("operator_id")
|
||||||
|
confirmedAt DateTime? @map("confirmed_at")
|
||||||
|
isResettle Boolean @default(false) @map("is_resettle")
|
||||||
|
reason String? @db.VarChar(500)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
match Match @relation(fields: [matchId], references: [id])
|
||||||
|
items SettlementItem[]
|
||||||
|
|
||||||
|
@@index([matchId])
|
||||||
|
@@map("settlement_batches")
|
||||||
|
}
|
||||||
|
|
||||||
|
model SettlementItem {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
batchId BigInt @map("batch_id")
|
||||||
|
betId BigInt @map("bet_id")
|
||||||
|
userId BigInt @map("user_id")
|
||||||
|
result String @db.VarChar(32)
|
||||||
|
payout Decimal @db.Decimal(18, 4)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
batch SettlementBatch @relation(fields: [batchId], references: [id])
|
||||||
|
|
||||||
|
@@index([batchId])
|
||||||
|
@@index([betId])
|
||||||
|
@@map("settlement_items")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Cashback ============
|
||||||
|
|
||||||
|
model CashbackRule {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
name String @db.VarChar(128)
|
||||||
|
targetType String @map("target_type") @db.VarChar(32)
|
||||||
|
targetId BigInt? @map("target_id")
|
||||||
|
rate Decimal @db.Decimal(8, 4)
|
||||||
|
marketType String? @map("market_type") @db.VarChar(64)
|
||||||
|
isActive Boolean @default(true) @map("is_active")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("cashback_rules")
|
||||||
|
}
|
||||||
|
|
||||||
|
model CashbackBatch {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
batchNo String @unique @map("batch_no") @db.VarChar(64)
|
||||||
|
periodStart DateTime @map("period_start")
|
||||||
|
periodEnd DateTime @map("period_end")
|
||||||
|
status String @default("PREVIEW") @db.VarChar(20)
|
||||||
|
totalAmount Decimal @default(0) @map("total_amount") @db.Decimal(18, 4)
|
||||||
|
playerCount Int @default(0) @map("player_count")
|
||||||
|
operatorId BigInt? @map("operator_id")
|
||||||
|
confirmedAt DateTime? @map("confirmed_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
items CashbackItem[]
|
||||||
|
|
||||||
|
@@map("cashback_batches")
|
||||||
|
}
|
||||||
|
|
||||||
|
model CashbackItem {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
batchId BigInt @map("batch_id")
|
||||||
|
userId BigInt @map("user_id")
|
||||||
|
effectiveStake Decimal @map("effective_stake") @db.Decimal(18, 4)
|
||||||
|
rate Decimal @db.Decimal(8, 4)
|
||||||
|
amount Decimal @db.Decimal(18, 4)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
batch CashbackBatch @relation(fields: [batchId], references: [id])
|
||||||
|
|
||||||
|
@@index([batchId])
|
||||||
|
@@index([userId])
|
||||||
|
@@map("cashback_items")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Content & i18n ============
|
||||||
|
|
||||||
|
model Content {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
contentType String @map("content_type") @db.VarChar(32)
|
||||||
|
sortOrder Int @default(0) @map("sort_order")
|
||||||
|
status String @default("DRAFT") @db.VarChar(20)
|
||||||
|
linkType String? @map("link_type") @db.VarChar(32)
|
||||||
|
linkTarget String? @map("link_target") @db.VarChar(500)
|
||||||
|
startTime DateTime? @map("start_time")
|
||||||
|
endTime DateTime? @map("end_time")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
translations ContentTranslation[]
|
||||||
|
|
||||||
|
@@index([contentType, status])
|
||||||
|
@@map("contents")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ContentTranslation {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
contentId BigInt @map("content_id")
|
||||||
|
locale String @db.VarChar(10)
|
||||||
|
title String? @db.VarChar(255)
|
||||||
|
body String? @db.Text
|
||||||
|
imageUrl String? @map("image_url") @db.VarChar(500)
|
||||||
|
|
||||||
|
content Content @relation(fields: [contentId], references: [id])
|
||||||
|
|
||||||
|
@@unique([contentId, locale])
|
||||||
|
@@map("content_translations")
|
||||||
|
}
|
||||||
|
|
||||||
|
model I18nMessage {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
msgKey String @map("msg_key") @db.VarChar(128)
|
||||||
|
locale String @db.VarChar(10)
|
||||||
|
value String @db.Text
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@unique([msgKey, locale])
|
||||||
|
@@map("i18n_messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ System Config & Audit ============
|
||||||
|
|
||||||
|
model SystemConfig {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
configKey String @unique @map("config_key") @db.VarChar(128)
|
||||||
|
configValue String @map("config_value") @db.Text
|
||||||
|
description String? @db.VarChar(255)
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
|
@@map("system_configs")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AuditLog {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
operatorId BigInt? @map("operator_id")
|
||||||
|
operatorType String @map("operator_type") @db.VarChar(20)
|
||||||
|
action String @db.VarChar(128)
|
||||||
|
module String @db.VarChar(64)
|
||||||
|
targetType String? @map("target_type") @db.VarChar(32)
|
||||||
|
targetId String? @map("target_id") @db.VarChar(64)
|
||||||
|
beforeData String? @map("before_data") @db.Text
|
||||||
|
afterData String? @map("after_data") @db.Text
|
||||||
|
ipAddress String? @map("ip_address") @db.VarChar(45)
|
||||||
|
userAgent String? @map("user_agent") @db.VarChar(500)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
@@index([operatorId])
|
||||||
|
@@index([module])
|
||||||
|
@@index([createdAt])
|
||||||
|
@@map("audit_logs")
|
||||||
|
}
|
||||||
182
apps/api/prisma/seed.ts
Normal file
182
apps/api/prisma/seed.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import * as bcrypt from 'bcryptjs';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Seeding database...');
|
||||||
|
|
||||||
|
const superAdminRole = await prisma.role.upsert({
|
||||||
|
where: { code: 'SUPER_ADMIN' },
|
||||||
|
create: { code: 'SUPER_ADMIN', name: 'Super Admin', description: 'Full access' },
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const permCodes = [
|
||||||
|
'users.create', 'users.view', 'agents.create', 'agents.view',
|
||||||
|
'wallet.deposit', 'wallet.withdraw', 'matches.manage', 'settlement.confirm',
|
||||||
|
'cashback.confirm', 'content.manage', 'reports.view',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const code of permCodes) {
|
||||||
|
const perm = await prisma.permission.upsert({
|
||||||
|
where: { code },
|
||||||
|
create: { code, name: code, module: code.split('.')[0] },
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
await prisma.rolePermission.upsert({
|
||||||
|
where: { roleId_permissionId: { roleId: superAdminRole.id, permissionId: perm.id } },
|
||||||
|
create: { roleId: superAdminRole.id, permissionId: perm.id },
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = await bcrypt.hash('Admin@123', 10);
|
||||||
|
const agentHash = await bcrypt.hash('Agent@123', 10);
|
||||||
|
const playerHash = await bcrypt.hash('Player@123', 10);
|
||||||
|
|
||||||
|
await prisma.user.upsert({
|
||||||
|
where: { username: 'admin' },
|
||||||
|
create: {
|
||||||
|
username: 'admin',
|
||||||
|
userType: 'ADMIN',
|
||||||
|
auth: { create: { passwordHash: hash } },
|
||||||
|
adminRole: { create: { roleId: superAdminRole.id } },
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent1 = await prisma.user.upsert({
|
||||||
|
where: { username: 'agent1' },
|
||||||
|
create: {
|
||||||
|
username: 'agent1',
|
||||||
|
userType: 'AGENT',
|
||||||
|
agentLevel: 1,
|
||||||
|
auth: { create: { passwordHash: agentHash } },
|
||||||
|
agentProfile: { create: { level: 1, creditLimit: 100000 } },
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.agentClosure.upsert({
|
||||||
|
where: { ancestorId_descendantId: { ancestorId: agent1.id, descendantId: agent1.id } },
|
||||||
|
create: { ancestorId: agent1.id, descendantId: agent1.id, depth: 0 },
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.user.upsert({
|
||||||
|
where: { username: 'agent2' },
|
||||||
|
create: {
|
||||||
|
username: 'agent2',
|
||||||
|
userType: 'AGENT',
|
||||||
|
agentLevel: 2,
|
||||||
|
parentId: agent1.id,
|
||||||
|
auth: { create: { passwordHash: agentHash } },
|
||||||
|
agentProfile: { create: { level: 2, parentAgentId: agent1.id, creditLimit: 30000 } },
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.user.upsert({
|
||||||
|
where: { username: 'player1' },
|
||||||
|
create: {
|
||||||
|
username: 'player1',
|
||||||
|
userType: 'PLAYER',
|
||||||
|
parentId: agent1.id,
|
||||||
|
auth: { create: { passwordHash: playerHash } },
|
||||||
|
wallet: { create: { availableBalance: 1000 } },
|
||||||
|
preferences: { create: { locale: 'zh-CN' } },
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
{ key: 'nav.home', zh: '首页', ms: 'Laman Utama', en: 'Home' },
|
||||||
|
{ key: 'nav.football', zh: '足球', ms: 'Bola Sepak', en: 'Football' },
|
||||||
|
{ key: 'bet.place_bet', zh: '确认下注', ms: 'Letak Pertaruhan', en: 'Place Bet' },
|
||||||
|
{ key: 'error.insufficient_balance', zh: '余额不足', ms: 'Baki tidak mencukupi', en: 'Insufficient balance' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const m of messages) {
|
||||||
|
for (const [locale, value] of [['zh-CN', m.zh], ['ms-MY', m.ms], ['en-US', m.en]] as const) {
|
||||||
|
await prisma.i18nMessage.upsert({
|
||||||
|
where: { msgKey_locale: { msgKey: m.key, locale } },
|
||||||
|
create: { msgKey: m.key, locale, value },
|
||||||
|
update: { value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const league = await prisma.league.upsert({
|
||||||
|
where: { code: 'EPL' },
|
||||||
|
create: { code: 'EPL' },
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.entityTranslation.upsert({
|
||||||
|
where: { entityType_entityId_locale_fieldName: { entityType: 'LEAGUE', entityId: league.id, locale: 'zh-CN', fieldName: 'name' } },
|
||||||
|
create: { entityType: 'LEAGUE', entityId: league.id, locale: 'zh-CN', fieldName: 'name', value: '英超' },
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const [code, name] of [['MUN', '曼联'], ['CHE', '切尔西']] as const) {
|
||||||
|
const team = await prisma.team.upsert({ where: { code }, create: { code }, update: {} });
|
||||||
|
await prisma.entityTranslation.upsert({
|
||||||
|
where: { entityType_entityId_locale_fieldName: { entityType: 'TEAM', entityId: team.id, locale: 'zh-CN', fieldName: 'name' } },
|
||||||
|
create: { entityType: 'TEAM', entityId: team.id, locale: 'zh-CN', fieldName: 'name', value: name },
|
||||||
|
update: { value: name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mun = await prisma.team.findUnique({ where: { code: 'MUN' } });
|
||||||
|
const che = await prisma.team.findUnique({ where: { code: 'CHE' } });
|
||||||
|
|
||||||
|
if (mun && che) {
|
||||||
|
const existing = await prisma.match.findFirst({ where: { homeTeamId: mun.id, awayTeamId: che.id } });
|
||||||
|
if (!existing) {
|
||||||
|
const match = await prisma.match.create({
|
||||||
|
data: {
|
||||||
|
leagueId: league.id,
|
||||||
|
homeTeamId: mun.id,
|
||||||
|
awayTeamId: che.id,
|
||||||
|
startTime: new Date(Date.now() + 86400000),
|
||||||
|
status: 'PUBLISHED',
|
||||||
|
isHot: true,
|
||||||
|
publishTime: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.market.create({
|
||||||
|
data: {
|
||||||
|
matchId: match.id,
|
||||||
|
marketType: 'FT_1X2',
|
||||||
|
period: 'FT',
|
||||||
|
selections: {
|
||||||
|
create: [
|
||||||
|
{ selectionCode: 'HOME', selectionName: 'Home', odds: 2.5 },
|
||||||
|
{ selectionCode: 'DRAW', selectionName: 'Draw', odds: 3.2 },
|
||||||
|
{ selectionCode: 'AWAY', selectionName: 'Away', odds: 2.8 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.content.create({
|
||||||
|
data: {
|
||||||
|
contentType: 'BANNER',
|
||||||
|
status: 'ACTIVE',
|
||||||
|
sortOrder: 1,
|
||||||
|
translations: {
|
||||||
|
create: [
|
||||||
|
{ locale: 'zh-CN', title: '欢迎投注', body: '足球赛事火热进行中' },
|
||||||
|
{ locale: 'en-US', title: 'Welcome', body: 'Football matches available' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).catch(() => {});
|
||||||
|
|
||||||
|
console.log('Seed completed! admin/Admin@123 agent1/Agent@123 player1/Player@123');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error).finally(() => prisma.$disconnect());
|
||||||
400
apps/api/src/admin/admin.controller.ts
Normal file
400
apps/api/src/admin/admin.controller.ts
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||||
|
import { JwtAuthGuard, AdminGuard } from '../auth/guards';
|
||||||
|
import { ContentService } from '../content/content.service';
|
||||||
|
import { CurrentUser } from '../common/decorators';
|
||||||
|
import { jsonResponse } from '../common/filters';
|
||||||
|
import { UsersService } from '../users/users.service';
|
||||||
|
import { AgentsService } from '../agents/agents.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { MatchesService } from '../matches/matches.service';
|
||||||
|
import { MarketsService } from '../markets/markets.service';
|
||||||
|
import { SettlementService } from '../settlement/settlement.service';
|
||||||
|
import { CashbackService } from '../cashback/cashback.service';
|
||||||
|
import { I18nService } from '../i18n/i18n.service';
|
||||||
|
import { AuditService } from '../audit/audit.service';
|
||||||
|
import { BetsService } from '../bets/bets.service';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { IsString, IsNumber, IsOptional, IsArray, IsBoolean, MinLength } from 'class-validator';
|
||||||
|
|
||||||
|
class CreateUserDto {
|
||||||
|
@IsString()
|
||||||
|
username!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@MinLength(8)
|
||||||
|
password!: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
parentId?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
creditLimit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DepositDto {
|
||||||
|
@IsNumber()
|
||||||
|
amount!: number;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
requestId!: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateMatchDto {
|
||||||
|
@IsString()
|
||||||
|
leagueId!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
homeTeamId!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
awayTeamId!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
startTime!: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
isHot?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScoreDto {
|
||||||
|
@IsNumber()
|
||||||
|
htHome!: number;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
htAway!: number;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
ftHome!: number;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
ftAway!: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MarketTemplatesDto {
|
||||||
|
@IsArray()
|
||||||
|
marketTypes!: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateOddsDto {
|
||||||
|
@IsNumber()
|
||||||
|
odds!: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CashbackPreviewDto {
|
||||||
|
@IsString()
|
||||||
|
periodStart!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
periodEnd!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiTags('Admin')
|
||||||
|
@Controller('admin')
|
||||||
|
@UseGuards(JwtAuthGuard, AdminGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
export class AdminController {
|
||||||
|
constructor(
|
||||||
|
private users: UsersService,
|
||||||
|
private agents: AgentsService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
private matches: MatchesService,
|
||||||
|
private markets: MarketsService,
|
||||||
|
private settlement: SettlementService,
|
||||||
|
private cashback: CashbackService,
|
||||||
|
private content: ContentService,
|
||||||
|
private i18n: I18nService,
|
||||||
|
private audit: AuditService,
|
||||||
|
private bets: BetsService,
|
||||||
|
private prisma: PrismaService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get('dashboard')
|
||||||
|
async dashboard() {
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const [todayBets, pendingMatches, totalPlayers] = await Promise.all([
|
||||||
|
this.prisma.bet.aggregate({
|
||||||
|
where: { placedAt: { gte: today } },
|
||||||
|
_sum: { stake: true, actualReturn: true },
|
||||||
|
_count: true,
|
||||||
|
}),
|
||||||
|
this.prisma.match.count({ where: { status: 'PENDING_SETTLEMENT' } }),
|
||||||
|
this.prisma.user.count({ where: { userType: 'PLAYER' } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return jsonResponse({
|
||||||
|
todayBetCount: todayBets._count,
|
||||||
|
todayStake: todayBets._sum.stake,
|
||||||
|
todayPayout: todayBets._sum.actualReturn,
|
||||||
|
pendingSettlement: pendingMatches,
|
||||||
|
totalPlayers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('users')
|
||||||
|
async listUsers(@Query('page') page?: string) {
|
||||||
|
const result = await this.users.listPlayers(page ? parseInt(page) : 1);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('users')
|
||||||
|
async createPlayer(@CurrentUser('id') operatorId: bigint, @Body() dto: CreateUserDto) {
|
||||||
|
const user = await this.agents.createPlayer(operatorId, {
|
||||||
|
username: dto.username,
|
||||||
|
password: dto.password,
|
||||||
|
parentId: dto.parentId ? BigInt(dto.parentId) : operatorId,
|
||||||
|
});
|
||||||
|
await this.audit.log({
|
||||||
|
operatorId,
|
||||||
|
operatorType: 'ADMIN',
|
||||||
|
action: 'CREATE_PLAYER',
|
||||||
|
module: 'USERS',
|
||||||
|
targetId: user.id.toString(),
|
||||||
|
});
|
||||||
|
return jsonResponse(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('agents')
|
||||||
|
async listAgents() {
|
||||||
|
const agents = await this.prisma.agentProfile.findMany({
|
||||||
|
include: { user: true },
|
||||||
|
});
|
||||||
|
return jsonResponse(agents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('agents')
|
||||||
|
async createAgent(@CurrentUser('id') operatorId: bigint, @Body() dto: CreateUserDto) {
|
||||||
|
const user = await this.agents.createAgent(operatorId, {
|
||||||
|
username: dto.username,
|
||||||
|
password: dto.password,
|
||||||
|
level: 1,
|
||||||
|
creditLimit: dto.creditLimit,
|
||||||
|
});
|
||||||
|
await this.audit.log({
|
||||||
|
operatorId,
|
||||||
|
operatorType: 'ADMIN',
|
||||||
|
action: 'CREATE_AGENT',
|
||||||
|
module: 'AGENTS',
|
||||||
|
targetId: user.id.toString(),
|
||||||
|
});
|
||||||
|
return jsonResponse(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('agents/:id/credit')
|
||||||
|
async adjustCredit(
|
||||||
|
@CurrentUser('id') operatorId: bigint,
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() dto: DepositDto,
|
||||||
|
) {
|
||||||
|
const result = await this.agents.adjustCredit(
|
||||||
|
BigInt(id),
|
||||||
|
dto.amount,
|
||||||
|
operatorId,
|
||||||
|
dto.requestId,
|
||||||
|
dto.remark,
|
||||||
|
);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('wallet/deposit')
|
||||||
|
async deposit(@CurrentUser('id') operatorId: bigint, @Body() dto: DepositDto & { userId: string }) {
|
||||||
|
const result = await this.wallet.deposit(
|
||||||
|
BigInt(dto.userId),
|
||||||
|
dto.amount,
|
||||||
|
operatorId,
|
||||||
|
dto.remark,
|
||||||
|
dto.requestId,
|
||||||
|
);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('wallet/withdraw')
|
||||||
|
async withdraw(@CurrentUser('id') operatorId: bigint, @Body() dto: DepositDto & { userId: string }) {
|
||||||
|
const result = await this.wallet.withdraw(
|
||||||
|
BigInt(dto.userId),
|
||||||
|
dto.amount,
|
||||||
|
operatorId,
|
||||||
|
dto.remark,
|
||||||
|
dto.requestId,
|
||||||
|
);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('wallet/transactions')
|
||||||
|
async walletTransactions(@Query('userId') userId: string, @Query('page') page?: string) {
|
||||||
|
const result = await this.wallet.getTransactions(BigInt(userId), page ? parseInt(page) : 1);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('leagues')
|
||||||
|
async createLeague(@Body() dto: { code: string; translations: Record<string, string> }) {
|
||||||
|
const league = await this.matches.createLeague(dto.code, dto.translations);
|
||||||
|
return jsonResponse(league);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('teams')
|
||||||
|
async createTeam(@Body() dto: { code: string; translations: Record<string, string> }) {
|
||||||
|
const team = await this.matches.createTeam(dto.code, dto.translations);
|
||||||
|
return jsonResponse(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('matches')
|
||||||
|
async listMatches() {
|
||||||
|
const matches = await this.prisma.match.findMany({
|
||||||
|
include: { markets: { include: { selections: true } } },
|
||||||
|
orderBy: { startTime: 'desc' },
|
||||||
|
});
|
||||||
|
return jsonResponse(matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches')
|
||||||
|
async createMatch(@CurrentUser('id') operatorId: bigint, @Body() dto: CreateMatchDto) {
|
||||||
|
const match = await this.matches.createMatch({
|
||||||
|
leagueId: BigInt(dto.leagueId),
|
||||||
|
homeTeamId: BigInt(dto.homeTeamId),
|
||||||
|
awayTeamId: BigInt(dto.awayTeamId),
|
||||||
|
startTime: new Date(dto.startTime),
|
||||||
|
isHot: dto.isHot,
|
||||||
|
createdBy: operatorId,
|
||||||
|
});
|
||||||
|
return jsonResponse(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches/:id/publish')
|
||||||
|
async publishMatch(@Param('id') id: string) {
|
||||||
|
const match = await this.matches.publishMatch(BigInt(id));
|
||||||
|
return jsonResponse(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches/:id/close')
|
||||||
|
async closeMatch(@Param('id') id: string) {
|
||||||
|
const match = await this.matches.closeMatch(BigInt(id));
|
||||||
|
return jsonResponse(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches/:id/cancel')
|
||||||
|
async cancelMatch(@Param('id') id: string) {
|
||||||
|
await this.matches.cancelMatch(BigInt(id));
|
||||||
|
const voided = await this.settlement.voidMatchBets(BigInt(id));
|
||||||
|
return jsonResponse(voided);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches/:id/markets/templates')
|
||||||
|
async generateTemplates(@Param('id') id: string, @Body() dto: MarketTemplatesDto) {
|
||||||
|
const markets = await this.markets.generateTemplates(BigInt(id), dto.marketTypes);
|
||||||
|
return jsonResponse(markets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put('selections/:id/odds')
|
||||||
|
async updateOdds(
|
||||||
|
@CurrentUser('id') operatorId: bigint,
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() dto: UpdateOddsDto,
|
||||||
|
) {
|
||||||
|
const selection = await this.markets.updateOdds(BigInt(id), dto.odds, operatorId);
|
||||||
|
return jsonResponse(selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches/:id/settlement/score')
|
||||||
|
async recordScore(
|
||||||
|
@CurrentUser('id') operatorId: bigint,
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() dto: ScoreDto,
|
||||||
|
) {
|
||||||
|
const result = await this.settlement.recordScore(
|
||||||
|
BigInt(id),
|
||||||
|
dto.htHome,
|
||||||
|
dto.htAway,
|
||||||
|
dto.ftHome,
|
||||||
|
dto.ftAway,
|
||||||
|
operatorId,
|
||||||
|
);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('matches/:id/settlement/preview')
|
||||||
|
async settlementPreview(@CurrentUser('id') operatorId: bigint, @Param('id') id: string) {
|
||||||
|
const preview = await this.settlement.previewSettlement(BigInt(id), operatorId);
|
||||||
|
return jsonResponse(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('settlement/:batchId/confirm')
|
||||||
|
async confirmSettlement(@CurrentUser('id') operatorId: bigint, @Param('batchId') batchId: string) {
|
||||||
|
const result = await this.settlement.confirmSettlement(BigInt(batchId), operatorId);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('bets')
|
||||||
|
async listBets(@Query('status') status?: string, @Query('page') page?: string) {
|
||||||
|
const skip = ((page ? parseInt(page) : 1) - 1) * 20;
|
||||||
|
const where = status ? { status } : {};
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.bet.findMany({
|
||||||
|
where,
|
||||||
|
include: { selections: true, user: true },
|
||||||
|
orderBy: { placedAt: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: 20,
|
||||||
|
}),
|
||||||
|
this.prisma.bet.count({ where }),
|
||||||
|
]);
|
||||||
|
return jsonResponse({ items, total });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('cashbacks/preview')
|
||||||
|
async cashbackPreview(@Body() dto: CashbackPreviewDto) {
|
||||||
|
const preview = await this.cashback.previewBatch(
|
||||||
|
new Date(dto.periodStart),
|
||||||
|
new Date(dto.periodEnd),
|
||||||
|
);
|
||||||
|
return jsonResponse(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('cashbacks/:batchId/confirm')
|
||||||
|
async cashbackConfirm(@CurrentUser('id') operatorId: bigint, @Param('batchId') batchId: string) {
|
||||||
|
const result = await this.cashback.confirmBatch(BigInt(batchId), operatorId);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('contents')
|
||||||
|
async listContents(@Query('type') type?: string) {
|
||||||
|
const items = await this.content.listAll(type);
|
||||||
|
return jsonResponse(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('contents')
|
||||||
|
async createContent(@Body() dto: Parameters<ContentService['create']>[0]) {
|
||||||
|
const item = await this.content.create(dto);
|
||||||
|
return jsonResponse(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('i18n/messages')
|
||||||
|
async getMessages(@Query('locale') locale = 'en-US') {
|
||||||
|
const messages = await this.i18n.getMessages(locale);
|
||||||
|
return jsonResponse(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('audit-logs')
|
||||||
|
async auditLogs(@Query('page') page?: string, @Query('module') module?: string) {
|
||||||
|
const result = await this.audit.list(page ? parseInt(page) : 1, 50, module);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
apps/api/src/admin/admin.module.ts
Normal file
29
apps/api/src/admin/admin.module.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AdminController } from './admin.controller';
|
||||||
|
import { UsersModule } from '../users/users.module';
|
||||||
|
import { AgentsModule } from '../agents/agents.module';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
import { MatchesModule } from '../matches/matches.module';
|
||||||
|
import { MarketsModule } from '../markets/markets.module';
|
||||||
|
import { SettlementModule } from '../settlement/settlement.module';
|
||||||
|
import { CashbackModule } from '../cashback/cashback.module';
|
||||||
|
import { ContentModule } from '../content/content.module';
|
||||||
|
import { I18nModule } from '../i18n/i18n.module';
|
||||||
|
import { BetsModule } from '../bets/bets.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
UsersModule,
|
||||||
|
AgentsModule,
|
||||||
|
WalletModule,
|
||||||
|
MatchesModule,
|
||||||
|
MarketsModule,
|
||||||
|
SettlementModule,
|
||||||
|
CashbackModule,
|
||||||
|
ContentModule,
|
||||||
|
I18nModule,
|
||||||
|
BetsModule,
|
||||||
|
],
|
||||||
|
controllers: [AdminController],
|
||||||
|
})
|
||||||
|
export class AdminModule {}
|
||||||
189
apps/api/src/agent-portal/agent-portal.controller.ts
Normal file
189
apps/api/src/agent-portal/agent-portal.controller.ts
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||||
|
import { JwtAuthGuard, AgentGuard } from '../auth/guards';
|
||||||
|
import { CurrentUser } from '../common/decorators';
|
||||||
|
import { jsonResponse } from '../common/filters';
|
||||||
|
import { AgentsService } from '../agents/agents.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { BetsService } from '../bets/bets.service';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { IsString, IsNumber, MinLength, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
|
class CreatePlayerDto {
|
||||||
|
@IsString()
|
||||||
|
username!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@MinLength(8)
|
||||||
|
password!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateSubAgentDto extends CreatePlayerDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
creditLimit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransferDto {
|
||||||
|
@IsNumber()
|
||||||
|
amount!: number;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
requestId!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreditDto extends TransferDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiTags('Agent Portal')
|
||||||
|
@Controller('agent')
|
||||||
|
@UseGuards(JwtAuthGuard, AgentGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
export class AgentPortalController {
|
||||||
|
constructor(
|
||||||
|
private agents: AgentsService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
private bets: BetsService,
|
||||||
|
private prisma: PrismaService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get('profile')
|
||||||
|
async profile(@CurrentUser('id') agentId: bigint) {
|
||||||
|
const profile = await this.agents.getProfile(agentId);
|
||||||
|
return jsonResponse(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('players')
|
||||||
|
async listPlayers(@CurrentUser('id') agentId: bigint) {
|
||||||
|
const players = await this.agents.getDirectPlayers(agentId);
|
||||||
|
return jsonResponse(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('players')
|
||||||
|
async createPlayer(@CurrentUser('id') agentId: bigint, @Body() dto: CreatePlayerDto) {
|
||||||
|
const user = await this.agents.createPlayer(agentId, {
|
||||||
|
username: dto.username,
|
||||||
|
password: dto.password,
|
||||||
|
parentId: agentId,
|
||||||
|
});
|
||||||
|
return jsonResponse(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('agents')
|
||||||
|
async listSubAgents(@CurrentUser('id') agentId: bigint, @CurrentUser('agentLevel') level: number) {
|
||||||
|
if (level !== 1) {
|
||||||
|
return jsonResponse([]);
|
||||||
|
}
|
||||||
|
const agents = await this.agents.getChildAgents(agentId);
|
||||||
|
return jsonResponse(agents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('agents')
|
||||||
|
async createSubAgent(@CurrentUser('id') agentId: bigint, @CurrentUser('agentLevel') level: number, @Body() dto: CreateSubAgentDto) {
|
||||||
|
if (level !== 1) {
|
||||||
|
return jsonResponse(null, 'Only level 1 agents can create sub-agents');
|
||||||
|
}
|
||||||
|
const user = await this.agents.createAgent(agentId, {
|
||||||
|
username: dto.username,
|
||||||
|
password: dto.password,
|
||||||
|
level: 2,
|
||||||
|
parentAgentId: agentId,
|
||||||
|
creditLimit: dto.creditLimit,
|
||||||
|
});
|
||||||
|
return jsonResponse(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('players/:id/deposit')
|
||||||
|
async depositToPlayer(
|
||||||
|
@CurrentUser('id') agentId: bigint,
|
||||||
|
@Param('id') playerId: string,
|
||||||
|
@Body() dto: TransferDto,
|
||||||
|
) {
|
||||||
|
const result = await this.agents.depositToPlayer(agentId, BigInt(playerId), dto.amount, dto.requestId);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('players/:id/withdraw')
|
||||||
|
async withdrawFromPlayer(
|
||||||
|
@CurrentUser('id') agentId: bigint,
|
||||||
|
@Param('id') playerId: string,
|
||||||
|
@Body() dto: TransferDto,
|
||||||
|
) {
|
||||||
|
const result = await this.agents.withdrawFromPlayer(agentId, BigInt(playerId), dto.amount, dto.requestId);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('agents/:id/credit')
|
||||||
|
async allocateCredit(
|
||||||
|
@CurrentUser('id') agentId: bigint,
|
||||||
|
@Param('id') subAgentId: string,
|
||||||
|
@Body() dto: CreditDto,
|
||||||
|
) {
|
||||||
|
const subAgent = await this.prisma.agentProfile.findUnique({
|
||||||
|
where: { userId: BigInt(subAgentId) },
|
||||||
|
});
|
||||||
|
if (!subAgent || subAgent.parentAgentId !== agentId) {
|
||||||
|
return jsonResponse(null, 'Not your sub-agent');
|
||||||
|
}
|
||||||
|
const result = await this.agents.adjustCredit(
|
||||||
|
BigInt(subAgentId),
|
||||||
|
dto.amount,
|
||||||
|
agentId,
|
||||||
|
dto.requestId,
|
||||||
|
dto.remark,
|
||||||
|
);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('bets')
|
||||||
|
async listBets(@CurrentUser('id') agentId: bigint, @Query('page') page?: string) {
|
||||||
|
const skip = ((page ? parseInt(page) : 1) - 1) * 20;
|
||||||
|
const descendants = await this.prisma.agentClosure.findMany({
|
||||||
|
where: { ancestorId: agentId },
|
||||||
|
});
|
||||||
|
const agentIds = descendants.map((d) => d.descendantId);
|
||||||
|
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.bet.findMany({
|
||||||
|
where: { agentId: { in: agentIds } },
|
||||||
|
include: { selections: true, user: true },
|
||||||
|
orderBy: { placedAt: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: 20,
|
||||||
|
}),
|
||||||
|
this.prisma.bet.count({ where: { agentId: { in: agentIds } } }),
|
||||||
|
]);
|
||||||
|
return jsonResponse({ items, total });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('reports/summary')
|
||||||
|
async reportSummary(@CurrentUser('id') agentId: bigint) {
|
||||||
|
const summary = await this.agents.getReportSummary(agentId);
|
||||||
|
return jsonResponse(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('wallet-transactions')
|
||||||
|
async walletTransactions(@CurrentUser('id') agentId: bigint, @Query('playerId') playerId?: string) {
|
||||||
|
const players = playerId
|
||||||
|
? [BigInt(playerId)]
|
||||||
|
: (await this.agents.getDirectPlayers(agentId)).map((p) => p.id);
|
||||||
|
|
||||||
|
const transactions = await this.prisma.walletTransaction.findMany({
|
||||||
|
where: { userId: { in: players } },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 50,
|
||||||
|
});
|
||||||
|
return jsonResponse(transactions);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
apps/api/src/agent-portal/agent-portal.module.ts
Normal file
11
apps/api/src/agent-portal/agent-portal.module.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AgentPortalController } from './agent-portal.controller';
|
||||||
|
import { AgentsModule } from '../agents/agents.module';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
import { BetsModule } from '../bets/bets.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [AgentsModule, WalletModule, BetsModule],
|
||||||
|
controllers: [AgentPortalController],
|
||||||
|
})
|
||||||
|
export class AgentPortalModule {}
|
||||||
11
apps/api/src/agents/agents.module.ts
Normal file
11
apps/api/src/agents/agents.module.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AgentsService } from './agents.service';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [WalletModule, AuthModule],
|
||||||
|
providers: [AgentsService],
|
||||||
|
exports: [AgentsService],
|
||||||
|
})
|
||||||
|
export class AgentsModule {}
|
||||||
296
apps/api/src/agents/agents.service.ts
Normal file
296
apps/api/src/agents/agents.service.ts
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
import { Injectable, BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { AuthService } from '../auth/auth.service';
|
||||||
|
import { Decimal } from '@prisma/client/runtime/library';
|
||||||
|
import { generateBatchNo } from '../common/decorators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AgentsService {
|
||||||
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
private auth: AuthService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getProfile(agentId: bigint) {
|
||||||
|
const profile = await this.prisma.agentProfile.findUnique({
|
||||||
|
where: { userId: agentId },
|
||||||
|
});
|
||||||
|
if (!profile) throw new BadRequestException('Agent profile not found');
|
||||||
|
const available = new Decimal(profile.creditLimit).sub(profile.usedCredit);
|
||||||
|
return { ...profile, availableCredit: available };
|
||||||
|
}
|
||||||
|
|
||||||
|
async recalculateUsedCredit(agentId: bigint) {
|
||||||
|
const directPlayers = await this.prisma.user.findMany({
|
||||||
|
where: { parentId: agentId, userType: 'PLAYER' },
|
||||||
|
include: { wallet: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
let directLiability = new Decimal(0);
|
||||||
|
for (const p of directPlayers) {
|
||||||
|
if (p.wallet) {
|
||||||
|
directLiability = directLiability
|
||||||
|
.add(p.wallet.availableBalance)
|
||||||
|
.add(p.wallet.frozenBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const childAgents = await this.prisma.agentProfile.findMany({
|
||||||
|
where: { parentAgentId: agentId },
|
||||||
|
});
|
||||||
|
|
||||||
|
let childExposure = new Decimal(0);
|
||||||
|
for (const child of childAgents) {
|
||||||
|
const exposure = Decimal.max(child.creditLimit, child.usedCredit);
|
||||||
|
childExposure = childExposure.add(exposure);
|
||||||
|
}
|
||||||
|
|
||||||
|
const usedCredit = directLiability.add(childExposure);
|
||||||
|
|
||||||
|
await this.prisma.agentProfile.update({
|
||||||
|
where: { userId: agentId },
|
||||||
|
data: {
|
||||||
|
usedCredit,
|
||||||
|
directPlayerLiability: directLiability,
|
||||||
|
childAgentExposure: childExposure,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return usedCredit;
|
||||||
|
}
|
||||||
|
|
||||||
|
async adjustCredit(
|
||||||
|
agentId: bigint,
|
||||||
|
amount: Decimal | number,
|
||||||
|
operatorId: bigint,
|
||||||
|
requestId: string,
|
||||||
|
remark?: string,
|
||||||
|
) {
|
||||||
|
const amt = new Decimal(amount);
|
||||||
|
const profile = await this.prisma.agentProfile.findUnique({
|
||||||
|
where: { userId: agentId },
|
||||||
|
});
|
||||||
|
if (!profile) throw new BadRequestException('Agent not found');
|
||||||
|
|
||||||
|
const creditBefore = profile.creditLimit;
|
||||||
|
const creditAfter = creditBefore.add(amt);
|
||||||
|
if (creditAfter.lt(0)) throw new BadRequestException('Credit limit cannot be negative');
|
||||||
|
|
||||||
|
await this.prisma.$transaction(async (tx) => {
|
||||||
|
await tx.agentProfile.update({
|
||||||
|
where: { userId: agentId },
|
||||||
|
data: { creditLimit: creditAfter },
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.agentCreditTransaction.create({
|
||||||
|
data: {
|
||||||
|
agentId,
|
||||||
|
transactionType: amt.gte(0) ? 'CREDIT_INCREASE' : 'CREDIT_DECREASE',
|
||||||
|
amount: amt,
|
||||||
|
creditBefore,
|
||||||
|
creditAfter,
|
||||||
|
operatorId,
|
||||||
|
requestId,
|
||||||
|
remark,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (profile.parentAgentId) {
|
||||||
|
await this.recalculateUsedCredit(profile.parentAgentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { creditAfter };
|
||||||
|
}
|
||||||
|
|
||||||
|
async depositToPlayer(
|
||||||
|
agentId: bigint,
|
||||||
|
playerId: bigint,
|
||||||
|
amount: number,
|
||||||
|
requestId: string,
|
||||||
|
) {
|
||||||
|
const player = await this.prisma.user.findUnique({ where: { id: playerId } });
|
||||||
|
if (!player || player.parentId !== agentId) {
|
||||||
|
throw new ForbiddenException('Can only deposit to direct players');
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = await this.getProfile(agentId);
|
||||||
|
const available = new Decimal(profile.creditLimit).sub(profile.usedCredit);
|
||||||
|
const amt = new Decimal(amount);
|
||||||
|
|
||||||
|
if (available.lt(amt)) {
|
||||||
|
throw new BadRequestException('Insufficient agent credit');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.wallet.deposit(playerId, amt, agentId, 'Agent deposit', requestId);
|
||||||
|
await this.recalculateUsedCredit(agentId);
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async withdrawFromPlayer(
|
||||||
|
agentId: bigint,
|
||||||
|
playerId: bigint,
|
||||||
|
amount: number,
|
||||||
|
requestId: string,
|
||||||
|
) {
|
||||||
|
const player = await this.prisma.user.findUnique({ where: { id: playerId } });
|
||||||
|
if (!player || player.parentId !== agentId) {
|
||||||
|
throw new ForbiddenException('Can only withdraw from direct players');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.wallet.withdraw(playerId, amount, agentId, 'Agent withdraw', requestId);
|
||||||
|
await this.recalculateUsedCredit(agentId);
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAgent(
|
||||||
|
operatorId: bigint,
|
||||||
|
data: {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
level: number;
|
||||||
|
parentAgentId?: bigint;
|
||||||
|
creditLimit?: number;
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
if (data.level === 2 && !data.parentAgentId) {
|
||||||
|
throw new BadRequestException('Level 2 agent requires parent');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = await this.auth.hashPassword(data.password);
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
const user = await tx.user.create({
|
||||||
|
data: {
|
||||||
|
username: data.username,
|
||||||
|
userType: 'AGENT',
|
||||||
|
parentId: data.parentAgentId,
|
||||||
|
agentLevel: data.level,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.userAuth.create({
|
||||||
|
data: { userId: user.id, passwordHash: hash },
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.agentProfile.create({
|
||||||
|
data: {
|
||||||
|
userId: user.id,
|
||||||
|
level: data.level,
|
||||||
|
parentAgentId: data.parentAgentId,
|
||||||
|
creditLimit: data.creditLimit ?? 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build closure table
|
||||||
|
await tx.agentClosure.create({
|
||||||
|
data: { ancestorId: user.id, descendantId: user.id, depth: 0 },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.parentAgentId) {
|
||||||
|
const ancestors = await tx.agentClosure.findMany({
|
||||||
|
where: { descendantId: data.parentAgentId },
|
||||||
|
});
|
||||||
|
for (const a of ancestors) {
|
||||||
|
await tx.agentClosure.create({
|
||||||
|
data: {
|
||||||
|
ancestorId: a.ancestorId,
|
||||||
|
descendantId: user.id,
|
||||||
|
depth: a.depth + 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (data.parentAgentId) {
|
||||||
|
await this.recalculateUsedCredit(data.parentAgentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPlayer(
|
||||||
|
operatorId: bigint,
|
||||||
|
data: { username: string; password: string; parentId: bigint },
|
||||||
|
) {
|
||||||
|
const hash = await this.auth.hashPassword(data.password);
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
const user = await tx.user.create({
|
||||||
|
data: {
|
||||||
|
username: data.username,
|
||||||
|
userType: 'PLAYER',
|
||||||
|
parentId: data.parentId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.userAuth.create({
|
||||||
|
data: { userId: user.id, passwordHash: hash },
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.wallet.create({
|
||||||
|
data: { userId: user.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.userPreference.create({
|
||||||
|
data: { userId: user.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
const parent = await tx.user.findUnique({ where: { id: data.parentId } });
|
||||||
|
if (parent?.userType === 'AGENT') {
|
||||||
|
await this.recalculateUsedCredit(data.parentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDirectPlayers(agentId: bigint) {
|
||||||
|
return this.prisma.user.findMany({
|
||||||
|
where: { parentId: agentId, userType: 'PLAYER' },
|
||||||
|
include: { wallet: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChildAgents(agentId: bigint) {
|
||||||
|
return this.prisma.agentProfile.findMany({
|
||||||
|
where: { parentAgentId: agentId },
|
||||||
|
include: { user: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getReportSummary(agentId: bigint) {
|
||||||
|
const profile = await this.getProfile(agentId);
|
||||||
|
const players = await this.getDirectPlayers(agentId);
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const todayBets = await this.prisma.bet.aggregate({
|
||||||
|
where: {
|
||||||
|
agentId,
|
||||||
|
placedAt: { gte: today },
|
||||||
|
},
|
||||||
|
_sum: { stake: true, actualReturn: true },
|
||||||
|
_count: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
profile,
|
||||||
|
directPlayerCount: players.length,
|
||||||
|
directPlayerTotalBalance: players.reduce(
|
||||||
|
(sum, p) =>
|
||||||
|
sum +
|
||||||
|
Number(p.wallet?.availableBalance ?? 0) +
|
||||||
|
Number(p.wallet?.frozenBalance ?? 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
todayBetCount: todayBets._count,
|
||||||
|
todayStake: todayBets._sum.stake,
|
||||||
|
todayReturn: todayBets._sum.actualReturn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
46
apps/api/src/app.module.ts
Normal file
46
apps/api/src/app.module.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
|
import { APP_GUARD } from '@nestjs/core';
|
||||||
|
import { JwtAuthGuard } from './auth/guards';
|
||||||
|
import { PrismaModule } from './prisma/prisma.module';
|
||||||
|
import { AuthModule } from './auth/auth.module';
|
||||||
|
import { UsersModule } from './users/users.module';
|
||||||
|
import { AgentsModule } from './agents/agents.module';
|
||||||
|
import { WalletModule } from './wallet/wallet.module';
|
||||||
|
import { MatchesModule } from './matches/matches.module';
|
||||||
|
import { MarketsModule } from './markets/markets.module';
|
||||||
|
import { BetsModule } from './bets/bets.module';
|
||||||
|
import { SettlementModule } from './settlement/settlement.module';
|
||||||
|
import { CashbackModule } from './cashback/cashback.module';
|
||||||
|
import { ContentModule } from './content/content.module';
|
||||||
|
import { I18nModule } from './i18n/i18n.module';
|
||||||
|
import { AuditModule } from './audit/audit.module';
|
||||||
|
import { AdminModule } from './admin/admin.module';
|
||||||
|
import { PlayerModule } from './player/player.module';
|
||||||
|
import { AgentPortalModule } from './agent-portal/agent-portal.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({ isGlobal: true }),
|
||||||
|
ScheduleModule.forRoot(),
|
||||||
|
PrismaModule,
|
||||||
|
AuthModule,
|
||||||
|
UsersModule,
|
||||||
|
AgentsModule,
|
||||||
|
WalletModule,
|
||||||
|
MatchesModule,
|
||||||
|
MarketsModule,
|
||||||
|
BetsModule,
|
||||||
|
SettlementModule,
|
||||||
|
CashbackModule,
|
||||||
|
ContentModule,
|
||||||
|
I18nModule,
|
||||||
|
AuditModule,
|
||||||
|
AdminModule,
|
||||||
|
PlayerModule,
|
||||||
|
AgentPortalModule,
|
||||||
|
],
|
||||||
|
providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
9
apps/api/src/audit/audit.module.ts
Normal file
9
apps/api/src/audit/audit.module.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { AuditService } from './audit.service';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [AuditService],
|
||||||
|
exports: [AuditService],
|
||||||
|
})
|
||||||
|
export class AuditModule {}
|
||||||
48
apps/api/src/audit/audit.service.ts
Normal file
48
apps/api/src/audit/audit.service.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuditService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async log(data: {
|
||||||
|
operatorId?: bigint;
|
||||||
|
operatorType: string;
|
||||||
|
action: string;
|
||||||
|
module: string;
|
||||||
|
targetType?: string;
|
||||||
|
targetId?: string;
|
||||||
|
beforeData?: unknown;
|
||||||
|
afterData?: unknown;
|
||||||
|
ipAddress?: string;
|
||||||
|
}) {
|
||||||
|
return this.prisma.auditLog.create({
|
||||||
|
data: {
|
||||||
|
operatorId: data.operatorId,
|
||||||
|
operatorType: data.operatorType,
|
||||||
|
action: data.action,
|
||||||
|
module: data.module,
|
||||||
|
targetType: data.targetType,
|
||||||
|
targetId: data.targetId,
|
||||||
|
beforeData: data.beforeData ? JSON.stringify(data.beforeData) : null,
|
||||||
|
afterData: data.afterData ? JSON.stringify(data.afterData) : null,
|
||||||
|
ipAddress: data.ipAddress,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async list(page = 1, pageSize = 50, module?: string) {
|
||||||
|
const skip = (page - 1) * pageSize;
|
||||||
|
const where = module ? { module } : {};
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.auditLog.findMany({
|
||||||
|
where,
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: pageSize,
|
||||||
|
}),
|
||||||
|
this.prisma.auditLog.count({ where }),
|
||||||
|
]);
|
||||||
|
return { items, total, page, pageSize };
|
||||||
|
}
|
||||||
|
}
|
||||||
45
apps/api/src/auth/auth.controller.ts
Normal file
45
apps/api/src/auth/auth.controller.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Controller, Post, Body, UseGuards } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { LoginDto, ChangePasswordDto } from './auth.dto';
|
||||||
|
import { Public, CurrentUser } from '../common/decorators';
|
||||||
|
import { JwtAuthGuard } from './guards';
|
||||||
|
import { jsonResponse } from '../common/filters';
|
||||||
|
|
||||||
|
@ApiTags('Auth')
|
||||||
|
@Controller()
|
||||||
|
export class AuthController {
|
||||||
|
constructor(private auth: AuthService) {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Post('player/auth/login')
|
||||||
|
async playerLogin(@Body() dto: LoginDto) {
|
||||||
|
const result = await this.auth.login(dto.username, dto.password, 'player');
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Post('admin/auth/login')
|
||||||
|
async adminLogin(@Body() dto: LoginDto) {
|
||||||
|
const result = await this.auth.login(dto.username, dto.password, 'admin');
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Post('agent/auth/login')
|
||||||
|
async agentLogin(@Body() dto: LoginDto) {
|
||||||
|
const result = await this.auth.login(dto.username, dto.password, 'agent');
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('player/auth/change-password')
|
||||||
|
async playerChangePassword(
|
||||||
|
@CurrentUser('id') userId: bigint,
|
||||||
|
@Body() dto: ChangePasswordDto,
|
||||||
|
) {
|
||||||
|
await this.auth.changePassword(userId, dto.oldPassword, dto.newPassword);
|
||||||
|
return jsonResponse(null, 'Password changed');
|
||||||
|
}
|
||||||
|
}
|
||||||
24
apps/api/src/auth/auth.dto.ts
Normal file
24
apps/api/src/auth/auth.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { IsString, MinLength } from 'class-validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class LoginDto {
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
username!: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@MinLength(1)
|
||||||
|
password!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangePasswordDto {
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
oldPassword!: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsString()
|
||||||
|
@MinLength(8)
|
||||||
|
newPassword!: string;
|
||||||
|
}
|
||||||
25
apps/api/src/auth/auth.module.ts
Normal file
25
apps/api/src/auth/auth.module.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
import { PassportModule } from '@nestjs/passport';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { JwtStrategy } from './jwt.strategy';
|
||||||
|
import { AuthController } from './auth.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
PassportModule.register({ defaultStrategy: 'jwt' }),
|
||||||
|
JwtModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: (config: ConfigService) => ({
|
||||||
|
secret: config.get('JWT_SECRET', 'dev-secret'),
|
||||||
|
signOptions: { expiresIn: config.get('JWT_PLAYER_EXPIRES', '24h') },
|
||||||
|
}),
|
||||||
|
inject: [ConfigService],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
providers: [AuthService, JwtStrategy],
|
||||||
|
controllers: [AuthController],
|
||||||
|
exports: [AuthService, JwtModule],
|
||||||
|
})
|
||||||
|
export class AuthModule {}
|
||||||
111
apps/api/src/auth/auth.service.ts
Normal file
111
apps/api/src/auth/auth.service.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { Injectable, UnauthorizedException, ForbiddenException } from '@nestjs/common';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import * as bcrypt from 'bcryptjs';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
const MAX_LOGIN_FAILS = 5;
|
||||||
|
const LOCK_DURATION_MS = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
export interface JwtPayload {
|
||||||
|
sub: string;
|
||||||
|
username: string;
|
||||||
|
userType: string;
|
||||||
|
role?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {
|
||||||
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private jwt: JwtService,
|
||||||
|
private config: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async login(username: string, password: string, portal: 'player' | 'admin' | 'agent') {
|
||||||
|
const user = await this.prisma.user.findUnique({
|
||||||
|
where: { username },
|
||||||
|
include: { auth: true, adminRole: { include: { role: true } } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user || !user.auth) {
|
||||||
|
throw new UnauthorizedException('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedType = portal === 'admin' ? 'ADMIN' : portal === 'agent' ? 'AGENT' : 'PLAYER';
|
||||||
|
if (user.userType !== expectedType) {
|
||||||
|
throw new UnauthorizedException('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.status === 'DISABLED') {
|
||||||
|
throw new ForbiddenException('Account disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.auth.lockedUntil && user.auth.lockedUntil > new Date()) {
|
||||||
|
throw new ForbiddenException('Account locked, try again later');
|
||||||
|
}
|
||||||
|
|
||||||
|
const valid = await bcrypt.compare(password, user.auth.passwordHash);
|
||||||
|
if (!valid) {
|
||||||
|
const failCount = user.auth.loginFailCount + 1;
|
||||||
|
const lockedUntil =
|
||||||
|
failCount >= MAX_LOGIN_FAILS ? new Date(Date.now() + LOCK_DURATION_MS) : null;
|
||||||
|
await this.prisma.userAuth.update({
|
||||||
|
where: { userId: user.id },
|
||||||
|
data: { loginFailCount: failCount, lockedUntil },
|
||||||
|
});
|
||||||
|
throw new UnauthorizedException('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.userAuth.update({
|
||||||
|
where: { userId: user.id },
|
||||||
|
data: { loginFailCount: 0, lockedUntil: null, lastLoginAt: new Date() },
|
||||||
|
});
|
||||||
|
|
||||||
|
const expiresIn =
|
||||||
|
portal === 'admin'
|
||||||
|
? this.config.get('JWT_ADMIN_EXPIRES', '2h')
|
||||||
|
: portal === 'agent'
|
||||||
|
? this.config.get('JWT_AGENT_EXPIRES', '8h')
|
||||||
|
: this.config.get('JWT_PLAYER_EXPIRES', '24h');
|
||||||
|
|
||||||
|
const payload: JwtPayload = {
|
||||||
|
sub: user.id.toString(),
|
||||||
|
username: user.username,
|
||||||
|
userType: user.userType,
|
||||||
|
role: user.adminRole?.role?.code,
|
||||||
|
};
|
||||||
|
|
||||||
|
const token = this.jwt.sign(payload, { expiresIn });
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
user: {
|
||||||
|
id: user.id.toString(),
|
||||||
|
username: user.username,
|
||||||
|
userType: user.userType,
|
||||||
|
locale: user.locale,
|
||||||
|
role: user.adminRole?.role?.code,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePassword(userId: bigint, oldPassword: string, newPassword: string) {
|
||||||
|
const auth = await this.prisma.userAuth.findUnique({ where: { userId } });
|
||||||
|
if (!auth) throw new UnauthorizedException('User not found');
|
||||||
|
|
||||||
|
const valid = await bcrypt.compare(oldPassword, auth.passwordHash);
|
||||||
|
if (!valid) throw new UnauthorizedException('Invalid old password');
|
||||||
|
|
||||||
|
const hash = await bcrypt.hash(newPassword, 10);
|
||||||
|
await this.prisma.userAuth.update({
|
||||||
|
where: { userId },
|
||||||
|
data: { passwordHash: hash },
|
||||||
|
});
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async hashPassword(password: string): Promise<string> {
|
||||||
|
return bcrypt.hash(password, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
87
apps/api/src/auth/guards.ts
Normal file
87
apps/api/src/auth/guards.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { IS_PUBLIC_KEY, PERMISSIONS_KEY } from '../common/decorators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtAuthGuard extends AuthGuard('jwt') {
|
||||||
|
constructor(private reflector: Reflector) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext) {
|
||||||
|
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||||
|
context.getHandler(),
|
||||||
|
context.getClass(),
|
||||||
|
]);
|
||||||
|
if (isPublic) return true;
|
||||||
|
return super.canActivate(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRequest<TUser = unknown>(err: Error | null, user: TUser): TUser {
|
||||||
|
if (err || !user) throw err || new UnauthorizedException();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PermissionsGuard implements CanActivate {
|
||||||
|
constructor(private reflector: Reflector) {}
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const required = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
|
||||||
|
context.getHandler(),
|
||||||
|
context.getClass(),
|
||||||
|
]);
|
||||||
|
if (!required?.length) return true;
|
||||||
|
|
||||||
|
const { user } = context.switchToHttp().getRequest();
|
||||||
|
const userPerms: string[] = user?.permissions ?? [];
|
||||||
|
if (user?.role === 'SUPER_ADMIN') return true;
|
||||||
|
|
||||||
|
const hasAll = required.every((p) => userPerms.includes(p));
|
||||||
|
if (!hasAll) throw new ForbiddenException('Insufficient permissions');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserTypeGuard(...types: string[]) {
|
||||||
|
@Injectable()
|
||||||
|
class MixedUserTypeGuard implements CanActivate {
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const { user } = context.switchToHttp().getRequest();
|
||||||
|
if (!types.includes(user?.userType)) {
|
||||||
|
throw new ForbiddenException('Access denied for this portal');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MixedUserTypeGuard;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PlayerGuard implements CanActivate {
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const { user } = context.switchToHttp().getRequest();
|
||||||
|
if (user?.userType !== 'PLAYER') throw new ForbiddenException('Player access only');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AdminGuard implements CanActivate {
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const { user } = context.switchToHttp().getRequest();
|
||||||
|
if (user?.userType !== 'ADMIN') throw new ForbiddenException('Admin access only');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AgentGuard implements CanActivate {
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const { user } = context.switchToHttp().getRequest();
|
||||||
|
if (user?.userType !== 'AGENT') throw new ForbiddenException('Agent access only');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
apps/api/src/auth/jwt.strategy.ts
Normal file
54
apps/api/src/auth/jwt.strategy.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { JwtPayload } from './auth.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
|
constructor(
|
||||||
|
config: ConfigService,
|
||||||
|
private prisma: PrismaService,
|
||||||
|
) {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
ignoreExpiration: false,
|
||||||
|
secretOrKey: config.get('JWT_SECRET', 'dev-secret'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(payload: JwtPayload) {
|
||||||
|
const user = await this.prisma.user.findUnique({
|
||||||
|
where: { id: BigInt(payload.sub) },
|
||||||
|
include: {
|
||||||
|
adminRole: {
|
||||||
|
include: {
|
||||||
|
role: {
|
||||||
|
include: {
|
||||||
|
permissions: {
|
||||||
|
include: { permission: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user || user.status !== 'ACTIVE') {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
const permissions =
|
||||||
|
user.adminRole?.role?.permissions?.map((rp) => rp.permission.code) ?? [];
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
userType: user.userType,
|
||||||
|
parentId: user.parentId,
|
||||||
|
agentLevel: user.agentLevel,
|
||||||
|
locale: user.locale,
|
||||||
|
role: payload.role,
|
||||||
|
permissions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
10
apps/api/src/bets/bets.module.ts
Normal file
10
apps/api/src/bets/bets.module.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { BetsService } from './bets.service';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [WalletModule],
|
||||||
|
providers: [BetsService],
|
||||||
|
exports: [BetsService],
|
||||||
|
})
|
||||||
|
export class BetsModule {}
|
||||||
211
apps/api/src/bets/bets.service.ts
Normal file
211
apps/api/src/bets/bets.service.ts
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
import { Injectable, BadRequestException, ConflictException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { Decimal } from '@prisma/client/runtime/library';
|
||||||
|
import { generateBetNo } from '../common/decorators';
|
||||||
|
import { isQuarterHandicapOrTotal } from '../settlement/settlement-calculator';
|
||||||
|
import { PARLAY_MIN_LEGS, PARLAY_MAX_LEGS } from '@thebet365/shared';
|
||||||
|
|
||||||
|
interface BetSelectionInput {
|
||||||
|
selectionId: bigint;
|
||||||
|
oddsVersion: bigint;
|
||||||
|
stake?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BetsService {
|
||||||
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private async validateSelection(selectionId: bigint, oddsVersion: bigint) {
|
||||||
|
const selection = await this.prisma.marketSelection.findUnique({
|
||||||
|
where: { id: selectionId },
|
||||||
|
include: { market: { include: { match: true } } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!selection) throw new BadRequestException('Selection not found');
|
||||||
|
if (selection.status !== 'OPEN') throw new BadRequestException('Selection closed');
|
||||||
|
if (selection.market.status !== 'OPEN') throw new BadRequestException('Market closed');
|
||||||
|
if (selection.market.match.status !== 'PUBLISHED') {
|
||||||
|
throw new BadRequestException('Match not available for betting');
|
||||||
|
}
|
||||||
|
if (selection.oddsVersion !== oddsVersion) {
|
||||||
|
throw new BadRequestException('Odds changed, please confirm again');
|
||||||
|
}
|
||||||
|
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
async placeSingleBet(
|
||||||
|
userId: bigint,
|
||||||
|
agentId: bigint | null,
|
||||||
|
selectionId: bigint,
|
||||||
|
oddsVersion: bigint,
|
||||||
|
stake: number,
|
||||||
|
requestId: string,
|
||||||
|
) {
|
||||||
|
if (stake <= 0) throw new BadRequestException('Invalid stake');
|
||||||
|
|
||||||
|
const existing = await this.prisma.bet.findUnique({
|
||||||
|
where: { userId_requestId: { userId, requestId } },
|
||||||
|
});
|
||||||
|
if (existing) return existing;
|
||||||
|
|
||||||
|
const selection = await this.validateSelection(selectionId, oddsVersion);
|
||||||
|
const odds = new Decimal(selection.odds.toString());
|
||||||
|
const stakeDec = new Decimal(stake);
|
||||||
|
const potentialReturn = stakeDec.mul(odds);
|
||||||
|
const betNo = generateBetNo();
|
||||||
|
|
||||||
|
const bet = await this.prisma.$transaction(async (tx) => {
|
||||||
|
const created = await tx.bet.create({
|
||||||
|
data: {
|
||||||
|
betNo,
|
||||||
|
userId,
|
||||||
|
agentId,
|
||||||
|
betType: 'SINGLE',
|
||||||
|
stake: stakeDec,
|
||||||
|
totalOdds: odds,
|
||||||
|
potentialReturn,
|
||||||
|
requestId,
|
||||||
|
selections: {
|
||||||
|
create: {
|
||||||
|
matchId: selection.market.matchId,
|
||||||
|
marketId: selection.marketId,
|
||||||
|
selectionId: selection.id,
|
||||||
|
marketType: selection.market.marketType,
|
||||||
|
period: selection.market.period,
|
||||||
|
selectionNameSnapshot: selection.selectionName,
|
||||||
|
handicapLine: selection.market.lineValue,
|
||||||
|
totalLine: selection.market.lineValue,
|
||||||
|
odds,
|
||||||
|
oddsVersion,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { selections: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.wallet.freezeForBet(userId, stakeDec, betNo);
|
||||||
|
return created;
|
||||||
|
});
|
||||||
|
|
||||||
|
return bet;
|
||||||
|
}
|
||||||
|
|
||||||
|
async placeParlayBet(
|
||||||
|
userId: bigint,
|
||||||
|
agentId: bigint | null,
|
||||||
|
legs: BetSelectionInput[],
|
||||||
|
stake: number,
|
||||||
|
requestId: string,
|
||||||
|
) {
|
||||||
|
if (stake <= 0) throw new BadRequestException('Invalid stake');
|
||||||
|
if (legs.length < PARLAY_MIN_LEGS || legs.length > PARLAY_MAX_LEGS) {
|
||||||
|
throw new BadRequestException(`Parlay must have ${PARLAY_MIN_LEGS}-${PARLAY_MAX_LEGS} legs`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await this.prisma.bet.findUnique({
|
||||||
|
where: { userId_requestId: { userId, requestId } },
|
||||||
|
});
|
||||||
|
if (existing) return existing;
|
||||||
|
|
||||||
|
const selections: Awaited<ReturnType<typeof this.validateSelection>>[] = [];
|
||||||
|
const matchIds = new Set<string>();
|
||||||
|
|
||||||
|
for (const leg of legs) {
|
||||||
|
const sel = await this.validateSelection(leg.selectionId, leg.oddsVersion);
|
||||||
|
|
||||||
|
if (sel.market.marketType === 'OUTRIGHT_WINNER') {
|
||||||
|
throw new BadRequestException('Outright cannot be in parlay');
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = sel.market.lineValue ? Number(sel.market.lineValue) : null;
|
||||||
|
if (
|
||||||
|
['FT_HANDICAP', 'HT_HANDICAP', 'FT_OVER_UNDER', 'HT_OVER_UNDER'].includes(
|
||||||
|
sel.market.marketType,
|
||||||
|
) &&
|
||||||
|
isQuarterHandicapOrTotal(line)
|
||||||
|
) {
|
||||||
|
throw new BadRequestException('Quarter line markets cannot be in parlay');
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchKey = sel.market.matchId.toString();
|
||||||
|
if (matchIds.has(matchKey)) {
|
||||||
|
throw new BadRequestException('Same match cannot be in parlay');
|
||||||
|
}
|
||||||
|
matchIds.add(matchKey);
|
||||||
|
selections.push(sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalOdds = new Decimal(1);
|
||||||
|
for (const sel of selections) {
|
||||||
|
totalOdds = totalOdds.mul(sel.odds.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const stakeDec = new Decimal(stake);
|
||||||
|
const potentialReturn = stakeDec.mul(totalOdds);
|
||||||
|
const betNo = generateBetNo();
|
||||||
|
|
||||||
|
const bet = await this.prisma.$transaction(async (tx) => {
|
||||||
|
const created = await tx.bet.create({
|
||||||
|
data: {
|
||||||
|
betNo,
|
||||||
|
userId,
|
||||||
|
agentId,
|
||||||
|
betType: 'PARLAY',
|
||||||
|
stake: stakeDec,
|
||||||
|
totalOdds,
|
||||||
|
potentialReturn,
|
||||||
|
requestId,
|
||||||
|
selections: {
|
||||||
|
create: selections.map((sel, i) => ({
|
||||||
|
matchId: sel.market.matchId,
|
||||||
|
marketId: sel.marketId,
|
||||||
|
selectionId: sel.id,
|
||||||
|
marketType: sel.market.marketType,
|
||||||
|
period: sel.market.period,
|
||||||
|
selectionNameSnapshot: sel.selectionName,
|
||||||
|
handicapLine: sel.market.lineValue,
|
||||||
|
totalLine: sel.market.lineValue,
|
||||||
|
odds: sel.odds,
|
||||||
|
oddsVersion: legs[i].oddsVersion,
|
||||||
|
sortOrder: i,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { selections: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.wallet.freezeForBet(userId, stakeDec, betNo);
|
||||||
|
return created;
|
||||||
|
});
|
||||||
|
|
||||||
|
return bet;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserBets(userId: bigint, status?: string, page = 1, pageSize = 20) {
|
||||||
|
const where = { userId, ...(status ? { status } : {}) };
|
||||||
|
const skip = (page - 1) * pageSize;
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.bet.findMany({
|
||||||
|
where,
|
||||||
|
include: { selections: true },
|
||||||
|
orderBy: { placedAt: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: pageSize,
|
||||||
|
}),
|
||||||
|
this.prisma.bet.count({ where }),
|
||||||
|
]);
|
||||||
|
return { items, total, page, pageSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBetByNo(betNo: string, userId?: bigint) {
|
||||||
|
return this.prisma.bet.findFirst({
|
||||||
|
where: { betNo, ...(userId ? { userId } : {}) },
|
||||||
|
include: { selections: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
10
apps/api/src/cashback/cashback.module.ts
Normal file
10
apps/api/src/cashback/cashback.module.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CashbackService } from './cashback.service';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [WalletModule],
|
||||||
|
providers: [CashbackService],
|
||||||
|
exports: [CashbackService],
|
||||||
|
})
|
||||||
|
export class CashbackModule {}
|
||||||
108
apps/api/src/cashback/cashback.service.ts
Normal file
108
apps/api/src/cashback/cashback.service.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { Decimal } from '@prisma/client/runtime/library';
|
||||||
|
import { generateBatchNo } from '../common/decorators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CashbackService {
|
||||||
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async previewBatch(periodStart: Date, periodEnd: Date) {
|
||||||
|
const settledBets = await this.prisma.bet.findMany({
|
||||||
|
where: {
|
||||||
|
status: { in: ['WON', 'LOST', 'SETTLED'] },
|
||||||
|
settledAt: { gte: periodStart, lte: periodEnd },
|
||||||
|
},
|
||||||
|
include: { user: { include: { agentProfile: true } } },
|
||||||
|
});
|
||||||
|
|
||||||
|
const playerStakes = new Map<string, { userId: bigint; stake: Decimal; rate: Decimal }>();
|
||||||
|
|
||||||
|
for (const bet of settledBets) {
|
||||||
|
if (bet.status === 'PUSH' || bet.status === 'VOID') continue;
|
||||||
|
|
||||||
|
const key = bet.userId.toString();
|
||||||
|
const existing = playerStakes.get(key) ?? {
|
||||||
|
userId: bet.userId,
|
||||||
|
stake: new Decimal(0),
|
||||||
|
rate: new Decimal(0.01),
|
||||||
|
};
|
||||||
|
existing.stake = existing.stake.add(bet.stake);
|
||||||
|
playerStakes.set(key, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = Array.from(playerStakes.values()).map((p) => ({
|
||||||
|
userId: p.userId,
|
||||||
|
effectiveStake: p.stake,
|
||||||
|
rate: p.rate,
|
||||||
|
amount: p.stake.mul(p.rate),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const totalAmount = items.reduce((s, i) => s.add(i.amount), new Decimal(0));
|
||||||
|
|
||||||
|
const batch = await this.prisma.cashbackBatch.create({
|
||||||
|
data: {
|
||||||
|
batchNo: generateBatchNo('CB'),
|
||||||
|
periodStart,
|
||||||
|
periodEnd,
|
||||||
|
status: 'PREVIEW',
|
||||||
|
totalAmount,
|
||||||
|
playerCount: items.length,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
await this.prisma.cashbackItem.create({
|
||||||
|
data: {
|
||||||
|
batchId: batch.id,
|
||||||
|
userId: item.userId,
|
||||||
|
effectiveStake: item.effectiveStake,
|
||||||
|
rate: item.rate,
|
||||||
|
amount: item.amount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { batch, items, totalAmount };
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmBatch(batchId: bigint, operatorId: bigint) {
|
||||||
|
const batch = await this.prisma.cashbackBatch.findUnique({
|
||||||
|
where: { id: batchId },
|
||||||
|
include: { items: true },
|
||||||
|
});
|
||||||
|
if (!batch) throw new BadRequestException('Batch not found');
|
||||||
|
if (batch.status !== 'PREVIEW') throw new BadRequestException('Already confirmed');
|
||||||
|
|
||||||
|
for (const item of batch.items) {
|
||||||
|
if (item.amount.gt(0)) {
|
||||||
|
await this.wallet.deposit(
|
||||||
|
item.userId,
|
||||||
|
item.amount,
|
||||||
|
operatorId,
|
||||||
|
`Cashback batch ${batch.batchNo}`,
|
||||||
|
batch.batchNo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.cashbackBatch.update({
|
||||||
|
where: { id: batchId },
|
||||||
|
data: { status: 'CONFIRMED', confirmedAt: new Date(), operatorId },
|
||||||
|
});
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserCashbacks(userId: bigint) {
|
||||||
|
return this.prisma.cashbackItem.findMany({
|
||||||
|
where: { userId },
|
||||||
|
include: { batch: true },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
48
apps/api/src/common/decorators.ts
Normal file
48
apps/api/src/common/decorators.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { createParamDecorator, ExecutionContext, SetMetadata } from '@nestjs/common';
|
||||||
|
|
||||||
|
export const IS_PUBLIC_KEY = 'isPublic';
|
||||||
|
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
||||||
|
|
||||||
|
export const PERMISSIONS_KEY = 'permissions';
|
||||||
|
export const RequirePermissions = (...permissions: string[]) =>
|
||||||
|
SetMetadata(PERMISSIONS_KEY, permissions);
|
||||||
|
|
||||||
|
export const CurrentUser = createParamDecorator(
|
||||||
|
(data: string | undefined, ctx: ExecutionContext) => {
|
||||||
|
const request = ctx.switchToHttp().getRequest();
|
||||||
|
const user = request.user;
|
||||||
|
return data ? user?.[data] : user;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export function generateBetNo(): string {
|
||||||
|
const ts = Date.now().toString(36).toUpperCase();
|
||||||
|
const rand = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||||
|
return `BET${ts}${rand}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateTransactionId(): string {
|
||||||
|
const ts = Date.now().toString(36).toUpperCase();
|
||||||
|
const rand = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||||
|
return `TXN${ts}${rand}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateBatchNo(prefix: string): string {
|
||||||
|
const ts = Date.now().toString(36).toUpperCase();
|
||||||
|
return `${prefix}${ts}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeBigInt(obj: unknown): unknown {
|
||||||
|
if (obj === null || obj === undefined) return obj;
|
||||||
|
if (typeof obj === 'bigint') return obj.toString();
|
||||||
|
if (obj instanceof Date) return obj.toISOString();
|
||||||
|
if (Array.isArray(obj)) return obj.map(serializeBigInt);
|
||||||
|
if (typeof obj === 'object') {
|
||||||
|
const result: Record<string, unknown> = {};
|
||||||
|
for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
|
||||||
|
result[key] = serializeBigInt(value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
42
apps/api/src/common/filters.ts
Normal file
42
apps/api/src/common/filters.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
ExceptionFilter,
|
||||||
|
Catch,
|
||||||
|
ArgumentsHost,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Response } from 'express';
|
||||||
|
import { serializeBigInt } from './decorators';
|
||||||
|
|
||||||
|
@Catch()
|
||||||
|
export class GlobalExceptionFilter implements ExceptionFilter {
|
||||||
|
catch(exception: unknown, host: ArgumentsHost) {
|
||||||
|
const ctx = host.switchToHttp();
|
||||||
|
const response = ctx.getResponse<Response>();
|
||||||
|
|
||||||
|
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
let message = 'Internal server error';
|
||||||
|
|
||||||
|
if (exception instanceof HttpException) {
|
||||||
|
status = exception.getStatus();
|
||||||
|
const res = exception.getResponse();
|
||||||
|
message = typeof res === 'string' ? res : (res as { message?: string }).message || message;
|
||||||
|
} else if (exception instanceof Error) {
|
||||||
|
message = exception.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.status(status).json({
|
||||||
|
success: false,
|
||||||
|
error: message,
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsonResponse<T>(data: T, message?: string) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: serializeBigInt(data),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
}
|
||||||
8
apps/api/src/content/content.module.ts
Normal file
8
apps/api/src/content/content.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ContentService } from './content.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [ContentService],
|
||||||
|
exports: [ContentService],
|
||||||
|
})
|
||||||
|
export class ContentModule {}
|
||||||
59
apps/api/src/content/content.service.ts
Normal file
59
apps/api/src/content/content.service.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ContentService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async listActive(contentType: string, locale: string) {
|
||||||
|
const now = new Date();
|
||||||
|
const items = await this.prisma.content.findMany({
|
||||||
|
where: {
|
||||||
|
contentType,
|
||||||
|
status: 'ACTIVE',
|
||||||
|
OR: [{ startTime: null }, { startTime: { lte: now } }],
|
||||||
|
AND: [{ OR: [{ endTime: null }, { endTime: { gte: now } }] }],
|
||||||
|
},
|
||||||
|
include: { translations: true },
|
||||||
|
orderBy: { sortOrder: 'asc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return items.map((item) => {
|
||||||
|
const t =
|
||||||
|
item.translations.find((tr) => tr.locale === locale) ||
|
||||||
|
item.translations.find((tr) => tr.locale === 'en-US') ||
|
||||||
|
item.translations[0];
|
||||||
|
return { ...item, translation: t };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(data: {
|
||||||
|
contentType: string;
|
||||||
|
sortOrder?: number;
|
||||||
|
linkType?: string;
|
||||||
|
linkTarget?: string;
|
||||||
|
translations: Array<{ locale: string; title?: string; body?: string; imageUrl?: string }>;
|
||||||
|
}) {
|
||||||
|
return this.prisma.content.create({
|
||||||
|
data: {
|
||||||
|
contentType: data.contentType,
|
||||||
|
sortOrder: data.sortOrder ?? 0,
|
||||||
|
linkType: data.linkType,
|
||||||
|
linkTarget: data.linkTarget,
|
||||||
|
status: 'ACTIVE',
|
||||||
|
translations: {
|
||||||
|
create: data.translations,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { translations: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async listAll(contentType?: string) {
|
||||||
|
return this.prisma.content.findMany({
|
||||||
|
where: contentType ? { contentType } : {},
|
||||||
|
include: { translations: true },
|
||||||
|
orderBy: { sortOrder: 'asc' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
8
apps/api/src/i18n/i18n.module.ts
Normal file
8
apps/api/src/i18n/i18n.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { I18nService } from './i18n.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [I18nService],
|
||||||
|
exports: [I18nService],
|
||||||
|
})
|
||||||
|
export class I18nModule {}
|
||||||
55
apps/api/src/i18n/i18n.service.ts
Normal file
55
apps/api/src/i18n/i18n.service.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { DEFAULT_LOCALE } from '@thebet365/shared';
|
||||||
|
|
||||||
|
const FALLBACK_ORDER = ['en-US', 'zh-CN', 'ms-MY'];
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class I18nService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getMessages(locale: string) {
|
||||||
|
const messages = await this.prisma.i18nMessage.findMany({
|
||||||
|
where: { locale: { in: [locale, ...FALLBACK_ORDER] } },
|
||||||
|
});
|
||||||
|
|
||||||
|
const byKey: Record<string, Record<string, string>> = {};
|
||||||
|
for (const m of messages) {
|
||||||
|
if (!byKey[m.msgKey]) byKey[m.msgKey] = {};
|
||||||
|
byKey[m.msgKey][m.locale] = m.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: Record<string, string> = {};
|
||||||
|
for (const [key, locales] of Object.entries(byKey)) {
|
||||||
|
result[key] =
|
||||||
|
locales[locale] ||
|
||||||
|
FALLBACK_ORDER.map((l) => locales[l]).find(Boolean) ||
|
||||||
|
key;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async upsertMessage(msgKey: string, locale: string, value: string) {
|
||||||
|
return this.prisma.i18nMessage.upsert({
|
||||||
|
where: { msgKey_locale: { msgKey, locale } },
|
||||||
|
create: { msgKey, locale, value },
|
||||||
|
update: { value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async listMissing() {
|
||||||
|
const keys = await this.prisma.i18nMessage.groupBy({ by: ['msgKey'] });
|
||||||
|
const locales = ['zh-CN', 'ms-MY', 'en-US'];
|
||||||
|
const missing = [];
|
||||||
|
|
||||||
|
for (const { msgKey } of keys) {
|
||||||
|
for (const locale of locales) {
|
||||||
|
const exists = await this.prisma.i18nMessage.findUnique({
|
||||||
|
where: { msgKey_locale: { msgKey, locale } },
|
||||||
|
});
|
||||||
|
if (!exists) missing.push({ msgKey, locale });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
apps/api/src/integration.spec.ts
Normal file
111
apps/api/src/integration.spec.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
settleSelection,
|
||||||
|
calculatePayout,
|
||||||
|
isQuarterHandicapOrTotal,
|
||||||
|
} from './settlement/settlement-calculator';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent credit & wallet integration scenarios (A001-A007)
|
||||||
|
* These tests validate business rules without DB dependency.
|
||||||
|
*/
|
||||||
|
describe('Agent Credit Rules', () => {
|
||||||
|
it('A001: deposit increases player balance and reduces agent available credit', () => {
|
||||||
|
const creditLimit = 10000;
|
||||||
|
const usedCredit = 1000;
|
||||||
|
const depositAmount = 500;
|
||||||
|
const newUsed = usedCredit + depositAmount;
|
||||||
|
expect(creditLimit - newUsed).toBe(8500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A002: bet freeze does not change total balance or agent credit', () => {
|
||||||
|
const available = 1000;
|
||||||
|
const frozen = 0;
|
||||||
|
const stake = 100;
|
||||||
|
const totalBefore = available + frozen;
|
||||||
|
const totalAfter = (available - stake) + (frozen + stake);
|
||||||
|
expect(totalAfter).toBe(totalBefore);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A003: player lose releases agent credit', () => {
|
||||||
|
const usedBefore = 1000;
|
||||||
|
const stake = 100;
|
||||||
|
const usedAfter = usedBefore - stake;
|
||||||
|
expect(usedAfter).toBe(900);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A004: player win increases agent credit usage', () => {
|
||||||
|
const usedBefore = 1000;
|
||||||
|
const payout = 185;
|
||||||
|
const stake = 100;
|
||||||
|
const netGain = payout - stake;
|
||||||
|
const usedAfter = usedBefore + netGain;
|
||||||
|
expect(usedAfter).toBe(1085);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A005: negative credit blocks further deposit', () => {
|
||||||
|
const creditLimit = 1000;
|
||||||
|
const usedCredit = 1200;
|
||||||
|
const available = creditLimit - usedCredit;
|
||||||
|
expect(available).toBeLessThan(0);
|
||||||
|
const canDeposit = available > 0;
|
||||||
|
expect(canDeposit).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A006: level 1 allocating credit to level 2 reduces available', () => {
|
||||||
|
const available = 10000;
|
||||||
|
const allocate = 3000;
|
||||||
|
expect(available - allocate).toBe(7000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('A007: non-direct player deposit should be rejected', () => {
|
||||||
|
const agentId = BigInt(1);
|
||||||
|
const playerParentId = BigInt(2);
|
||||||
|
const canDeposit = agentId === playerParentId;
|
||||||
|
expect(canDeposit).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Bet Validation Rules (B001-B010)', () => {
|
||||||
|
it('B003: odds version mismatch should reject', () => {
|
||||||
|
const submitted = BigInt(1);
|
||||||
|
const current = BigInt(2);
|
||||||
|
expect(submitted === current).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('B007: same match in parlay rejected', () => {
|
||||||
|
const matchIds = ['1', '1', '2'];
|
||||||
|
const unique = new Set(matchIds);
|
||||||
|
expect(unique.size !== matchIds.length).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('B008: quarter line in parlay rejected', () => {
|
||||||
|
expect(isQuarterHandicapOrTotal(-0.25)).toBe(true);
|
||||||
|
expect(isQuarterHandicapOrTotal(-0.5)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('B009: more than 5 legs rejected', () => {
|
||||||
|
const legs = 6;
|
||||||
|
expect(legs > 5).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Settlement payout accuracy', () => {
|
||||||
|
it('half win payout formula', () => {
|
||||||
|
const payout = calculatePayout(100, 1.85, 'HALF_WIN');
|
||||||
|
expect(payout.toNumber()).toBe(142.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('half lose payout formula', () => {
|
||||||
|
const payout = calculatePayout(100, 1.85, 'HALF_LOSE');
|
||||||
|
expect(payout.toNumber()).toBe(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S004: 0-0 odd/even is even', () => {
|
||||||
|
const result = settleSelection({
|
||||||
|
marketType: 'FT_ODD_EVEN',
|
||||||
|
selectionCode: 'EVEN',
|
||||||
|
score: { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 },
|
||||||
|
});
|
||||||
|
expect(result).toBe('WIN');
|
||||||
|
});
|
||||||
|
});
|
||||||
33
apps/api/src/main.ts
Normal file
33
apps/api/src/main.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
|
app.setGlobalPrefix('api');
|
||||||
|
app.enableCors({ origin: true, credentials: true });
|
||||||
|
app.useGlobalPipes(
|
||||||
|
new ValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
transform: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const config = new DocumentBuilder()
|
||||||
|
.setTitle('TheBet365 API')
|
||||||
|
.setDescription('足球投注平台 MVP API')
|
||||||
|
.setVersion('1.0')
|
||||||
|
.addBearerAuth()
|
||||||
|
.build();
|
||||||
|
SwaggerModule.setup('api/docs', app, SwaggerModule.createDocument(app, config));
|
||||||
|
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
await app.listen(port);
|
||||||
|
console.log(`API running on http://localhost:${port}`);
|
||||||
|
console.log(`Swagger docs: http://localhost:${port}/api/docs`);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap();
|
||||||
8
apps/api/src/markets/markets.module.ts
Normal file
8
apps/api/src/markets/markets.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { MarketsService } from './markets.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [MarketsService],
|
||||||
|
exports: [MarketsService],
|
||||||
|
})
|
||||||
|
export class MarketsModule {}
|
||||||
203
apps/api/src/markets/markets.service.ts
Normal file
203
apps/api/src/markets/markets.service.ts
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { Decimal } from '@prisma/client/runtime/library';
|
||||||
|
import {
|
||||||
|
FT_CORRECT_SCORE_TEMPLATE,
|
||||||
|
HT_CORRECT_SCORE_TEMPLATE,
|
||||||
|
} from '../settlement/settlement-calculator';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MarketsService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async generateTemplates(matchId: bigint, marketTypes: string[]) {
|
||||||
|
const match = await this.prisma.match.findUnique({ where: { id: matchId } });
|
||||||
|
if (!match) throw new NotFoundException('Match not found');
|
||||||
|
|
||||||
|
const created = [];
|
||||||
|
|
||||||
|
for (const marketType of marketTypes) {
|
||||||
|
const existing = await this.prisma.market.findFirst({
|
||||||
|
where: { matchId, marketType },
|
||||||
|
});
|
||||||
|
if (existing) continue;
|
||||||
|
|
||||||
|
const config = this.getMarketConfig(marketType);
|
||||||
|
const market = await this.prisma.market.create({
|
||||||
|
data: {
|
||||||
|
matchId,
|
||||||
|
marketType,
|
||||||
|
period: config.period,
|
||||||
|
lineValue: config.lineValue,
|
||||||
|
allowSingle: true,
|
||||||
|
allowParlay: config.allowParlay,
|
||||||
|
sortOrder: config.sortOrder,
|
||||||
|
selections: {
|
||||||
|
create: config.selections.map((s, i) => ({
|
||||||
|
selectionCode: s.code,
|
||||||
|
selectionName: s.name,
|
||||||
|
odds: s.odds ?? 1.01,
|
||||||
|
sortOrder: i,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { selections: true },
|
||||||
|
});
|
||||||
|
created.push(market);
|
||||||
|
}
|
||||||
|
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMarketConfig(marketType: string) {
|
||||||
|
const configs: Record<string, {
|
||||||
|
period: string;
|
||||||
|
lineValue?: number;
|
||||||
|
allowParlay: boolean;
|
||||||
|
sortOrder: number;
|
||||||
|
selections: Array<{ code: string; name: string; odds?: number }>;
|
||||||
|
}> = {
|
||||||
|
FT_1X2: {
|
||||||
|
period: 'FT',
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 1,
|
||||||
|
selections: [
|
||||||
|
{ code: 'HOME', name: 'Home', odds: 2.5 },
|
||||||
|
{ code: 'DRAW', name: 'Draw', odds: 3.2 },
|
||||||
|
{ code: 'AWAY', name: 'Away', odds: 2.8 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
HT_1X2: {
|
||||||
|
period: 'HT',
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 5,
|
||||||
|
selections: [
|
||||||
|
{ code: 'HOME', name: 'HT Home', odds: 3.0 },
|
||||||
|
{ code: 'DRAW', name: 'HT Draw', odds: 2.0 },
|
||||||
|
{ code: 'AWAY', name: 'HT Away', odds: 3.5 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
FT_HANDICAP: {
|
||||||
|
period: 'FT',
|
||||||
|
lineValue: -0.5,
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 2,
|
||||||
|
selections: [
|
||||||
|
{ code: 'HOME', name: 'Home -0.5', odds: 1.9 },
|
||||||
|
{ code: 'AWAY', name: 'Away +0.5', odds: 1.9 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
HT_HANDICAP: {
|
||||||
|
period: 'HT',
|
||||||
|
lineValue: -0.5,
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 6,
|
||||||
|
selections: [
|
||||||
|
{ code: 'HOME', name: 'HT Home -0.5', odds: 1.9 },
|
||||||
|
{ code: 'AWAY', name: 'HT Away +0.5', odds: 1.9 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
FT_OVER_UNDER: {
|
||||||
|
period: 'FT',
|
||||||
|
lineValue: 2.5,
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 3,
|
||||||
|
selections: [
|
||||||
|
{ code: 'OVER', name: 'Over 2.5', odds: 1.85 },
|
||||||
|
{ code: 'UNDER', name: 'Under 2.5', odds: 1.95 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
HT_OVER_UNDER: {
|
||||||
|
period: 'HT',
|
||||||
|
lineValue: 1.5,
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 7,
|
||||||
|
selections: [
|
||||||
|
{ code: 'OVER', name: 'HT Over 1.5', odds: 2.0 },
|
||||||
|
{ code: 'UNDER', name: 'HT Under 1.5', odds: 1.75 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
FT_ODD_EVEN: {
|
||||||
|
period: 'FT',
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 4,
|
||||||
|
selections: [
|
||||||
|
{ code: 'ODD', name: 'Odd', odds: 1.9 },
|
||||||
|
{ code: 'EVEN', name: 'Even', odds: 1.9 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
FT_CORRECT_SCORE: {
|
||||||
|
period: 'FT',
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 8,
|
||||||
|
selections: FT_CORRECT_SCORE_TEMPLATE.map((code) => ({
|
||||||
|
code,
|
||||||
|
name: code.replace('SCORE_', '').replace('_', '-') || code,
|
||||||
|
odds: 8.0,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
HT_CORRECT_SCORE: {
|
||||||
|
period: 'HT',
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 9,
|
||||||
|
selections: HT_CORRECT_SCORE_TEMPLATE.map((code) => ({
|
||||||
|
code,
|
||||||
|
name: code.replace('SCORE_', '').replace('_', '-') || code,
|
||||||
|
odds: 6.0,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
SH_CORRECT_SCORE: {
|
||||||
|
period: 'SH',
|
||||||
|
allowParlay: true,
|
||||||
|
sortOrder: 10,
|
||||||
|
selections: HT_CORRECT_SCORE_TEMPLATE.map((code) => ({
|
||||||
|
code,
|
||||||
|
name: code.replace('SCORE_', '').replace('_', '-') || code,
|
||||||
|
odds: 6.0,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = configs[marketType];
|
||||||
|
if (!config) throw new BadRequestException(`Unknown market type: ${marketType}`);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateOdds(selectionId: bigint, newOdds: number, operatorId: bigint) {
|
||||||
|
const selection = await this.prisma.marketSelection.findUnique({
|
||||||
|
where: { id: selectionId },
|
||||||
|
});
|
||||||
|
if (!selection) throw new NotFoundException('Selection not found');
|
||||||
|
if (newOdds <= 1) throw new BadRequestException('Odds must be > 1.00');
|
||||||
|
|
||||||
|
const newVersion = selection.oddsVersion + BigInt(1);
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
await tx.oddsChangeLog.create({
|
||||||
|
data: {
|
||||||
|
selectionId,
|
||||||
|
oldOdds: selection.odds,
|
||||||
|
newOdds,
|
||||||
|
oddsVersion: newVersion,
|
||||||
|
changedBy: operatorId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return tx.marketSelection.update({
|
||||||
|
where: { id: selectionId },
|
||||||
|
data: { odds: newOdds, oddsVersion: newVersion },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async batchUpdateOdds(
|
||||||
|
updates: Array<{ selectionId: bigint; odds: number }>,
|
||||||
|
operatorId: bigint,
|
||||||
|
) {
|
||||||
|
const results = [];
|
||||||
|
for (const u of updates) {
|
||||||
|
results.push(await this.updateOdds(u.selectionId, u.odds, operatorId));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
apps/api/src/matches/matches.module.ts
Normal file
8
apps/api/src/matches/matches.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { MatchesService } from './matches.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [MatchesService],
|
||||||
|
exports: [MatchesService],
|
||||||
|
})
|
||||||
|
export class MatchesModule {}
|
||||||
161
apps/api/src/matches/matches.service.ts
Normal file
161
apps/api/src/matches/matches.service.ts
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||||
|
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MatchesService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async createLeague(code: string, translations: Record<string, string>) {
|
||||||
|
const league = await this.prisma.league.create({ data: { code } });
|
||||||
|
for (const [locale, value] of Object.entries(translations)) {
|
||||||
|
await this.prisma.entityTranslation.create({
|
||||||
|
data: {
|
||||||
|
entityType: 'LEAGUE',
|
||||||
|
entityId: league.id,
|
||||||
|
locale,
|
||||||
|
fieldName: 'name',
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return league;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTeam(code: string, translations: Record<string, string>) {
|
||||||
|
const team = await this.prisma.team.create({ data: { code } });
|
||||||
|
for (const [locale, value] of Object.entries(translations)) {
|
||||||
|
await this.prisma.entityTranslation.create({
|
||||||
|
data: {
|
||||||
|
entityType: 'TEAM',
|
||||||
|
entityId: team.id,
|
||||||
|
locale,
|
||||||
|
fieldName: 'name',
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createMatch(data: {
|
||||||
|
leagueId: bigint;
|
||||||
|
homeTeamId: bigint;
|
||||||
|
awayTeamId: bigint;
|
||||||
|
startTime: Date;
|
||||||
|
isHot?: boolean;
|
||||||
|
createdBy?: bigint;
|
||||||
|
}) {
|
||||||
|
return this.prisma.match.create({
|
||||||
|
data: {
|
||||||
|
leagueId: data.leagueId,
|
||||||
|
homeTeamId: data.homeTeamId,
|
||||||
|
awayTeamId: data.awayTeamId,
|
||||||
|
startTime: data.startTime,
|
||||||
|
isHot: data.isHot ?? false,
|
||||||
|
createdBy: data.createdBy,
|
||||||
|
status: 'DRAFT',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async publishMatch(matchId: bigint) {
|
||||||
|
return this.prisma.match.update({
|
||||||
|
where: { id: matchId },
|
||||||
|
data: { status: 'PUBLISHED', publishTime: new Date() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeMatch(matchId: bigint) {
|
||||||
|
return this.prisma.match.update({
|
||||||
|
where: { id: matchId },
|
||||||
|
data: { status: 'CLOSED', closeTime: new Date() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelMatch(matchId: bigint) {
|
||||||
|
return this.prisma.match.update({
|
||||||
|
where: { id: matchId },
|
||||||
|
data: { status: 'CANCELLED' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTranslation(entityType: string, entityId: bigint, locale: string) {
|
||||||
|
const translations = await this.prisma.entityTranslation.findMany({
|
||||||
|
where: { entityType, entityId },
|
||||||
|
});
|
||||||
|
const map = Object.fromEntries(
|
||||||
|
translations.filter((t) => t.fieldName === 'name').map((t) => [t.locale, t.value]),
|
||||||
|
);
|
||||||
|
return map[locale] || map['en-US'] || map['zh-CN'] || Object.values(map)[0] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async enrichMatch(match: Record<string, unknown>, locale: string) {
|
||||||
|
const m = match as {
|
||||||
|
id: bigint;
|
||||||
|
leagueId: bigint;
|
||||||
|
homeTeamId: bigint;
|
||||||
|
awayTeamId: bigint;
|
||||||
|
league?: unknown;
|
||||||
|
homeTeam?: unknown;
|
||||||
|
awayTeam?: unknown;
|
||||||
|
markets?: unknown[];
|
||||||
|
};
|
||||||
|
const [leagueName, homeName, awayName] = await Promise.all([
|
||||||
|
this.getTranslation('LEAGUE', m.leagueId, locale),
|
||||||
|
this.getTranslation('TEAM', m.homeTeamId, locale),
|
||||||
|
this.getTranslation('TEAM', m.awayTeamId, locale),
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
...match,
|
||||||
|
leagueName,
|
||||||
|
homeTeamName: homeName,
|
||||||
|
awayTeamName: awayName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async listPublished(locale = 'en-US', leagueId?: bigint) {
|
||||||
|
const matches = await this.prisma.match.findMany({
|
||||||
|
where: {
|
||||||
|
status: 'PUBLISHED',
|
||||||
|
...(leagueId ? { leagueId } : {}),
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
markets: {
|
||||||
|
where: { status: 'OPEN' },
|
||||||
|
include: { selections: { where: { status: 'OPEN' } } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: [{ isHot: 'desc' }, { startTime: 'asc' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(matches.map((m) => this.enrichMatch(m, locale)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMatchDetail(matchId: bigint, locale = 'en-US') {
|
||||||
|
const match = await this.prisma.match.findUnique({
|
||||||
|
where: { id: matchId },
|
||||||
|
include: {
|
||||||
|
markets: {
|
||||||
|
include: { selections: true },
|
||||||
|
orderBy: { sortOrder: 'asc' },
|
||||||
|
},
|
||||||
|
score: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!match) throw new NotFoundException('Match not found');
|
||||||
|
return this.enrichMatch(match, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cron(CronExpression.EVERY_MINUTE)
|
||||||
|
async autoCloseMatches() {
|
||||||
|
const now = new Date();
|
||||||
|
await this.prisma.match.updateMany({
|
||||||
|
where: {
|
||||||
|
status: 'PUBLISHED',
|
||||||
|
startTime: { lte: now },
|
||||||
|
},
|
||||||
|
data: { status: 'CLOSED', closeTime: now },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
179
apps/api/src/player/player.controller.ts
Normal file
179
apps/api/src/player/player.controller.ts
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||||
|
import { JwtAuthGuard, PlayerGuard } from '../auth/guards';
|
||||||
|
import { CurrentUser } from '../common/decorators';
|
||||||
|
import { jsonResponse } from '../common/filters';
|
||||||
|
import { UsersService } from '../users/users.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { MatchesService } from '../matches/matches.service';
|
||||||
|
import { BetsService } from '../bets/bets.service';
|
||||||
|
import { ContentService } from '../content/content.service';
|
||||||
|
import { CashbackService } from '../cashback/cashback.service';
|
||||||
|
import { IsString, IsNumber, IsArray, ValidateNested, Min, IsOptional } from 'class-validator';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
|
class SingleBetDto {
|
||||||
|
@IsString()
|
||||||
|
selectionId!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
oddsVersion!: string;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0.01)
|
||||||
|
stake!: number;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
requestId!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParlayLegDto {
|
||||||
|
@IsString()
|
||||||
|
selectionId!: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
oddsVersion!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParlayBetDto {
|
||||||
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => ParlayLegDto)
|
||||||
|
legs!: ParlayLegDto[];
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0.01)
|
||||||
|
stake!: number;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
requestId!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocaleDto {
|
||||||
|
@IsString()
|
||||||
|
locale!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiTags('Player')
|
||||||
|
@Controller('player')
|
||||||
|
@UseGuards(JwtAuthGuard, PlayerGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
export class PlayerController {
|
||||||
|
constructor(
|
||||||
|
private users: UsersService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
private matches: MatchesService,
|
||||||
|
private bets: BetsService,
|
||||||
|
private content: ContentService,
|
||||||
|
private cashback: CashbackService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get('profile')
|
||||||
|
async profile(@CurrentUser('id') userId: bigint) {
|
||||||
|
const user = await this.users.findById(userId);
|
||||||
|
return jsonResponse(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('language')
|
||||||
|
async setLanguage(@CurrentUser('id') userId: bigint, @Body() dto: LocaleDto) {
|
||||||
|
const result = await this.users.updateLocale(userId, dto.locale);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('home')
|
||||||
|
async home(@CurrentUser('locale') locale: string) {
|
||||||
|
const [banners, notices, ticker, hotMatches, todayMatches] = await Promise.all([
|
||||||
|
this.content.listActive('BANNER', locale),
|
||||||
|
this.content.listActive('NOTICE', locale),
|
||||||
|
this.content.listActive('TICKER', locale),
|
||||||
|
this.matches.listPublished(locale),
|
||||||
|
this.matches.listPublished(locale),
|
||||||
|
]);
|
||||||
|
return jsonResponse({
|
||||||
|
banners,
|
||||||
|
notices,
|
||||||
|
ticker,
|
||||||
|
hotMatches: (hotMatches as Array<{ isHot?: boolean }>).filter((m) => m.isHot),
|
||||||
|
todayMatches,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('matches')
|
||||||
|
async listMatches(
|
||||||
|
@CurrentUser('locale') locale: string,
|
||||||
|
@Query('leagueId') leagueId?: string,
|
||||||
|
) {
|
||||||
|
const items = await this.matches.listPublished(locale, leagueId ? BigInt(leagueId) : undefined);
|
||||||
|
return jsonResponse(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('matches/:id')
|
||||||
|
async matchDetail(@Param('id') id: string, @CurrentUser('locale') locale: string) {
|
||||||
|
const match = await this.matches.getMatchDetail(BigInt(id), locale);
|
||||||
|
return jsonResponse(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('bets/single')
|
||||||
|
async singleBet(@CurrentUser('id') userId: bigint, @CurrentUser('parentId') parentId: bigint, @Body() dto: SingleBetDto) {
|
||||||
|
const bet = await this.bets.placeSingleBet(
|
||||||
|
userId,
|
||||||
|
parentId,
|
||||||
|
BigInt(dto.selectionId),
|
||||||
|
BigInt(dto.oddsVersion),
|
||||||
|
dto.stake,
|
||||||
|
dto.requestId,
|
||||||
|
);
|
||||||
|
return jsonResponse(bet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('bets/parlay')
|
||||||
|
async parlayBet(@CurrentUser('id') userId: bigint, @CurrentUser('parentId') parentId: bigint, @Body() dto: ParlayBetDto) {
|
||||||
|
const bet = await this.bets.placeParlayBet(
|
||||||
|
userId,
|
||||||
|
parentId,
|
||||||
|
dto.legs.map((l) => ({ selectionId: BigInt(l.selectionId), oddsVersion: BigInt(l.oddsVersion) })),
|
||||||
|
dto.stake,
|
||||||
|
dto.requestId,
|
||||||
|
);
|
||||||
|
return jsonResponse(bet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('bets')
|
||||||
|
async myBets(
|
||||||
|
@CurrentUser('id') userId: bigint,
|
||||||
|
@Query('status') status?: string,
|
||||||
|
@Query('page') page?: string,
|
||||||
|
) {
|
||||||
|
const result = await this.bets.getUserBets(userId, status, page ? parseInt(page) : 1);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('bets/:betNo')
|
||||||
|
async betDetail(@CurrentUser('id') userId: bigint, @Param('betNo') betNo: string) {
|
||||||
|
const bet = await this.bets.getBetByNo(betNo, userId);
|
||||||
|
return jsonResponse(bet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('wallet/transactions')
|
||||||
|
async transactions(
|
||||||
|
@CurrentUser('id') userId: bigint,
|
||||||
|
@Query('page') page?: string,
|
||||||
|
) {
|
||||||
|
const result = await this.wallet.getTransactions(userId, page ? parseInt(page) : 1);
|
||||||
|
return jsonResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('cashbacks')
|
||||||
|
async cashbacks(@CurrentUser('id') userId: bigint) {
|
||||||
|
const items = await this.cashback.getUserCashbacks(userId);
|
||||||
|
return jsonResponse(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
apps/api/src/player/player.module.ts
Normal file
14
apps/api/src/player/player.module.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { PlayerController } from './player.controller';
|
||||||
|
import { UsersModule } from '../users/users.module';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
import { MatchesModule } from '../matches/matches.module';
|
||||||
|
import { BetsModule } from '../bets/bets.module';
|
||||||
|
import { ContentModule } from '../content/content.module';
|
||||||
|
import { CashbackModule } from '../cashback/cashback.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [UsersModule, WalletModule, MatchesModule, BetsModule, ContentModule, CashbackModule],
|
||||||
|
controllers: [PlayerController],
|
||||||
|
})
|
||||||
|
export class PlayerModule {}
|
||||||
9
apps/api/src/prisma/prisma.module.ts
Normal file
9
apps/api/src/prisma/prisma.module.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { PrismaService } from './prisma.service';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [PrismaService],
|
||||||
|
exports: [PrismaService],
|
||||||
|
})
|
||||||
|
export class PrismaModule {}
|
||||||
13
apps/api/src/prisma/prisma.service.ts
Normal file
13
apps/api/src/prisma/prisma.service.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||||
|
async onModuleInit() {
|
||||||
|
await this.$connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onModuleDestroy() {
|
||||||
|
await this.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
187
apps/api/src/settlement/settlement-calculator.spec.ts
Normal file
187
apps/api/src/settlement/settlement-calculator.spec.ts
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import {
|
||||||
|
settleSelection,
|
||||||
|
calculatePayout,
|
||||||
|
calculateParlayPayout,
|
||||||
|
isQuarterHandicapOrTotal,
|
||||||
|
ScoreInput,
|
||||||
|
} from './settlement-calculator';
|
||||||
|
|
||||||
|
describe('SettlementCalculator', () => {
|
||||||
|
const score: ScoreInput = { htHome: 1, htAway: 0, ftHome: 2, ftAway: 1 };
|
||||||
|
|
||||||
|
describe('FT_1X2', () => {
|
||||||
|
it('S001: home win', () => {
|
||||||
|
expect(
|
||||||
|
settleSelection({ marketType: 'FT_1X2', selectionCode: 'HOME', score }),
|
||||||
|
).toBe('WIN');
|
||||||
|
expect(
|
||||||
|
settleSelection({ marketType: 'FT_1X2', selectionCode: 'DRAW', score }),
|
||||||
|
).toBe('LOSE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S002: draw', () => {
|
||||||
|
const draw = { htHome: 0, htAway: 0, ftHome: 1, ftAway: 1 };
|
||||||
|
expect(
|
||||||
|
settleSelection({ marketType: 'FT_1X2', selectionCode: 'DRAW', score: draw }),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('HT_1X2', () => {
|
||||||
|
it('S003: half time result', () => {
|
||||||
|
expect(
|
||||||
|
settleSelection({ marketType: 'HT_1X2', selectionCode: 'HOME', score }),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('FT_ODD_EVEN', () => {
|
||||||
|
it('S004: 0-0 is even', () => {
|
||||||
|
const s = { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 };
|
||||||
|
expect(
|
||||||
|
settleSelection({ marketType: 'FT_ODD_EVEN', selectionCode: 'EVEN', score: s }),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Correct Score', () => {
|
||||||
|
it('S005: exact score 2-1', () => {
|
||||||
|
expect(
|
||||||
|
settleSelection({
|
||||||
|
marketType: 'FT_CORRECT_SCORE',
|
||||||
|
selectionCode: 'SCORE_2_1',
|
||||||
|
score,
|
||||||
|
}),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S006: other home win', () => {
|
||||||
|
const s = { htHome: 2, htAway: 0, ftHome: 5, ftAway: 0 };
|
||||||
|
expect(
|
||||||
|
settleSelection({
|
||||||
|
marketType: 'FT_CORRECT_SCORE',
|
||||||
|
selectionCode: 'OTHER_HOME',
|
||||||
|
score: s,
|
||||||
|
templateScores: ['SCORE_1_0', 'SCORE_2_0'],
|
||||||
|
}),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S008: second half correct score', () => {
|
||||||
|
expect(
|
||||||
|
settleSelection({
|
||||||
|
marketType: 'SH_CORRECT_SCORE',
|
||||||
|
selectionCode: 'SCORE_1_1',
|
||||||
|
score,
|
||||||
|
}),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Handicap', () => {
|
||||||
|
it('S009: full win', () => {
|
||||||
|
const r = settleSelection({
|
||||||
|
marketType: 'FT_HANDICAP',
|
||||||
|
selectionCode: 'HOME',
|
||||||
|
handicapLine: -1,
|
||||||
|
score: { htHome: 0, htAway: 0, ftHome: 2, ftAway: 0 },
|
||||||
|
});
|
||||||
|
expect(r).toBe('WIN');
|
||||||
|
expect(calculatePayout(100, 1.85, r).toNumber()).toBe(185);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S010: push', () => {
|
||||||
|
const r = settleSelection({
|
||||||
|
marketType: 'FT_HANDICAP',
|
||||||
|
selectionCode: 'HOME',
|
||||||
|
handicapLine: -1,
|
||||||
|
score: { htHome: 0, htAway: 0, ftHome: 1, ftAway: 0 },
|
||||||
|
});
|
||||||
|
expect(r).toBe('PUSH');
|
||||||
|
expect(calculatePayout(100, 1.85, r).toNumber()).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S011: half win -0.25', () => {
|
||||||
|
const r = settleSelection({
|
||||||
|
marketType: 'FT_HANDICAP',
|
||||||
|
selectionCode: 'HOME',
|
||||||
|
handicapLine: -0.25,
|
||||||
|
score: { htHome: 0, htAway: 0, ftHome: 0, ftAway: 0 },
|
||||||
|
});
|
||||||
|
expect(r).toBe('HALF_LOSE');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Over/Under', () => {
|
||||||
|
it('S013: over 2.5 wins with 3 goals', () => {
|
||||||
|
const s = { htHome: 1, htAway: 1, ftHome: 2, ftAway: 1 };
|
||||||
|
expect(
|
||||||
|
settleSelection({
|
||||||
|
marketType: 'FT_OVER_UNDER',
|
||||||
|
selectionCode: 'OVER',
|
||||||
|
totalLine: 2.5,
|
||||||
|
score: s,
|
||||||
|
}),
|
||||||
|
).toBe('WIN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S014: push on integer line', () => {
|
||||||
|
const s = { htHome: 1, htAway: 0, ftHome: 1, ftAway: 1 };
|
||||||
|
expect(
|
||||||
|
settleSelection({
|
||||||
|
marketType: 'FT_OVER_UNDER',
|
||||||
|
selectionCode: 'OVER',
|
||||||
|
totalLine: 2,
|
||||||
|
score: s,
|
||||||
|
}),
|
||||||
|
).toBe('PUSH');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Parlay', () => {
|
||||||
|
it('S016: all win', () => {
|
||||||
|
const result = calculateParlayPayout(100, [
|
||||||
|
{ odds: 1.8, result: 'WIN' },
|
||||||
|
{ odds: 2.0, result: 'WIN' },
|
||||||
|
]);
|
||||||
|
expect(result.betResult).toBe('WON');
|
||||||
|
expect(result.payout.toNumber()).toBe(360);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S017: one lose', () => {
|
||||||
|
const result = calculateParlayPayout(100, [
|
||||||
|
{ odds: 1.8, result: 'WIN' },
|
||||||
|
{ odds: 2.0, result: 'LOSE' },
|
||||||
|
]);
|
||||||
|
expect(result.betResult).toBe('LOST');
|
||||||
|
expect(result.payout.toNumber()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S018: one push', () => {
|
||||||
|
const result = calculateParlayPayout(100, [
|
||||||
|
{ odds: 1.8, result: 'WIN' },
|
||||||
|
{ odds: 2.0, result: 'PUSH' },
|
||||||
|
{ odds: 1.9, result: 'WIN' },
|
||||||
|
]);
|
||||||
|
expect(result.betResult).toBe('WON');
|
||||||
|
expect(result.payout.toNumber()).toBe(342);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('S019: all push', () => {
|
||||||
|
const result = calculateParlayPayout(100, [
|
||||||
|
{ odds: 1.8, result: 'PUSH' },
|
||||||
|
{ odds: 2.0, result: 'VOID' },
|
||||||
|
]);
|
||||||
|
expect(result.betResult).toBe('PUSH');
|
||||||
|
expect(result.payout.toNumber()).toBe(100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Quarter line detection', () => {
|
||||||
|
it('detects quarter lines', () => {
|
||||||
|
expect(isQuarterHandicapOrTotal(-0.25)).toBe(true);
|
||||||
|
expect(isQuarterHandicapOrTotal(2.5)).toBe(false);
|
||||||
|
expect(isQuarterHandicapOrTotal(-1)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
279
apps/api/src/settlement/settlement-calculator.ts
Normal file
279
apps/api/src/settlement/settlement-calculator.ts
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
import Decimal from 'decimal.js';
|
||||||
|
|
||||||
|
export type SelectionResult = 'WIN' | 'HALF_WIN' | 'PUSH' | 'HALF_LOSE' | 'LOSE' | 'VOID';
|
||||||
|
|
||||||
|
export interface ScoreInput {
|
||||||
|
htHome: number;
|
||||||
|
htAway: number;
|
||||||
|
ftHome: number;
|
||||||
|
ftAway: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SettlementInput {
|
||||||
|
marketType: string;
|
||||||
|
selectionCode: string;
|
||||||
|
handicapLine?: number | null;
|
||||||
|
totalLine?: number | null;
|
||||||
|
score: ScoreInput;
|
||||||
|
templateScores?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShScore(score: ScoreInput): { home: number; away: number } {
|
||||||
|
return {
|
||||||
|
home: score.ftHome - score.htHome,
|
||||||
|
away: score.ftAway - score.htAway,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isQuarterLine(line: number): boolean {
|
||||||
|
const frac = Math.abs(line % 1);
|
||||||
|
return Math.abs(frac - 0.25) < 0.001 || Math.abs(frac - 0.75) < 0.001;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitQuarterLine(line: number): [number, number] {
|
||||||
|
const sign = line >= 0 ? 1 : -1;
|
||||||
|
const abs = Math.abs(line);
|
||||||
|
const lower = Math.floor(abs * 2) / 2 * sign;
|
||||||
|
const upper = (Math.floor(abs * 2) + 1) / 2 * sign;
|
||||||
|
if (Math.abs(abs % 1 - 0.25) < 0.001) {
|
||||||
|
return [lower, upper];
|
||||||
|
}
|
||||||
|
// 0.75 case: e.g. -0.75 => -0.5 and -1
|
||||||
|
const l = Math.floor(abs) * sign;
|
||||||
|
const u = (Math.floor(abs) + 0.5) * sign;
|
||||||
|
return abs % 1 > 0.5 ? [l, u] : [lower, upper];
|
||||||
|
}
|
||||||
|
|
||||||
|
function settleHandicap(
|
||||||
|
teamGoals: number,
|
||||||
|
oppGoals: number,
|
||||||
|
handicap: number,
|
||||||
|
isHome: boolean,
|
||||||
|
): SelectionResult {
|
||||||
|
const adj = teamGoals + handicap - oppGoals;
|
||||||
|
|
||||||
|
if (!isQuarterLine(handicap)) {
|
||||||
|
if (adj > 0) return 'WIN';
|
||||||
|
if (adj === 0) return 'PUSH';
|
||||||
|
return 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
const [line1, line2] = splitQuarterLine(handicap);
|
||||||
|
const r1 = teamGoals + line1 - oppGoals;
|
||||||
|
const r2 = teamGoals + line2 - oppGoals;
|
||||||
|
|
||||||
|
const results: SelectionResult[] = [];
|
||||||
|
for (const r of [r1, r2]) {
|
||||||
|
if (r > 0) results.push('WIN');
|
||||||
|
else if (r === 0) results.push('PUSH');
|
||||||
|
else results.push('LOSE');
|
||||||
|
}
|
||||||
|
|
||||||
|
const winCount = results.filter((r) => r === 'WIN').length;
|
||||||
|
const loseCount = results.filter((r) => r === 'LOSE').length;
|
||||||
|
|
||||||
|
if (winCount === 2) return 'WIN';
|
||||||
|
if (loseCount === 2) return 'LOSE';
|
||||||
|
if (winCount === 1 && loseCount === 0) return 'HALF_WIN';
|
||||||
|
if (loseCount === 1 && winCount === 0) return 'HALF_LOSE';
|
||||||
|
return 'PUSH';
|
||||||
|
}
|
||||||
|
|
||||||
|
function settleOverUnder(
|
||||||
|
totalGoals: number,
|
||||||
|
line: number,
|
||||||
|
isOver: boolean,
|
||||||
|
): SelectionResult {
|
||||||
|
if (!isQuarterLine(line)) {
|
||||||
|
if (isOver) {
|
||||||
|
if (totalGoals > line) return 'WIN';
|
||||||
|
if (totalGoals === line) return 'PUSH';
|
||||||
|
return 'LOSE';
|
||||||
|
}
|
||||||
|
if (totalGoals < line) return 'WIN';
|
||||||
|
if (totalGoals === line) return 'PUSH';
|
||||||
|
return 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
const [line1, line2] = splitQuarterLine(line);
|
||||||
|
const r1 = settleOverUnder(totalGoals, line1, isOver);
|
||||||
|
const r2 = settleOverUnder(totalGoals, line2, isOver);
|
||||||
|
|
||||||
|
const winCount = [r1, r2].filter((r) => r === 'WIN').length;
|
||||||
|
const loseCount = [r1, r2].filter((r) => r === 'LOSE').length;
|
||||||
|
|
||||||
|
if (winCount === 2) return 'WIN';
|
||||||
|
if (loseCount === 2) return 'LOSE';
|
||||||
|
if (winCount === 1) return 'HALF_WIN';
|
||||||
|
if (loseCount === 1) return 'HALF_LOSE';
|
||||||
|
return 'PUSH';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseScoreCode(code: string): { home: number; away: number } | null {
|
||||||
|
const match = code.match(/SCORE_(\d+)_(\d+)/);
|
||||||
|
if (match) return { home: parseInt(match[1]), away: parseInt(match[2]) };
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function settleCorrectScore(
|
||||||
|
home: number,
|
||||||
|
away: number,
|
||||||
|
selectionCode: string,
|
||||||
|
templateScores: string[],
|
||||||
|
): SelectionResult {
|
||||||
|
const parsed = parseScoreCode(selectionCode);
|
||||||
|
if (parsed) {
|
||||||
|
return parsed.home === home && parsed.away === away ? 'WIN' : 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualKey = `SCORE_${home}_${away}`;
|
||||||
|
if (templateScores.includes(actualKey)) {
|
||||||
|
return 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDraw = home === away;
|
||||||
|
const isHomeWin = home > away;
|
||||||
|
|
||||||
|
if (selectionCode === 'OTHER_DRAW' && isDraw) return 'WIN';
|
||||||
|
if (selectionCode === 'OTHER_HOME' && isHomeWin) return 'WIN';
|
||||||
|
if (selectionCode === 'OTHER_AWAY' && !isHomeWin && !isDraw) return 'WIN';
|
||||||
|
|
||||||
|
return 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function settleSelection(input: SettlementInput): SelectionResult {
|
||||||
|
const { marketType, selectionCode, handicapLine, totalLine, score } = input;
|
||||||
|
const templates = input.templateScores ?? [];
|
||||||
|
|
||||||
|
switch (marketType) {
|
||||||
|
case 'FT_1X2': {
|
||||||
|
if (selectionCode === 'HOME') return score.ftHome > score.ftAway ? 'WIN' : 'LOSE';
|
||||||
|
if (selectionCode === 'DRAW') return score.ftHome === score.ftAway ? 'WIN' : 'LOSE';
|
||||||
|
if (selectionCode === 'AWAY') return score.ftHome < score.ftAway ? 'WIN' : 'LOSE';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'HT_1X2': {
|
||||||
|
if (selectionCode === 'HOME') return score.htHome > score.htAway ? 'WIN' : 'LOSE';
|
||||||
|
if (selectionCode === 'DRAW') return score.htHome === score.htAway ? 'WIN' : 'LOSE';
|
||||||
|
if (selectionCode === 'AWAY') return score.htHome < score.htAway ? 'WIN' : 'LOSE';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'FT_ODD_EVEN': {
|
||||||
|
const total = score.ftHome + score.ftAway;
|
||||||
|
const isOdd = total % 2 === 1;
|
||||||
|
if (selectionCode === 'ODD') return isOdd ? 'WIN' : 'LOSE';
|
||||||
|
if (selectionCode === 'EVEN') return !isOdd ? 'WIN' : 'LOSE';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'FT_HANDICAP': {
|
||||||
|
const line = handicapLine ?? 0;
|
||||||
|
const isHome = selectionCode === 'HOME';
|
||||||
|
const goals = isHome ? score.ftHome : score.ftAway;
|
||||||
|
const opp = isHome ? score.ftAway : score.ftHome;
|
||||||
|
return settleHandicap(goals, opp, isHome ? line : -line, isHome);
|
||||||
|
}
|
||||||
|
case 'HT_HANDICAP': {
|
||||||
|
const line = handicapLine ?? 0;
|
||||||
|
const isHome = selectionCode === 'HOME';
|
||||||
|
const goals = isHome ? score.htHome : score.htAway;
|
||||||
|
const opp = isHome ? score.htAway : score.htHome;
|
||||||
|
return settleHandicap(goals, opp, isHome ? line : -line, isHome);
|
||||||
|
}
|
||||||
|
case 'FT_OVER_UNDER': {
|
||||||
|
const total = score.ftHome + score.ftAway;
|
||||||
|
return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
|
||||||
|
}
|
||||||
|
case 'HT_OVER_UNDER': {
|
||||||
|
const total = score.htHome + score.htAway;
|
||||||
|
return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
|
||||||
|
}
|
||||||
|
case 'FT_CORRECT_SCORE':
|
||||||
|
return settleCorrectScore(score.ftHome, score.ftAway, selectionCode, templates);
|
||||||
|
case 'HT_CORRECT_SCORE':
|
||||||
|
return settleCorrectScore(score.htHome, score.htAway, selectionCode, templates);
|
||||||
|
case 'SH_CORRECT_SCORE': {
|
||||||
|
const sh = getShScore(score);
|
||||||
|
return settleCorrectScore(sh.home, sh.away, selectionCode, templates);
|
||||||
|
}
|
||||||
|
case 'OUTRIGHT_WINNER':
|
||||||
|
return selectionCode === `TEAM_${input.score.ftHome}` ? 'WIN' : 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'LOSE';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculatePayout(
|
||||||
|
stake: Decimal | number,
|
||||||
|
odds: Decimal | number,
|
||||||
|
result: SelectionResult,
|
||||||
|
): Decimal {
|
||||||
|
const s = new Decimal(stake);
|
||||||
|
const o = new Decimal(odds);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case 'WIN':
|
||||||
|
return s.mul(o);
|
||||||
|
case 'HALF_WIN':
|
||||||
|
return s.div(2).mul(o).add(s.div(2));
|
||||||
|
case 'PUSH':
|
||||||
|
case 'VOID':
|
||||||
|
return s;
|
||||||
|
case 'HALF_LOSE':
|
||||||
|
return s.div(2);
|
||||||
|
case 'LOSE':
|
||||||
|
return new Decimal(0);
|
||||||
|
default:
|
||||||
|
return new Decimal(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateParlayPayout(
|
||||||
|
stake: Decimal | number,
|
||||||
|
selections: Array<{ odds: Decimal | number; result: SelectionResult }>,
|
||||||
|
): { betResult: 'WON' | 'LOST' | 'PUSH'; payout: Decimal; effectiveOdds: Decimal } {
|
||||||
|
const s = new Decimal(stake);
|
||||||
|
|
||||||
|
if (selections.some((sel) => sel.result === 'LOSE')) {
|
||||||
|
return { betResult: 'LOST', payout: new Decimal(0), effectiveOdds: new Decimal(0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
let combinedOdds = new Decimal(1);
|
||||||
|
for (const sel of selections) {
|
||||||
|
if (sel.result === 'WIN') {
|
||||||
|
combinedOdds = combinedOdds.mul(sel.odds);
|
||||||
|
} else if (sel.result === 'HALF_WIN') {
|
||||||
|
combinedOdds = combinedOdds.mul(new Decimal(sel.odds).add(1).div(2));
|
||||||
|
} else if (sel.result === 'HALF_LOSE') {
|
||||||
|
combinedOdds = combinedOdds.mul(0.5);
|
||||||
|
}
|
||||||
|
// PUSH/VOID => odds 1.00
|
||||||
|
}
|
||||||
|
|
||||||
|
const allPush = selections.every(
|
||||||
|
(sel) => sel.result === 'PUSH' || sel.result === 'VOID',
|
||||||
|
);
|
||||||
|
if (allPush) {
|
||||||
|
return { betResult: 'PUSH', payout: s, effectiveOdds: new Decimal(1) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { betResult: 'WON', payout: s.mul(combinedOdds), effectiveOdds: combinedOdds };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isQuarterHandicapOrTotal(line: number | null | undefined): boolean {
|
||||||
|
if (line == null) return false;
|
||||||
|
return isQuarterLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FT_CORRECT_SCORE_TEMPLATE = [
|
||||||
|
'SCORE_0_0', 'SCORE_1_1', 'SCORE_2_2', 'SCORE_3_3', 'SCORE_4_4', 'OTHER_DRAW',
|
||||||
|
'SCORE_1_0', 'SCORE_2_0', 'SCORE_2_1', 'SCORE_3_0', 'SCORE_3_1', 'SCORE_3_2',
|
||||||
|
'SCORE_4_0', 'SCORE_4_1', 'SCORE_4_2', 'SCORE_4_3', 'OTHER_HOME',
|
||||||
|
'SCORE_0_1', 'SCORE_0_2', 'SCORE_1_2', 'SCORE_0_3', 'SCORE_1_3', 'SCORE_2_3',
|
||||||
|
'SCORE_0_4', 'SCORE_1_4', 'SCORE_2_4', 'SCORE_3_4', 'OTHER_AWAY',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const HT_CORRECT_SCORE_TEMPLATE = [
|
||||||
|
'SCORE_0_0', 'SCORE_1_1', 'SCORE_2_2', 'OTHER_DRAW',
|
||||||
|
'SCORE_1_0', 'SCORE_2_0', 'SCORE_2_1', 'SCORE_3_0', 'OTHER_HOME',
|
||||||
|
'SCORE_0_1', 'SCORE_0_2', 'SCORE_1_2', 'SCORE_0_3', 'OTHER_AWAY',
|
||||||
|
];
|
||||||
11
apps/api/src/settlement/settlement.module.ts
Normal file
11
apps/api/src/settlement/settlement.module.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { SettlementService } from './settlement.service';
|
||||||
|
import { WalletModule } from '../wallet/wallet.module';
|
||||||
|
import { AgentsModule } from '../agents/agents.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [WalletModule, AgentsModule],
|
||||||
|
providers: [SettlementService],
|
||||||
|
exports: [SettlementService],
|
||||||
|
})
|
||||||
|
export class SettlementModule {}
|
||||||
312
apps/api/src/settlement/settlement.service.ts
Normal file
312
apps/api/src/settlement/settlement.service.ts
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { WalletService } from '../wallet/wallet.service';
|
||||||
|
import { AgentsService } from '../agents/agents.service';
|
||||||
|
import { Decimal } from '@prisma/client/runtime/library';
|
||||||
|
import { generateBatchNo } from '../common/decorators';
|
||||||
|
import {
|
||||||
|
settleSelection,
|
||||||
|
calculatePayout,
|
||||||
|
calculateParlayPayout,
|
||||||
|
ScoreInput,
|
||||||
|
FT_CORRECT_SCORE_TEMPLATE,
|
||||||
|
HT_CORRECT_SCORE_TEMPLATE,
|
||||||
|
} from './settlement-calculator';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SettlementService {
|
||||||
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private wallet: WalletService,
|
||||||
|
private agents: AgentsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async recordScore(
|
||||||
|
matchId: bigint,
|
||||||
|
htHome: number,
|
||||||
|
htAway: number,
|
||||||
|
ftHome: number,
|
||||||
|
ftAway: number,
|
||||||
|
operatorId: bigint,
|
||||||
|
) {
|
||||||
|
await this.prisma.matchScore.upsert({
|
||||||
|
where: { matchId },
|
||||||
|
create: { matchId, htHomeScore: htHome, htAwayScore: htAway, ftHomeScore: ftHome, ftAwayScore: ftAway, recordedBy: operatorId },
|
||||||
|
update: { htHomeScore: htHome, htAwayScore: htAway, ftHomeScore: ftHome, ftAwayScore: ftAway, recordedBy: operatorId },
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.match.update({
|
||||||
|
where: { id: matchId },
|
||||||
|
data: { status: 'PENDING_SETTLEMENT' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return { matchId, htHome, htAway, ftHome, ftAway };
|
||||||
|
}
|
||||||
|
|
||||||
|
async previewSettlement(matchId: bigint, operatorId: bigint) {
|
||||||
|
const score = await this.prisma.matchScore.findUnique({ where: { matchId } });
|
||||||
|
if (!score) throw new BadRequestException('Score not recorded');
|
||||||
|
|
||||||
|
const scoreInput: ScoreInput = {
|
||||||
|
htHome: score.htHomeScore ?? 0,
|
||||||
|
htAway: score.htAwayScore ?? 0,
|
||||||
|
ftHome: score.ftHomeScore ?? 0,
|
||||||
|
ftAway: score.ftAwayScore ?? 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pendingBets = await this.prisma.bet.findMany({
|
||||||
|
where: {
|
||||||
|
status: 'PENDING',
|
||||||
|
selections: { some: { matchId } },
|
||||||
|
},
|
||||||
|
include: { selections: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const parlayBets = await this.prisma.bet.findMany({
|
||||||
|
where: {
|
||||||
|
status: 'PENDING',
|
||||||
|
betType: 'PARLAY',
|
||||||
|
selections: { some: { matchId } },
|
||||||
|
},
|
||||||
|
include: { selections: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
let totalPayout = new Decimal(0);
|
||||||
|
let totalRefund = new Decimal(0);
|
||||||
|
const items: Array<{ betId: bigint; betNo: string; result: string; payout: Decimal }> = [];
|
||||||
|
|
||||||
|
for (const bet of pendingBets) {
|
||||||
|
if (bet.betType === 'SINGLE') {
|
||||||
|
const sel = bet.selections[0];
|
||||||
|
const template =
|
||||||
|
sel.marketType === 'FT_CORRECT_SCORE'
|
||||||
|
? FT_CORRECT_SCORE_TEMPLATE
|
||||||
|
: sel.marketType.includes('CORRECT_SCORE')
|
||||||
|
? HT_CORRECT_SCORE_TEMPLATE
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const result = settleSelection({
|
||||||
|
marketType: sel.marketType,
|
||||||
|
selectionCode: sel.selectionNameSnapshot.includes('-')
|
||||||
|
? `SCORE_${sel.selectionNameSnapshot.replace('-', '_')}`
|
||||||
|
: sel.selectionNameSnapshot,
|
||||||
|
handicapLine: sel.handicapLine ? Number(sel.handicapLine) : null,
|
||||||
|
totalLine: sel.totalLine ? Number(sel.totalLine) : null,
|
||||||
|
score: scoreInput,
|
||||||
|
templateScores: template,
|
||||||
|
});
|
||||||
|
|
||||||
|
const payout = calculatePayout(bet.stake, sel.odds, result);
|
||||||
|
items.push({ betId: bet.id, betNo: bet.betNo, result, payout });
|
||||||
|
|
||||||
|
if (result === 'LOSE') {
|
||||||
|
// no payout
|
||||||
|
} else if (result === 'PUSH' || result === 'VOID') {
|
||||||
|
totalRefund = totalRefund.add(bet.stake);
|
||||||
|
} else {
|
||||||
|
totalPayout = totalPayout.add(payout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const batch = await this.prisma.settlementBatch.create({
|
||||||
|
data: {
|
||||||
|
matchId,
|
||||||
|
batchNo: generateBatchNo('STL'),
|
||||||
|
htHomeScore: score.htHomeScore,
|
||||||
|
htAwayScore: score.htAwayScore,
|
||||||
|
ftHomeScore: score.ftHomeScore,
|
||||||
|
ftAwayScore: score.ftAwayScore,
|
||||||
|
status: 'PREVIEW',
|
||||||
|
totalBets: pendingBets.length,
|
||||||
|
totalPayout,
|
||||||
|
totalRefund,
|
||||||
|
operatorId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
batch,
|
||||||
|
score: scoreInput,
|
||||||
|
singleBetCount: pendingBets.filter((b) => b.betType === 'SINGLE').length,
|
||||||
|
parlayBetCount: parlayBets.length,
|
||||||
|
items,
|
||||||
|
totalPayout,
|
||||||
|
totalRefund,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmSettlement(batchId: bigint, operatorId: bigint) {
|
||||||
|
const batch = await this.prisma.settlementBatch.findUnique({
|
||||||
|
where: { id: batchId },
|
||||||
|
include: { match: true },
|
||||||
|
});
|
||||||
|
if (!batch) throw new NotFoundException('Batch not found');
|
||||||
|
if (batch.status !== 'PREVIEW') throw new BadRequestException('Batch already confirmed');
|
||||||
|
|
||||||
|
const score = await this.prisma.matchScore.findUnique({
|
||||||
|
where: { matchId: batch.matchId },
|
||||||
|
});
|
||||||
|
if (!score) throw new BadRequestException('Score not found');
|
||||||
|
|
||||||
|
const scoreInput: ScoreInput = {
|
||||||
|
htHome: score.htHomeScore ?? 0,
|
||||||
|
htAway: score.htAwayScore ?? 0,
|
||||||
|
ftHome: score.ftHomeScore ?? 0,
|
||||||
|
ftAway: score.ftAwayScore ?? 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pendingBets = await this.prisma.bet.findMany({
|
||||||
|
where: {
|
||||||
|
status: 'PENDING',
|
||||||
|
selections: { some: { matchId: batch.matchId } },
|
||||||
|
},
|
||||||
|
include: { selections: true, user: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const agentIds = new Set<bigint>();
|
||||||
|
|
||||||
|
await this.prisma.$transaction(async (tx) => {
|
||||||
|
for (const bet of pendingBets) {
|
||||||
|
if (bet.betType === 'SINGLE') {
|
||||||
|
const sel = bet.selections[0];
|
||||||
|
const selection = await tx.marketSelection.findUnique({
|
||||||
|
where: { id: sel.selectionId },
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = settleSelection({
|
||||||
|
marketType: sel.marketType,
|
||||||
|
selectionCode: selection?.selectionCode ?? sel.selectionNameSnapshot,
|
||||||
|
handicapLine: sel.handicapLine ? Number(sel.handicapLine) : null,
|
||||||
|
totalLine: sel.totalLine ? Number(sel.totalLine) : null,
|
||||||
|
score: scoreInput,
|
||||||
|
templateScores:
|
||||||
|
sel.marketType === 'FT_CORRECT_SCORE'
|
||||||
|
? FT_CORRECT_SCORE_TEMPLATE
|
||||||
|
: HT_CORRECT_SCORE_TEMPLATE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const payout = calculatePayout(bet.stake, sel.odds, result);
|
||||||
|
const betStatus =
|
||||||
|
result === 'LOSE' ? 'LOST' : result === 'PUSH' || result === 'VOID' ? 'PUSH' : 'WON';
|
||||||
|
|
||||||
|
await tx.bet.update({
|
||||||
|
where: { id: bet.id },
|
||||||
|
data: {
|
||||||
|
status: betStatus,
|
||||||
|
actualReturn: payout,
|
||||||
|
settledAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.betSelection.update({
|
||||||
|
where: { id: sel.id },
|
||||||
|
data: { resultStatus: result, effectiveOdds: sel.odds },
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.wallet.settleBet(
|
||||||
|
bet.userId,
|
||||||
|
bet.stake,
|
||||||
|
payout,
|
||||||
|
bet.betNo,
|
||||||
|
result === 'HALF_WIN' ? 'HALF_WIN' : result === 'HALF_LOSE' ? 'HALF_LOSE' : result as 'WIN' | 'LOSE' | 'PUSH' | 'VOID',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bet.agentId) agentIds.add(bet.agentId);
|
||||||
|
|
||||||
|
await tx.settlementItem.create({
|
||||||
|
data: {
|
||||||
|
batchId,
|
||||||
|
betId: bet.id,
|
||||||
|
userId: bet.userId,
|
||||||
|
result: betStatus,
|
||||||
|
payout,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Parlay: update this leg's result, check if all legs settled
|
||||||
|
for (const sel of bet.selections) {
|
||||||
|
if (sel.matchId?.toString() === batch.matchId.toString()) {
|
||||||
|
const selection = await tx.marketSelection.findUnique({
|
||||||
|
where: { id: sel.selectionId },
|
||||||
|
});
|
||||||
|
const result = settleSelection({
|
||||||
|
marketType: sel.marketType,
|
||||||
|
selectionCode: selection?.selectionCode ?? '',
|
||||||
|
handicapLine: sel.handicapLine ? Number(sel.handicapLine) : null,
|
||||||
|
totalLine: sel.totalLine ? Number(sel.totalLine) : null,
|
||||||
|
score: scoreInput,
|
||||||
|
});
|
||||||
|
await tx.betSelection.update({
|
||||||
|
where: { id: sel.id },
|
||||||
|
data: { resultStatus: result },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await tx.betSelection.findMany({ where: { betId: bet.id } });
|
||||||
|
const allHaveResult = updated.every((s) => s.resultStatus != null);
|
||||||
|
|
||||||
|
if (allHaveResult) {
|
||||||
|
const legResults = updated.map((s) => ({
|
||||||
|
odds: s.odds,
|
||||||
|
result: s.resultStatus as 'WIN' | 'LOSE' | 'PUSH' | 'VOID' | 'HALF_WIN' | 'HALF_LOSE',
|
||||||
|
}));
|
||||||
|
const parlayResult = calculateParlayPayout(bet.stake, legResults);
|
||||||
|
|
||||||
|
await tx.bet.update({
|
||||||
|
where: { id: bet.id },
|
||||||
|
data: {
|
||||||
|
status: parlayResult.betResult === 'LOST' ? 'LOST' : parlayResult.betResult === 'PUSH' ? 'PUSH' : 'WON',
|
||||||
|
actualReturn: parlayResult.payout,
|
||||||
|
settledAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.wallet.settleBet(
|
||||||
|
bet.userId,
|
||||||
|
bet.stake,
|
||||||
|
parlayResult.payout,
|
||||||
|
bet.betNo,
|
||||||
|
parlayResult.betResult === 'LOST' ? 'LOSE' : parlayResult.betResult === 'PUSH' ? 'PUSH' : 'WIN',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bet.agentId) agentIds.add(bet.agentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tx.settlementBatch.update({
|
||||||
|
where: { id: batchId },
|
||||||
|
data: { status: 'CONFIRMED', confirmedAt: new Date() },
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.match.update({
|
||||||
|
where: { id: batch.matchId },
|
||||||
|
data: { status: 'SETTLED' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const agentId of agentIds) {
|
||||||
|
await this.agents.recalculateUsedCredit(agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, batchId: batchId.toString() };
|
||||||
|
}
|
||||||
|
|
||||||
|
async voidMatchBets(matchId: bigint) {
|
||||||
|
const bets = await this.prisma.bet.findMany({
|
||||||
|
where: { status: 'PENDING', selections: { some: { matchId } } },
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const bet of bets) {
|
||||||
|
await this.wallet.settleBet(bet.userId, bet.stake, bet.stake, bet.betNo, 'VOID');
|
||||||
|
await this.prisma.bet.update({
|
||||||
|
where: { id: bet.id },
|
||||||
|
data: { status: 'VOID', actualReturn: bet.stake, settledAt: new Date() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { voidedCount: bets.length };
|
||||||
|
}
|
||||||
|
}
|
||||||
8
apps/api/src/users/users.module.ts
Normal file
8
apps/api/src/users/users.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UsersService } from './users.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [UsersService],
|
||||||
|
exports: [UsersService],
|
||||||
|
})
|
||||||
|
export class UsersModule {}
|
||||||
43
apps/api/src/users/users.service.ts
Normal file
43
apps/api/src/users/users.service.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UsersService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async findById(id: bigint) {
|
||||||
|
return this.prisma.user.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { wallet: true, agentProfile: true, preferences: true },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateLocale(userId: bigint, locale: string) {
|
||||||
|
await this.prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: { locale },
|
||||||
|
});
|
||||||
|
await this.prisma.userPreference.upsert({
|
||||||
|
where: { userId },
|
||||||
|
create: { userId, locale },
|
||||||
|
update: { locale },
|
||||||
|
});
|
||||||
|
return { locale };
|
||||||
|
}
|
||||||
|
|
||||||
|
async listPlayers(page = 1, pageSize = 20, parentId?: bigint) {
|
||||||
|
const where = { userType: 'PLAYER', ...(parentId ? { parentId } : {}) };
|
||||||
|
const skip = (page - 1) * pageSize;
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.user.findMany({
|
||||||
|
where,
|
||||||
|
include: { wallet: true },
|
||||||
|
skip,
|
||||||
|
take: pageSize,
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
}),
|
||||||
|
this.prisma.user.count({ where }),
|
||||||
|
]);
|
||||||
|
return { items, total, page, pageSize };
|
||||||
|
}
|
||||||
|
}
|
||||||
8
apps/api/src/wallet/wallet.module.ts
Normal file
8
apps/api/src/wallet/wallet.module.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { WalletService } from './wallet.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [WalletService],
|
||||||
|
exports: [WalletService],
|
||||||
|
})
|
||||||
|
export class WalletModule {}
|
||||||
222
apps/api/src/wallet/wallet.service.ts
Normal file
222
apps/api/src/wallet/wallet.service.ts
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { Decimal } from '@prisma/client/runtime/library';
|
||||||
|
import { generateTransactionId } from '../common/decorators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WalletService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getWallet(userId: bigint) {
|
||||||
|
const wallet = await this.prisma.wallet.findUnique({ where: { userId } });
|
||||||
|
if (!wallet) throw new BadRequestException('Wallet not found');
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createWallet(userId: bigint, currency = 'USD') {
|
||||||
|
return this.prisma.wallet.create({
|
||||||
|
data: { userId, currency },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async lockWallet(tx: Parameters<Parameters<PrismaService['$transaction']>[0]>[0], userId: bigint) {
|
||||||
|
const wallets = await tx.$queryRaw<Array<{ id: bigint; available_balance: Decimal; frozen_balance: Decimal; version: number }>>`
|
||||||
|
SELECT id, available_balance, frozen_balance, version FROM wallets WHERE user_id = ${userId} FOR UPDATE
|
||||||
|
`;
|
||||||
|
if (!wallets.length) throw new BadRequestException('Wallet not found');
|
||||||
|
return wallets[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async deposit(
|
||||||
|
userId: bigint,
|
||||||
|
amount: Decimal | number,
|
||||||
|
operatorId: bigint,
|
||||||
|
remark?: string,
|
||||||
|
referenceId?: string,
|
||||||
|
) {
|
||||||
|
const amt = new Decimal(amount);
|
||||||
|
if (amt.lte(0)) throw new BadRequestException('Amount must be positive');
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
const w = await this.lockWallet(tx, userId);
|
||||||
|
const balanceBefore = new Decimal(w.available_balance);
|
||||||
|
const balanceAfter = balanceBefore.add(amt);
|
||||||
|
|
||||||
|
await tx.wallet.update({
|
||||||
|
where: { id: w.id },
|
||||||
|
data: {
|
||||||
|
availableBalance: balanceAfter,
|
||||||
|
version: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.walletTransaction.create({
|
||||||
|
data: {
|
||||||
|
transactionId: generateTransactionId(),
|
||||||
|
userId,
|
||||||
|
walletId: w.id,
|
||||||
|
transactionType: 'MANUAL_DEPOSIT',
|
||||||
|
amount: amt,
|
||||||
|
balanceBefore,
|
||||||
|
balanceAfter,
|
||||||
|
frozenBefore: w.frozen_balance,
|
||||||
|
frozenAfter: w.frozen_balance,
|
||||||
|
referenceType: 'DEPOSIT',
|
||||||
|
referenceId,
|
||||||
|
operatorId,
|
||||||
|
remark,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { balanceAfter };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async withdraw(
|
||||||
|
userId: bigint,
|
||||||
|
amount: Decimal | number,
|
||||||
|
operatorId: bigint,
|
||||||
|
remark?: string,
|
||||||
|
referenceId?: string,
|
||||||
|
) {
|
||||||
|
const amt = new Decimal(amount);
|
||||||
|
if (amt.lte(0)) throw new BadRequestException('Amount must be positive');
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
const w = await this.lockWallet(tx, userId);
|
||||||
|
const balanceBefore = new Decimal(w.available_balance);
|
||||||
|
if (balanceBefore.lt(amt)) throw new BadRequestException('Insufficient balance');
|
||||||
|
const balanceAfter = balanceBefore.sub(amt);
|
||||||
|
|
||||||
|
await tx.wallet.update({
|
||||||
|
where: { id: w.id },
|
||||||
|
data: {
|
||||||
|
availableBalance: balanceAfter,
|
||||||
|
version: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.walletTransaction.create({
|
||||||
|
data: {
|
||||||
|
transactionId: generateTransactionId(),
|
||||||
|
userId,
|
||||||
|
walletId: w.id,
|
||||||
|
transactionType: 'MANUAL_WITHDRAW',
|
||||||
|
amount: amt.neg(),
|
||||||
|
balanceBefore,
|
||||||
|
balanceAfter,
|
||||||
|
frozenBefore: w.frozen_balance,
|
||||||
|
frozenAfter: w.frozen_balance,
|
||||||
|
referenceType: 'WITHDRAW',
|
||||||
|
referenceId,
|
||||||
|
operatorId,
|
||||||
|
remark,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { balanceAfter };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async freezeForBet(userId: bigint, stake: Decimal | number, betId: string) {
|
||||||
|
const amt = new Decimal(stake);
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
const w = await this.lockWallet(tx, userId);
|
||||||
|
const avail = new Decimal(w.available_balance);
|
||||||
|
if (avail.lt(amt)) throw new BadRequestException('Insufficient balance');
|
||||||
|
|
||||||
|
const balanceAfter = avail.sub(amt);
|
||||||
|
const frozenAfter = new Decimal(w.frozen_balance).add(amt);
|
||||||
|
|
||||||
|
await tx.wallet.update({
|
||||||
|
where: { id: w.id },
|
||||||
|
data: {
|
||||||
|
availableBalance: balanceAfter,
|
||||||
|
frozenBalance: frozenAfter,
|
||||||
|
version: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.walletTransaction.create({
|
||||||
|
data: {
|
||||||
|
transactionId: generateTransactionId(),
|
||||||
|
userId,
|
||||||
|
walletId: w.id,
|
||||||
|
transactionType: 'BET_FREEZE',
|
||||||
|
amount: amt.neg(),
|
||||||
|
balanceBefore: avail,
|
||||||
|
balanceAfter,
|
||||||
|
frozenBefore: w.frozen_balance,
|
||||||
|
frozenAfter,
|
||||||
|
referenceType: 'BET',
|
||||||
|
referenceId: betId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async settleBet(
|
||||||
|
userId: bigint,
|
||||||
|
stake: Decimal,
|
||||||
|
payout: Decimal,
|
||||||
|
betId: string,
|
||||||
|
result: 'WIN' | 'LOSE' | 'PUSH' | 'VOID' | 'HALF_WIN' | 'HALF_LOSE',
|
||||||
|
) {
|
||||||
|
const txTypeMap: Record<string, string> = {
|
||||||
|
WIN: 'BET_SETTLE_WIN',
|
||||||
|
LOSE: 'BET_SETTLE_LOSE',
|
||||||
|
PUSH: 'BET_SETTLE_PUSH',
|
||||||
|
VOID: 'BET_VOID_REFUND',
|
||||||
|
HALF_WIN: 'BET_SETTLE_WIN',
|
||||||
|
HALF_LOSE: 'BET_SETTLE_LOSE',
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.prisma.$transaction(async (tx) => {
|
||||||
|
const w = await this.lockWallet(tx, userId);
|
||||||
|
const avail = new Decimal(w.available_balance);
|
||||||
|
const frozen = new Decimal(w.frozen_balance);
|
||||||
|
const frozenAfter = frozen.sub(stake);
|
||||||
|
const balanceAfter = avail.add(payout);
|
||||||
|
|
||||||
|
await tx.wallet.update({
|
||||||
|
where: { id: w.id },
|
||||||
|
data: {
|
||||||
|
availableBalance: balanceAfter,
|
||||||
|
frozenBalance: frozenAfter.lt(0) ? new Decimal(0) : frozenAfter,
|
||||||
|
version: { increment: 1 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.walletTransaction.create({
|
||||||
|
data: {
|
||||||
|
transactionId: generateTransactionId(),
|
||||||
|
userId,
|
||||||
|
walletId: w.id,
|
||||||
|
transactionType: txTypeMap[result] || 'BET_SETTLE_WIN',
|
||||||
|
amount: payout,
|
||||||
|
balanceBefore: avail,
|
||||||
|
balanceAfter,
|
||||||
|
frozenBefore: frozen,
|
||||||
|
frozenAfter: frozenAfter.lt(0) ? new Decimal(0) : frozenAfter,
|
||||||
|
referenceType: 'BET',
|
||||||
|
referenceId: betId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTransactions(userId: bigint, page = 1, pageSize = 20) {
|
||||||
|
const skip = (page - 1) * pageSize;
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.walletTransaction.findMany({
|
||||||
|
where: { userId },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: pageSize,
|
||||||
|
}),
|
||||||
|
this.prisma.walletTransaction.count({ where: { userId } }),
|
||||||
|
]);
|
||||||
|
return { items, total, page, pageSize };
|
||||||
|
}
|
||||||
|
}
|
||||||
25
apps/api/tsconfig.json
Normal file
25
apps/api/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"incremental": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictBindCallApply": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "prisma/seed.ts"]
|
||||||
|
}
|
||||||
12
apps/player/index.html
Normal file
12
apps/player/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<title>TheBet365</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
25
apps/player/package.json
Normal file
25
apps/player/package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "@thebet365/player",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --port 5173",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@thebet365/shared": "workspace:*",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"pinia": "^2.3.1",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-i18n": "^11.1.1",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vite": "^6.0.11",
|
||||||
|
"vue-tsc": "^2.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
apps/player/src/App.vue
Normal file
6
apps/player/src/App.vue
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterView } from 'vue-router';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<RouterView />
|
||||||
|
</template>
|
||||||
22
apps/player/src/api/index.ts
Normal file
22
apps/player/src/api/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const api = axios.create({ baseURL: '/api' });
|
||||||
|
|
||||||
|
api.interceptors.request.use((config) => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(res) => res,
|
||||||
|
(err) => {
|
||||||
|
if (err.response?.status === 401) {
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
return Promise.reject(err);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default api;
|
||||||
121
apps/player/src/components/BetSlipDrawer.vue
Normal file
121
apps/player/src/components/BetSlipDrawer.vue
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useBetSlipStore } from '../stores/betSlip';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: boolean }>();
|
||||||
|
const emit = defineEmits<{ 'update:modelValue': [boolean] }>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const slip = useBetSlipStore();
|
||||||
|
const show = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (v) => emit('update:modelValue', v),
|
||||||
|
});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const error = ref('');
|
||||||
|
const success = ref('');
|
||||||
|
|
||||||
|
function genId() {
|
||||||
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function placeBet() {
|
||||||
|
if (!slip.items.length) return;
|
||||||
|
loading.value = true;
|
||||||
|
error.value = '';
|
||||||
|
success.value = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const requestId = genId();
|
||||||
|
if (slip.mode === 'parlay' && slip.items.length >= 2) {
|
||||||
|
if (slip.hasSameMatch) {
|
||||||
|
error.value = '同一场比赛不能串关';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await api.post('/player/bets/parlay', {
|
||||||
|
legs: slip.items.map((i) => ({
|
||||||
|
selectionId: i.selectionId,
|
||||||
|
oddsVersion: i.oddsVersion,
|
||||||
|
})),
|
||||||
|
stake: slip.stake,
|
||||||
|
requestId,
|
||||||
|
});
|
||||||
|
} else if (slip.items.length === 1) {
|
||||||
|
const item = slip.items[0];
|
||||||
|
await api.post('/player/bets/single', {
|
||||||
|
selectionId: item.selectionId,
|
||||||
|
oddsVersion: item.oddsVersion,
|
||||||
|
stake: slip.stake,
|
||||||
|
requestId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
error.value = '请选择投注项';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
success.value = '下注成功!';
|
||||||
|
slip.clear();
|
||||||
|
setTimeout(() => { show.value = false; success.value = ''; }, 1500);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
error.value = (e as { response?: { data?: { error?: string } } })?.response?.data?.error || '下注失败';
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="show" class="overlay" @click.self="show = false">
|
||||||
|
<div class="drawer">
|
||||||
|
<div class="drawer-header">
|
||||||
|
<h3>{{ t('bet.bet_slip') }} ({{ slip.count }})</h3>
|
||||||
|
<button @click="show = false">✕</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!slip.items.length" class="empty">点击赔率添加投注</div>
|
||||||
|
|
||||||
|
<div v-for="item in slip.items" :key="item.selectionId" class="slip-item">
|
||||||
|
<div class="item-name">{{ item.matchName }}</div>
|
||||||
|
<div class="item-sel">{{ item.selectionName }} @ {{ item.odds }}</div>
|
||||||
|
<button class="remove" @click="slip.removeItem(item.selectionId)">移除</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="slip.hasSameMatch" class="warn">同场比赛不能串关,可作为单关分别投注</div>
|
||||||
|
|
||||||
|
<div v-if="slip.items.length" class="stake-area">
|
||||||
|
<label>{{ t('bet.stake') }}</label>
|
||||||
|
<input v-model.number="slip.stake" type="number" min="1" />
|
||||||
|
<div v-if="slip.isParlay" class="mode-tag">{{ t('bet.parlay') }} · 赔率 {{ slip.totalOdds.toFixed(2) }}</div>
|
||||||
|
<div class="return">预计返还: {{ slip.potentialReturn.toFixed(2) }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-if="error" class="error">{{ error }}</p>
|
||||||
|
<p v-if="success" class="success">{{ success }}</p>
|
||||||
|
|
||||||
|
<button class="btn-primary" :disabled="loading || !slip.items.length" @click="placeBet">
|
||||||
|
{{ t('bet.place_bet') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 200; display: flex; align-items: flex-end; }
|
||||||
|
.drawer { background: #1a2332; width: 100%; max-height: 80vh; border-radius: 16px 16px 0 0; padding: 16px; overflow-y: auto; }
|
||||||
|
.drawer-header { display: flex; justify-content: space-between; margin-bottom: 16px; }
|
||||||
|
.drawer-header button { background: none; color: var(--text-muted); font-size: 20px; }
|
||||||
|
.slip-item { padding: 12px; background: var(--bg-hover); border-radius: 6px; margin-bottom: 8px; }
|
||||||
|
.item-name { font-size: 13px; font-weight: 600; }
|
||||||
|
.item-sel { font-size: 12px; color: var(--text-muted); }
|
||||||
|
.remove { background: none; color: var(--danger); font-size: 12px; margin-top: 4px; }
|
||||||
|
.stake-area { margin: 16px 0; }
|
||||||
|
.stake-area label { font-size: 13px; color: var(--text-muted); display: block; margin-bottom: 4px; }
|
||||||
|
.return { font-size: 14px; color: #ffd700; margin-top: 8px; }
|
||||||
|
.mode-tag { font-size: 12px; color: var(--primary); margin-top: 4px; }
|
||||||
|
.warn { color: #ff9800; font-size: 12px; margin-bottom: 8px; }
|
||||||
|
.error { color: var(--danger); font-size: 13px; }
|
||||||
|
.success { color: var(--primary); font-size: 13px; }
|
||||||
|
.empty { text-align: center; color: var(--text-muted); padding: 24px; }
|
||||||
|
</style>
|
||||||
84
apps/player/src/layouts/MainLayout.vue
Normal file
84
apps/player/src/layouts/MainLayout.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterView, RouterLink, useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useAuthStore } from '../stores/auth';
|
||||||
|
import { useBetSlipStore } from '../stores/betSlip';
|
||||||
|
import BetSlipDrawer from '../components/BetSlipDrawer.vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const { t, locale } = useI18n();
|
||||||
|
const auth = useAuthStore();
|
||||||
|
const slip = useBetSlipStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const showSlip = ref(false);
|
||||||
|
|
||||||
|
const locales = [
|
||||||
|
{ code: 'zh-CN', label: '中文' },
|
||||||
|
{ code: 'en-US', label: 'EN' },
|
||||||
|
{ code: 'ms-MY', label: 'BM' },
|
||||||
|
];
|
||||||
|
|
||||||
|
function setLocale(code: string) {
|
||||||
|
locale.value = code;
|
||||||
|
localStorage.setItem('locale', code);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="layout">
|
||||||
|
<header class="header">
|
||||||
|
<span class="logo">TheBet365</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<select :value="locale" @change="setLocale(($event.target as HTMLSelectElement).value)" class="lang-select">
|
||||||
|
<option v-for="l in locales" :key="l.code" :value="l.code">{{ l.label }}</option>
|
||||||
|
</select>
|
||||||
|
<span class="balance">{{ auth.user?.username }}</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="main">
|
||||||
|
<RouterView />
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="bottom-nav">
|
||||||
|
<RouterLink to="/" :class="{ active: route.path === '/' }">{{ t('nav.home') }}</RouterLink>
|
||||||
|
<RouterLink to="/football" :class="{ active: route.path.startsWith('/football') || route.path.startsWith('/match') }">{{ t('nav.football') }}</RouterLink>
|
||||||
|
<button class="slip-btn" @click="showSlip = true">
|
||||||
|
{{ t('bet.bet_slip') }}
|
||||||
|
<span v-if="slip.count" class="badge">{{ slip.count }}</span>
|
||||||
|
</button>
|
||||||
|
<RouterLink to="/bets" :class="{ active: route.path === '/bets' }">{{ t('nav.my_bets') }}</RouterLink>
|
||||||
|
<RouterLink to="/profile" :class="{ active: route.path === '/profile' }">{{ t('nav.profile') }}</RouterLink>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<BetSlipDrawer v-model="showSlip" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.layout { display: flex; flex-direction: column; min-height: 100vh; padding-bottom: 60px; }
|
||||||
|
.header {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
padding: 12px 16px; background: #1a2332; border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.logo { font-weight: 800; color: var(--primary); font-size: 18px; }
|
||||||
|
.header-actions { display: flex; gap: 12px; align-items: center; }
|
||||||
|
.lang-select { background: var(--bg-card); color: #fff; border: 1px solid var(--border); padding: 4px 8px; border-radius: 4px; width: auto; }
|
||||||
|
.balance { font-size: 13px; color: var(--text-muted); }
|
||||||
|
.main { flex: 1; padding: 12px; }
|
||||||
|
.bottom-nav {
|
||||||
|
position: fixed; bottom: 0; left: 0; right: 0;
|
||||||
|
display: flex; background: #1a2332; border-top: 1px solid var(--border);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.bottom-nav a, .slip-btn {
|
||||||
|
flex: 1; text-align: center; padding: 10px 4px; font-size: 11px;
|
||||||
|
color: var(--text-muted); background: none; position: relative;
|
||||||
|
}
|
||||||
|
.bottom-nav a.active, .slip-btn.active { color: var(--primary); }
|
||||||
|
.badge {
|
||||||
|
position: absolute; top: 2px; right: 20%;
|
||||||
|
background: var(--danger); color: #fff; font-size: 10px;
|
||||||
|
padding: 1px 5px; border-radius: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
34
apps/player/src/main.ts
Normal file
34
apps/player/src/main.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { createApp } from 'vue';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
import { createI18n } from 'vue-i18n';
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
const i18n = createI18n({
|
||||||
|
legacy: false,
|
||||||
|
locale: localStorage.getItem('locale') || 'zh-CN',
|
||||||
|
fallbackLocale: 'en-US',
|
||||||
|
messages: {
|
||||||
|
'zh-CN': {
|
||||||
|
nav: { home: '首页', football: '足球', my_bets: '我的投注', profile: '我的' },
|
||||||
|
auth: { login: '登录', username: '账号', password: '密码' },
|
||||||
|
wallet: { balance: '余额' },
|
||||||
|
bet: { bet_slip: '投注单', stake: '投注金额', place_bet: '确认下注', parlay: '串关' },
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
nav: { home: 'Home', football: 'Football', my_bets: 'My Bets', profile: 'Profile' },
|
||||||
|
auth: { login: 'Login', username: 'Username', password: 'Password' },
|
||||||
|
wallet: { balance: 'Balance' },
|
||||||
|
bet: { bet_slip: 'Bet Slip', stake: 'Stake', place_bet: 'Place Bet', parlay: 'Parlay' },
|
||||||
|
},
|
||||||
|
'ms-MY': {
|
||||||
|
nav: { home: 'Laman Utama', football: 'Bola Sepak', my_bets: 'Pertaruhan Saya', profile: 'Profil' },
|
||||||
|
auth: { login: 'Log Masuk', username: 'Nama Pengguna', password: 'Kata Laluan' },
|
||||||
|
wallet: { balance: 'Baki' },
|
||||||
|
bet: { bet_slip: 'Slip Pertaruhan', stake: 'Jumlah', place_bet: 'Letak Pertaruhan', parlay: 'Berganda' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createApp(App).use(createPinia()).use(router).use(i18n).mount('#app');
|
||||||
29
apps/player/src/router/index.ts
Normal file
29
apps/player/src/router/index.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import { useAuthStore } from './stores/auth';
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{ path: '/login', component: () => import('./views/LoginView.vue') },
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('./layouts/MainLayout.vue'),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
children: [
|
||||||
|
{ path: '', component: () => import('./views/HomeView.vue') },
|
||||||
|
{ path: 'football', component: () => import('./views/FootballView.vue') },
|
||||||
|
{ path: 'match/:id', component: () => import('./views/MatchDetailView.vue') },
|
||||||
|
{ path: 'bets', component: () => import('./views/MyBetsView.vue') },
|
||||||
|
{ path: 'profile', component: () => import('./views/ProfileView.vue') },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to) => {
|
||||||
|
const auth = useAuthStore();
|
||||||
|
if (to.meta.requiresAuth && !auth.token) return '/login';
|
||||||
|
if (to.path === '/login' && auth.token) return '/';
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
25
apps/player/src/stores/auth.ts
Normal file
25
apps/player/src/stores/auth.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
|
const token = ref(localStorage.getItem('token') || '');
|
||||||
|
const user = ref(JSON.parse(localStorage.getItem('user') || 'null'));
|
||||||
|
|
||||||
|
async function login(username: string, password: string) {
|
||||||
|
const { data } = await api.post('/player/auth/login', { username, password });
|
||||||
|
token.value = data.data.token;
|
||||||
|
user.value = data.data.user;
|
||||||
|
localStorage.setItem('token', token.value);
|
||||||
|
localStorage.setItem('user', JSON.stringify(user.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
token.value = '';
|
||||||
|
user.value = null;
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
localStorage.removeItem('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { token, user, login, logout };
|
||||||
|
});
|
||||||
74
apps/player/src/stores/betSlip.ts
Normal file
74
apps/player/src/stores/betSlip.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
export interface SlipItem {
|
||||||
|
selectionId: string;
|
||||||
|
oddsVersion: string;
|
||||||
|
matchId: string;
|
||||||
|
matchName: string;
|
||||||
|
selectionName: string;
|
||||||
|
odds: number;
|
||||||
|
marketType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useBetSlipStore = defineStore('betSlip', () => {
|
||||||
|
const items = ref<SlipItem[]>([]);
|
||||||
|
const stake = ref<number>(10);
|
||||||
|
const mode = ref<'single' | 'parlay'>('single');
|
||||||
|
|
||||||
|
const count = computed(() => items.value.length);
|
||||||
|
const isParlay = computed(() => items.value.length >= 2);
|
||||||
|
|
||||||
|
function addItem(item: SlipItem) {
|
||||||
|
const existing = items.value.findIndex(
|
||||||
|
(i) => i.selectionId === item.selectionId,
|
||||||
|
);
|
||||||
|
if (existing >= 0) {
|
||||||
|
items.value.splice(existing, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items.value.push(item);
|
||||||
|
if (items.value.length >= 2) mode.value = 'parlay';
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeItem(selectionId: string) {
|
||||||
|
items.value = items.value.filter((i) => i.selectionId !== selectionId);
|
||||||
|
if (items.value.length < 2) mode.value = 'single';
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
items.value = [];
|
||||||
|
mode.value = 'single';
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalOdds = computed(() =>
|
||||||
|
items.value.reduce((acc, i) => acc * i.odds, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
const potentialReturn = computed(() =>
|
||||||
|
mode.value === 'parlay'
|
||||||
|
? stake.value * totalOdds.value
|
||||||
|
: items.value.length === 1
|
||||||
|
? stake.value * items.value[0].odds
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasSameMatch = computed(() => {
|
||||||
|
const matchIds = items.value.map((i) => i.matchId);
|
||||||
|
return new Set(matchIds).size !== matchIds.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
stake,
|
||||||
|
mode,
|
||||||
|
count,
|
||||||
|
isParlay,
|
||||||
|
totalOdds,
|
||||||
|
potentialReturn,
|
||||||
|
hasSameMatch,
|
||||||
|
addItem,
|
||||||
|
removeItem,
|
||||||
|
clear,
|
||||||
|
};
|
||||||
|
});
|
||||||
58
apps/player/src/styles.css
Normal file
58
apps/player/src/styles.css
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: #0f1419;
|
||||||
|
color: #e8eaed;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--primary: #00a826;
|
||||||
|
--primary-dark: #008a1f;
|
||||||
|
--bg-card: #1a2332;
|
||||||
|
--bg-hover: #243044;
|
||||||
|
--text-muted: #8b95a5;
|
||||||
|
--border: #2d3a4d;
|
||||||
|
--danger: #ff4444;
|
||||||
|
}
|
||||||
|
a { color: inherit; text-decoration: none; }
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background: var(--primary);
|
||||||
|
color: #fff;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-weight: 600;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
|
.odds-btn {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
.odds-btn.selected { background: var(--primary); border-color: var(--primary); }
|
||||||
|
.odds-btn .label { font-size: 11px; color: var(--text-muted); }
|
||||||
|
.odds-btn .value { font-size: 15px; font-weight: 700; color: #ffd700; }
|
||||||
|
.card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
66
apps/player/src/views/FootballView.vue
Normal file
66
apps/player/src/views/FootballView.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const matches = ref<Match[]>([]);
|
||||||
|
|
||||||
|
interface Match {
|
||||||
|
id: string;
|
||||||
|
homeTeamName: string;
|
||||||
|
awayTeamName: string;
|
||||||
|
startTime: string;
|
||||||
|
leagueName: string;
|
||||||
|
markets?: Array<{ marketType: string; selections: Selection[] }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Selection {
|
||||||
|
id: string;
|
||||||
|
selectionCode: string;
|
||||||
|
selectionName: string;
|
||||||
|
odds: string;
|
||||||
|
oddsVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/player/matches');
|
||||||
|
matches.value = data.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
function goMatch(id: string) {
|
||||||
|
router.push(`/match/${id}`);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2 class="title">足球赛事</h2>
|
||||||
|
<div v-for="match in matches" :key="match.id" class="card">
|
||||||
|
<div class="league">{{ match.leagueName }}</div>
|
||||||
|
<div class="teams" @click="goMatch(match.id)">
|
||||||
|
{{ match.homeTeamName }} vs {{ match.awayTeamName }}
|
||||||
|
</div>
|
||||||
|
<div class="time">{{ new Date(match.startTime).toLocaleString() }}</div>
|
||||||
|
|
||||||
|
<div v-if="match.markets?.length" class="odds-row">
|
||||||
|
<template v-for="market in match.markets.filter(m => m.marketType === 'FT_1X2')" :key="market.marketType">
|
||||||
|
<div v-for="sel in market.selections" :key="sel.id" class="odds-btn" @click="goMatch(match.id)">
|
||||||
|
<div class="label">{{ sel.selectionName }}</div>
|
||||||
|
<div class="value">{{ sel.odds }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!matches.length" class="empty">暂无赛事</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title { font-size: 18px; margin-bottom: 16px; }
|
||||||
|
.league { font-size: 11px; color: var(--primary); margin-bottom: 4px; }
|
||||||
|
.teams { font-weight: 600; cursor: pointer; margin-bottom: 4px; }
|
||||||
|
.time { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
|
||||||
|
.odds-row { display: flex; gap: 8px; }
|
||||||
|
.empty { text-align: center; color: var(--text-muted); padding: 40px; }
|
||||||
|
</style>
|
||||||
57
apps/player/src/views/HomeView.vue
Normal file
57
apps/player/src/views/HomeView.vue
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const home = ref<{ banners: unknown[]; hotMatches: Match[]; ticker: unknown[] } | null>(null);
|
||||||
|
|
||||||
|
interface Match {
|
||||||
|
id: string;
|
||||||
|
homeTeamName: string;
|
||||||
|
awayTeamName: string;
|
||||||
|
startTime: string;
|
||||||
|
isHot: boolean;
|
||||||
|
markets?: Array<{ marketType: string; selections: Array<{ id: string; selectionName: string; odds: string; oddsVersion: string }> }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get('/player/home');
|
||||||
|
home.value = data.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
function goMatch(id: string) {
|
||||||
|
router.push(`/match/${id}`);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="home?.banners?.length" class="banner card">
|
||||||
|
{{ (home.banners[0] as { translation?: { title?: string } })?.translation?.title || 'Welcome' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="home?.ticker?.length" class="ticker">
|
||||||
|
{{ (home.ticker[0] as { translation?: { body?: string } })?.translation?.body }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="section-title">热门赛事</h2>
|
||||||
|
<div v-for="match in home?.hotMatches || []" :key="match.id" class="card match-card" @click="goMatch(match.id)">
|
||||||
|
<div class="match-teams">{{ match.homeTeamName }} vs {{ match.awayTeamName }}</div>
|
||||||
|
<div class="match-time">{{ new Date(match.startTime).toLocaleString() }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!home?.hotMatches?.length" class="empty">暂无赛事</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.banner { background: linear-gradient(135deg, #1a472a, #0f1419); padding: 24px; font-size: 18px; font-weight: 600; }
|
||||||
|
.ticker { background: #243044; padding: 8px 12px; font-size: 12px; margin-bottom: 12px; border-radius: 4px; overflow: hidden; white-space: nowrap; }
|
||||||
|
.section-title { font-size: 16px; margin-bottom: 12px; }
|
||||||
|
.match-card { cursor: pointer; }
|
||||||
|
.match-card:hover { background: var(--bg-hover); }
|
||||||
|
.match-teams { font-weight: 600; margin-bottom: 4px; }
|
||||||
|
.match-time { font-size: 12px; color: var(--text-muted); }
|
||||||
|
.empty { text-align: center; color: var(--text-muted); padding: 40px; }
|
||||||
|
</style>
|
||||||
54
apps/player/src/views/LoginView.vue
Normal file
54
apps/player/src/views/LoginView.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useAuthStore } from '../stores/auth';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const auth = useAuthStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const username = ref('player1');
|
||||||
|
const password = ref('Player@123');
|
||||||
|
const error = ref('');
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
loading.value = true;
|
||||||
|
error.value = '';
|
||||||
|
try {
|
||||||
|
await auth.login(username.value, password.value);
|
||||||
|
router.push('/');
|
||||||
|
} catch (e: unknown) {
|
||||||
|
error.value = (e as { response?: { data?: { error?: string } } })?.response?.data?.error || 'Login failed';
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="login-page">
|
||||||
|
<h1 class="logo">TheBet365</h1>
|
||||||
|
<form @submit.prevent="submit" class="login-form">
|
||||||
|
<label>{{ t('auth.username') }}</label>
|
||||||
|
<input v-model="username" required />
|
||||||
|
<label>{{ t('auth.password') }}</label>
|
||||||
|
<input v-model="password" type="password" required />
|
||||||
|
<p v-if="error" class="error">{{ error }}</p>
|
||||||
|
<button type="submit" class="btn-primary" :disabled="loading">
|
||||||
|
{{ t('auth.login') }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-page {
|
||||||
|
min-height: 100vh; display: flex; flex-direction: column;
|
||||||
|
align-items: center; justify-content: center; padding: 24px;
|
||||||
|
}
|
||||||
|
.logo { color: var(--primary); font-size: 32px; margin-bottom: 32px; }
|
||||||
|
.login-form { width: 100%; max-width: 320px; display: flex; flex-direction: column; gap: 12px; }
|
||||||
|
label { font-size: 13px; color: var(--text-muted); }
|
||||||
|
.error { color: var(--danger); font-size: 13px; }
|
||||||
|
</style>
|
||||||
102
apps/player/src/views/MatchDetailView.vue
Normal file
102
apps/player/src/views/MatchDetailView.vue
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import api from '../api';
|
||||||
|
import { useBetSlipStore } from '../stores/betSlip';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const slip = useBetSlipStore();
|
||||||
|
const match = ref<MatchDetail | null>(null);
|
||||||
|
|
||||||
|
interface MatchDetail {
|
||||||
|
id: string;
|
||||||
|
homeTeamName: string;
|
||||||
|
awayTeamName: string;
|
||||||
|
startTime: string;
|
||||||
|
markets: Market[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Market {
|
||||||
|
id: string;
|
||||||
|
marketType: string;
|
||||||
|
period: string;
|
||||||
|
lineValue?: string;
|
||||||
|
selections: Selection[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Selection {
|
||||||
|
id: string;
|
||||||
|
selectionCode: string;
|
||||||
|
selectionName: string;
|
||||||
|
odds: string;
|
||||||
|
oddsVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const marketLabels: Record<string, string> = {
|
||||||
|
FT_1X2: '全场独赢',
|
||||||
|
FT_HANDICAP: '全场让球',
|
||||||
|
FT_OVER_UNDER: '全场大小',
|
||||||
|
FT_ODD_EVEN: '全场单双',
|
||||||
|
HT_1X2: '半场独赢',
|
||||||
|
FT_CORRECT_SCORE: '波胆',
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await api.get(`/player/matches/${route.params.id}`);
|
||||||
|
match.value = data.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
function isSelected(id: string) {
|
||||||
|
return slip.items.some((i) => i.selectionId === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSelection(sel: Selection, market: Market) {
|
||||||
|
if (!match.value) return;
|
||||||
|
slip.addItem({
|
||||||
|
selectionId: sel.id,
|
||||||
|
oddsVersion: sel.oddsVersion,
|
||||||
|
matchId: match.value.id,
|
||||||
|
matchName: `${match.value.homeTeamName} vs ${match.value.awayTeamName}`,
|
||||||
|
selectionName: sel.selectionName,
|
||||||
|
odds: parseFloat(sel.odds),
|
||||||
|
marketType: market.marketType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupedMarkets = computed(() => {
|
||||||
|
if (!match.value) return [];
|
||||||
|
return match.value.markets;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="match">
|
||||||
|
<div class="match-header card">
|
||||||
|
<h2>{{ match.homeTeamName }} vs {{ match.awayTeamName }}</h2>
|
||||||
|
<p class="time">{{ new Date(match.startTime).toLocaleString() }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="market in groupedMarkets" :key="market.id" class="card market-group">
|
||||||
|
<h3>{{ marketLabels[market.marketType] || market.marketType }}</h3>
|
||||||
|
<div class="selections">
|
||||||
|
<button
|
||||||
|
v-for="sel in market.selections"
|
||||||
|
:key="sel.id"
|
||||||
|
class="odds-btn"
|
||||||
|
:class="{ selected: isSelected(sel.id) }"
|
||||||
|
@click="toggleSelection(sel, market)"
|
||||||
|
>
|
||||||
|
<div class="label">{{ sel.selectionName }}</div>
|
||||||
|
<div class="value">{{ sel.odds }}</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.match-header h2 { font-size: 18px; margin-bottom: 4px; }
|
||||||
|
.time { color: var(--text-muted); font-size: 13px; }
|
||||||
|
.market-group h3 { font-size: 14px; margin-bottom: 12px; color: var(--text-muted); }
|
||||||
|
.selections { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
|
</style>
|
||||||
62
apps/player/src/views/MyBetsView.vue
Normal file
62
apps/player/src/views/MyBetsView.vue
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const bets = ref<{ items: Bet[]; total: number }>({ items: [], total: 0 });
|
||||||
|
|
||||||
|
interface Bet {
|
||||||
|
betNo: string;
|
||||||
|
betType: string;
|
||||||
|
stake: string;
|
||||||
|
totalOdds: string;
|
||||||
|
potentialReturn: string;
|
||||||
|
actualReturn: string;
|
||||||
|
status: string;
|
||||||
|
placedAt: string;
|
||||||
|
selections: Array<{ selectionNameSnapshot: string; odds: string; resultStatus?: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(load);
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
const { data } = await api.get('/player/bets');
|
||||||
|
bets.value = data.data;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>我的投注</h2>
|
||||||
|
<div v-for="bet in bets.items" :key="bet.betNo" class="card bet-card">
|
||||||
|
<div class="bet-header">
|
||||||
|
<span class="bet-no">{{ bet.betNo }}</span>
|
||||||
|
<span :class="['status', bet.status.toLowerCase()]">{{ bet.status }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-for="(sel, i) in bet.selections" :key="i" class="sel">
|
||||||
|
{{ sel.selectionNameSnapshot }} @ {{ sel.odds }}
|
||||||
|
<span v-if="sel.resultStatus"> → {{ sel.resultStatus }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="bet-footer">
|
||||||
|
<span>{{ bet.betType }} · 投注 {{ bet.stake }}</span>
|
||||||
|
<span v-if="bet.status === 'WON'">返还 {{ bet.actualReturn }}</span>
|
||||||
|
<span v-else-if="bet.status === 'PENDING'">预计 {{ bet.potentialReturn }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="time">{{ new Date(bet.placedAt).toLocaleString() }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!bets.items.length" class="empty">暂无投注</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
h2 { margin-bottom: 16px; }
|
||||||
|
.bet-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
|
||||||
|
.bet-no { font-size: 12px; color: var(--text-muted); }
|
||||||
|
.status { font-size: 12px; font-weight: 600; }
|
||||||
|
.status.pending { color: #ff9800; }
|
||||||
|
.status.won { color: var(--primary); }
|
||||||
|
.status.lost { color: var(--danger); }
|
||||||
|
.sel { font-size: 13px; margin-bottom: 4px; }
|
||||||
|
.bet-footer { font-size: 13px; margin-top: 8px; display: flex; justify-content: space-between; }
|
||||||
|
.time { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
|
||||||
|
.empty { text-align: center; color: var(--text-muted); padding: 40px; }
|
||||||
|
</style>
|
||||||
86
apps/player/src/views/ProfileView.vue
Normal file
86
apps/player/src/views/ProfileView.vue
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useAuthStore } from '../stores/auth';
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
const { t, locale } = useI18n();
|
||||||
|
const auth = useAuthStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const profile = ref<{ wallet?: { availableBalance: string; frozenBalance: string } } | null>(null);
|
||||||
|
const transactions = ref<unknown[]>([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const [prof, txns] = await Promise.all([
|
||||||
|
api.get('/player/profile'),
|
||||||
|
api.get('/player/wallet/transactions'),
|
||||||
|
]);
|
||||||
|
profile.value = prof.data.data;
|
||||||
|
transactions.value = txns.data.data.items;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function changeLocale(code: string) {
|
||||||
|
locale.value = code;
|
||||||
|
localStorage.setItem('locale', code);
|
||||||
|
await api.post('/player/language', { locale: code });
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
auth.logout();
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="card profile-card">
|
||||||
|
<div class="username">{{ auth.user?.username }}</div>
|
||||||
|
<div class="balance-row">
|
||||||
|
<span>{{ t('wallet.balance') }}</span>
|
||||||
|
<span class="amount">{{ profile?.wallet?.availableBalance ?? '0' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="frozen">冻结: {{ profile?.wallet?.frozenBalance ?? '0' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>语言</h3>
|
||||||
|
<div class="lang-btns">
|
||||||
|
<button @click="changeLocale('zh-CN')" :class="{ active: locale === 'zh-CN' }">中文</button>
|
||||||
|
<button @click="changeLocale('en-US')" :class="{ active: locale === 'en-US' }">English</button>
|
||||||
|
<button @click="changeLocale('ms-MY')" :class="{ active: locale === 'ms-MY' }">BM</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>账变记录</h3>
|
||||||
|
<div v-for="tx in transactions as Array<{ transactionType: string; amount: string; createdAt: string }>" :key="(tx as { transactionId?: string }).transactionId" class="tx-row">
|
||||||
|
<span>{{ tx.transactionType }}</span>
|
||||||
|
<span :class="parseFloat(tx.amount) >= 0 ? 'pos' : 'neg'">{{ tx.amount }}</span>
|
||||||
|
<span class="tx-time">{{ new Date(tx.createdAt).toLocaleString() }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn-logout" @click="logout">退出登录</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.profile-card { margin-bottom: 12px; }
|
||||||
|
.username { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
|
||||||
|
.balance-row { display: flex; justify-content: space-between; font-size: 16px; }
|
||||||
|
.amount { color: #ffd700; font-weight: 700; }
|
||||||
|
.frozen { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
|
||||||
|
h3 { font-size: 14px; margin-bottom: 12px; }
|
||||||
|
.lang-btns { display: flex; gap: 8px; }
|
||||||
|
.lang-btns button {
|
||||||
|
flex: 1; padding: 8px; background: var(--bg-hover); color: #fff;
|
||||||
|
border-radius: 6px; border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.lang-btns button.active { border-color: var(--primary); color: var(--primary); }
|
||||||
|
.tx-row { display: flex; justify-content: space-between; font-size: 13px; padding: 8px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
|
||||||
|
.pos { color: var(--primary); }
|
||||||
|
.neg { color: var(--danger); }
|
||||||
|
.tx-time { width: 100%; font-size: 11px; color: var(--text-muted); }
|
||||||
|
.btn-logout { width: 100%; margin-top: 16px; padding: 12px; background: var(--bg-card); color: var(--danger); border-radius: 6px; border: 1px solid var(--border); }
|
||||||
|
</style>
|
||||||
5
apps/player/src/vite-env.d.ts
vendored
Normal file
5
apps/player/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue';
|
||||||
|
const component: DefineComponent<object, object, unknown>;
|
||||||
|
export default component;
|
||||||
|
}
|
||||||
11
apps/player/tsconfig.json
Normal file
11
apps/player/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"paths": { "@/*": ["./src/*"] }
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.vue"]
|
||||||
|
}
|
||||||
12
apps/player/vite.config.ts
Normal file
12
apps/player/vite.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
server: {
|
||||||
|
port: 5173,
|
||||||
|
proxy: {
|
||||||
|
'/api': { target: 'http://localhost:3000', changeOrigin: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
34
docker-compose.yml
Normal file
34
docker-compose.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: thebet365-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: thebet365
|
||||||
|
POSTGRES_PASSWORD: thebet365
|
||||||
|
POSTGRES_DB: thebet365
|
||||||
|
ports:
|
||||||
|
- '5432:5432'
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U thebet365']
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: thebet365-redis
|
||||||
|
ports:
|
||||||
|
- '6379:6379'
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'redis-cli', 'ping']
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
redis_data:
|
||||||
52
docs/UAT_CHECKLIST.md
Normal file
52
docs/UAT_CHECKLIST.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# UAT 测试与上线准备
|
||||||
|
|
||||||
|
## 测试账号
|
||||||
|
|
||||||
|
| 角色 | 用户名 | 密码 | 说明 |
|
||||||
|
|------|--------|------|------|
|
||||||
|
| 超级管理员 | admin | Admin@123 | 平台后台 |
|
||||||
|
| 一级代理 | agent1 | Agent@123 | 代理后台,授信 100000 |
|
||||||
|
| 二级代理 | agent2 | Agent@123 | 隶属 agent1 |
|
||||||
|
| 测试玩家 | player1 | Player@123 | 初始余额 1000 |
|
||||||
|
|
||||||
|
## MVP 验收清单(18 项)
|
||||||
|
|
||||||
|
- [ ] 玩家可登录、改密码、切换语言
|
||||||
|
- [ ] 代理可创建直属玩家
|
||||||
|
- [ ] 代理可给直属玩家上分/下分
|
||||||
|
- [ ] 代理额度正确扣减和释放
|
||||||
|
- [ ] 后台可创建比赛和盘口
|
||||||
|
- [ ] 后台可批量录入波胆赔率
|
||||||
|
- [ ] 前台可展示赛事和盘口
|
||||||
|
- [ ] 玩家可单关下注
|
||||||
|
- [ ] 玩家可 2-5 串 1
|
||||||
|
- [ ] 同场串关被禁止
|
||||||
|
- [ ] 四分之一盘口不能进入串关
|
||||||
|
- [ ] 开赛自动封盘
|
||||||
|
- [ ] 后台可录入比分并生成预览
|
||||||
|
- [ ] 确认结算后钱包正确变化
|
||||||
|
- [ ] 玩家可查看注单和账变
|
||||||
|
- [ ] 后台可生成并发放返水
|
||||||
|
- [ ] Banner/公告/走马灯可配置
|
||||||
|
- [ ] 所有关键操作有日志
|
||||||
|
|
||||||
|
## 回归测试流程
|
||||||
|
|
||||||
|
1. 运行单元测试:`pnpm --filter @thebet365/api test`
|
||||||
|
2. 代理上分 → 玩家下注 → 封盘 → 录入比分 → 预览 → 确认结算
|
||||||
|
3. 验证钱包余额与代理额度变化
|
||||||
|
4. 测试串关限制(同场、四分之一球、冠军竞猜)
|
||||||
|
5. 测试返水批次生成与确认
|
||||||
|
|
||||||
|
## 备份与回滚
|
||||||
|
|
||||||
|
- PostgreSQL:每日 `pg_dump` 备份
|
||||||
|
- 回滚:恢复备份 + 回退部署版本
|
||||||
|
- 结算错误:使用重结算冲正流程,禁止直接改余额
|
||||||
|
|
||||||
|
## 后台操作培训要点
|
||||||
|
|
||||||
|
1. 赛事创建:联赛 → 球队 → 比赛 → 生成盘口模板 → 录入赔率 → 发布
|
||||||
|
2. 结算:录入半场/全场比分 → 生成预览 → 核对派彩 → 确认
|
||||||
|
3. 代理管理:授信额度 → 监控占用 → 负数时禁止继续放款
|
||||||
|
4. 返水:选择周期 → 预览 → 财务确认 → 发放
|
||||||
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "thebet365",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "足球投注平台 MVP - Monorepo",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "pnpm --parallel -r run dev",
|
||||||
|
"dev:api": "pnpm --filter @thebet365/api dev",
|
||||||
|
"dev:player": "pnpm --filter @thebet365/player dev",
|
||||||
|
"dev:admin": "pnpm --filter @thebet365/admin dev",
|
||||||
|
"dev:agent": "pnpm --filter @thebet365/agent dev",
|
||||||
|
"build": "pnpm -r run build",
|
||||||
|
"test": "pnpm -r run test",
|
||||||
|
"db:generate": "pnpm --filter @thebet365/api db:generate",
|
||||||
|
"db:migrate": "pnpm --filter @thebet365/api db:migrate",
|
||||||
|
"db:seed": "pnpm --filter @thebet365/api db:seed",
|
||||||
|
"db:studio": "pnpm --filter @thebet365/api db:studio"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
packages/shared/package.json
Normal file
14
packages/shared/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "@thebet365/shared",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
123
packages/shared/src/index.ts
Normal file
123
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// User & Auth
|
||||||
|
export enum UserType {
|
||||||
|
PLAYER = 'PLAYER',
|
||||||
|
AGENT = 'AGENT',
|
||||||
|
ADMIN = 'ADMIN',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum UserStatus {
|
||||||
|
ACTIVE = 'ACTIVE',
|
||||||
|
DISABLED = 'DISABLED',
|
||||||
|
LOCKED = 'LOCKED',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AgentLevel {
|
||||||
|
LEVEL_1 = 1,
|
||||||
|
LEVEL_2 = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match
|
||||||
|
export enum MatchStatus {
|
||||||
|
DRAFT = 'DRAFT',
|
||||||
|
PUBLISHED = 'PUBLISHED',
|
||||||
|
CLOSED = 'CLOSED',
|
||||||
|
PENDING_SETTLEMENT = 'PENDING_SETTLEMENT',
|
||||||
|
SETTLED = 'SETTLED',
|
||||||
|
CANCELLED = 'CANCELLED',
|
||||||
|
VOID = 'VOID',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MarketStatus {
|
||||||
|
OPEN = 'OPEN',
|
||||||
|
SUSPENDED = 'SUSPENDED',
|
||||||
|
CLOSED = 'CLOSED',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MarketType {
|
||||||
|
FT_CORRECT_SCORE = 'FT_CORRECT_SCORE',
|
||||||
|
HT_CORRECT_SCORE = 'HT_CORRECT_SCORE',
|
||||||
|
SH_CORRECT_SCORE = 'SH_CORRECT_SCORE',
|
||||||
|
FT_HANDICAP = 'FT_HANDICAP',
|
||||||
|
FT_OVER_UNDER = 'FT_OVER_UNDER',
|
||||||
|
FT_1X2 = 'FT_1X2',
|
||||||
|
FT_ODD_EVEN = 'FT_ODD_EVEN',
|
||||||
|
HT_HANDICAP = 'HT_HANDICAP',
|
||||||
|
HT_OVER_UNDER = 'HT_OVER_UNDER',
|
||||||
|
HT_1X2 = 'HT_1X2',
|
||||||
|
OUTRIGHT_WINNER = 'OUTRIGHT_WINNER',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Period {
|
||||||
|
FT = 'FT',
|
||||||
|
HT = 'HT',
|
||||||
|
SH = 'SH',
|
||||||
|
OUTRIGHT = 'OUTRIGHT',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bet
|
||||||
|
export enum BetType {
|
||||||
|
SINGLE = 'SINGLE',
|
||||||
|
PARLAY = 'PARLAY',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BetStatus {
|
||||||
|
PENDING = 'PENDING',
|
||||||
|
WON = 'WON',
|
||||||
|
LOST = 'LOST',
|
||||||
|
PUSH = 'PUSH',
|
||||||
|
VOID = 'VOID',
|
||||||
|
CANCELLED = 'CANCELLED',
|
||||||
|
SETTLED = 'SETTLED',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SelectionResult {
|
||||||
|
WIN = 'WIN',
|
||||||
|
HALF_WIN = 'HALF_WIN',
|
||||||
|
PUSH = 'PUSH',
|
||||||
|
HALF_LOSE = 'HALF_LOSE',
|
||||||
|
LOSE = 'LOSE',
|
||||||
|
VOID = 'VOID',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallet
|
||||||
|
export enum WalletTransactionType {
|
||||||
|
MANUAL_DEPOSIT = 'MANUAL_DEPOSIT',
|
||||||
|
MANUAL_WITHDRAW = 'MANUAL_WITHDRAW',
|
||||||
|
BET_FREEZE = 'BET_FREEZE',
|
||||||
|
BET_SETTLE_WIN = 'BET_SETTLE_WIN',
|
||||||
|
BET_SETTLE_LOSE = 'BET_SETTLE_LOSE',
|
||||||
|
BET_SETTLE_PUSH = 'BET_SETTLE_PUSH',
|
||||||
|
BET_VOID_REFUND = 'BET_VOID_REFUND',
|
||||||
|
CASHBACK = 'CASHBACK',
|
||||||
|
RESETTLE_REVERSE = 'RESETTLE_REVERSE',
|
||||||
|
MANUAL_ADJUST = 'MANUAL_ADJUST',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WalletStatus {
|
||||||
|
ACTIVE = 'ACTIVE',
|
||||||
|
FROZEN = 'FROZEN',
|
||||||
|
DISABLED = 'DISABLED',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locale
|
||||||
|
export const SUPPORTED_LOCALES = ['zh-CN', 'ms-MY', 'en-US'] as const;
|
||||||
|
export type Locale = (typeof SUPPORTED_LOCALES)[number];
|
||||||
|
export const DEFAULT_LOCALE: Locale = 'en-US';
|
||||||
|
|
||||||
|
// Admin roles
|
||||||
|
export enum AdminRole {
|
||||||
|
SUPER_ADMIN = 'SUPER_ADMIN',
|
||||||
|
MATCH_ADMIN = 'MATCH_ADMIN',
|
||||||
|
FINANCE_ADMIN = 'FINANCE_ADMIN',
|
||||||
|
SUPPORT = 'SUPPORT',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PARLAY_MIN_LEGS = 2;
|
||||||
|
export const PARLAY_MAX_LEGS = 5;
|
||||||
|
|
||||||
|
export interface ApiResponse<T = unknown> {
|
||||||
|
success: boolean;
|
||||||
|
data?: T;
|
||||||
|
message?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
13
packages/shared/tsconfig.json
Normal file
13
packages/shared/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
7559
pnpm-lock.yaml
generated
Normal file
7559
pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,7559 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.: {}
|
||||||
|
|
||||||
|
apps/admin:
|
||||||
|
dependencies:
|
||||||
|
axios:
|
||||||
|
specifier: ^1.7.9
|
||||||
|
version: 1.16.1
|
||||||
|
element-plus:
|
||||||
|
specifier: ^2.9.3
|
||||||
|
version: 2.14.1(vue@3.5.35(typescript@5.7.3))
|
||||||
|
vue:
|
||||||
|
specifier: ^3.5.13
|
||||||
|
version: 3.5.35(typescript@5.7.3)
|
||||||
|
vue-router:
|
||||||
|
specifier: ^4.5.0
|
||||||
|
version: 4.6.4(vue@3.5.35(typescript@5.7.3))
|
||||||
|
devDependencies:
|
||||||
|
'@vitejs/plugin-vue':
|
||||||
|
specifier: ^5.2.1
|
||||||
|
version: 5.2.1(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0))(vue@3.5.35(typescript@5.7.3))
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.3
|
||||||
|
version: 5.7.3
|
||||||
|
vite:
|
||||||
|
specifier: ^6.0.11
|
||||||
|
version: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)
|
||||||
|
vue-tsc:
|
||||||
|
specifier: ^2.2.0
|
||||||
|
version: 2.2.0(typescript@5.7.3)
|
||||||
|
|
||||||
|
apps/agent:
|
||||||
|
dependencies:
|
||||||
|
axios:
|
||||||
|
specifier: ^1.7.9
|
||||||
|
version: 1.16.1
|
||||||
|
element-plus:
|
||||||
|
specifier: ^2.9.3
|
||||||
|
version: 2.14.1(vue@3.5.35(typescript@5.7.3))
|
||||||
|
vue:
|
||||||
|
specifier: ^3.5.13
|
||||||
|
version: 3.5.35(typescript@5.7.3)
|
||||||
|
vue-router:
|
||||||
|
specifier: ^4.5.0
|
||||||
|
version: 4.6.4(vue@3.5.35(typescript@5.7.3))
|
||||||
|
devDependencies:
|
||||||
|
'@vitejs/plugin-vue':
|
||||||
|
specifier: ^5.2.1
|
||||||
|
version: 5.2.1(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0))(vue@3.5.35(typescript@5.7.3))
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.3
|
||||||
|
version: 5.7.3
|
||||||
|
vite:
|
||||||
|
specifier: ^6.0.11
|
||||||
|
version: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)
|
||||||
|
vue-tsc:
|
||||||
|
specifier: ^2.2.0
|
||||||
|
version: 2.2.0(typescript@5.7.3)
|
||||||
|
|
||||||
|
apps/api:
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common':
|
||||||
|
specifier: ^11.0.6
|
||||||
|
version: 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/config':
|
||||||
|
specifier: ^4.0.0
|
||||||
|
version: 4.0.4(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)
|
||||||
|
'@nestjs/core':
|
||||||
|
specifier: ^11.0.6
|
||||||
|
version: 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/jwt':
|
||||||
|
specifier: ^11.0.0
|
||||||
|
version: 11.0.2(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))
|
||||||
|
'@nestjs/passport':
|
||||||
|
specifier: ^11.0.5
|
||||||
|
version: 11.0.5(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)
|
||||||
|
'@nestjs/platform-express':
|
||||||
|
specifier: ^11.0.6
|
||||||
|
version: 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)
|
||||||
|
'@nestjs/schedule':
|
||||||
|
specifier: ^5.0.1
|
||||||
|
version: 5.0.1(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)
|
||||||
|
'@nestjs/swagger':
|
||||||
|
specifier: ^11.0.3
|
||||||
|
version: 11.4.4(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)
|
||||||
|
'@prisma/client':
|
||||||
|
specifier: ^6.3.1
|
||||||
|
version: 6.19.3(prisma@6.19.3(typescript@5.7.3))(typescript@5.7.3)
|
||||||
|
'@thebet365/shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/shared
|
||||||
|
bcryptjs:
|
||||||
|
specifier: ^2.4.3
|
||||||
|
version: 2.4.3
|
||||||
|
class-transformer:
|
||||||
|
specifier: ^0.5.1
|
||||||
|
version: 0.5.1
|
||||||
|
class-validator:
|
||||||
|
specifier: ^0.14.1
|
||||||
|
version: 0.14.4
|
||||||
|
decimal.js:
|
||||||
|
specifier: ^10.4.3
|
||||||
|
version: 10.6.0
|
||||||
|
ioredis:
|
||||||
|
specifier: ^5.4.2
|
||||||
|
version: 5.11.0
|
||||||
|
passport:
|
||||||
|
specifier: ^0.7.0
|
||||||
|
version: 0.7.0
|
||||||
|
passport-jwt:
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1
|
||||||
|
reflect-metadata:
|
||||||
|
specifier: ^0.2.2
|
||||||
|
version: 0.2.2
|
||||||
|
rxjs:
|
||||||
|
specifier: ^7.8.1
|
||||||
|
version: 7.8.2
|
||||||
|
uuid:
|
||||||
|
specifier: ^11.0.5
|
||||||
|
version: 11.1.1
|
||||||
|
devDependencies:
|
||||||
|
'@nestjs/cli':
|
||||||
|
specifier: ^11.0.2
|
||||||
|
version: 11.0.21(@types/node@22.19.19)
|
||||||
|
'@nestjs/schematics':
|
||||||
|
specifier: ^11.0.0
|
||||||
|
version: 11.1.0(chokidar@4.0.3)(typescript@5.7.3)
|
||||||
|
'@nestjs/testing':
|
||||||
|
specifier: ^11.0.6
|
||||||
|
version: 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(@nestjs/platform-express@11.1.24)
|
||||||
|
'@types/bcryptjs':
|
||||||
|
specifier: ^2.4.6
|
||||||
|
version: 2.4.6
|
||||||
|
'@types/express':
|
||||||
|
specifier: ^5.0.0
|
||||||
|
version: 5.0.6
|
||||||
|
'@types/jest':
|
||||||
|
specifier: ^29.5.14
|
||||||
|
version: 29.5.14
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^22.10.7
|
||||||
|
version: 22.19.19
|
||||||
|
'@types/passport-jwt':
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1
|
||||||
|
'@types/uuid':
|
||||||
|
specifier: ^10.0.0
|
||||||
|
version: 10.0.0
|
||||||
|
jest:
|
||||||
|
specifier: ^29.7.0
|
||||||
|
version: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
prisma:
|
||||||
|
specifier: ^6.3.1
|
||||||
|
version: 6.19.3(typescript@5.7.3)
|
||||||
|
ts-jest:
|
||||||
|
specifier: ^29.2.5
|
||||||
|
version: 29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3)))(typescript@5.7.3)
|
||||||
|
ts-node:
|
||||||
|
specifier: ^10.9.2
|
||||||
|
version: 10.9.2(@types/node@22.19.19)(typescript@5.7.3)
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.3
|
||||||
|
version: 5.7.3
|
||||||
|
|
||||||
|
apps/player:
|
||||||
|
dependencies:
|
||||||
|
'@thebet365/shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/shared
|
||||||
|
axios:
|
||||||
|
specifier: ^1.7.9
|
||||||
|
version: 1.16.1
|
||||||
|
pinia:
|
||||||
|
specifier: ^2.3.1
|
||||||
|
version: 2.3.1(typescript@5.7.3)(vue@3.5.35(typescript@5.7.3))
|
||||||
|
vue:
|
||||||
|
specifier: ^3.5.13
|
||||||
|
version: 3.5.35(typescript@5.7.3)
|
||||||
|
vue-i18n:
|
||||||
|
specifier: ^11.1.1
|
||||||
|
version: 11.4.4(vue@3.5.35(typescript@5.7.3))
|
||||||
|
vue-router:
|
||||||
|
specifier: ^4.5.0
|
||||||
|
version: 4.6.4(vue@3.5.35(typescript@5.7.3))
|
||||||
|
devDependencies:
|
||||||
|
'@vitejs/plugin-vue':
|
||||||
|
specifier: ^5.2.1
|
||||||
|
version: 5.2.1(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0))(vue@3.5.35(typescript@5.7.3))
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.3
|
||||||
|
version: 5.7.3
|
||||||
|
vite:
|
||||||
|
specifier: ^6.0.11
|
||||||
|
version: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)
|
||||||
|
vue-tsc:
|
||||||
|
specifier: ^2.2.0
|
||||||
|
version: 2.2.0(typescript@5.7.3)
|
||||||
|
|
||||||
|
packages/shared:
|
||||||
|
devDependencies:
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.7.3
|
||||||
|
version: 5.7.3
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@angular-devkit/core@19.2.24':
|
||||||
|
resolution: {integrity: sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==}
|
||||||
|
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||||
|
peerDependencies:
|
||||||
|
chokidar: ^4.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
chokidar:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@angular-devkit/schematics-cli@19.2.24':
|
||||||
|
resolution: {integrity: sha512-bsStZQG67J1HBqTmWxtIcobvgrn32L4UOdL7hGyOru5VxDWPNA8pRnDYavT3hnJeBkJYPoQIw8u7Dm0ecoQprw==}
|
||||||
|
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
'@angular-devkit/schematics@19.2.24':
|
||||||
|
resolution: {integrity: sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==}
|
||||||
|
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
|
||||||
|
|
||||||
|
'@babel/code-frame@7.29.7':
|
||||||
|
resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/compat-data@7.29.7':
|
||||||
|
resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/core@7.29.7':
|
||||||
|
resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/generator@7.29.7':
|
||||||
|
resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-compilation-targets@7.29.7':
|
||||||
|
resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-globals@7.29.7':
|
||||||
|
resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-module-imports@7.29.7':
|
||||||
|
resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-module-transforms@7.29.7':
|
||||||
|
resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0
|
||||||
|
|
||||||
|
'@babel/helper-plugin-utils@7.29.7':
|
||||||
|
resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-string-parser@7.29.7':
|
||||||
|
resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-validator-identifier@7.29.7':
|
||||||
|
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helper-validator-option@7.29.7':
|
||||||
|
resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/helpers@7.29.7':
|
||||||
|
resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/parser@7.29.7':
|
||||||
|
resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-async-generators@7.8.4':
|
||||||
|
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-bigint@7.8.3':
|
||||||
|
resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-class-properties@7.12.13':
|
||||||
|
resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-class-static-block@7.14.5':
|
||||||
|
resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-import-attributes@7.29.7':
|
||||||
|
resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-import-meta@7.10.4':
|
||||||
|
resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-json-strings@7.8.3':
|
||||||
|
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-jsx@7.29.7':
|
||||||
|
resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-logical-assignment-operators@7.10.4':
|
||||||
|
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
|
||||||
|
resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-numeric-separator@7.10.4':
|
||||||
|
resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-object-rest-spread@7.8.3':
|
||||||
|
resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-optional-catch-binding@7.8.3':
|
||||||
|
resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-optional-chaining@7.8.3':
|
||||||
|
resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-private-property-in-object@7.14.5':
|
||||||
|
resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-top-level-await@7.14.5':
|
||||||
|
resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-typescript@7.29.7':
|
||||||
|
resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
|
'@babel/template@7.29.7':
|
||||||
|
resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/traverse@7.29.7':
|
||||||
|
resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/types@7.29.7':
|
||||||
|
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@bcoe/v8-coverage@0.2.3':
|
||||||
|
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||||
|
|
||||||
|
'@borewit/text-codec@0.2.2':
|
||||||
|
resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==}
|
||||||
|
|
||||||
|
'@colors/colors@1.5.0':
|
||||||
|
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
||||||
|
engines: {node: '>=0.1.90'}
|
||||||
|
|
||||||
|
'@cspotcode/source-map-support@0.8.1':
|
||||||
|
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
'@ctrl/tinycolor@4.2.0':
|
||||||
|
resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@element-plus/icons-vue@2.3.2':
|
||||||
|
resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.2.0
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [aix]
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.25.0':
|
||||||
|
resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.25.0':
|
||||||
|
resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.25.0':
|
||||||
|
resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.25.0':
|
||||||
|
resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.25.0':
|
||||||
|
resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.25.0':
|
||||||
|
resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.25.0':
|
||||||
|
resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@floating-ui/core@1.7.5':
|
||||||
|
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.7.6':
|
||||||
|
resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.11':
|
||||||
|
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
|
||||||
|
|
||||||
|
'@inquirer/ansi@1.0.2':
|
||||||
|
resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/checkbox@4.1.2':
|
||||||
|
resolution: {integrity: sha512-PL9ixC5YsPXzXhAZFUPmkXGxfgjkdfZdPEPPmt4kFwQ4LBMDG9n/nHXYRGGZSKZJs+d1sGKWgS2GiPzVRKUdtQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/checkbox@4.3.2':
|
||||||
|
resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/confirm@5.1.21':
|
||||||
|
resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/confirm@5.1.6':
|
||||||
|
resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/core@10.1.7':
|
||||||
|
resolution: {integrity: sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/core@10.3.2':
|
||||||
|
resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/editor@4.2.23':
|
||||||
|
resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/editor@4.2.7':
|
||||||
|
resolution: {integrity: sha512-gktCSQtnSZHaBytkJKMKEuswSk2cDBuXX5rxGFv306mwHfBPjg5UAldw9zWGoEyvA9KpRDkeM4jfrx0rXn0GyA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/expand@4.0.23':
|
||||||
|
resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/expand@4.0.9':
|
||||||
|
resolution: {integrity: sha512-Xxt6nhomWTAmuSX61kVgglLjMEFGa+7+F6UUtdEUeg7fg4r9vaFttUUKrtkViYYrQBA5Ia1tkOJj2koP9BuLig==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/external-editor@1.0.3':
|
||||||
|
resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/figures@1.0.10':
|
||||||
|
resolution: {integrity: sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/figures@1.0.15':
|
||||||
|
resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/input@4.1.6':
|
||||||
|
resolution: {integrity: sha512-1f5AIsZuVjPT4ecA8AwaxDFNHny/tSershP/cTvTDxLdiIGTeILNcKozB0LaYt6mojJLUbOYhpIxicaYf7UKIQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/input@4.3.1':
|
||||||
|
resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/number@3.0.23':
|
||||||
|
resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/number@3.0.9':
|
||||||
|
resolution: {integrity: sha512-iN2xZvH3tyIYXLXBvlVh0npk1q/aVuKXZo5hj+K3W3D4ngAEq/DkLpofRzx6oebTUhBvOgryZ+rMV0yImKnG3w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/password@4.0.23':
|
||||||
|
resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/password@4.0.9':
|
||||||
|
resolution: {integrity: sha512-xBEoOw1XKb0rIN208YU7wM7oJEHhIYkfG7LpTJAEW913GZeaoQerzf5U/LSHI45EVvjAdgNXmXgH51cUXKZcJQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/prompts@7.10.1':
|
||||||
|
resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/prompts@7.3.2':
|
||||||
|
resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/rawlist@4.0.9':
|
||||||
|
resolution: {integrity: sha512-+5t6ebehKqgoxV8fXwE49HkSF2Rc9ijNiVGEQZwvbMI61/Q5RcD+jWD6Gs1tKdz5lkI8GRBL31iO0HjGK1bv+A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/rawlist@4.1.11':
|
||||||
|
resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/search@3.0.9':
|
||||||
|
resolution: {integrity: sha512-DWmKztkYo9CvldGBaRMr0ETUHgR86zE6sPDVOHsqz4ISe9o1LuiWfgJk+2r75acFclA93J/lqzhT0dTjCzHuoA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/search@3.2.2':
|
||||||
|
resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/select@4.0.9':
|
||||||
|
resolution: {integrity: sha512-BpJyJe7Dkhv2kz7yG7bPSbJLQuu/rqyNlF1CfiiFeFwouegfH+zh13KDyt6+d9DwucKo7hqM3wKLLyJxZMO+Xg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/select@4.4.2':
|
||||||
|
resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/type@3.0.10':
|
||||||
|
resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/type@3.0.4':
|
||||||
|
resolution: {integrity: sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '>=18'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@intlify/core-base@11.4.4':
|
||||||
|
resolution: {integrity: sha512-w/vItlylrAmhebkIbVl5YY8XMCtj8Mb2g70ttxktMYuf5AuRahgEHL2iLgLIsZBIbTSgs4hkUo7ucCL0uTJvOg==}
|
||||||
|
engines: {node: '>= 22'}
|
||||||
|
|
||||||
|
'@intlify/devtools-types@11.4.4':
|
||||||
|
resolution: {integrity: sha512-PcBLmGmDQsTSVV911P8upzpcLJO1CNVYi/IH6bGnLR2nA+0L963+kXN1ZrisTEnbtw2ewN6HMMSldqzjronA0Q==}
|
||||||
|
engines: {node: '>= 22'}
|
||||||
|
|
||||||
|
'@intlify/message-compiler@11.4.4':
|
||||||
|
resolution: {integrity: sha512-vn0OAV9pYkJlPPmgnsSm5eAG3mL0+9C/oaded2JY9jmxBbhmUXT3TcAUY8WRgLY9Hte7lkUJKpXrVlYjMXBD2w==}
|
||||||
|
engines: {node: '>= 22'}
|
||||||
|
|
||||||
|
'@intlify/shared@11.4.4':
|
||||||
|
resolution: {integrity: sha512-QRUCHqda1U6aR14FR0vvXD4+4gj6+fm0AhAozvSuRCw0fCvrmCugWpgiR4xH2NI6s8am6N9p5OhirplsX8ZS3g==}
|
||||||
|
engines: {node: '>= 22'}
|
||||||
|
|
||||||
|
'@ioredis/commands@1.10.0':
|
||||||
|
resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==}
|
||||||
|
|
||||||
|
'@istanbuljs/load-nyc-config@1.1.0':
|
||||||
|
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
'@istanbuljs/schema@0.1.6':
|
||||||
|
resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
'@jest/console@29.7.0':
|
||||||
|
resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/core@29.7.0':
|
||||||
|
resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
node-notifier:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@jest/environment@29.7.0':
|
||||||
|
resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/expect-utils@29.0.0':
|
||||||
|
resolution: {integrity: sha512-odQ+cjUpui6++a9Ua/oWn7CG0Af+EZe9weWZbfUQHTg7C3K9PCb0AnD4X7nyAe4WjfeZmVVyG5SJELMQaUbCtg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/expect-utils@29.7.0':
|
||||||
|
resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/expect@29.7.0':
|
||||||
|
resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/fake-timers@29.7.0':
|
||||||
|
resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/globals@29.7.0':
|
||||||
|
resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/reporters@29.7.0':
|
||||||
|
resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
node-notifier:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@jest/schemas@29.0.0':
|
||||||
|
resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/schemas@29.6.3':
|
||||||
|
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/source-map@29.6.3':
|
||||||
|
resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/test-result@29.7.0':
|
||||||
|
resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/test-sequencer@29.7.0':
|
||||||
|
resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/transform@29.7.0':
|
||||||
|
resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/types@29.0.0':
|
||||||
|
resolution: {integrity: sha512-ErShruvByUF7vphEtPugMAphCtDIDdfWh3DxpBLxPEtHhL/H5MaidHsOutnOUhKpPL7QA6/7GitjFgLOLeGa1A==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jest/types@29.6.3':
|
||||||
|
resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
'@jridgewell/gen-mapping@0.3.12':
|
||||||
|
resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
|
||||||
|
|
||||||
|
'@jridgewell/gen-mapping@0.3.5':
|
||||||
|
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
|
'@jridgewell/remapping@2.3.5':
|
||||||
|
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
|
||||||
|
|
||||||
|
'@jridgewell/resolve-uri@3.1.2':
|
||||||
|
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
|
'@jridgewell/set-array@1.2.1':
|
||||||
|
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
|
'@jridgewell/source-map@0.3.11':
|
||||||
|
resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.4.10':
|
||||||
|
resolution: {integrity: sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg==}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.4.14':
|
||||||
|
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.0':
|
||||||
|
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.5':
|
||||||
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||||
|
|
||||||
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
|
'@jridgewell/trace-mapping@0.3.9':
|
||||||
|
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||||
|
|
||||||
|
'@lukeed/csprng@1.1.0':
|
||||||
|
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
'@microsoft/tsdoc@0.16.0':
|
||||||
|
resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==}
|
||||||
|
|
||||||
|
'@nestjs/cli@11.0.21':
|
||||||
|
resolution: {integrity: sha512-F8mV0Sj/zVEouzR3NxBuJy08YHTUOmC5Xdcx3qIIaJWzrm8Vw86CHkhkaPBJ5ewRMHPDCShPmhsfwhpCcjts3A==}
|
||||||
|
engines: {node: '>= 20.11'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0
|
||||||
|
'@swc/core': ^1.3.62
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@swc/cli':
|
||||||
|
optional: true
|
||||||
|
'@swc/core':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/common@11.1.24':
|
||||||
|
resolution: {integrity: sha512-9zHxaDDM+oXW9As6UsP5yYB+UqczBmpeSCIFWdPEtEukMnZhxODG1BBjaUcdBB8Sc1uzojSJSJlp3yFp853t1g==}
|
||||||
|
peerDependencies:
|
||||||
|
class-transformer: '>=0.4.1'
|
||||||
|
class-validator: '>=0.13.2'
|
||||||
|
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||||
|
rxjs: ^7.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
class-transformer:
|
||||||
|
optional: true
|
||||||
|
class-validator:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/config@4.0.4':
|
||||||
|
resolution: {integrity: sha512-CJPjNitr0bAufSEnRe2N+JbnVmMmDoo6hvKCPzXgZoGwJSmp/dZPk9f/RMbuD/+Q1ZJPjwsRpq0vxna++Knwow==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||||
|
rxjs: ^7.1.0
|
||||||
|
|
||||||
|
'@nestjs/core@11.1.24':
|
||||||
|
resolution: {integrity: sha512-K4bzT+lEdd0Hhcsw3jtk56QAW6s6skK3ViN7hIROSN0kUf4ROwWEAKopJID6yhPQxB45kDtP2wEcjzE8171J3g==}
|
||||||
|
engines: {node: '>= 20'}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^11.0.0
|
||||||
|
'@nestjs/microservices': ^11.0.0
|
||||||
|
'@nestjs/platform-express': ^11.0.0
|
||||||
|
'@nestjs/websockets': ^11.0.0
|
||||||
|
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||||
|
rxjs: ^7.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@nestjs/microservices':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/platform-express':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/websockets':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/jwt@11.0.2':
|
||||||
|
resolution: {integrity: sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
|
||||||
|
|
||||||
|
'@nestjs/mapped-types@2.1.1':
|
||||||
|
resolution: {integrity: sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||||
|
class-transformer: ^0.4.0 || ^0.5.0
|
||||||
|
class-validator: ^0.13.0 || ^0.14.0 || ^0.15.0
|
||||||
|
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
class-transformer:
|
||||||
|
optional: true
|
||||||
|
class-validator:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/passport@11.0.5':
|
||||||
|
resolution: {integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||||
|
passport: ^0.5.0 || ^0.6.0 || ^0.7.0
|
||||||
|
|
||||||
|
'@nestjs/platform-express@11.1.24':
|
||||||
|
resolution: {integrity: sha512-CeMKbRBm05aOBiWhIHWO2xDeHbxynBF9ySQv3gRjObz2N5+uJnYriAYkHvVqvC4JIydmMPmT5VdICFNlNz3qyA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^11.0.0
|
||||||
|
'@nestjs/core': ^11.0.0
|
||||||
|
|
||||||
|
'@nestjs/schedule@5.0.1':
|
||||||
|
resolution: {integrity: sha512-kFoel84I4RyS2LNPH6yHYTKxB16tb3auAEciFuc788C3ph6nABkUfzX5IE+unjVaRX+3GuruJwurNepMlHXpQg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||||
|
'@nestjs/core': ^10.0.0 || ^11.0.0
|
||||||
|
|
||||||
|
'@nestjs/schematics@11.1.0':
|
||||||
|
resolution: {integrity: sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==}
|
||||||
|
peerDependencies:
|
||||||
|
prettier: ^3.0.0
|
||||||
|
typescript: '>=4.8.2'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
prettier:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/swagger@11.4.4':
|
||||||
|
resolution: {integrity: sha512-VaIo1ruV2G7b+f2zPzkBSUNy9a/WQ9sg8TLKhWlrTfg4O6U10M/PA7Xi6XMXadOVhwOqoesijba8jH3i/3adrA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@fastify/static': ^8.0.0 || ^9.0.0
|
||||||
|
'@nestjs/common': ^11.0.1
|
||||||
|
'@nestjs/core': ^11.0.1
|
||||||
|
class-transformer: '*'
|
||||||
|
class-validator: '*'
|
||||||
|
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@fastify/static':
|
||||||
|
optional: true
|
||||||
|
class-transformer:
|
||||||
|
optional: true
|
||||||
|
class-validator:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/testing@11.1.24':
|
||||||
|
resolution: {integrity: sha512-+4M4UAnhtprBQN0J2uI6IP0wDqhy9aH8XCMu5SO8oCi0oB04YXA4a4PAEkxmsPn7gHW4dj1u4GFteNQOWgvTJw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@nestjs/common': ^11.0.0
|
||||||
|
'@nestjs/core': ^11.0.0
|
||||||
|
'@nestjs/microservices': ^11.0.0
|
||||||
|
'@nestjs/platform-express': ^11.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@nestjs/microservices':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/platform-express':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@nuxt/opencollective@0.4.1':
|
||||||
|
resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==}
|
||||||
|
engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
'@prisma/client@6.19.3':
|
||||||
|
resolution: {integrity: sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==}
|
||||||
|
engines: {node: '>=18.18'}
|
||||||
|
peerDependencies:
|
||||||
|
prisma: '*'
|
||||||
|
typescript: '>=5.1.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
prisma:
|
||||||
|
optional: true
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@prisma/config@6.19.3':
|
||||||
|
resolution: {integrity: sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==}
|
||||||
|
|
||||||
|
'@prisma/debug@6.19.3':
|
||||||
|
resolution: {integrity: sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==}
|
||||||
|
|
||||||
|
'@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7':
|
||||||
|
resolution: {integrity: sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==}
|
||||||
|
|
||||||
|
'@prisma/engines@6.19.3':
|
||||||
|
resolution: {integrity: sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==}
|
||||||
|
|
||||||
|
'@prisma/fetch-engine@6.19.3':
|
||||||
|
resolution: {integrity: sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==}
|
||||||
|
|
||||||
|
'@prisma/get-platform@6.19.3':
|
||||||
|
resolution: {integrity: sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==}
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm-eabi@4.61.0':
|
||||||
|
resolution: {integrity: sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-arm64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-x64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-arm64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-x64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf@4.61.0':
|
||||||
|
resolution: {integrity: sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf@4.61.0':
|
||||||
|
resolution: {integrity: sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-musl@4.61.0':
|
||||||
|
resolution: {integrity: sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-musl@4.61.0':
|
||||||
|
resolution: {integrity: sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-musl@4.61.0':
|
||||||
|
resolution: {integrity: sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-musl@4.61.0':
|
||||||
|
resolution: {integrity: sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-s390x-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-musl@4.61.0':
|
||||||
|
resolution: {integrity: sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rollup/rollup-openbsd-x64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-openharmony-arm64@4.61.0':
|
||||||
|
resolution: {integrity: sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-arm64-msvc@4.61.0':
|
||||||
|
resolution: {integrity: sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-ia32-msvc@4.61.0':
|
||||||
|
resolution: {integrity: sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-gnu@4.61.0':
|
||||||
|
resolution: {integrity: sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-msvc@4.61.0':
|
||||||
|
resolution: {integrity: sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@scarf/scarf@1.4.0':
|
||||||
|
resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
|
||||||
|
|
||||||
|
'@sinclair/typebox@0.24.1':
|
||||||
|
resolution: {integrity: sha512-v59jW0KQIy4iMP0Pn7gcpQ48YDXa9vOopsRhaPWmsStTxaVNYIqFY3eK6y4PU+53hlQoaQ8pAxU4cPIaMFEJwg==}
|
||||||
|
|
||||||
|
'@sinclair/typebox@0.27.10':
|
||||||
|
resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==}
|
||||||
|
|
||||||
|
'@sinonjs/commons@2.0.0':
|
||||||
|
resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
|
||||||
|
|
||||||
|
'@sinonjs/fake-timers@10.0.2':
|
||||||
|
resolution: {integrity: sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==}
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.1.0':
|
||||||
|
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||||
|
|
||||||
|
'@sxzz/popperjs-es@2.11.8':
|
||||||
|
resolution: {integrity: sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==}
|
||||||
|
|
||||||
|
'@tokenizer/inflate@0.4.1':
|
||||||
|
resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@tokenizer/token@0.3.0':
|
||||||
|
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||||
|
|
||||||
|
'@tsconfig/node10@1.0.12':
|
||||||
|
resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==}
|
||||||
|
|
||||||
|
'@tsconfig/node12@1.0.11':
|
||||||
|
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
|
||||||
|
|
||||||
|
'@tsconfig/node14@1.0.3':
|
||||||
|
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
|
||||||
|
|
||||||
|
'@tsconfig/node16@1.0.4':
|
||||||
|
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
|
||||||
|
|
||||||
|
'@types/babel__core@7.20.5':
|
||||||
|
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||||
|
|
||||||
|
'@types/babel__generator@7.27.0':
|
||||||
|
resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
|
||||||
|
|
||||||
|
'@types/babel__template@7.4.4':
|
||||||
|
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
|
||||||
|
|
||||||
|
'@types/babel__traverse@7.28.0':
|
||||||
|
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
|
||||||
|
|
||||||
|
'@types/bcryptjs@2.4.6':
|
||||||
|
resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==}
|
||||||
|
|
||||||
|
'@types/body-parser@1.19.6':
|
||||||
|
resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
|
||||||
|
|
||||||
|
'@types/connect@3.4.38':
|
||||||
|
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
||||||
|
|
||||||
|
'@types/eslint-scope@3.7.7':
|
||||||
|
resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
|
||||||
|
|
||||||
|
'@types/eslint@9.6.1':
|
||||||
|
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
|
||||||
|
|
||||||
|
'@types/estree@1.0.9':
|
||||||
|
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||||
|
|
||||||
|
'@types/express-serve-static-core@5.1.1':
|
||||||
|
resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==}
|
||||||
|
|
||||||
|
'@types/express@5.0.6':
|
||||||
|
resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==}
|
||||||
|
|
||||||
|
'@types/graceful-fs@4.1.9':
|
||||||
|
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
|
||||||
|
|
||||||
|
'@types/http-errors@2.0.5':
|
||||||
|
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
|
||||||
|
|
||||||
|
'@types/istanbul-lib-coverage@2.0.6':
|
||||||
|
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
|
||||||
|
|
||||||
|
'@types/istanbul-lib-report@3.0.3':
|
||||||
|
resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
|
||||||
|
|
||||||
|
'@types/istanbul-reports@3.0.4':
|
||||||
|
resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
|
||||||
|
|
||||||
|
'@types/jest@29.5.14':
|
||||||
|
resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
|
||||||
|
|
||||||
|
'@types/json-schema@7.0.15':
|
||||||
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
|
|
||||||
|
'@types/jsonwebtoken@9.0.10':
|
||||||
|
resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
|
||||||
|
|
||||||
|
'@types/lodash-es@4.17.12':
|
||||||
|
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
|
||||||
|
|
||||||
|
'@types/lodash@4.17.24':
|
||||||
|
resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==}
|
||||||
|
|
||||||
|
'@types/luxon@3.4.2':
|
||||||
|
resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==}
|
||||||
|
|
||||||
|
'@types/ms@2.1.0':
|
||||||
|
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||||
|
|
||||||
|
'@types/node@22.19.19':
|
||||||
|
resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==}
|
||||||
|
|
||||||
|
'@types/passport-jwt@4.0.1':
|
||||||
|
resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==}
|
||||||
|
|
||||||
|
'@types/passport-strategy@0.2.38':
|
||||||
|
resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==}
|
||||||
|
|
||||||
|
'@types/passport@1.0.17':
|
||||||
|
resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==}
|
||||||
|
|
||||||
|
'@types/qs@6.15.1':
|
||||||
|
resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==}
|
||||||
|
|
||||||
|
'@types/range-parser@1.2.7':
|
||||||
|
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
||||||
|
|
||||||
|
'@types/send@1.2.1':
|
||||||
|
resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==}
|
||||||
|
|
||||||
|
'@types/serve-static@2.2.0':
|
||||||
|
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
|
||||||
|
|
||||||
|
'@types/stack-utils@2.0.3':
|
||||||
|
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
|
||||||
|
|
||||||
|
'@types/uuid@10.0.0':
|
||||||
|
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
|
||||||
|
|
||||||
|
'@types/validator@13.15.10':
|
||||||
|
resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==}
|
||||||
|
|
||||||
|
'@types/web-bluetooth@0.0.21':
|
||||||
|
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||||
|
|
||||||
|
'@types/yargs-parser@21.0.3':
|
||||||
|
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
|
||||||
|
|
||||||
|
'@types/yargs@17.0.35':
|
||||||
|
resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
|
||||||
|
|
||||||
|
'@vitejs/plugin-vue@5.2.1':
|
||||||
|
resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==}
|
||||||
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
vite: ^5.0.0 || ^6.0.0
|
||||||
|
vue: ^3.2.25
|
||||||
|
|
||||||
|
'@volar/language-core@2.4.28':
|
||||||
|
resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==}
|
||||||
|
|
||||||
|
'@volar/source-map@2.4.28':
|
||||||
|
resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==}
|
||||||
|
|
||||||
|
'@volar/typescript@2.4.28':
|
||||||
|
resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==}
|
||||||
|
|
||||||
|
'@vue/compiler-core@3.5.35':
|
||||||
|
resolution: {integrity: sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==}
|
||||||
|
|
||||||
|
'@vue/compiler-dom@3.5.35':
|
||||||
|
resolution: {integrity: sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==}
|
||||||
|
|
||||||
|
'@vue/compiler-sfc@3.5.35':
|
||||||
|
resolution: {integrity: sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==}
|
||||||
|
|
||||||
|
'@vue/compiler-ssr@3.5.35':
|
||||||
|
resolution: {integrity: sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==}
|
||||||
|
|
||||||
|
'@vue/compiler-vue2@2.7.16':
|
||||||
|
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
|
||||||
|
|
||||||
|
'@vue/devtools-api@6.5.0':
|
||||||
|
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
|
||||||
|
|
||||||
|
'@vue/devtools-api@6.6.3':
|
||||||
|
resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
|
||||||
|
|
||||||
|
'@vue/devtools-api@6.6.4':
|
||||||
|
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
|
||||||
|
|
||||||
|
'@vue/language-core@2.2.0':
|
||||||
|
resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@vue/reactivity@3.5.35':
|
||||||
|
resolution: {integrity: sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==}
|
||||||
|
|
||||||
|
'@vue/runtime-core@3.5.35':
|
||||||
|
resolution: {integrity: sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==}
|
||||||
|
|
||||||
|
'@vue/runtime-dom@3.5.35':
|
||||||
|
resolution: {integrity: sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==}
|
||||||
|
|
||||||
|
'@vue/server-renderer@3.5.35':
|
||||||
|
resolution: {integrity: sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: 3.5.35
|
||||||
|
|
||||||
|
'@vue/shared@3.5.35':
|
||||||
|
resolution: {integrity: sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==}
|
||||||
|
|
||||||
|
'@vueuse/core@14.3.0':
|
||||||
|
resolution: {integrity: sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.0
|
||||||
|
|
||||||
|
'@vueuse/metadata@14.3.0':
|
||||||
|
resolution: {integrity: sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==}
|
||||||
|
|
||||||
|
'@vueuse/shared@14.3.0':
|
||||||
|
resolution: {integrity: sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.0
|
||||||
|
|
||||||
|
'@webassemblyjs/ast@1.14.1':
|
||||||
|
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
|
||||||
|
|
||||||
|
'@webassemblyjs/floating-point-hex-parser@1.13.2':
|
||||||
|
resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-api-error@1.13.2':
|
||||||
|
resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-buffer@1.14.1':
|
||||||
|
resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-numbers@1.13.2':
|
||||||
|
resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode@1.13.2':
|
||||||
|
resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-wasm-section@1.14.1':
|
||||||
|
resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==}
|
||||||
|
|
||||||
|
'@webassemblyjs/ieee754@1.13.2':
|
||||||
|
resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==}
|
||||||
|
|
||||||
|
'@webassemblyjs/leb128@1.13.2':
|
||||||
|
resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==}
|
||||||
|
|
||||||
|
'@webassemblyjs/utf8@1.13.2':
|
||||||
|
resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==}
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-edit@1.14.1':
|
||||||
|
resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==}
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-gen@1.14.1':
|
||||||
|
resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==}
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-opt@1.14.1':
|
||||||
|
resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==}
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-parser@1.14.1':
|
||||||
|
resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==}
|
||||||
|
|
||||||
|
'@webassemblyjs/wast-printer@1.14.1':
|
||||||
|
resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==}
|
||||||
|
|
||||||
|
'@xtuc/ieee754@1.2.0':
|
||||||
|
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
|
||||||
|
|
||||||
|
'@xtuc/long@4.2.2':
|
||||||
|
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
|
||||||
|
|
||||||
|
accepts@2.0.0:
|
||||||
|
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
acorn-import-phases@1.0.4:
|
||||||
|
resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
peerDependencies:
|
||||||
|
acorn: ^8.14.0
|
||||||
|
|
||||||
|
acorn-walk@8.3.5:
|
||||||
|
resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
|
acorn@8.16.0:
|
||||||
|
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
agent-base@6.0.0:
|
||||||
|
resolution: {integrity: sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==}
|
||||||
|
engines: {node: '>= 6.0.0'}
|
||||||
|
|
||||||
|
ajv-formats@2.1.1:
|
||||||
|
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
ajv-formats@3.0.1:
|
||||||
|
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
ajv-keywords@3.5.2:
|
||||||
|
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^6.9.1
|
||||||
|
|
||||||
|
ajv-keywords@5.1.0:
|
||||||
|
resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.8.2
|
||||||
|
|
||||||
|
ajv@6.15.0:
|
||||||
|
resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
|
||||||
|
|
||||||
|
ajv@8.18.0:
|
||||||
|
resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
|
||||||
|
|
||||||
|
ajv@8.20.0:
|
||||||
|
resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
|
||||||
|
|
||||||
|
alien-signals@0.4.9:
|
||||||
|
resolution: {integrity: sha512-piRGlMgQ65uRiY06mGU7I432AwPwAGf64TK1RXtM1Px4pPfLMTGI9TmsHTfioW1GukZRsNzkVQ/uHjhhd231Ow==}
|
||||||
|
|
||||||
|
ansi-colors@4.1.3:
|
||||||
|
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
ansi-escapes@4.2.1:
|
||||||
|
resolution: {integrity: sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-escapes@4.3.2:
|
||||||
|
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-regex@4.1.1:
|
||||||
|
resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
ansi-regex@5.0.1:
|
||||||
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-styles@5.2.0:
|
||||||
|
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
ansis@4.2.0:
|
||||||
|
resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
append-field@1.0.0:
|
||||||
|
resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
|
||||||
|
|
||||||
|
arg@4.1.3:
|
||||||
|
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||||
|
|
||||||
|
argparse@1.0.10:
|
||||||
|
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||||
|
|
||||||
|
argparse@2.0.1:
|
||||||
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
|
|
||||||
|
array-timsort@1.0.3:
|
||||||
|
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
|
||||||
|
|
||||||
|
async-validator@4.2.5:
|
||||||
|
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
|
||||||
|
|
||||||
|
asynckit@0.4.0:
|
||||||
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
|
|
||||||
|
axios@1.16.1:
|
||||||
|
resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==}
|
||||||
|
|
||||||
|
babel-jest@29.7.0:
|
||||||
|
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.8.0
|
||||||
|
|
||||||
|
babel-plugin-istanbul@6.1.1:
|
||||||
|
resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
babel-plugin-jest-hoist@29.6.3:
|
||||||
|
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
babel-preset-current-node-syntax@1.2.0:
|
||||||
|
resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0 || ^8.0.0-0
|
||||||
|
|
||||||
|
babel-preset-jest@29.6.3:
|
||||||
|
resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': ^7.0.0
|
||||||
|
|
||||||
|
balanced-match@1.0.0:
|
||||||
|
resolution: {integrity: sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==}
|
||||||
|
|
||||||
|
balanced-match@4.0.4:
|
||||||
|
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
|
base64-js@1.5.1:
|
||||||
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
|
baseline-browser-mapping@2.10.33:
|
||||||
|
resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
bcryptjs@2.4.3:
|
||||||
|
resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||||
|
|
||||||
|
body-parser@2.2.2:
|
||||||
|
resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
brace-expansion@1.1.15:
|
||||||
|
resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
|
||||||
|
|
||||||
|
brace-expansion@2.1.1:
|
||||||
|
resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==}
|
||||||
|
|
||||||
|
brace-expansion@5.0.6:
|
||||||
|
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
browserslist@4.28.2:
|
||||||
|
resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
|
||||||
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
bs-logger@0.2.6:
|
||||||
|
resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
bser@2.1.1:
|
||||||
|
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
|
||||||
|
|
||||||
|
buffer-equal-constant-time@1.0.1:
|
||||||
|
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||||
|
|
||||||
|
buffer-from@1.1.2:
|
||||||
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
|
|
||||||
|
busboy@1.6.0:
|
||||||
|
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
|
||||||
|
engines: {node: '>=10.16.0'}
|
||||||
|
|
||||||
|
bytes@3.1.2:
|
||||||
|
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
c12@3.1.0:
|
||||||
|
resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==}
|
||||||
|
peerDependencies:
|
||||||
|
magicast: ^0.3.5
|
||||||
|
peerDependenciesMeta:
|
||||||
|
magicast:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
call-bind-apply-helpers@1.0.2:
|
||||||
|
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
call-bound@1.0.4:
|
||||||
|
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
callsites@3.1.0:
|
||||||
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
camelcase@5.3.1:
|
||||||
|
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
camelcase@6.3.0:
|
||||||
|
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
caniuse-lite@1.0.30001793:
|
||||||
|
resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
char-regex@1.0.2:
|
||||||
|
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
chardet@0.7.0:
|
||||||
|
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||||
|
|
||||||
|
chardet@2.1.1:
|
||||||
|
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
|
||||||
|
|
||||||
|
chokidar@4.0.3:
|
||||||
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
|
engines: {node: '>= 14.16.0'}
|
||||||
|
|
||||||
|
chrome-trace-event@1.0.4:
|
||||||
|
resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
|
||||||
|
ci-info@3.2.0:
|
||||||
|
resolution: {integrity: sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==}
|
||||||
|
|
||||||
|
citty@0.1.6:
|
||||||
|
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
||||||
|
|
||||||
|
citty@0.2.2:
|
||||||
|
resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==}
|
||||||
|
|
||||||
|
cjs-module-lexer@1.0.0:
|
||||||
|
resolution: {integrity: sha512-bLSEjEwg4knnuXt7LIWegvgTOClk6ZonZY6g4CFGBly1EjRqVjTjI8Dwnb/dsu1PwJjYBKxnguE5bRTdk+bFOA==}
|
||||||
|
|
||||||
|
class-transformer@0.5.1:
|
||||||
|
resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
|
||||||
|
|
||||||
|
class-validator@0.14.4:
|
||||||
|
resolution: {integrity: sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==}
|
||||||
|
|
||||||
|
cli-cursor@3.1.0:
|
||||||
|
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
cli-spinners@2.5.0:
|
||||||
|
resolution: {integrity: sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
cli-table3@0.6.5:
|
||||||
|
resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==}
|
||||||
|
engines: {node: 10.* || >= 12.*}
|
||||||
|
|
||||||
|
cli-width@4.1.0:
|
||||||
|
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
|
cliui@8.0.1:
|
||||||
|
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
clone@1.0.4:
|
||||||
|
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
cluster-key-slot@1.1.1:
|
||||||
|
resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
co@4.6.0:
|
||||||
|
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||||
|
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||||
|
|
||||||
|
collect-v8-coverage@1.0.3:
|
||||||
|
resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==}
|
||||||
|
|
||||||
|
color-convert@2.0.1:
|
||||||
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
|
engines: {node: '>=7.0.0'}
|
||||||
|
|
||||||
|
color-name@1.1.4:
|
||||||
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
combined-stream@1.0.8:
|
||||||
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
commander@2.20.0:
|
||||||
|
resolution: {integrity: sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==}
|
||||||
|
|
||||||
|
commander@4.1.1:
|
||||||
|
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
comment-json@5.0.0:
|
||||||
|
resolution: {integrity: sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
concat-map@0.0.1:
|
||||||
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
|
||||||
|
concat-stream@2.0.0:
|
||||||
|
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
|
||||||
|
engines: {'0': node >= 6.0}
|
||||||
|
|
||||||
|
confbox@0.2.4:
|
||||||
|
resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==}
|
||||||
|
|
||||||
|
consola@3.4.2:
|
||||||
|
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
|
||||||
|
engines: {node: ^14.18.0 || >=16.10.0}
|
||||||
|
|
||||||
|
content-disposition@1.1.0:
|
||||||
|
resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
content-type@1.0.5:
|
||||||
|
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
content-type@2.0.0:
|
||||||
|
resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
convert-source-map@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
|
cookie-signature@1.2.2:
|
||||||
|
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
|
||||||
|
engines: {node: '>=6.6.0'}
|
||||||
|
|
||||||
|
cookie@0.7.2:
|
||||||
|
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
cors@2.8.6:
|
||||||
|
resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
cosmiconfig@8.2.0:
|
||||||
|
resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
create-jest@29.7.0:
|
||||||
|
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
create-require@1.1.1:
|
||||||
|
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
|
||||||
|
|
||||||
|
cron@3.5.0:
|
||||||
|
resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==}
|
||||||
|
|
||||||
|
cross-spawn@7.0.6:
|
||||||
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
csstype@3.2.3:
|
||||||
|
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||||
|
|
||||||
|
dayjs@1.11.21:
|
||||||
|
resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
|
||||||
|
|
||||||
|
de-indent@1.0.2:
|
||||||
|
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||||
|
|
||||||
|
debug@4.4.3:
|
||||||
|
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
decimal.js@10.6.0:
|
||||||
|
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||||
|
|
||||||
|
dedent@1.7.2:
|
||||||
|
resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==}
|
||||||
|
peerDependencies:
|
||||||
|
babel-plugin-macros: ^3.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
babel-plugin-macros:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
deepmerge-ts@7.1.5:
|
||||||
|
resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
deepmerge@4.3.1:
|
||||||
|
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
defaults@1.0.4:
|
||||||
|
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
|
||||||
|
|
||||||
|
defu@6.1.7:
|
||||||
|
resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
|
||||||
|
|
||||||
|
delayed-stream@1.0.0:
|
||||||
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
|
denque@2.1.0:
|
||||||
|
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||||
|
engines: {node: '>=0.10'}
|
||||||
|
|
||||||
|
depd@2.0.0:
|
||||||
|
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
destr@2.0.5:
|
||||||
|
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
||||||
|
|
||||||
|
detect-newline@3.1.0:
|
||||||
|
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
diff-sequences@29.6.3:
|
||||||
|
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
diff@4.0.4:
|
||||||
|
resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==}
|
||||||
|
engines: {node: '>=0.3.1'}
|
||||||
|
|
||||||
|
dotenv-expand@12.0.3:
|
||||||
|
resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
dotenv@16.4.5:
|
||||||
|
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
dotenv@16.6.1:
|
||||||
|
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
dotenv@17.4.1:
|
||||||
|
resolution: {integrity: sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
dunder-proto@1.0.1:
|
||||||
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
ecdsa-sig-formatter@1.0.11:
|
||||||
|
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||||
|
|
||||||
|
ee-first@1.1.1:
|
||||||
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
|
|
||||||
|
effect@3.21.0:
|
||||||
|
resolution: {integrity: sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==}
|
||||||
|
|
||||||
|
electron-to-chromium@1.5.364:
|
||||||
|
resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==}
|
||||||
|
|
||||||
|
element-plus@2.14.1:
|
||||||
|
resolution: {integrity: sha512-UFnm1+BckNi+azkKJ7L32q1uXs9ekr99Z9pWTQPeDR05jqEWUwQq51ro4kZMVrANbjknX3Z7ukCZwTi2T6Tr9A==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.3.7
|
||||||
|
|
||||||
|
emittery@0.13.1:
|
||||||
|
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
emoji-regex@8.0.0:
|
||||||
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
|
empathic@2.0.0:
|
||||||
|
resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
encodeurl@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
enhanced-resolve@5.22.1:
|
||||||
|
resolution: {integrity: sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
entities@7.0.1:
|
||||||
|
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
|
||||||
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
|
error-ex@1.3.4:
|
||||||
|
resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
|
||||||
|
|
||||||
|
es-define-property@1.0.1:
|
||||||
|
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-errors@1.3.0:
|
||||||
|
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-module-lexer@2.1.0:
|
||||||
|
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
|
||||||
|
|
||||||
|
es-object-atoms@1.1.2:
|
||||||
|
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-set-tostringtag@2.1.0:
|
||||||
|
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
esbuild@0.25.0:
|
||||||
|
resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
escalade@3.2.0:
|
||||||
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
escape-html@1.0.3:
|
||||||
|
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||||
|
|
||||||
|
escape-string-regexp@2.0.0:
|
||||||
|
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
eslint-scope@5.1.1:
|
||||||
|
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
esprima@4.0.1:
|
||||||
|
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
esrecurse@4.3.0:
|
||||||
|
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
|
estraverse@4.3.0:
|
||||||
|
resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
|
estraverse@5.3.0:
|
||||||
|
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
|
estree-walker@2.0.2:
|
||||||
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
|
|
||||||
|
etag@1.8.1:
|
||||||
|
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
events@3.3.0:
|
||||||
|
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||||
|
engines: {node: '>=0.8.x'}
|
||||||
|
|
||||||
|
execa@5.1.1:
|
||||||
|
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
exit@0.1.2:
|
||||||
|
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
|
expect@29.0.0:
|
||||||
|
resolution: {integrity: sha512-OKAHGwaBqZ6I7bas0cnrrvomEL2d0yp2XXYQhhnVHfaqDaKStUBxjWtlGu/uI2tBqwb9sBMvaS41DSJFsRRJHQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
expect@29.7.0:
|
||||||
|
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
express@5.2.1:
|
||||||
|
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
exsolve@1.0.8:
|
||||||
|
resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==}
|
||||||
|
|
||||||
|
external-editor@3.1.0:
|
||||||
|
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
fast-check@3.23.1:
|
||||||
|
resolution: {integrity: sha512-u/MudsoQEgBUZgR5N1v87vEgybeVYus9VnDVaIkxkkGP2jt54naghQ3PCQHJiogS8U/GavZCUPFfx3Xkp+NaHw==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
fast-deep-equal@3.1.3:
|
||||||
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
|
fast-json-stable-stringify@2.1.0:
|
||||||
|
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
|
||||||
|
|
||||||
|
fast-safe-stringify@2.1.1:
|
||||||
|
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
||||||
|
|
||||||
|
fast-uri@3.1.2:
|
||||||
|
resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==}
|
||||||
|
|
||||||
|
fb-watchman@2.0.2:
|
||||||
|
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
|
||||||
|
|
||||||
|
fdir@6.5.0:
|
||||||
|
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
picomatch: ^3 || ^4
|
||||||
|
peerDependenciesMeta:
|
||||||
|
picomatch:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
file-type@21.3.4:
|
||||||
|
resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
finalhandler@2.1.1:
|
||||||
|
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
|
||||||
|
engines: {node: '>= 18.0.0'}
|
||||||
|
|
||||||
|
find-up@4.1.0:
|
||||||
|
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
follow-redirects@1.16.0:
|
||||||
|
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
peerDependencies:
|
||||||
|
debug: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
debug:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
fork-ts-checker-webpack-plugin@9.1.0:
|
||||||
|
resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==}
|
||||||
|
engines: {node: '>=14.21.3'}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>3.6.0'
|
||||||
|
webpack: ^5.11.0
|
||||||
|
|
||||||
|
form-data@4.0.5:
|
||||||
|
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
forwarded@0.2.0:
|
||||||
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
fresh@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
fs-extra@10.0.0:
|
||||||
|
resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
fs-monkey@1.0.3:
|
||||||
|
resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==}
|
||||||
|
|
||||||
|
fs.realpath@1.0.0:
|
||||||
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
function-bind@1.1.2:
|
||||||
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
gensync@1.0.0-beta.2:
|
||||||
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5:
|
||||||
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
|
|
||||||
|
get-intrinsic@1.3.0:
|
||||||
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
get-package-type@0.1.0:
|
||||||
|
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
|
||||||
|
get-proto@1.0.1:
|
||||||
|
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
get-stream@6.0.1:
|
||||||
|
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
giget@2.0.0:
|
||||||
|
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
glob-to-regexp@0.4.1:
|
||||||
|
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
|
||||||
|
|
||||||
|
glob@13.0.6:
|
||||||
|
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
|
glob@7.2.0:
|
||||||
|
resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
|
||||||
|
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||||
|
|
||||||
|
gopd@1.2.0:
|
||||||
|
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
graceful-fs@4.2.11:
|
||||||
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
|
|
||||||
|
handlebars@4.7.9:
|
||||||
|
resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==}
|
||||||
|
engines: {node: '>=0.4.7'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
has-flag@4.0.0:
|
||||||
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
has-symbols@1.1.0:
|
||||||
|
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
has-tostringtag@1.0.2:
|
||||||
|
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hasown@2.0.4:
|
||||||
|
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
he@1.2.0:
|
||||||
|
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
html-escaper@2.0.2:
|
||||||
|
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||||
|
|
||||||
|
http-errors@2.0.1:
|
||||||
|
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
https-proxy-agent@5.0.1:
|
||||||
|
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
human-signals@2.1.0:
|
||||||
|
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
|
||||||
|
engines: {node: '>=10.17.0'}
|
||||||
|
|
||||||
|
iconv-lite@0.4.24:
|
||||||
|
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
iconv-lite@0.7.2:
|
||||||
|
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
ieee754@1.2.1:
|
||||||
|
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||||
|
|
||||||
|
import-fresh@3.2.1:
|
||||||
|
resolution: {integrity: sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
import-local@3.2.0:
|
||||||
|
resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
imurmurhash@0.1.4:
|
||||||
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
|
engines: {node: '>=0.8.19'}
|
||||||
|
|
||||||
|
inflight@1.0.6:
|
||||||
|
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||||
|
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||||
|
|
||||||
|
inherits@2.0.4:
|
||||||
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
|
||||||
|
ioredis@5.11.0:
|
||||||
|
resolution: {integrity: sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==}
|
||||||
|
engines: {node: '>=12.22.0'}
|
||||||
|
|
||||||
|
ipaddr.js@1.9.1:
|
||||||
|
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
is-arrayish@0.2.1:
|
||||||
|
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
|
||||||
|
|
||||||
|
is-core-module@2.16.2:
|
||||||
|
resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0:
|
||||||
|
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
is-generator-fn@2.1.0:
|
||||||
|
resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
is-interactive@1.0.0:
|
||||||
|
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
is-number@7.0.0:
|
||||||
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
is-promise@4.0.0:
|
||||||
|
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
|
||||||
|
|
||||||
|
is-stream@2.0.1:
|
||||||
|
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
is-unicode-supported@0.1.0:
|
||||||
|
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
isexe@2.0.0:
|
||||||
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
|
||||||
|
istanbul-lib-coverage@3.2.2:
|
||||||
|
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
istanbul-lib-instrument@5.2.1:
|
||||||
|
resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
istanbul-lib-instrument@6.0.3:
|
||||||
|
resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
istanbul-lib-report@3.0.1:
|
||||||
|
resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
istanbul-lib-source-maps@4.0.1:
|
||||||
|
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
istanbul-reports@3.2.0:
|
||||||
|
resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
iterare@1.2.1:
|
||||||
|
resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
jest-changed-files@29.7.0:
|
||||||
|
resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-circus@29.7.0:
|
||||||
|
resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-cli@29.7.0:
|
||||||
|
resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
node-notifier:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jest-config@29.7.0:
|
||||||
|
resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': '*'
|
||||||
|
ts-node: '>=9.0.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
ts-node:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jest-diff@29.0.0:
|
||||||
|
resolution: {integrity: sha512-erkuYf1dQBHow3XJmS+bH6t9TZ0GwrSdQGauN8sTqyZlFByOjRadmHgTTcAHINeeSwxzGHN2ob3PXVvZphD7XQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-diff@29.7.0:
|
||||||
|
resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-docblock@29.7.0:
|
||||||
|
resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-each@29.7.0:
|
||||||
|
resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-environment-node@29.7.0:
|
||||||
|
resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-get-type@29.6.3:
|
||||||
|
resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-haste-map@29.7.0:
|
||||||
|
resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-leak-detector@29.7.0:
|
||||||
|
resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-matcher-utils@29.0.0:
|
||||||
|
resolution: {integrity: sha512-HtCxFHI8lQSbN1RppFjtl6DIrS+x4d3lOhpJljVxFEXob4lxlKon3FunW0XoGxNSvIoD00AfTFspnufpOqszrg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-matcher-utils@29.7.0:
|
||||||
|
resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-message-util@29.0.0:
|
||||||
|
resolution: {integrity: sha512-4U0RdNV0TBTgVGzEchjryEpq4sqLO3gUQT7TEIbO5+q0K5MuiofOPcXk4GLpWviWByMRJjliQNMuzJ4YGT+oGQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-message-util@29.7.0:
|
||||||
|
resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-mock@29.7.0:
|
||||||
|
resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-pnp-resolver@1.2.3:
|
||||||
|
resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
peerDependencies:
|
||||||
|
jest-resolve: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
jest-resolve:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jest-regex-util@29.6.3:
|
||||||
|
resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-resolve-dependencies@29.7.0:
|
||||||
|
resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-resolve@29.7.0:
|
||||||
|
resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-runner@29.7.0:
|
||||||
|
resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-runtime@29.7.0:
|
||||||
|
resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-snapshot@29.7.0:
|
||||||
|
resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-util@29.0.0:
|
||||||
|
resolution: {integrity: sha512-HMjW/pkFgi34LGKumjNDK03DYonV+nPMNUZ63rZX8PFdBkdIWUtOCEiaa7sAJkWrw5MyMVzSpa22NcOJjoQ3JQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-util@29.7.0:
|
||||||
|
resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-validate@29.7.0:
|
||||||
|
resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-watcher@29.7.0:
|
||||||
|
resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest-worker@27.4.5:
|
||||||
|
resolution: {integrity: sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==}
|
||||||
|
engines: {node: '>= 10.13.0'}
|
||||||
|
|
||||||
|
jest-worker@29.7.0:
|
||||||
|
resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
jest@29.7.0:
|
||||||
|
resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
node-notifier:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
jiti@2.7.0:
|
||||||
|
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
js-tokens@4.0.0:
|
||||||
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
|
js-yaml@3.13.1:
|
||||||
|
resolution: {integrity: sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
js-yaml@4.1.1:
|
||||||
|
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
js-yaml@4.2.0:
|
||||||
|
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
jsesc@3.1.0:
|
||||||
|
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
json-parse-even-better-errors@2.3.0:
|
||||||
|
resolution: {integrity: sha512-o3aP+RsWDJZayj1SbHNQAI8x0v3T3SKiGoZlNYfbUP1S3omJQ6i9CnqADqkSPaOAxwua4/1YWx5CM7oiChJt2Q==}
|
||||||
|
|
||||||
|
json-parse-even-better-errors@2.3.1:
|
||||||
|
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
||||||
|
|
||||||
|
json-schema-traverse@0.4.1:
|
||||||
|
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||||
|
|
||||||
|
json-schema-traverse@1.0.0:
|
||||||
|
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||||
|
|
||||||
|
json5@2.2.3:
|
||||||
|
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
jsonc-parser@3.3.1:
|
||||||
|
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
|
||||||
|
|
||||||
|
jsonfile@6.2.1:
|
||||||
|
resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==}
|
||||||
|
|
||||||
|
jsonwebtoken@9.0.3:
|
||||||
|
resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==}
|
||||||
|
engines: {node: '>=12', npm: '>=6'}
|
||||||
|
|
||||||
|
jwa@2.0.1:
|
||||||
|
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
|
||||||
|
|
||||||
|
jws@4.0.1:
|
||||||
|
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
|
||||||
|
|
||||||
|
kleur@3.0.3:
|
||||||
|
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
leven@3.1.0:
|
||||||
|
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
libphonenumber-js@1.13.4:
|
||||||
|
resolution: {integrity: sha512-/lhWr7vq8foWN9Apksnd9v8/cfwzW6g6qKOCo25XBGkNaVCHucXO57hLy4CWHGvytvLz6Nt3J5Gs8p3jlCGFXA==}
|
||||||
|
|
||||||
|
lines-and-columns@1.2.4:
|
||||||
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
|
|
||||||
|
load-esm@1.0.3:
|
||||||
|
resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==}
|
||||||
|
engines: {node: '>=13.2.0'}
|
||||||
|
|
||||||
|
loader-runner@4.3.2:
|
||||||
|
resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==}
|
||||||
|
engines: {node: '>=6.11.5'}
|
||||||
|
|
||||||
|
locate-path@5.0.0:
|
||||||
|
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
lodash-es@4.18.1:
|
||||||
|
resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
|
||||||
|
|
||||||
|
lodash-unified@1.0.3:
|
||||||
|
resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/lodash-es': '*'
|
||||||
|
lodash: '*'
|
||||||
|
lodash-es: '*'
|
||||||
|
|
||||||
|
lodash.includes@4.3.0:
|
||||||
|
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||||
|
|
||||||
|
lodash.isboolean@3.0.3:
|
||||||
|
resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
|
||||||
|
|
||||||
|
lodash.isinteger@4.0.4:
|
||||||
|
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
|
||||||
|
|
||||||
|
lodash.isnumber@3.0.3:
|
||||||
|
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
|
||||||
|
|
||||||
|
lodash.isplainobject@4.0.6:
|
||||||
|
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||||
|
|
||||||
|
lodash.isstring@4.0.1:
|
||||||
|
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
||||||
|
|
||||||
|
lodash.memoize@4.1.2:
|
||||||
|
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
|
||||||
|
|
||||||
|
lodash.once@4.1.1:
|
||||||
|
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||||
|
|
||||||
|
lodash@4.18.1:
|
||||||
|
resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
|
||||||
|
|
||||||
|
log-symbols@4.1.0:
|
||||||
|
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
lru-cache@11.5.1:
|
||||||
|
resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
|
||||||
|
engines: {node: 20 || >=22}
|
||||||
|
|
||||||
|
lru-cache@5.1.1:
|
||||||
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
|
|
||||||
|
luxon@3.5.0:
|
||||||
|
resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
magic-string@0.30.17:
|
||||||
|
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||||
|
|
||||||
|
magic-string@0.30.21:
|
||||||
|
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||||
|
|
||||||
|
make-dir@4.0.0:
|
||||||
|
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
make-error@1.3.6:
|
||||||
|
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
||||||
|
|
||||||
|
makeerror@1.0.12:
|
||||||
|
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
|
||||||
|
|
||||||
|
math-intrinsics@1.1.0:
|
||||||
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
media-typer@0.3.0:
|
||||||
|
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
media-typer@1.1.0:
|
||||||
|
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
memfs@3.4.1:
|
||||||
|
resolution: {integrity: sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==}
|
||||||
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|
||||||
|
memoize-one@6.0.0:
|
||||||
|
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
|
||||||
|
|
||||||
|
merge-descriptors@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
merge-stream@2.0.0:
|
||||||
|
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||||
|
|
||||||
|
micromatch@4.0.8:
|
||||||
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-db@1.54.0:
|
||||||
|
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-types@3.0.2:
|
||||||
|
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
mimic-fn@2.1.0:
|
||||||
|
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
minimatch@10.2.5:
|
||||||
|
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
|
minimatch@3.1.5:
|
||||||
|
resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
|
||||||
|
|
||||||
|
minimatch@9.0.9:
|
||||||
|
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
|
||||||
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
||||||
|
minimist@1.2.8:
|
||||||
|
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||||
|
|
||||||
|
minipass@7.1.3:
|
||||||
|
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
|
||||||
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
||||||
|
ms@2.1.3:
|
||||||
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
muggle-string@0.4.1:
|
||||||
|
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||||
|
|
||||||
|
multer@2.1.1:
|
||||||
|
resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
|
||||||
|
engines: {node: '>= 10.16.0'}
|
||||||
|
|
||||||
|
mute-stream@2.0.0:
|
||||||
|
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
|
||||||
|
engines: {node: ^18.17.0 || >=20.5.0}
|
||||||
|
|
||||||
|
nanoid@3.3.12:
|
||||||
|
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
|
||||||
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
natural-compare@1.4.0:
|
||||||
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
|
negotiator@1.0.0:
|
||||||
|
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
neo-async@2.6.2:
|
||||||
|
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||||
|
|
||||||
|
node-abort-controller@3.1.1:
|
||||||
|
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
|
||||||
|
|
||||||
|
node-emoji@1.11.0:
|
||||||
|
resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
|
||||||
|
|
||||||
|
node-fetch-native@1.6.7:
|
||||||
|
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||||
|
|
||||||
|
node-int64@0.4.0:
|
||||||
|
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
||||||
|
|
||||||
|
node-releases@2.0.46:
|
||||||
|
resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
normalize-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
normalize-wheel-es@1.2.0:
|
||||||
|
resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
|
||||||
|
|
||||||
|
npm-run-path@4.0.1:
|
||||||
|
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
nypm@0.6.6:
|
||||||
|
resolution: {integrity: sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
object-assign@4.1.1:
|
||||||
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
object-inspect@1.13.4:
|
||||||
|
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
ohash@2.0.11:
|
||||||
|
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
|
||||||
|
|
||||||
|
on-finished@2.4.1:
|
||||||
|
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
once@1.4.0:
|
||||||
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
|
|
||||||
|
onetime@5.1.0:
|
||||||
|
resolution: {integrity: sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
onetime@5.1.2:
|
||||||
|
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
ora@5.4.1:
|
||||||
|
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
os-tmpdir@1.0.2:
|
||||||
|
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
p-limit@2.2.0:
|
||||||
|
resolution: {integrity: sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
p-limit@3.1.0:
|
||||||
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
p-locate@4.1.0:
|
||||||
|
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
p-try@2.2.0:
|
||||||
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
parent-module@1.0.1:
|
||||||
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
parse-json@5.2.0:
|
||||||
|
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
parseurl@1.3.3:
|
||||||
|
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
passport-jwt@4.0.1:
|
||||||
|
resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==}
|
||||||
|
|
||||||
|
passport-strategy@1.0.0:
|
||||||
|
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
|
||||||
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
passport@0.7.0:
|
||||||
|
resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==}
|
||||||
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
path-browserify@1.0.1:
|
||||||
|
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||||
|
|
||||||
|
path-exists@4.0.0:
|
||||||
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
path-is-absolute@1.0.1:
|
||||||
|
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
path-key@3.1.1:
|
||||||
|
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
path-parse@1.0.7:
|
||||||
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
|
||||||
|
path-scurry@2.0.2:
|
||||||
|
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
|
||||||
|
engines: {node: 18 || 20 || >=22}
|
||||||
|
|
||||||
|
path-to-regexp@8.4.2:
|
||||||
|
resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==}
|
||||||
|
|
||||||
|
path-type@4.0.0:
|
||||||
|
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
pathe@2.0.3:
|
||||||
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
|
pause@0.0.1:
|
||||||
|
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
|
||||||
|
|
||||||
|
perfect-debounce@1.0.0:
|
||||||
|
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||||
|
|
||||||
|
picocolors@1.1.1:
|
||||||
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
|
picomatch@2.3.2:
|
||||||
|
resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
picomatch@4.0.4:
|
||||||
|
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
pinia@2.3.1:
|
||||||
|
resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>=4.4.4'
|
||||||
|
vue: ^2.7.0 || ^3.5.11
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
pirates@4.0.7:
|
||||||
|
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
pkg-dir@4.2.0:
|
||||||
|
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
pkg-types@2.3.1:
|
||||||
|
resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
|
||||||
|
|
||||||
|
pluralize@8.0.0:
|
||||||
|
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
postcss@8.5.15:
|
||||||
|
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
|
||||||
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
pretty-format@29.0.0:
|
||||||
|
resolution: {integrity: sha512-tMkFRn1vxRwZdiDETcveuNeonRKDg4doOvI+iyb1sOAtxYioGzRicqnsr+d3C/lLv9hBiM/2lDBi5ilR81h2bQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
pretty-format@29.7.0:
|
||||||
|
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
prisma@6.19.3:
|
||||||
|
resolution: {integrity: sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==}
|
||||||
|
engines: {node: '>=18.18'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>=5.1.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
prompts@2.4.2:
|
||||||
|
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
proxy-addr@2.0.7:
|
||||||
|
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
proxy-from-env@2.1.0:
|
||||||
|
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
punycode@2.3.1:
|
||||||
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
pure-rand@6.0.0:
|
||||||
|
resolution: {integrity: sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==}
|
||||||
|
|
||||||
|
pure-rand@6.1.0:
|
||||||
|
resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
|
||||||
|
|
||||||
|
qs@6.15.2:
|
||||||
|
resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==}
|
||||||
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
range-parser@1.2.1:
|
||||||
|
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
raw-body@3.0.2:
|
||||||
|
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
rc9@2.1.2:
|
||||||
|
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
||||||
|
|
||||||
|
react-is@18.0.0:
|
||||||
|
resolution: {integrity: sha512-yUcBYdBBbo3QiPsgYDcfQcIkGZHfxOaoE6HLSnr1sPzMhdyxusbfKOSUbSd/ocGi32dxcj366PsTj+5oggeKKw==}
|
||||||
|
|
||||||
|
readable-stream@3.6.2:
|
||||||
|
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
readdirp@4.1.2:
|
||||||
|
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||||
|
engines: {node: '>= 14.18.0'}
|
||||||
|
|
||||||
|
redis-errors@1.2.0:
|
||||||
|
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
redis-parser@3.0.0:
|
||||||
|
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
reflect-metadata@0.2.2:
|
||||||
|
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
|
||||||
|
|
||||||
|
require-directory@2.1.1:
|
||||||
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
require-from-string@2.0.2:
|
||||||
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
resolve-cwd@3.0.0:
|
||||||
|
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
resolve-from@4.0.0:
|
||||||
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
resolve-from@5.0.0:
|
||||||
|
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
resolve.exports@2.0.3:
|
||||||
|
resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
resolve@1.22.12:
|
||||||
|
resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
restore-cursor@3.1.0:
|
||||||
|
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
rollup@4.61.0:
|
||||||
|
resolution: {integrity: sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==}
|
||||||
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
router@2.2.0:
|
||||||
|
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
rxjs@7.8.1:
|
||||||
|
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
|
||||||
|
|
||||||
|
rxjs@7.8.2:
|
||||||
|
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||||
|
|
||||||
|
safe-buffer@5.2.1:
|
||||||
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
|
|
||||||
|
safer-buffer@2.1.2:
|
||||||
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
|
||||||
|
schema-utils@3.3.0:
|
||||||
|
resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
|
||||||
|
engines: {node: '>= 10.13.0'}
|
||||||
|
|
||||||
|
schema-utils@4.3.3:
|
||||||
|
resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==}
|
||||||
|
engines: {node: '>= 10.13.0'}
|
||||||
|
|
||||||
|
semver@6.3.0:
|
||||||
|
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
semver@6.3.1:
|
||||||
|
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
semver@7.8.1:
|
||||||
|
resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
send@1.2.1:
|
||||||
|
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
serve-static@2.2.1:
|
||||||
|
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
setprototypeof@1.2.0:
|
||||||
|
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||||
|
|
||||||
|
shebang-command@2.0.0:
|
||||||
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
shebang-regex@3.0.0:
|
||||||
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
side-channel-list@1.0.1:
|
||||||
|
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
side-channel-map@1.0.1:
|
||||||
|
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
side-channel-weakmap@1.0.2:
|
||||||
|
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
side-channel@1.1.0:
|
||||||
|
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
signal-exit@3.0.7:
|
||||||
|
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||||
|
|
||||||
|
signal-exit@4.1.0:
|
||||||
|
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
sisteransi@1.0.5:
|
||||||
|
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||||
|
|
||||||
|
slash@3.0.0:
|
||||||
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
source-map-js@1.2.1:
|
||||||
|
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
source-map-support@0.5.13:
|
||||||
|
resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
|
||||||
|
|
||||||
|
source-map-support@0.5.21:
|
||||||
|
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||||
|
|
||||||
|
source-map@0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
source-map@0.7.4:
|
||||||
|
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
sprintf-js@1.0.3:
|
||||||
|
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||||
|
|
||||||
|
stack-utils@2.0.6:
|
||||||
|
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
standard-as-callback@2.1.0:
|
||||||
|
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||||
|
|
||||||
|
statuses@2.0.2:
|
||||||
|
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
streamsearch@1.1.0:
|
||||||
|
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
|
string-length@4.0.1:
|
||||||
|
resolution: {integrity: sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
string-width@4.1.0:
|
||||||
|
resolution: {integrity: sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
string-width@4.2.0:
|
||||||
|
resolution: {integrity: sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
string_decoder@1.3.0:
|
||||||
|
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||||
|
|
||||||
|
strip-ansi@5.2.0:
|
||||||
|
resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
strip-ansi@6.0.0:
|
||||||
|
resolution: {integrity: sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-ansi@6.0.1:
|
||||||
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-bom@3.0.0:
|
||||||
|
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
strip-bom@4.0.0:
|
||||||
|
resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-final-newline@2.0.0:
|
||||||
|
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
strip-json-comments@3.1.1:
|
||||||
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strtok3@10.3.5:
|
||||||
|
resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
supports-color@8.1.1:
|
||||||
|
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
supports-preserve-symlinks-flag@1.0.0:
|
||||||
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
swagger-ui-dist@5.32.6:
|
||||||
|
resolution: {integrity: sha512-75ttZNaYCLoFPnozPZcTUU6mS3wKT8l7WLjU5zJSHFeJa23i5vtnze6IiCl4jDMPeQTXVXIgovq4M11NNfQvSA==}
|
||||||
|
|
||||||
|
symbol-observable@4.0.0:
|
||||||
|
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
|
||||||
|
engines: {node: '>=0.10'}
|
||||||
|
|
||||||
|
tapable@2.3.3:
|
||||||
|
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
terser-webpack-plugin@5.6.1:
|
||||||
|
resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==}
|
||||||
|
engines: {node: '>= 10.13.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@minify-html/node': '*'
|
||||||
|
'@swc/core': '*'
|
||||||
|
'@swc/css': '*'
|
||||||
|
'@swc/html': '*'
|
||||||
|
clean-css: '*'
|
||||||
|
cssnano: '*'
|
||||||
|
csso: '*'
|
||||||
|
esbuild: '*'
|
||||||
|
html-minifier-terser: '*'
|
||||||
|
lightningcss: '*'
|
||||||
|
postcss: '*'
|
||||||
|
uglify-js: '*'
|
||||||
|
webpack: ^5.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@minify-html/node':
|
||||||
|
optional: true
|
||||||
|
'@swc/core':
|
||||||
|
optional: true
|
||||||
|
'@swc/css':
|
||||||
|
optional: true
|
||||||
|
'@swc/html':
|
||||||
|
optional: true
|
||||||
|
clean-css:
|
||||||
|
optional: true
|
||||||
|
cssnano:
|
||||||
|
optional: true
|
||||||
|
csso:
|
||||||
|
optional: true
|
||||||
|
esbuild:
|
||||||
|
optional: true
|
||||||
|
html-minifier-terser:
|
||||||
|
optional: true
|
||||||
|
lightningcss:
|
||||||
|
optional: true
|
||||||
|
postcss:
|
||||||
|
optional: true
|
||||||
|
uglify-js:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
terser@5.48.0:
|
||||||
|
resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
test-exclude@6.0.0:
|
||||||
|
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
tinyexec@1.2.4:
|
||||||
|
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
tinyglobby@0.2.17:
|
||||||
|
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
tmp@0.0.33:
|
||||||
|
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
||||||
|
engines: {node: '>=0.6.0'}
|
||||||
|
|
||||||
|
tmpl@1.0.5:
|
||||||
|
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
|
toidentifier@1.0.1:
|
||||||
|
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||||
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
token-types@6.1.2:
|
||||||
|
resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
|
ts-jest@29.4.11:
|
||||||
|
resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==}
|
||||||
|
engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': '>=7.0.0-beta.0 <8'
|
||||||
|
'@jest/transform': ^29.0.0 || ^30.0.0
|
||||||
|
'@jest/types': ^29.0.0 || ^30.0.0
|
||||||
|
babel-jest: ^29.0.0 || ^30.0.0
|
||||||
|
esbuild: '*'
|
||||||
|
jest: ^29.0.0 || ^30.0.0
|
||||||
|
jest-util: ^29.0.0 || ^30.0.0
|
||||||
|
typescript: '>=4.3 <7'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@babel/core':
|
||||||
|
optional: true
|
||||||
|
'@jest/transform':
|
||||||
|
optional: true
|
||||||
|
'@jest/types':
|
||||||
|
optional: true
|
||||||
|
babel-jest:
|
||||||
|
optional: true
|
||||||
|
esbuild:
|
||||||
|
optional: true
|
||||||
|
jest-util:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
ts-node@10.9.2:
|
||||||
|
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@swc/core': '>=1.2.50'
|
||||||
|
'@swc/wasm': '>=1.2.50'
|
||||||
|
'@types/node': '*'
|
||||||
|
typescript: '>=2.7'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@swc/core':
|
||||||
|
optional: true
|
||||||
|
'@swc/wasm':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
tsconfig-paths-webpack-plugin@4.2.0:
|
||||||
|
resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
tsconfig-paths@4.2.0:
|
||||||
|
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
tslib@2.8.1:
|
||||||
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
type-detect@4.0.8:
|
||||||
|
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
type-fest@0.21.3:
|
||||||
|
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
type-fest@0.5.2:
|
||||||
|
resolution: {integrity: sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
type-fest@4.41.0:
|
||||||
|
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
|
type-is@1.6.18:
|
||||||
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
type-is@2.1.0:
|
||||||
|
resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
typedarray@0.0.6:
|
||||||
|
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
|
||||||
|
|
||||||
|
typescript@5.7.3:
|
||||||
|
resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
typescript@5.9.3:
|
||||||
|
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
uglify-js@3.19.3:
|
||||||
|
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
uid@2.0.2:
|
||||||
|
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
uint8array-extras@1.5.0:
|
||||||
|
resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
undici-types@6.21.0:
|
||||||
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
|
universalify@2.0.1:
|
||||||
|
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||||
|
engines: {node: '>= 10.0.0'}
|
||||||
|
|
||||||
|
unpipe@1.0.0:
|
||||||
|
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
update-browserslist-db@1.2.3:
|
||||||
|
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
browserslist: '>= 4.21.0'
|
||||||
|
|
||||||
|
uri-js@4.4.1:
|
||||||
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
|
||||||
|
util-deprecate@1.0.2:
|
||||||
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
|
utils-merge@1.0.1:
|
||||||
|
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||||
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
uuid@11.1.1:
|
||||||
|
resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
v8-compile-cache-lib@3.0.1:
|
||||||
|
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||||
|
|
||||||
|
v8-to-istanbul@9.3.0:
|
||||||
|
resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
|
||||||
|
engines: {node: '>=10.12.0'}
|
||||||
|
|
||||||
|
validator@13.15.35:
|
||||||
|
resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
vary@1.1.2:
|
||||||
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
vite@6.4.2:
|
||||||
|
resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||||
|
jiti: '>=1.21.0'
|
||||||
|
less: '*'
|
||||||
|
lightningcss: ^1.21.0
|
||||||
|
sass: '*'
|
||||||
|
sass-embedded: '*'
|
||||||
|
stylus: '*'
|
||||||
|
sugarss: '*'
|
||||||
|
terser: ^5.16.0
|
||||||
|
tsx: ^4.8.1
|
||||||
|
yaml: ^2.4.2
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
jiti:
|
||||||
|
optional: true
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
lightningcss:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
sass-embedded:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
sugarss:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
tsx:
|
||||||
|
optional: true
|
||||||
|
yaml:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
vscode-uri@3.1.0:
|
||||||
|
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||||
|
|
||||||
|
vue-component-type-helpers@3.3.3:
|
||||||
|
resolution: {integrity: sha512-x4nsFpy5Pe8fqPzp/5vkTPeTTDBpAx4WVtV47Ejt0+2FQrq4pRRsJs7JmYRqMFzTu/LW+pCWEjQ3YVCkPV7f9g==}
|
||||||
|
|
||||||
|
vue-demi@0.14.10:
|
||||||
|
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@vue/composition-api': ^1.0.0-rc.1
|
||||||
|
vue: ^3.0.0-0 || ^2.6.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@vue/composition-api':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
vue-i18n@11.4.4:
|
||||||
|
resolution: {integrity: sha512-gIbXVSFQV4jcSJxfwdZ5zSZmZ+12CnX0K3vBkRSd6Zn+HSzCp+QwUgPwpD/uN0oKNKI9RzlUXPKVedEuMgNG0A==}
|
||||||
|
engines: {node: '>= 22'}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.0.0
|
||||||
|
|
||||||
|
vue-router@4.6.4:
|
||||||
|
resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.0
|
||||||
|
|
||||||
|
vue-tsc@2.2.0:
|
||||||
|
resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>=5.0.0'
|
||||||
|
|
||||||
|
vue@3.5.35:
|
||||||
|
resolution: {integrity: sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
walker@1.0.8:
|
||||||
|
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
|
||||||
|
|
||||||
|
watchpack@2.5.1:
|
||||||
|
resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
wcwidth@1.0.1:
|
||||||
|
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||||
|
|
||||||
|
webpack-node-externals@3.0.0:
|
||||||
|
resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
webpack-sources@3.5.0:
|
||||||
|
resolution: {integrity: sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
|
webpack@5.106.0:
|
||||||
|
resolution: {integrity: sha512-Pkx5joZ9RrdgO5LBkyX1L2ZAJeK/Taz3vqZ9CbcP0wS5LEMx5QkKsEwLl29QJfihZ+DKRBFldzy1O30pJ1MDpA==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
webpack-cli: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
webpack-cli:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
which@2.0.1:
|
||||||
|
resolution: {integrity: sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
wordwrap@1.0.0:
|
||||||
|
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
||||||
|
|
||||||
|
wrap-ansi@6.2.0:
|
||||||
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
wrap-ansi@7.0.0:
|
||||||
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
wrappy@1.0.2:
|
||||||
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
|
write-file-atomic@4.0.2:
|
||||||
|
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
|
||||||
|
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||||
|
|
||||||
|
y18n@5.0.8:
|
||||||
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
yallist@3.1.1:
|
||||||
|
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||||
|
|
||||||
|
yargs-parser@21.1.1:
|
||||||
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
yargs@17.7.2:
|
||||||
|
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
yn@3.1.1:
|
||||||
|
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
yocto-queue@0.1.0:
|
||||||
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
yoctocolors-cjs@2.1.3:
|
||||||
|
resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@angular-devkit/core@19.2.24(chokidar@4.0.3)':
|
||||||
|
dependencies:
|
||||||
|
ajv: 8.18.0
|
||||||
|
ajv-formats: 3.0.1(ajv@8.18.0)
|
||||||
|
jsonc-parser: 3.3.1
|
||||||
|
picomatch: 4.0.4
|
||||||
|
rxjs: 7.8.1
|
||||||
|
source-map: 0.7.4
|
||||||
|
optionalDependencies:
|
||||||
|
chokidar: 4.0.3
|
||||||
|
|
||||||
|
'@angular-devkit/schematics-cli@19.2.24(@types/node@22.19.19)(chokidar@4.0.3)':
|
||||||
|
dependencies:
|
||||||
|
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||||
|
'@angular-devkit/schematics': 19.2.24(chokidar@4.0.3)
|
||||||
|
'@inquirer/prompts': 7.3.2(@types/node@22.19.19)
|
||||||
|
ansi-colors: 4.1.3
|
||||||
|
symbol-observable: 4.0.0
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- chokidar
|
||||||
|
|
||||||
|
'@angular-devkit/schematics@19.2.24(chokidar@4.0.3)':
|
||||||
|
dependencies:
|
||||||
|
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||||
|
jsonc-parser: 3.3.1
|
||||||
|
magic-string: 0.30.17
|
||||||
|
ora: 5.4.1
|
||||||
|
rxjs: 7.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- chokidar
|
||||||
|
|
||||||
|
'@babel/code-frame@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
|
js-tokens: 4.0.0
|
||||||
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
'@babel/compat-data@7.29.7': {}
|
||||||
|
|
||||||
|
'@babel/core@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
'@babel/generator': 7.29.7
|
||||||
|
'@babel/helper-compilation-targets': 7.29.7
|
||||||
|
'@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/helpers': 7.29.7
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@babel/template': 7.29.7
|
||||||
|
'@babel/traverse': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
'@jridgewell/remapping': 2.3.5
|
||||||
|
convert-source-map: 2.0.0
|
||||||
|
debug: 4.4.3
|
||||||
|
gensync: 1.0.0-beta.2
|
||||||
|
json5: 2.2.3
|
||||||
|
semver: 6.3.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@babel/generator@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
'@jridgewell/gen-mapping': 0.3.12
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
jsesc: 3.1.0
|
||||||
|
|
||||||
|
'@babel/helper-compilation-targets@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/compat-data': 7.29.7
|
||||||
|
'@babel/helper-validator-option': 7.29.7
|
||||||
|
browserslist: 4.28.2
|
||||||
|
lru-cache: 5.1.1
|
||||||
|
semver: 6.3.1
|
||||||
|
|
||||||
|
'@babel/helper-globals@7.29.7': {}
|
||||||
|
|
||||||
|
'@babel/helper-module-imports@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/traverse': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-module-imports': 7.29.7
|
||||||
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
|
'@babel/traverse': 7.29.7
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@babel/helper-plugin-utils@7.29.7': {}
|
||||||
|
|
||||||
|
'@babel/helper-string-parser@7.29.7': {}
|
||||||
|
|
||||||
|
'@babel/helper-validator-identifier@7.29.7': {}
|
||||||
|
|
||||||
|
'@babel/helper-validator-option@7.29.7': {}
|
||||||
|
|
||||||
|
'@babel/helpers@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/template': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
|
||||||
|
'@babel/parser@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
|
||||||
|
'@babel/template@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
|
||||||
|
'@babel/traverse@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
'@babel/generator': 7.29.7
|
||||||
|
'@babel/helper-globals': 7.29.7
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@babel/template': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
debug: 4.4.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@babel/types@7.29.7':
|
||||||
|
dependencies:
|
||||||
|
'@babel/helper-string-parser': 7.29.7
|
||||||
|
'@babel/helper-validator-identifier': 7.29.7
|
||||||
|
|
||||||
|
'@bcoe/v8-coverage@0.2.3': {}
|
||||||
|
|
||||||
|
'@borewit/text-codec@0.2.2': {}
|
||||||
|
|
||||||
|
'@colors/colors@1.5.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@cspotcode/source-map-support@0.8.1':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
|
|
||||||
|
'@ctrl/tinycolor@4.2.0': {}
|
||||||
|
|
||||||
|
'@element-plus/icons-vue@2.3.2(vue@3.5.35(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.25.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@floating-ui/core@1.7.5':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/utils': 0.2.11
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.7.6':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/core': 1.7.5
|
||||||
|
'@floating-ui/utils': 0.2.11
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.11': {}
|
||||||
|
|
||||||
|
'@inquirer/ansi@1.0.2': {}
|
||||||
|
|
||||||
|
'@inquirer/checkbox@4.1.2(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/figures': 1.0.10
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/checkbox@4.3.2(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/ansi': 1.0.2
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/figures': 1.0.15
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/confirm@5.1.21(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/confirm@5.1.6(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/core@10.1.7(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/figures': 1.0.10
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
cli-width: 4.1.0
|
||||||
|
mute-stream: 2.0.0
|
||||||
|
signal-exit: 4.1.0
|
||||||
|
wrap-ansi: 6.2.0
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/core@10.3.2(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/ansi': 1.0.2
|
||||||
|
'@inquirer/figures': 1.0.15
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
cli-width: 4.1.0
|
||||||
|
mute-stream: 2.0.0
|
||||||
|
signal-exit: 4.1.0
|
||||||
|
wrap-ansi: 6.2.0
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/editor@4.2.23(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/external-editor': 1.0.3(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/editor@4.2.7(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
external-editor: 3.1.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/expand@4.0.23(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/expand@4.0.9(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/external-editor@1.0.3(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
chardet: 2.1.1
|
||||||
|
iconv-lite: 0.7.2
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/figures@1.0.10': {}
|
||||||
|
|
||||||
|
'@inquirer/figures@1.0.15': {}
|
||||||
|
|
||||||
|
'@inquirer/input@4.1.6(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/input@4.3.1(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/number@3.0.23(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/number@3.0.9(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/password@4.0.23(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/ansi': 1.0.2
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/password@4.0.9(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/prompts@7.10.1(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/checkbox': 4.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/confirm': 5.1.21(@types/node@22.19.19)
|
||||||
|
'@inquirer/editor': 4.2.23(@types/node@22.19.19)
|
||||||
|
'@inquirer/expand': 4.0.23(@types/node@22.19.19)
|
||||||
|
'@inquirer/input': 4.3.1(@types/node@22.19.19)
|
||||||
|
'@inquirer/number': 3.0.23(@types/node@22.19.19)
|
||||||
|
'@inquirer/password': 4.0.23(@types/node@22.19.19)
|
||||||
|
'@inquirer/rawlist': 4.1.11(@types/node@22.19.19)
|
||||||
|
'@inquirer/search': 3.2.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/select': 4.4.2(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/prompts@7.3.2(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/checkbox': 4.1.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/confirm': 5.1.6(@types/node@22.19.19)
|
||||||
|
'@inquirer/editor': 4.2.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/expand': 4.0.9(@types/node@22.19.19)
|
||||||
|
'@inquirer/input': 4.1.6(@types/node@22.19.19)
|
||||||
|
'@inquirer/number': 3.0.9(@types/node@22.19.19)
|
||||||
|
'@inquirer/password': 4.0.9(@types/node@22.19.19)
|
||||||
|
'@inquirer/rawlist': 4.0.9(@types/node@22.19.19)
|
||||||
|
'@inquirer/search': 3.0.9(@types/node@22.19.19)
|
||||||
|
'@inquirer/select': 4.0.9(@types/node@22.19.19)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/rawlist@4.0.9(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/rawlist@4.1.11(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/search@3.0.9(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/figures': 1.0.10
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/search@3.2.2(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/figures': 1.0.15
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/select@4.0.9(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 10.1.7(@types/node@22.19.19)
|
||||||
|
'@inquirer/figures': 1.0.10
|
||||||
|
'@inquirer/type': 3.0.4(@types/node@22.19.19)
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/select@4.4.2(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/ansi': 1.0.2
|
||||||
|
'@inquirer/core': 10.3.2(@types/node@22.19.19)
|
||||||
|
'@inquirer/figures': 1.0.15
|
||||||
|
'@inquirer/type': 3.0.10(@types/node@22.19.19)
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/type@3.0.10(@types/node@22.19.19)':
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@inquirer/type@3.0.4(@types/node@22.19.19)':
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@intlify/core-base@11.4.4':
|
||||||
|
dependencies:
|
||||||
|
'@intlify/devtools-types': 11.4.4
|
||||||
|
'@intlify/message-compiler': 11.4.4
|
||||||
|
'@intlify/shared': 11.4.4
|
||||||
|
|
||||||
|
'@intlify/devtools-types@11.4.4':
|
||||||
|
dependencies:
|
||||||
|
'@intlify/core-base': 11.4.4
|
||||||
|
'@intlify/shared': 11.4.4
|
||||||
|
|
||||||
|
'@intlify/message-compiler@11.4.4':
|
||||||
|
dependencies:
|
||||||
|
'@intlify/shared': 11.4.4
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
'@intlify/shared@11.4.4': {}
|
||||||
|
|
||||||
|
'@ioredis/commands@1.10.0': {}
|
||||||
|
|
||||||
|
'@istanbuljs/load-nyc-config@1.1.0':
|
||||||
|
dependencies:
|
||||||
|
camelcase: 5.3.1
|
||||||
|
find-up: 4.1.0
|
||||||
|
get-package-type: 0.1.0
|
||||||
|
js-yaml: 3.13.1
|
||||||
|
resolve-from: 5.0.0
|
||||||
|
|
||||||
|
'@istanbuljs/schema@0.1.6': {}
|
||||||
|
|
||||||
|
'@jest/console@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
|
||||||
|
'@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
'@jest/console': 29.7.0
|
||||||
|
'@jest/reporters': 29.7.0
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
ansi-escapes: 4.2.1
|
||||||
|
chalk: 4.1.2
|
||||||
|
ci-info: 3.2.0
|
||||||
|
exit: 0.1.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-changed-files: 29.7.0
|
||||||
|
jest-config: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
jest-haste-map: 29.7.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-resolve: 29.7.0
|
||||||
|
jest-resolve-dependencies: 29.7.0
|
||||||
|
jest-runner: 29.7.0
|
||||||
|
jest-runtime: 29.7.0
|
||||||
|
jest-snapshot: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-validate: 29.7.0
|
||||||
|
jest-watcher: 29.7.0
|
||||||
|
micromatch: 4.0.8
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
strip-ansi: 6.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
- ts-node
|
||||||
|
|
||||||
|
'@jest/environment@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/fake-timers': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
jest-mock: 29.7.0
|
||||||
|
|
||||||
|
'@jest/expect-utils@29.0.0':
|
||||||
|
dependencies:
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
|
||||||
|
'@jest/expect-utils@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
|
||||||
|
'@jest/expect@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
expect: 29.7.0
|
||||||
|
jest-snapshot: 29.7.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@jest/fake-timers@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@sinonjs/fake-timers': 10.0.2
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-mock: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
|
||||||
|
'@jest/globals@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/environment': 29.7.0
|
||||||
|
'@jest/expect': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
jest-mock: 29.7.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@jest/reporters@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@bcoe/v8-coverage': 0.2.3
|
||||||
|
'@jest/console': 29.7.0
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
collect-v8-coverage: 1.0.3
|
||||||
|
exit: 0.1.2
|
||||||
|
glob: 7.2.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
istanbul-lib-coverage: 3.2.2
|
||||||
|
istanbul-lib-instrument: 6.0.3
|
||||||
|
istanbul-lib-report: 3.0.1
|
||||||
|
istanbul-lib-source-maps: 4.0.1
|
||||||
|
istanbul-reports: 3.2.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-worker: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
string-length: 4.0.1
|
||||||
|
strip-ansi: 6.0.0
|
||||||
|
v8-to-istanbul: 9.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@jest/schemas@29.0.0':
|
||||||
|
dependencies:
|
||||||
|
'@sinclair/typebox': 0.24.1
|
||||||
|
|
||||||
|
'@jest/schemas@29.6.3':
|
||||||
|
dependencies:
|
||||||
|
'@sinclair/typebox': 0.27.10
|
||||||
|
|
||||||
|
'@jest/source-map@29.6.3':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
callsites: 3.1.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
|
||||||
|
'@jest/test-result@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/console': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/istanbul-lib-coverage': 2.0.6
|
||||||
|
collect-v8-coverage: 1.0.3
|
||||||
|
|
||||||
|
'@jest/test-sequencer@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-haste-map: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
|
||||||
|
'@jest/transform@29.7.0':
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
babel-plugin-istanbul: 6.1.1
|
||||||
|
chalk: 4.1.2
|
||||||
|
convert-source-map: 2.0.0
|
||||||
|
fast-json-stable-stringify: 2.1.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-haste-map: 29.7.0
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-util: 29.7.0
|
||||||
|
micromatch: 4.0.8
|
||||||
|
pirates: 4.0.7
|
||||||
|
slash: 3.0.0
|
||||||
|
write-file-atomic: 4.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@jest/types@29.0.0':
|
||||||
|
dependencies:
|
||||||
|
'@jest/schemas': 29.0.0
|
||||||
|
'@types/istanbul-lib-coverage': 2.0.6
|
||||||
|
'@types/istanbul-reports': 3.0.4
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
'@types/yargs': 17.0.35
|
||||||
|
chalk: 4.1.2
|
||||||
|
|
||||||
|
'@jest/types@29.6.3':
|
||||||
|
dependencies:
|
||||||
|
'@jest/schemas': 29.6.3
|
||||||
|
'@types/istanbul-lib-coverage': 2.0.6
|
||||||
|
'@types/istanbul-reports': 3.0.4
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
'@types/yargs': 17.0.35
|
||||||
|
chalk: 4.1.2
|
||||||
|
|
||||||
|
'@jridgewell/gen-mapping@0.3.12':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
|
||||||
|
'@jridgewell/gen-mapping@0.3.5':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/set-array': 1.2.1
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.10
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
|
||||||
|
'@jridgewell/remapping@2.3.5':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/gen-mapping': 0.3.5
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
|
||||||
|
'@jridgewell/resolve-uri@3.1.2': {}
|
||||||
|
|
||||||
|
'@jridgewell/set-array@1.2.1': {}
|
||||||
|
|
||||||
|
'@jridgewell/source-map@0.3.11':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/gen-mapping': 0.3.5
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.4.10': {}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.4.14': {}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||||
|
|
||||||
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
|
||||||
|
'@jridgewell/trace-mapping@0.3.9':
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.10
|
||||||
|
|
||||||
|
'@lukeed/csprng@1.1.0': {}
|
||||||
|
|
||||||
|
'@microsoft/tsdoc@0.16.0': {}
|
||||||
|
|
||||||
|
'@nestjs/cli@11.0.21(@types/node@22.19.19)':
|
||||||
|
dependencies:
|
||||||
|
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||||
|
'@angular-devkit/schematics': 19.2.24(chokidar@4.0.3)
|
||||||
|
'@angular-devkit/schematics-cli': 19.2.24(@types/node@22.19.19)(chokidar@4.0.3)
|
||||||
|
'@inquirer/prompts': 7.10.1(@types/node@22.19.19)
|
||||||
|
'@nestjs/schematics': 11.1.0(chokidar@4.0.3)(typescript@5.9.3)
|
||||||
|
ansis: 4.2.0
|
||||||
|
chokidar: 4.0.3
|
||||||
|
cli-table3: 0.6.5
|
||||||
|
commander: 4.1.1
|
||||||
|
fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.106.0)
|
||||||
|
glob: 13.0.6
|
||||||
|
node-emoji: 1.11.0
|
||||||
|
ora: 5.4.1
|
||||||
|
tsconfig-paths: 4.2.0
|
||||||
|
tsconfig-paths-webpack-plugin: 4.2.0
|
||||||
|
typescript: 5.9.3
|
||||||
|
webpack: 5.106.0
|
||||||
|
webpack-node-externals: 3.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@minify-html/node'
|
||||||
|
- '@swc/css'
|
||||||
|
- '@swc/html'
|
||||||
|
- '@types/node'
|
||||||
|
- clean-css
|
||||||
|
- cssnano
|
||||||
|
- csso
|
||||||
|
- esbuild
|
||||||
|
- html-minifier-terser
|
||||||
|
- lightningcss
|
||||||
|
- postcss
|
||||||
|
- prettier
|
||||||
|
- uglify-js
|
||||||
|
- webpack-cli
|
||||||
|
|
||||||
|
'@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
|
||||||
|
dependencies:
|
||||||
|
file-type: 21.3.4
|
||||||
|
iterare: 1.2.1
|
||||||
|
load-esm: 1.0.3
|
||||||
|
reflect-metadata: 0.2.2
|
||||||
|
rxjs: 7.8.2
|
||||||
|
tslib: 2.8.1
|
||||||
|
uid: 2.0.2
|
||||||
|
optionalDependencies:
|
||||||
|
class-transformer: 0.5.1
|
||||||
|
class-validator: 0.14.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@nestjs/config@4.0.4(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
dotenv: 17.4.1
|
||||||
|
dotenv-expand: 12.0.3
|
||||||
|
lodash: 4.18.1
|
||||||
|
rxjs: 7.8.2
|
||||||
|
|
||||||
|
'@nestjs/core@11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nuxt/opencollective': 0.4.1
|
||||||
|
fast-safe-stringify: 2.1.1
|
||||||
|
iterare: 1.2.1
|
||||||
|
path-to-regexp: 8.4.2
|
||||||
|
reflect-metadata: 0.2.2
|
||||||
|
rxjs: 7.8.2
|
||||||
|
tslib: 2.8.1
|
||||||
|
uid: 2.0.2
|
||||||
|
optionalDependencies:
|
||||||
|
'@nestjs/platform-express': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)
|
||||||
|
|
||||||
|
'@nestjs/jwt@11.0.2(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@types/jsonwebtoken': 9.0.10
|
||||||
|
jsonwebtoken: 9.0.3
|
||||||
|
|
||||||
|
'@nestjs/mapped-types@2.1.1(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
reflect-metadata: 0.2.2
|
||||||
|
optionalDependencies:
|
||||||
|
class-transformer: 0.5.1
|
||||||
|
class-validator: 0.14.4
|
||||||
|
|
||||||
|
'@nestjs/passport@11.0.5(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
passport: 0.7.0
|
||||||
|
|
||||||
|
'@nestjs/platform-express@11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/core': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
cors: 2.8.6
|
||||||
|
express: 5.2.1
|
||||||
|
multer: 2.1.1
|
||||||
|
path-to-regexp: 8.4.2
|
||||||
|
tslib: 2.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@nestjs/schedule@5.0.1(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/core': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
cron: 3.5.0
|
||||||
|
|
||||||
|
'@nestjs/schematics@11.1.0(chokidar@4.0.3)(typescript@5.7.3)':
|
||||||
|
dependencies:
|
||||||
|
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||||
|
'@angular-devkit/schematics': 19.2.24(chokidar@4.0.3)
|
||||||
|
comment-json: 5.0.0
|
||||||
|
jsonc-parser: 3.3.1
|
||||||
|
pluralize: 8.0.0
|
||||||
|
typescript: 5.7.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- chokidar
|
||||||
|
|
||||||
|
'@nestjs/schematics@11.1.0(chokidar@4.0.3)(typescript@5.9.3)':
|
||||||
|
dependencies:
|
||||||
|
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||||
|
'@angular-devkit/schematics': 19.2.24(chokidar@4.0.3)
|
||||||
|
comment-json: 5.0.0
|
||||||
|
jsonc-parser: 3.3.1
|
||||||
|
pluralize: 8.0.0
|
||||||
|
typescript: 5.9.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- chokidar
|
||||||
|
|
||||||
|
'@nestjs/swagger@11.4.4(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@microsoft/tsdoc': 0.16.0
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/core': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/mapped-types': 2.1.1(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)
|
||||||
|
js-yaml: 4.1.1
|
||||||
|
lodash: 4.18.1
|
||||||
|
path-to-regexp: 8.4.2
|
||||||
|
reflect-metadata: 0.2.2
|
||||||
|
swagger-ui-dist: 5.32.6
|
||||||
|
optionalDependencies:
|
||||||
|
class-transformer: 0.5.1
|
||||||
|
class-validator: 0.14.4
|
||||||
|
|
||||||
|
'@nestjs/testing@11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)(@nestjs/platform-express@11.1.24)':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
'@nestjs/core': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.24)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@nestjs/platform-express': 11.1.24(@nestjs/common@11.1.24(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.24)
|
||||||
|
|
||||||
|
'@nuxt/opencollective@0.4.1':
|
||||||
|
dependencies:
|
||||||
|
consola: 3.4.2
|
||||||
|
|
||||||
|
'@prisma/client@6.19.3(prisma@6.19.3(typescript@5.7.3))(typescript@5.7.3)':
|
||||||
|
optionalDependencies:
|
||||||
|
prisma: 6.19.3(typescript@5.7.3)
|
||||||
|
typescript: 5.7.3
|
||||||
|
|
||||||
|
'@prisma/config@6.19.3':
|
||||||
|
dependencies:
|
||||||
|
c12: 3.1.0
|
||||||
|
deepmerge-ts: 7.1.5
|
||||||
|
effect: 3.21.0
|
||||||
|
empathic: 2.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- magicast
|
||||||
|
|
||||||
|
'@prisma/debug@6.19.3': {}
|
||||||
|
|
||||||
|
'@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7': {}
|
||||||
|
|
||||||
|
'@prisma/engines@6.19.3':
|
||||||
|
dependencies:
|
||||||
|
'@prisma/debug': 6.19.3
|
||||||
|
'@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||||
|
'@prisma/fetch-engine': 6.19.3
|
||||||
|
'@prisma/get-platform': 6.19.3
|
||||||
|
|
||||||
|
'@prisma/fetch-engine@6.19.3':
|
||||||
|
dependencies:
|
||||||
|
'@prisma/debug': 6.19.3
|
||||||
|
'@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||||
|
'@prisma/get-platform': 6.19.3
|
||||||
|
|
||||||
|
'@prisma/get-platform@6.19.3':
|
||||||
|
dependencies:
|
||||||
|
'@prisma/debug': 6.19.3
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm-eabi@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-arm64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-x64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-arm64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-x64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-musl@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loong64-musl@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-ppc64-musl@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-musl@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-s390x-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-musl@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-openbsd-x64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-openharmony-arm64@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-arm64-msvc@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-ia32-msvc@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-gnu@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-msvc@4.61.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@scarf/scarf@1.4.0': {}
|
||||||
|
|
||||||
|
'@sinclair/typebox@0.24.1': {}
|
||||||
|
|
||||||
|
'@sinclair/typebox@0.27.10': {}
|
||||||
|
|
||||||
|
'@sinonjs/commons@2.0.0':
|
||||||
|
dependencies:
|
||||||
|
type-detect: 4.0.8
|
||||||
|
|
||||||
|
'@sinonjs/fake-timers@10.0.2':
|
||||||
|
dependencies:
|
||||||
|
'@sinonjs/commons': 2.0.0
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.1.0': {}
|
||||||
|
|
||||||
|
'@sxzz/popperjs-es@2.11.8': {}
|
||||||
|
|
||||||
|
'@tokenizer/inflate@0.4.1':
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
token-types: 6.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@tokenizer/token@0.3.0': {}
|
||||||
|
|
||||||
|
'@tsconfig/node10@1.0.12': {}
|
||||||
|
|
||||||
|
'@tsconfig/node12@1.0.11': {}
|
||||||
|
|
||||||
|
'@tsconfig/node14@1.0.3': {}
|
||||||
|
|
||||||
|
'@tsconfig/node16@1.0.4': {}
|
||||||
|
|
||||||
|
'@types/babel__core@7.20.5':
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
'@types/babel__generator': 7.27.0
|
||||||
|
'@types/babel__template': 7.4.4
|
||||||
|
'@types/babel__traverse': 7.28.0
|
||||||
|
|
||||||
|
'@types/babel__generator@7.27.0':
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
|
||||||
|
'@types/babel__template@7.4.4':
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
|
||||||
|
'@types/babel__traverse@7.28.0':
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
|
||||||
|
'@types/bcryptjs@2.4.6': {}
|
||||||
|
|
||||||
|
'@types/body-parser@1.19.6':
|
||||||
|
dependencies:
|
||||||
|
'@types/connect': 3.4.38
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@types/connect@3.4.38':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@types/eslint-scope@3.7.7':
|
||||||
|
dependencies:
|
||||||
|
'@types/eslint': 9.6.1
|
||||||
|
'@types/estree': 1.0.9
|
||||||
|
|
||||||
|
'@types/eslint@9.6.1':
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.9
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
|
||||||
|
'@types/estree@1.0.9': {}
|
||||||
|
|
||||||
|
'@types/express-serve-static-core@5.1.1':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
'@types/qs': 6.15.1
|
||||||
|
'@types/range-parser': 1.2.7
|
||||||
|
'@types/send': 1.2.1
|
||||||
|
|
||||||
|
'@types/express@5.0.6':
|
||||||
|
dependencies:
|
||||||
|
'@types/body-parser': 1.19.6
|
||||||
|
'@types/express-serve-static-core': 5.1.1
|
||||||
|
'@types/serve-static': 2.2.0
|
||||||
|
|
||||||
|
'@types/graceful-fs@4.1.9':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@types/http-errors@2.0.5': {}
|
||||||
|
|
||||||
|
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||||
|
|
||||||
|
'@types/istanbul-lib-report@3.0.3':
|
||||||
|
dependencies:
|
||||||
|
'@types/istanbul-lib-coverage': 2.0.6
|
||||||
|
|
||||||
|
'@types/istanbul-reports@3.0.4':
|
||||||
|
dependencies:
|
||||||
|
'@types/istanbul-lib-report': 3.0.3
|
||||||
|
|
||||||
|
'@types/jest@29.5.14':
|
||||||
|
dependencies:
|
||||||
|
expect: 29.0.0
|
||||||
|
pretty-format: 29.0.0
|
||||||
|
|
||||||
|
'@types/json-schema@7.0.15': {}
|
||||||
|
|
||||||
|
'@types/jsonwebtoken@9.0.10':
|
||||||
|
dependencies:
|
||||||
|
'@types/ms': 2.1.0
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@types/lodash-es@4.17.12':
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.17.24
|
||||||
|
|
||||||
|
'@types/lodash@4.17.24': {}
|
||||||
|
|
||||||
|
'@types/luxon@3.4.2': {}
|
||||||
|
|
||||||
|
'@types/ms@2.1.0': {}
|
||||||
|
|
||||||
|
'@types/node@22.19.19':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 6.21.0
|
||||||
|
|
||||||
|
'@types/passport-jwt@4.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@types/jsonwebtoken': 9.0.10
|
||||||
|
'@types/passport-strategy': 0.2.38
|
||||||
|
|
||||||
|
'@types/passport-strategy@0.2.38':
|
||||||
|
dependencies:
|
||||||
|
'@types/express': 5.0.6
|
||||||
|
'@types/passport': 1.0.17
|
||||||
|
|
||||||
|
'@types/passport@1.0.17':
|
||||||
|
dependencies:
|
||||||
|
'@types/express': 5.0.6
|
||||||
|
|
||||||
|
'@types/qs@6.15.1': {}
|
||||||
|
|
||||||
|
'@types/range-parser@1.2.7': {}
|
||||||
|
|
||||||
|
'@types/send@1.2.1':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@types/serve-static@2.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@types/http-errors': 2.0.5
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
|
||||||
|
'@types/stack-utils@2.0.3': {}
|
||||||
|
|
||||||
|
'@types/uuid@10.0.0': {}
|
||||||
|
|
||||||
|
'@types/validator@13.15.10': {}
|
||||||
|
|
||||||
|
'@types/web-bluetooth@0.0.21': {}
|
||||||
|
|
||||||
|
'@types/yargs-parser@21.0.3': {}
|
||||||
|
|
||||||
|
'@types/yargs@17.0.35':
|
||||||
|
dependencies:
|
||||||
|
'@types/yargs-parser': 21.0.3
|
||||||
|
|
||||||
|
'@vitejs/plugin-vue@5.2.1(vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0))(vue@3.5.35(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
vite: 6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
'@volar/language-core@2.4.28':
|
||||||
|
dependencies:
|
||||||
|
'@volar/source-map': 2.4.28
|
||||||
|
|
||||||
|
'@volar/source-map@2.4.28': {}
|
||||||
|
|
||||||
|
'@volar/typescript@2.4.28':
|
||||||
|
dependencies:
|
||||||
|
'@volar/language-core': 2.4.28
|
||||||
|
path-browserify: 1.0.1
|
||||||
|
vscode-uri: 3.1.0
|
||||||
|
|
||||||
|
'@vue/compiler-core@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
entities: 7.0.1
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
'@vue/compiler-dom@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-core': 3.5.35
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
|
||||||
|
'@vue/compiler-sfc@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@vue/compiler-core': 3.5.35
|
||||||
|
'@vue/compiler-dom': 3.5.35
|
||||||
|
'@vue/compiler-ssr': 3.5.35
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
magic-string: 0.30.21
|
||||||
|
postcss: 8.5.15
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
'@vue/compiler-ssr@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-dom': 3.5.35
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
|
||||||
|
'@vue/compiler-vue2@2.7.16':
|
||||||
|
dependencies:
|
||||||
|
de-indent: 1.0.2
|
||||||
|
he: 1.2.0
|
||||||
|
|
||||||
|
'@vue/devtools-api@6.5.0': {}
|
||||||
|
|
||||||
|
'@vue/devtools-api@6.6.3': {}
|
||||||
|
|
||||||
|
'@vue/devtools-api@6.6.4': {}
|
||||||
|
|
||||||
|
'@vue/language-core@2.2.0(typescript@5.7.3)':
|
||||||
|
dependencies:
|
||||||
|
'@volar/language-core': 2.4.28
|
||||||
|
'@vue/compiler-dom': 3.5.35
|
||||||
|
'@vue/compiler-vue2': 2.7.16
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
alien-signals: 0.4.9
|
||||||
|
minimatch: 9.0.9
|
||||||
|
muggle-string: 0.4.1
|
||||||
|
path-browserify: 1.0.1
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.3
|
||||||
|
|
||||||
|
'@vue/reactivity@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
|
||||||
|
'@vue/runtime-core@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@vue/reactivity': 3.5.35
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
|
||||||
|
'@vue/runtime-dom@3.5.35':
|
||||||
|
dependencies:
|
||||||
|
'@vue/reactivity': 3.5.35
|
||||||
|
'@vue/runtime-core': 3.5.35
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
csstype: 3.2.3
|
||||||
|
|
||||||
|
'@vue/server-renderer@3.5.35(vue@3.5.35(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-ssr': 3.5.35
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
'@vue/shared@3.5.35': {}
|
||||||
|
|
||||||
|
'@vueuse/core@14.3.0(vue@3.5.35(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
'@types/web-bluetooth': 0.0.21
|
||||||
|
'@vueuse/metadata': 14.3.0
|
||||||
|
'@vueuse/shared': 14.3.0(vue@3.5.35(typescript@5.7.3))
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
'@vueuse/metadata@14.3.0': {}
|
||||||
|
|
||||||
|
'@vueuse/shared@14.3.0(vue@3.5.35(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
'@webassemblyjs/ast@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/helper-numbers': 1.13.2
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode': 1.13.2
|
||||||
|
|
||||||
|
'@webassemblyjs/floating-point-hex-parser@1.13.2': {}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-api-error@1.13.2': {}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-buffer@1.14.1': {}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-numbers@1.13.2':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/floating-point-hex-parser': 1.13.2
|
||||||
|
'@webassemblyjs/helper-api-error': 1.13.2
|
||||||
|
'@xtuc/long': 4.2.2
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode@1.13.2': {}
|
||||||
|
|
||||||
|
'@webassemblyjs/helper-wasm-section@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/helper-buffer': 1.14.1
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode': 1.13.2
|
||||||
|
'@webassemblyjs/wasm-gen': 1.14.1
|
||||||
|
|
||||||
|
'@webassemblyjs/ieee754@1.13.2':
|
||||||
|
dependencies:
|
||||||
|
'@xtuc/ieee754': 1.2.0
|
||||||
|
|
||||||
|
'@webassemblyjs/leb128@1.13.2':
|
||||||
|
dependencies:
|
||||||
|
'@xtuc/long': 4.2.2
|
||||||
|
|
||||||
|
'@webassemblyjs/utf8@1.13.2': {}
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-edit@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/helper-buffer': 1.14.1
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode': 1.13.2
|
||||||
|
'@webassemblyjs/helper-wasm-section': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-gen': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-opt': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-parser': 1.14.1
|
||||||
|
'@webassemblyjs/wast-printer': 1.14.1
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-gen@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode': 1.13.2
|
||||||
|
'@webassemblyjs/ieee754': 1.13.2
|
||||||
|
'@webassemblyjs/leb128': 1.13.2
|
||||||
|
'@webassemblyjs/utf8': 1.13.2
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-opt@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/helper-buffer': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-gen': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-parser': 1.14.1
|
||||||
|
|
||||||
|
'@webassemblyjs/wasm-parser@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/helper-api-error': 1.13.2
|
||||||
|
'@webassemblyjs/helper-wasm-bytecode': 1.13.2
|
||||||
|
'@webassemblyjs/ieee754': 1.13.2
|
||||||
|
'@webassemblyjs/leb128': 1.13.2
|
||||||
|
'@webassemblyjs/utf8': 1.13.2
|
||||||
|
|
||||||
|
'@webassemblyjs/wast-printer@1.14.1':
|
||||||
|
dependencies:
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@xtuc/long': 4.2.2
|
||||||
|
|
||||||
|
'@xtuc/ieee754@1.2.0': {}
|
||||||
|
|
||||||
|
'@xtuc/long@4.2.2': {}
|
||||||
|
|
||||||
|
accepts@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
mime-types: 3.0.2
|
||||||
|
negotiator: 1.0.0
|
||||||
|
|
||||||
|
acorn-import-phases@1.0.4(acorn@8.16.0):
|
||||||
|
dependencies:
|
||||||
|
acorn: 8.16.0
|
||||||
|
|
||||||
|
acorn-walk@8.3.5:
|
||||||
|
dependencies:
|
||||||
|
acorn: 8.16.0
|
||||||
|
|
||||||
|
acorn@8.16.0: {}
|
||||||
|
|
||||||
|
agent-base@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
ajv-formats@2.1.1(ajv@8.20.0):
|
||||||
|
optionalDependencies:
|
||||||
|
ajv: 8.20.0
|
||||||
|
|
||||||
|
ajv-formats@3.0.1(ajv@8.18.0):
|
||||||
|
optionalDependencies:
|
||||||
|
ajv: 8.18.0
|
||||||
|
|
||||||
|
ajv-keywords@3.5.2(ajv@6.15.0):
|
||||||
|
dependencies:
|
||||||
|
ajv: 6.15.0
|
||||||
|
|
||||||
|
ajv-keywords@5.1.0(ajv@8.20.0):
|
||||||
|
dependencies:
|
||||||
|
ajv: 8.20.0
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
|
||||||
|
ajv@6.15.0:
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
fast-json-stable-stringify: 2.1.0
|
||||||
|
json-schema-traverse: 0.4.1
|
||||||
|
uri-js: 4.4.1
|
||||||
|
|
||||||
|
ajv@8.18.0:
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
fast-uri: 3.1.2
|
||||||
|
json-schema-traverse: 1.0.0
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
|
ajv@8.20.0:
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
fast-uri: 3.1.2
|
||||||
|
json-schema-traverse: 1.0.0
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
|
alien-signals@0.4.9: {}
|
||||||
|
|
||||||
|
ansi-colors@4.1.3: {}
|
||||||
|
|
||||||
|
ansi-escapes@4.2.1:
|
||||||
|
dependencies:
|
||||||
|
type-fest: 0.5.2
|
||||||
|
|
||||||
|
ansi-escapes@4.3.2:
|
||||||
|
dependencies:
|
||||||
|
type-fest: 0.21.3
|
||||||
|
|
||||||
|
ansi-regex@4.1.1: {}
|
||||||
|
|
||||||
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
|
ansi-styles@4.3.0:
|
||||||
|
dependencies:
|
||||||
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
ansi-styles@5.2.0: {}
|
||||||
|
|
||||||
|
ansis@4.2.0: {}
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
dependencies:
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
picomatch: 2.3.2
|
||||||
|
|
||||||
|
append-field@1.0.0: {}
|
||||||
|
|
||||||
|
arg@4.1.3: {}
|
||||||
|
|
||||||
|
argparse@1.0.10:
|
||||||
|
dependencies:
|
||||||
|
sprintf-js: 1.0.3
|
||||||
|
|
||||||
|
argparse@2.0.1: {}
|
||||||
|
|
||||||
|
array-timsort@1.0.3: {}
|
||||||
|
|
||||||
|
async-validator@4.2.5: {}
|
||||||
|
|
||||||
|
asynckit@0.4.0: {}
|
||||||
|
|
||||||
|
axios@1.16.1:
|
||||||
|
dependencies:
|
||||||
|
follow-redirects: 1.16.0
|
||||||
|
form-data: 4.0.5
|
||||||
|
https-proxy-agent: 5.0.1
|
||||||
|
proxy-from-env: 2.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
babel-jest@29.7.0(@babel/core@7.29.7):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@types/babel__core': 7.20.5
|
||||||
|
babel-plugin-istanbul: 6.1.1
|
||||||
|
babel-preset-jest: 29.6.3(@babel/core@7.29.7)
|
||||||
|
chalk: 4.1.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
slash: 3.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
babel-plugin-istanbul@6.1.1:
|
||||||
|
dependencies:
|
||||||
|
'@babel/helper-plugin-utils': 7.29.7
|
||||||
|
'@istanbuljs/load-nyc-config': 1.1.0
|
||||||
|
'@istanbuljs/schema': 0.1.6
|
||||||
|
istanbul-lib-instrument: 5.2.1
|
||||||
|
test-exclude: 6.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
babel-plugin-jest-hoist@29.6.3:
|
||||||
|
dependencies:
|
||||||
|
'@babel/template': 7.29.7
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
'@types/babel__core': 7.20.5
|
||||||
|
'@types/babel__traverse': 7.28.0
|
||||||
|
|
||||||
|
babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7)
|
||||||
|
|
||||||
|
babel-preset-jest@29.6.3(@babel/core@7.29.7):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
babel-plugin-jest-hoist: 29.6.3
|
||||||
|
babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7)
|
||||||
|
|
||||||
|
balanced-match@1.0.0: {}
|
||||||
|
|
||||||
|
balanced-match@4.0.4: {}
|
||||||
|
|
||||||
|
base64-js@1.5.1: {}
|
||||||
|
|
||||||
|
baseline-browser-mapping@2.10.33: {}
|
||||||
|
|
||||||
|
bcryptjs@2.4.3: {}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
buffer: 5.7.1
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
|
body-parser@2.2.2:
|
||||||
|
dependencies:
|
||||||
|
bytes: 3.1.2
|
||||||
|
content-type: 1.0.5
|
||||||
|
debug: 4.4.3
|
||||||
|
http-errors: 2.0.1
|
||||||
|
iconv-lite: 0.7.2
|
||||||
|
on-finished: 2.4.1
|
||||||
|
qs: 6.15.2
|
||||||
|
raw-body: 3.0.2
|
||||||
|
type-is: 2.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
brace-expansion@1.1.15:
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 1.0.0
|
||||||
|
concat-map: 0.0.1
|
||||||
|
|
||||||
|
brace-expansion@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 1.0.0
|
||||||
|
|
||||||
|
brace-expansion@5.0.6:
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 4.0.4
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
fill-range: 7.1.1
|
||||||
|
|
||||||
|
browserslist@4.28.2:
|
||||||
|
dependencies:
|
||||||
|
baseline-browser-mapping: 2.10.33
|
||||||
|
caniuse-lite: 1.0.30001793
|
||||||
|
electron-to-chromium: 1.5.364
|
||||||
|
node-releases: 2.0.46
|
||||||
|
update-browserslist-db: 1.2.3(browserslist@4.28.2)
|
||||||
|
|
||||||
|
bs-logger@0.2.6:
|
||||||
|
dependencies:
|
||||||
|
fast-json-stable-stringify: 2.1.0
|
||||||
|
|
||||||
|
bser@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
node-int64: 0.4.0
|
||||||
|
|
||||||
|
buffer-equal-constant-time@1.0.1: {}
|
||||||
|
|
||||||
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
ieee754: 1.2.1
|
||||||
|
|
||||||
|
busboy@1.6.0:
|
||||||
|
dependencies:
|
||||||
|
streamsearch: 1.1.0
|
||||||
|
|
||||||
|
bytes@3.1.2: {}
|
||||||
|
|
||||||
|
c12@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
chokidar: 4.0.3
|
||||||
|
confbox: 0.2.4
|
||||||
|
defu: 6.1.7
|
||||||
|
dotenv: 16.6.1
|
||||||
|
exsolve: 1.0.8
|
||||||
|
giget: 2.0.0
|
||||||
|
jiti: 2.7.0
|
||||||
|
ohash: 2.0.11
|
||||||
|
pathe: 2.0.3
|
||||||
|
perfect-debounce: 1.0.0
|
||||||
|
pkg-types: 2.3.1
|
||||||
|
rc9: 2.1.2
|
||||||
|
|
||||||
|
call-bind-apply-helpers@1.0.2:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
call-bound@1.0.4:
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
|
callsites@3.1.0: {}
|
||||||
|
|
||||||
|
camelcase@5.3.1: {}
|
||||||
|
|
||||||
|
camelcase@6.3.0: {}
|
||||||
|
|
||||||
|
caniuse-lite@1.0.30001793: {}
|
||||||
|
|
||||||
|
chalk@4.1.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
char-regex@1.0.2: {}
|
||||||
|
|
||||||
|
chardet@0.7.0: {}
|
||||||
|
|
||||||
|
chardet@2.1.1: {}
|
||||||
|
|
||||||
|
chokidar@4.0.3:
|
||||||
|
dependencies:
|
||||||
|
readdirp: 4.1.2
|
||||||
|
|
||||||
|
chrome-trace-event@1.0.4: {}
|
||||||
|
|
||||||
|
ci-info@3.2.0: {}
|
||||||
|
|
||||||
|
citty@0.1.6:
|
||||||
|
dependencies:
|
||||||
|
consola: 3.4.2
|
||||||
|
|
||||||
|
citty@0.2.2: {}
|
||||||
|
|
||||||
|
cjs-module-lexer@1.0.0: {}
|
||||||
|
|
||||||
|
class-transformer@0.5.1: {}
|
||||||
|
|
||||||
|
class-validator@0.14.4:
|
||||||
|
dependencies:
|
||||||
|
'@types/validator': 13.15.10
|
||||||
|
libphonenumber-js: 1.13.4
|
||||||
|
validator: 13.15.35
|
||||||
|
|
||||||
|
cli-cursor@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
restore-cursor: 3.1.0
|
||||||
|
|
||||||
|
cli-spinners@2.5.0: {}
|
||||||
|
|
||||||
|
cli-table3@0.6.5:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@colors/colors': 1.5.0
|
||||||
|
|
||||||
|
cli-width@4.1.0: {}
|
||||||
|
|
||||||
|
cliui@8.0.1:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
wrap-ansi: 7.0.0
|
||||||
|
|
||||||
|
clone@1.0.4: {}
|
||||||
|
|
||||||
|
cluster-key-slot@1.1.1: {}
|
||||||
|
|
||||||
|
co@4.6.0: {}
|
||||||
|
|
||||||
|
collect-v8-coverage@1.0.3: {}
|
||||||
|
|
||||||
|
color-convert@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
color-name: 1.1.4
|
||||||
|
|
||||||
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
|
combined-stream@1.0.8:
|
||||||
|
dependencies:
|
||||||
|
delayed-stream: 1.0.0
|
||||||
|
|
||||||
|
commander@2.20.0: {}
|
||||||
|
|
||||||
|
commander@4.1.1: {}
|
||||||
|
|
||||||
|
comment-json@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
array-timsort: 1.0.3
|
||||||
|
esprima: 4.0.1
|
||||||
|
|
||||||
|
concat-map@0.0.1: {}
|
||||||
|
|
||||||
|
concat-stream@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
typedarray: 0.0.6
|
||||||
|
|
||||||
|
confbox@0.2.4: {}
|
||||||
|
|
||||||
|
consola@3.4.2: {}
|
||||||
|
|
||||||
|
content-disposition@1.1.0: {}
|
||||||
|
|
||||||
|
content-type@1.0.5: {}
|
||||||
|
|
||||||
|
content-type@2.0.0: {}
|
||||||
|
|
||||||
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
|
cookie-signature@1.2.2: {}
|
||||||
|
|
||||||
|
cookie@0.7.2: {}
|
||||||
|
|
||||||
|
cors@2.8.6:
|
||||||
|
dependencies:
|
||||||
|
object-assign: 4.1.1
|
||||||
|
vary: 1.1.2
|
||||||
|
|
||||||
|
cosmiconfig@8.2.0:
|
||||||
|
dependencies:
|
||||||
|
import-fresh: 3.2.1
|
||||||
|
js-yaml: 4.2.0
|
||||||
|
parse-json: 5.2.0
|
||||||
|
path-type: 4.0.0
|
||||||
|
|
||||||
|
create-jest@29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
chalk: 4.1.2
|
||||||
|
exit: 0.1.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-config: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
jest-util: 29.7.0
|
||||||
|
prompts: 2.4.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
- ts-node
|
||||||
|
|
||||||
|
create-require@1.1.1: {}
|
||||||
|
|
||||||
|
cron@3.5.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/luxon': 3.4.2
|
||||||
|
luxon: 3.5.0
|
||||||
|
|
||||||
|
cross-spawn@7.0.6:
|
||||||
|
dependencies:
|
||||||
|
path-key: 3.1.1
|
||||||
|
shebang-command: 2.0.0
|
||||||
|
which: 2.0.1
|
||||||
|
|
||||||
|
csstype@3.2.3: {}
|
||||||
|
|
||||||
|
dayjs@1.11.21: {}
|
||||||
|
|
||||||
|
de-indent@1.0.2: {}
|
||||||
|
|
||||||
|
debug@4.4.3:
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decimal.js@10.6.0: {}
|
||||||
|
|
||||||
|
dedent@1.7.2: {}
|
||||||
|
|
||||||
|
deepmerge-ts@7.1.5: {}
|
||||||
|
|
||||||
|
deepmerge@4.3.1: {}
|
||||||
|
|
||||||
|
defaults@1.0.4:
|
||||||
|
dependencies:
|
||||||
|
clone: 1.0.4
|
||||||
|
|
||||||
|
defu@6.1.7: {}
|
||||||
|
|
||||||
|
delayed-stream@1.0.0: {}
|
||||||
|
|
||||||
|
denque@2.1.0: {}
|
||||||
|
|
||||||
|
depd@2.0.0: {}
|
||||||
|
|
||||||
|
destr@2.0.5: {}
|
||||||
|
|
||||||
|
detect-newline@3.1.0: {}
|
||||||
|
|
||||||
|
diff-sequences@29.6.3: {}
|
||||||
|
|
||||||
|
diff@4.0.4: {}
|
||||||
|
|
||||||
|
dotenv-expand@12.0.3:
|
||||||
|
dependencies:
|
||||||
|
dotenv: 16.4.5
|
||||||
|
|
||||||
|
dotenv@16.4.5: {}
|
||||||
|
|
||||||
|
dotenv@16.6.1: {}
|
||||||
|
|
||||||
|
dotenv@17.4.1: {}
|
||||||
|
|
||||||
|
dunder-proto@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
es-errors: 1.3.0
|
||||||
|
gopd: 1.2.0
|
||||||
|
|
||||||
|
ecdsa-sig-formatter@1.0.11:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
ee-first@1.1.1: {}
|
||||||
|
|
||||||
|
effect@3.21.0:
|
||||||
|
dependencies:
|
||||||
|
'@standard-schema/spec': 1.1.0
|
||||||
|
fast-check: 3.23.1
|
||||||
|
|
||||||
|
electron-to-chromium@1.5.364: {}
|
||||||
|
|
||||||
|
element-plus@2.14.1(vue@3.5.35(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@ctrl/tinycolor': 4.2.0
|
||||||
|
'@element-plus/icons-vue': 2.3.2(vue@3.5.35(typescript@5.7.3))
|
||||||
|
'@floating-ui/dom': 1.7.6
|
||||||
|
'@popperjs/core': '@sxzz/popperjs-es@2.11.8'
|
||||||
|
'@types/lodash': 4.17.24
|
||||||
|
'@types/lodash-es': 4.17.12
|
||||||
|
'@vueuse/core': 14.3.0(vue@3.5.35(typescript@5.7.3))
|
||||||
|
async-validator: 4.2.5
|
||||||
|
dayjs: 1.11.21
|
||||||
|
lodash: 4.18.1
|
||||||
|
lodash-es: 4.18.1
|
||||||
|
lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.18.1)(lodash@4.18.1)
|
||||||
|
memoize-one: 6.0.0
|
||||||
|
normalize-wheel-es: 1.2.0
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
vue-component-type-helpers: 3.3.3
|
||||||
|
|
||||||
|
emittery@0.13.1: {}
|
||||||
|
|
||||||
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
|
empathic@2.0.0: {}
|
||||||
|
|
||||||
|
encodeurl@2.0.0: {}
|
||||||
|
|
||||||
|
enhanced-resolve@5.22.1:
|
||||||
|
dependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
tapable: 2.3.3
|
||||||
|
|
||||||
|
entities@7.0.1: {}
|
||||||
|
|
||||||
|
error-ex@1.3.4:
|
||||||
|
dependencies:
|
||||||
|
is-arrayish: 0.2.1
|
||||||
|
|
||||||
|
es-define-property@1.0.1: {}
|
||||||
|
|
||||||
|
es-errors@1.3.0: {}
|
||||||
|
|
||||||
|
es-module-lexer@2.1.0: {}
|
||||||
|
|
||||||
|
es-object-atoms@1.1.2:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
|
||||||
|
es-set-tostringtag@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
get-intrinsic: 1.3.0
|
||||||
|
has-tostringtag: 1.0.2
|
||||||
|
hasown: 2.0.4
|
||||||
|
|
||||||
|
esbuild@0.25.0:
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/aix-ppc64': 0.25.0
|
||||||
|
'@esbuild/android-arm': 0.25.0
|
||||||
|
'@esbuild/android-arm64': 0.25.0
|
||||||
|
'@esbuild/android-x64': 0.25.0
|
||||||
|
'@esbuild/darwin-arm64': 0.25.0
|
||||||
|
'@esbuild/darwin-x64': 0.25.0
|
||||||
|
'@esbuild/freebsd-arm64': 0.25.0
|
||||||
|
'@esbuild/freebsd-x64': 0.25.0
|
||||||
|
'@esbuild/linux-arm': 0.25.0
|
||||||
|
'@esbuild/linux-arm64': 0.25.0
|
||||||
|
'@esbuild/linux-ia32': 0.25.0
|
||||||
|
'@esbuild/linux-loong64': 0.25.0
|
||||||
|
'@esbuild/linux-mips64el': 0.25.0
|
||||||
|
'@esbuild/linux-ppc64': 0.25.0
|
||||||
|
'@esbuild/linux-riscv64': 0.25.0
|
||||||
|
'@esbuild/linux-s390x': 0.25.0
|
||||||
|
'@esbuild/linux-x64': 0.25.0
|
||||||
|
'@esbuild/netbsd-arm64': 0.25.0
|
||||||
|
'@esbuild/netbsd-x64': 0.25.0
|
||||||
|
'@esbuild/openbsd-arm64': 0.25.0
|
||||||
|
'@esbuild/openbsd-x64': 0.25.0
|
||||||
|
'@esbuild/sunos-x64': 0.25.0
|
||||||
|
'@esbuild/win32-arm64': 0.25.0
|
||||||
|
'@esbuild/win32-ia32': 0.25.0
|
||||||
|
'@esbuild/win32-x64': 0.25.0
|
||||||
|
|
||||||
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
|
escape-html@1.0.3: {}
|
||||||
|
|
||||||
|
escape-string-regexp@2.0.0: {}
|
||||||
|
|
||||||
|
eslint-scope@5.1.1:
|
||||||
|
dependencies:
|
||||||
|
esrecurse: 4.3.0
|
||||||
|
estraverse: 4.3.0
|
||||||
|
|
||||||
|
esprima@4.0.1: {}
|
||||||
|
|
||||||
|
esrecurse@4.3.0:
|
||||||
|
dependencies:
|
||||||
|
estraverse: 5.3.0
|
||||||
|
|
||||||
|
estraverse@4.3.0: {}
|
||||||
|
|
||||||
|
estraverse@5.3.0: {}
|
||||||
|
|
||||||
|
estree-walker@2.0.2: {}
|
||||||
|
|
||||||
|
etag@1.8.1: {}
|
||||||
|
|
||||||
|
events@3.3.0: {}
|
||||||
|
|
||||||
|
execa@5.1.1:
|
||||||
|
dependencies:
|
||||||
|
cross-spawn: 7.0.6
|
||||||
|
get-stream: 6.0.1
|
||||||
|
human-signals: 2.1.0
|
||||||
|
is-stream: 2.0.1
|
||||||
|
merge-stream: 2.0.0
|
||||||
|
npm-run-path: 4.0.1
|
||||||
|
onetime: 5.1.2
|
||||||
|
signal-exit: 3.0.7
|
||||||
|
strip-final-newline: 2.0.0
|
||||||
|
|
||||||
|
exit@0.1.2: {}
|
||||||
|
|
||||||
|
expect@29.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/expect-utils': 29.0.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
jest-matcher-utils: 29.0.0
|
||||||
|
jest-message-util: 29.0.0
|
||||||
|
jest-util: 29.0.0
|
||||||
|
|
||||||
|
expect@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/expect-utils': 29.7.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
jest-matcher-utils: 29.7.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
|
||||||
|
express@5.2.1:
|
||||||
|
dependencies:
|
||||||
|
accepts: 2.0.0
|
||||||
|
body-parser: 2.2.2
|
||||||
|
content-disposition: 1.1.0
|
||||||
|
content-type: 1.0.5
|
||||||
|
cookie: 0.7.2
|
||||||
|
cookie-signature: 1.2.2
|
||||||
|
debug: 4.4.3
|
||||||
|
depd: 2.0.0
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
etag: 1.8.1
|
||||||
|
finalhandler: 2.1.1
|
||||||
|
fresh: 2.0.0
|
||||||
|
http-errors: 2.0.1
|
||||||
|
merge-descriptors: 2.0.0
|
||||||
|
mime-types: 3.0.2
|
||||||
|
on-finished: 2.4.1
|
||||||
|
once: 1.4.0
|
||||||
|
parseurl: 1.3.3
|
||||||
|
proxy-addr: 2.0.7
|
||||||
|
qs: 6.15.2
|
||||||
|
range-parser: 1.2.1
|
||||||
|
router: 2.2.0
|
||||||
|
send: 1.2.1
|
||||||
|
serve-static: 2.2.1
|
||||||
|
statuses: 2.0.2
|
||||||
|
type-is: 2.1.0
|
||||||
|
vary: 1.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
exsolve@1.0.8: {}
|
||||||
|
|
||||||
|
external-editor@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
chardet: 0.7.0
|
||||||
|
iconv-lite: 0.4.24
|
||||||
|
tmp: 0.0.33
|
||||||
|
|
||||||
|
fast-check@3.23.1:
|
||||||
|
dependencies:
|
||||||
|
pure-rand: 6.1.0
|
||||||
|
|
||||||
|
fast-deep-equal@3.1.3: {}
|
||||||
|
|
||||||
|
fast-json-stable-stringify@2.1.0: {}
|
||||||
|
|
||||||
|
fast-safe-stringify@2.1.1: {}
|
||||||
|
|
||||||
|
fast-uri@3.1.2: {}
|
||||||
|
|
||||||
|
fb-watchman@2.0.2:
|
||||||
|
dependencies:
|
||||||
|
bser: 2.1.1
|
||||||
|
|
||||||
|
fdir@6.5.0(picomatch@4.0.4):
|
||||||
|
optionalDependencies:
|
||||||
|
picomatch: 4.0.4
|
||||||
|
|
||||||
|
file-type@21.3.4:
|
||||||
|
dependencies:
|
||||||
|
'@tokenizer/inflate': 0.4.1
|
||||||
|
strtok3: 10.3.5
|
||||||
|
token-types: 6.1.2
|
||||||
|
uint8array-extras: 1.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
dependencies:
|
||||||
|
to-regex-range: 5.0.1
|
||||||
|
|
||||||
|
finalhandler@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
on-finished: 2.4.1
|
||||||
|
parseurl: 1.3.3
|
||||||
|
statuses: 2.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
find-up@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
locate-path: 5.0.0
|
||||||
|
path-exists: 4.0.0
|
||||||
|
|
||||||
|
follow-redirects@1.16.0: {}
|
||||||
|
|
||||||
|
fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.106.0):
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
chalk: 4.1.2
|
||||||
|
chokidar: 4.0.3
|
||||||
|
cosmiconfig: 8.2.0
|
||||||
|
deepmerge: 4.3.1
|
||||||
|
fs-extra: 10.0.0
|
||||||
|
memfs: 3.4.1
|
||||||
|
minimatch: 3.1.5
|
||||||
|
node-abort-controller: 3.1.1
|
||||||
|
schema-utils: 3.3.0
|
||||||
|
semver: 7.8.1
|
||||||
|
tapable: 2.3.3
|
||||||
|
typescript: 5.9.3
|
||||||
|
webpack: 5.106.0
|
||||||
|
|
||||||
|
form-data@4.0.5:
|
||||||
|
dependencies:
|
||||||
|
asynckit: 0.4.0
|
||||||
|
combined-stream: 1.0.8
|
||||||
|
es-set-tostringtag: 2.1.0
|
||||||
|
hasown: 2.0.4
|
||||||
|
mime-types: 2.1.35
|
||||||
|
|
||||||
|
forwarded@0.2.0: {}
|
||||||
|
|
||||||
|
fresh@2.0.0: {}
|
||||||
|
|
||||||
|
fs-extra@10.0.0:
|
||||||
|
dependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jsonfile: 6.2.1
|
||||||
|
universalify: 2.0.1
|
||||||
|
|
||||||
|
fs-monkey@1.0.3: {}
|
||||||
|
|
||||||
|
fs.realpath@1.0.0: {}
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
|
get-intrinsic@1.3.0:
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
es-define-property: 1.0.1
|
||||||
|
es-errors: 1.3.0
|
||||||
|
es-object-atoms: 1.1.2
|
||||||
|
function-bind: 1.1.2
|
||||||
|
get-proto: 1.0.1
|
||||||
|
gopd: 1.2.0
|
||||||
|
has-symbols: 1.1.0
|
||||||
|
hasown: 2.0.4
|
||||||
|
math-intrinsics: 1.1.0
|
||||||
|
|
||||||
|
get-package-type@0.1.0: {}
|
||||||
|
|
||||||
|
get-proto@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
dunder-proto: 1.0.1
|
||||||
|
es-object-atoms: 1.1.2
|
||||||
|
|
||||||
|
get-stream@6.0.1: {}
|
||||||
|
|
||||||
|
giget@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
citty: 0.1.6
|
||||||
|
consola: 3.4.2
|
||||||
|
defu: 6.1.7
|
||||||
|
node-fetch-native: 1.6.7
|
||||||
|
nypm: 0.6.6
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
glob-to-regexp@0.4.1: {}
|
||||||
|
|
||||||
|
glob@13.0.6:
|
||||||
|
dependencies:
|
||||||
|
minimatch: 10.2.5
|
||||||
|
minipass: 7.1.3
|
||||||
|
path-scurry: 2.0.2
|
||||||
|
|
||||||
|
glob@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
fs.realpath: 1.0.0
|
||||||
|
inflight: 1.0.6
|
||||||
|
inherits: 2.0.4
|
||||||
|
minimatch: 3.1.5
|
||||||
|
once: 1.4.0
|
||||||
|
path-is-absolute: 1.0.1
|
||||||
|
|
||||||
|
gopd@1.2.0: {}
|
||||||
|
|
||||||
|
graceful-fs@4.2.11: {}
|
||||||
|
|
||||||
|
handlebars@4.7.9:
|
||||||
|
dependencies:
|
||||||
|
minimist: 1.2.8
|
||||||
|
neo-async: 2.6.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
wordwrap: 1.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
uglify-js: 3.19.3
|
||||||
|
|
||||||
|
has-flag@4.0.0: {}
|
||||||
|
|
||||||
|
has-symbols@1.1.0: {}
|
||||||
|
|
||||||
|
has-tostringtag@1.0.2:
|
||||||
|
dependencies:
|
||||||
|
has-symbols: 1.1.0
|
||||||
|
|
||||||
|
hasown@2.0.4:
|
||||||
|
dependencies:
|
||||||
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
he@1.2.0: {}
|
||||||
|
|
||||||
|
html-escaper@2.0.2: {}
|
||||||
|
|
||||||
|
http-errors@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
depd: 2.0.0
|
||||||
|
inherits: 2.0.4
|
||||||
|
setprototypeof: 1.2.0
|
||||||
|
statuses: 2.0.2
|
||||||
|
toidentifier: 1.0.1
|
||||||
|
|
||||||
|
https-proxy-agent@5.0.1:
|
||||||
|
dependencies:
|
||||||
|
agent-base: 6.0.0
|
||||||
|
debug: 4.4.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
human-signals@2.1.0: {}
|
||||||
|
|
||||||
|
iconv-lite@0.4.24:
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
|
iconv-lite@0.7.2:
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
|
ieee754@1.2.1: {}
|
||||||
|
|
||||||
|
import-fresh@3.2.1:
|
||||||
|
dependencies:
|
||||||
|
parent-module: 1.0.1
|
||||||
|
resolve-from: 4.0.0
|
||||||
|
|
||||||
|
import-local@3.2.0:
|
||||||
|
dependencies:
|
||||||
|
pkg-dir: 4.2.0
|
||||||
|
resolve-cwd: 3.0.0
|
||||||
|
|
||||||
|
imurmurhash@0.1.4: {}
|
||||||
|
|
||||||
|
inflight@1.0.6:
|
||||||
|
dependencies:
|
||||||
|
once: 1.4.0
|
||||||
|
wrappy: 1.0.2
|
||||||
|
|
||||||
|
inherits@2.0.4: {}
|
||||||
|
|
||||||
|
ioredis@5.11.0:
|
||||||
|
dependencies:
|
||||||
|
'@ioredis/commands': 1.10.0
|
||||||
|
cluster-key-slot: 1.1.1
|
||||||
|
debug: 4.4.3
|
||||||
|
denque: 2.1.0
|
||||||
|
redis-errors: 1.2.0
|
||||||
|
redis-parser: 3.0.0
|
||||||
|
standard-as-callback: 2.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
ipaddr.js@1.9.1: {}
|
||||||
|
|
||||||
|
is-arrayish@0.2.1: {}
|
||||||
|
|
||||||
|
is-core-module@2.16.2:
|
||||||
|
dependencies:
|
||||||
|
hasown: 2.0.4
|
||||||
|
|
||||||
|
is-fullwidth-code-point@3.0.0: {}
|
||||||
|
|
||||||
|
is-generator-fn@2.1.0: {}
|
||||||
|
|
||||||
|
is-interactive@1.0.0: {}
|
||||||
|
|
||||||
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
|
is-promise@4.0.0: {}
|
||||||
|
|
||||||
|
is-stream@2.0.1: {}
|
||||||
|
|
||||||
|
is-unicode-supported@0.1.0: {}
|
||||||
|
|
||||||
|
isexe@2.0.0: {}
|
||||||
|
|
||||||
|
istanbul-lib-coverage@3.2.2: {}
|
||||||
|
|
||||||
|
istanbul-lib-instrument@5.2.1:
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@istanbuljs/schema': 0.1.6
|
||||||
|
istanbul-lib-coverage: 3.2.2
|
||||||
|
semver: 6.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
istanbul-lib-instrument@6.0.3:
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/parser': 7.29.7
|
||||||
|
'@istanbuljs/schema': 0.1.6
|
||||||
|
istanbul-lib-coverage: 3.2.2
|
||||||
|
semver: 7.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
istanbul-lib-report@3.0.1:
|
||||||
|
dependencies:
|
||||||
|
istanbul-lib-coverage: 3.2.2
|
||||||
|
make-dir: 4.0.0
|
||||||
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
istanbul-lib-source-maps@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
istanbul-lib-coverage: 3.2.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
istanbul-reports@3.2.0:
|
||||||
|
dependencies:
|
||||||
|
html-escaper: 2.0.2
|
||||||
|
istanbul-lib-report: 3.0.1
|
||||||
|
|
||||||
|
iterare@1.2.1: {}
|
||||||
|
|
||||||
|
jest-changed-files@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
execa: 5.1.1
|
||||||
|
jest-util: 29.7.0
|
||||||
|
p-limit: 3.1.0
|
||||||
|
|
||||||
|
jest-circus@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/environment': 29.7.0
|
||||||
|
'@jest/expect': 29.7.0
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
co: 4.6.0
|
||||||
|
dedent: 1.7.2
|
||||||
|
is-generator-fn: 2.1.0
|
||||||
|
jest-each: 29.7.0
|
||||||
|
jest-matcher-utils: 29.7.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-runtime: 29.7.0
|
||||||
|
jest-snapshot: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
p-limit: 3.1.0
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
pure-rand: 6.0.0
|
||||||
|
slash: 3.0.0
|
||||||
|
stack-utils: 2.0.6
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
jest-cli@29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
chalk: 4.1.2
|
||||||
|
create-jest: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
exit: 0.1.2
|
||||||
|
import-local: 3.2.0
|
||||||
|
jest-config: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-validate: 29.7.0
|
||||||
|
yargs: 17.7.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
- ts-node
|
||||||
|
|
||||||
|
jest-config@29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@jest/test-sequencer': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
babel-jest: 29.7.0(@babel/core@7.29.7)
|
||||||
|
chalk: 4.1.2
|
||||||
|
ci-info: 3.2.0
|
||||||
|
deepmerge: 4.3.1
|
||||||
|
glob: 7.2.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-circus: 29.7.0
|
||||||
|
jest-environment-node: 29.7.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-resolve: 29.7.0
|
||||||
|
jest-runner: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-validate: 29.7.0
|
||||||
|
micromatch: 4.0.8
|
||||||
|
parse-json: 5.2.0
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
strip-json-comments: 3.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
ts-node: 10.9.2(@types/node@22.19.19)(typescript@5.7.3)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
jest-diff@29.0.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
diff-sequences: 29.6.3
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
pretty-format: 29.0.0
|
||||||
|
|
||||||
|
jest-diff@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
diff-sequences: 29.6.3
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
|
||||||
|
jest-docblock@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
detect-newline: 3.1.0
|
||||||
|
|
||||||
|
jest-each@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
chalk: 4.1.2
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
jest-util: 29.7.0
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
|
||||||
|
jest-environment-node@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/environment': 29.7.0
|
||||||
|
'@jest/fake-timers': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
jest-mock: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
|
||||||
|
jest-get-type@29.6.3: {}
|
||||||
|
|
||||||
|
jest-haste-map@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/graceful-fs': 4.1.9
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
anymatch: 3.1.3
|
||||||
|
fb-watchman: 2.0.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-worker: 29.7.0
|
||||||
|
micromatch: 4.0.8
|
||||||
|
walker: 1.0.8
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
jest-leak-detector@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
|
||||||
|
jest-matcher-utils@29.0.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
jest-diff: 29.0.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
pretty-format: 29.0.0
|
||||||
|
|
||||||
|
jest-matcher-utils@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
jest-diff: 29.7.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
|
||||||
|
jest-message-util@29.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
'@jest/types': 29.0.0
|
||||||
|
'@types/stack-utils': 2.0.3
|
||||||
|
chalk: 4.1.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
micromatch: 4.0.8
|
||||||
|
pretty-format: 29.0.0
|
||||||
|
slash: 3.0.0
|
||||||
|
stack-utils: 2.0.6
|
||||||
|
|
||||||
|
jest-message-util@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/stack-utils': 2.0.3
|
||||||
|
chalk: 4.1.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
micromatch: 4.0.8
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
stack-utils: 2.0.6
|
||||||
|
|
||||||
|
jest-mock@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
jest-util: 29.7.0
|
||||||
|
|
||||||
|
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
|
||||||
|
optionalDependencies:
|
||||||
|
jest-resolve: 29.7.0
|
||||||
|
|
||||||
|
jest-regex-util@29.6.3: {}
|
||||||
|
|
||||||
|
jest-resolve-dependencies@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-snapshot: 29.7.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
jest-resolve@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-haste-map: 29.7.0
|
||||||
|
jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0)
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-validate: 29.7.0
|
||||||
|
resolve: 1.22.12
|
||||||
|
resolve.exports: 2.0.3
|
||||||
|
slash: 3.0.0
|
||||||
|
|
||||||
|
jest-runner@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/console': 29.7.0
|
||||||
|
'@jest/environment': 29.7.0
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
emittery: 0.13.1
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-docblock: 29.7.0
|
||||||
|
jest-environment-node: 29.7.0
|
||||||
|
jest-haste-map: 29.7.0
|
||||||
|
jest-leak-detector: 29.7.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-resolve: 29.7.0
|
||||||
|
jest-runtime: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
jest-watcher: 29.7.0
|
||||||
|
jest-worker: 29.7.0
|
||||||
|
p-limit: 3.1.0
|
||||||
|
source-map-support: 0.5.13
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
jest-runtime@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/environment': 29.7.0
|
||||||
|
'@jest/fake-timers': 29.7.0
|
||||||
|
'@jest/globals': 29.7.0
|
||||||
|
'@jest/source-map': 29.6.3
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
cjs-module-lexer: 1.0.0
|
||||||
|
collect-v8-coverage: 1.0.3
|
||||||
|
glob: 7.2.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-haste-map: 29.7.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-mock: 29.7.0
|
||||||
|
jest-regex-util: 29.6.3
|
||||||
|
jest-resolve: 29.7.0
|
||||||
|
jest-snapshot: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
slash: 3.0.0
|
||||||
|
strip-bom: 4.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
jest-snapshot@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@babel/generator': 7.29.7
|
||||||
|
'@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
|
||||||
|
'@babel/types': 7.29.7
|
||||||
|
'@jest/expect-utils': 29.7.0
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7)
|
||||||
|
chalk: 4.1.2
|
||||||
|
expect: 29.7.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jest-diff: 29.7.0
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
jest-matcher-utils: 29.7.0
|
||||||
|
jest-message-util: 29.7.0
|
||||||
|
jest-util: 29.7.0
|
||||||
|
natural-compare: 1.4.0
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
semver: 7.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
jest-util@29.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.0.0
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
ci-info: 3.2.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
picomatch: 2.3.2
|
||||||
|
|
||||||
|
jest-util@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
chalk: 4.1.2
|
||||||
|
ci-info: 3.2.0
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
picomatch: 2.3.2
|
||||||
|
|
||||||
|
jest-validate@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
camelcase: 6.3.0
|
||||||
|
chalk: 4.1.2
|
||||||
|
jest-get-type: 29.6.3
|
||||||
|
leven: 3.1.0
|
||||||
|
pretty-format: 29.7.0
|
||||||
|
|
||||||
|
jest-watcher@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/test-result': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
ansi-escapes: 4.2.1
|
||||||
|
chalk: 4.1.2
|
||||||
|
emittery: 0.13.1
|
||||||
|
jest-util: 29.7.0
|
||||||
|
string-length: 4.0.1
|
||||||
|
|
||||||
|
jest-worker@27.4.5:
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
merge-stream: 2.0.0
|
||||||
|
supports-color: 8.1.1
|
||||||
|
|
||||||
|
jest-worker@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
jest-util: 29.7.0
|
||||||
|
merge-stream: 2.0.0
|
||||||
|
supports-color: 8.1.1
|
||||||
|
|
||||||
|
jest@29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
import-local: 3.2.0
|
||||||
|
jest-cli: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- babel-plugin-macros
|
||||||
|
- supports-color
|
||||||
|
- ts-node
|
||||||
|
|
||||||
|
jiti@2.7.0: {}
|
||||||
|
|
||||||
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
|
js-yaml@3.13.1:
|
||||||
|
dependencies:
|
||||||
|
argparse: 1.0.10
|
||||||
|
esprima: 4.0.1
|
||||||
|
|
||||||
|
js-yaml@4.1.1:
|
||||||
|
dependencies:
|
||||||
|
argparse: 2.0.1
|
||||||
|
|
||||||
|
js-yaml@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
argparse: 2.0.1
|
||||||
|
|
||||||
|
jsesc@3.1.0: {}
|
||||||
|
|
||||||
|
json-parse-even-better-errors@2.3.0: {}
|
||||||
|
|
||||||
|
json-parse-even-better-errors@2.3.1: {}
|
||||||
|
|
||||||
|
json-schema-traverse@0.4.1: {}
|
||||||
|
|
||||||
|
json-schema-traverse@1.0.0: {}
|
||||||
|
|
||||||
|
json5@2.2.3: {}
|
||||||
|
|
||||||
|
jsonc-parser@3.3.1: {}
|
||||||
|
|
||||||
|
jsonfile@6.2.1:
|
||||||
|
dependencies:
|
||||||
|
universalify: 2.0.1
|
||||||
|
optionalDependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
|
||||||
|
jsonwebtoken@9.0.3:
|
||||||
|
dependencies:
|
||||||
|
jws: 4.0.1
|
||||||
|
lodash.includes: 4.3.0
|
||||||
|
lodash.isboolean: 3.0.3
|
||||||
|
lodash.isinteger: 4.0.4
|
||||||
|
lodash.isnumber: 3.0.3
|
||||||
|
lodash.isplainobject: 4.0.6
|
||||||
|
lodash.isstring: 4.0.1
|
||||||
|
lodash.once: 4.1.1
|
||||||
|
ms: 2.1.3
|
||||||
|
semver: 7.8.1
|
||||||
|
|
||||||
|
jwa@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
buffer-equal-constant-time: 1.0.1
|
||||||
|
ecdsa-sig-formatter: 1.0.11
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
jws@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
jwa: 2.0.1
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
kleur@3.0.3: {}
|
||||||
|
|
||||||
|
leven@3.1.0: {}
|
||||||
|
|
||||||
|
libphonenumber-js@1.13.4: {}
|
||||||
|
|
||||||
|
lines-and-columns@1.2.4: {}
|
||||||
|
|
||||||
|
load-esm@1.0.3: {}
|
||||||
|
|
||||||
|
loader-runner@4.3.2: {}
|
||||||
|
|
||||||
|
locate-path@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
p-locate: 4.1.0
|
||||||
|
|
||||||
|
lodash-es@4.18.1: {}
|
||||||
|
|
||||||
|
lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.18.1)(lodash@4.18.1):
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash-es': 4.17.12
|
||||||
|
lodash: 4.18.1
|
||||||
|
lodash-es: 4.18.1
|
||||||
|
|
||||||
|
lodash.includes@4.3.0: {}
|
||||||
|
|
||||||
|
lodash.isboolean@3.0.3: {}
|
||||||
|
|
||||||
|
lodash.isinteger@4.0.4: {}
|
||||||
|
|
||||||
|
lodash.isnumber@3.0.3: {}
|
||||||
|
|
||||||
|
lodash.isplainobject@4.0.6: {}
|
||||||
|
|
||||||
|
lodash.isstring@4.0.1: {}
|
||||||
|
|
||||||
|
lodash.memoize@4.1.2: {}
|
||||||
|
|
||||||
|
lodash.once@4.1.1: {}
|
||||||
|
|
||||||
|
lodash@4.18.1: {}
|
||||||
|
|
||||||
|
log-symbols@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
is-unicode-supported: 0.1.0
|
||||||
|
|
||||||
|
lru-cache@11.5.1: {}
|
||||||
|
|
||||||
|
lru-cache@5.1.1:
|
||||||
|
dependencies:
|
||||||
|
yallist: 3.1.1
|
||||||
|
|
||||||
|
luxon@3.5.0: {}
|
||||||
|
|
||||||
|
magic-string@0.30.17:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
|
||||||
|
magic-string@0.30.21:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
make-dir@4.0.0:
|
||||||
|
dependencies:
|
||||||
|
semver: 7.8.1
|
||||||
|
|
||||||
|
make-error@1.3.6: {}
|
||||||
|
|
||||||
|
makeerror@1.0.12:
|
||||||
|
dependencies:
|
||||||
|
tmpl: 1.0.5
|
||||||
|
|
||||||
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
media-typer@0.3.0: {}
|
||||||
|
|
||||||
|
media-typer@1.1.0: {}
|
||||||
|
|
||||||
|
memfs@3.4.1:
|
||||||
|
dependencies:
|
||||||
|
fs-monkey: 1.0.3
|
||||||
|
|
||||||
|
memoize-one@6.0.0: {}
|
||||||
|
|
||||||
|
merge-descriptors@2.0.0: {}
|
||||||
|
|
||||||
|
merge-stream@2.0.0: {}
|
||||||
|
|
||||||
|
micromatch@4.0.8:
|
||||||
|
dependencies:
|
||||||
|
braces: 3.0.3
|
||||||
|
picomatch: 2.3.2
|
||||||
|
|
||||||
|
mime-db@1.52.0: {}
|
||||||
|
|
||||||
|
mime-db@1.54.0: {}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.52.0
|
||||||
|
|
||||||
|
mime-types@3.0.2:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.54.0
|
||||||
|
|
||||||
|
mimic-fn@2.1.0: {}
|
||||||
|
|
||||||
|
minimatch@10.2.5:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 5.0.6
|
||||||
|
|
||||||
|
minimatch@3.1.5:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 1.1.15
|
||||||
|
|
||||||
|
minimatch@9.0.9:
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 2.1.1
|
||||||
|
|
||||||
|
minimist@1.2.8: {}
|
||||||
|
|
||||||
|
minipass@7.1.3: {}
|
||||||
|
|
||||||
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
muggle-string@0.4.1: {}
|
||||||
|
|
||||||
|
multer@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
append-field: 1.0.0
|
||||||
|
busboy: 1.6.0
|
||||||
|
concat-stream: 2.0.0
|
||||||
|
type-is: 1.6.18
|
||||||
|
|
||||||
|
mute-stream@2.0.0: {}
|
||||||
|
|
||||||
|
nanoid@3.3.12: {}
|
||||||
|
|
||||||
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
|
negotiator@1.0.0: {}
|
||||||
|
|
||||||
|
neo-async@2.6.2: {}
|
||||||
|
|
||||||
|
node-abort-controller@3.1.1: {}
|
||||||
|
|
||||||
|
node-emoji@1.11.0:
|
||||||
|
dependencies:
|
||||||
|
lodash: 4.18.1
|
||||||
|
|
||||||
|
node-fetch-native@1.6.7: {}
|
||||||
|
|
||||||
|
node-int64@0.4.0: {}
|
||||||
|
|
||||||
|
node-releases@2.0.46: {}
|
||||||
|
|
||||||
|
normalize-path@3.0.0: {}
|
||||||
|
|
||||||
|
normalize-wheel-es@1.2.0: {}
|
||||||
|
|
||||||
|
npm-run-path@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
path-key: 3.1.1
|
||||||
|
|
||||||
|
nypm@0.6.6:
|
||||||
|
dependencies:
|
||||||
|
citty: 0.2.2
|
||||||
|
pathe: 2.0.3
|
||||||
|
tinyexec: 1.2.4
|
||||||
|
|
||||||
|
object-assign@4.1.1: {}
|
||||||
|
|
||||||
|
object-inspect@1.13.4: {}
|
||||||
|
|
||||||
|
ohash@2.0.11: {}
|
||||||
|
|
||||||
|
on-finished@2.4.1:
|
||||||
|
dependencies:
|
||||||
|
ee-first: 1.1.1
|
||||||
|
|
||||||
|
once@1.4.0:
|
||||||
|
dependencies:
|
||||||
|
wrappy: 1.0.2
|
||||||
|
|
||||||
|
onetime@5.1.0:
|
||||||
|
dependencies:
|
||||||
|
mimic-fn: 2.1.0
|
||||||
|
|
||||||
|
onetime@5.1.2:
|
||||||
|
dependencies:
|
||||||
|
mimic-fn: 2.1.0
|
||||||
|
|
||||||
|
ora@5.4.1:
|
||||||
|
dependencies:
|
||||||
|
bl: 4.1.0
|
||||||
|
chalk: 4.1.2
|
||||||
|
cli-cursor: 3.1.0
|
||||||
|
cli-spinners: 2.5.0
|
||||||
|
is-interactive: 1.0.0
|
||||||
|
is-unicode-supported: 0.1.0
|
||||||
|
log-symbols: 4.1.0
|
||||||
|
strip-ansi: 6.0.0
|
||||||
|
wcwidth: 1.0.1
|
||||||
|
|
||||||
|
os-tmpdir@1.0.2: {}
|
||||||
|
|
||||||
|
p-limit@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
p-try: 2.2.0
|
||||||
|
|
||||||
|
p-limit@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
yocto-queue: 0.1.0
|
||||||
|
|
||||||
|
p-locate@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
p-limit: 2.2.0
|
||||||
|
|
||||||
|
p-try@2.2.0: {}
|
||||||
|
|
||||||
|
parent-module@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
callsites: 3.1.0
|
||||||
|
|
||||||
|
parse-json@5.2.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/code-frame': 7.29.7
|
||||||
|
error-ex: 1.3.4
|
||||||
|
json-parse-even-better-errors: 2.3.0
|
||||||
|
lines-and-columns: 1.2.4
|
||||||
|
|
||||||
|
parseurl@1.3.3: {}
|
||||||
|
|
||||||
|
passport-jwt@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
jsonwebtoken: 9.0.3
|
||||||
|
passport-strategy: 1.0.0
|
||||||
|
|
||||||
|
passport-strategy@1.0.0: {}
|
||||||
|
|
||||||
|
passport@0.7.0:
|
||||||
|
dependencies:
|
||||||
|
passport-strategy: 1.0.0
|
||||||
|
pause: 0.0.1
|
||||||
|
utils-merge: 1.0.1
|
||||||
|
|
||||||
|
path-browserify@1.0.1: {}
|
||||||
|
|
||||||
|
path-exists@4.0.0: {}
|
||||||
|
|
||||||
|
path-is-absolute@1.0.1: {}
|
||||||
|
|
||||||
|
path-key@3.1.1: {}
|
||||||
|
|
||||||
|
path-parse@1.0.7: {}
|
||||||
|
|
||||||
|
path-scurry@2.0.2:
|
||||||
|
dependencies:
|
||||||
|
lru-cache: 11.5.1
|
||||||
|
minipass: 7.1.3
|
||||||
|
|
||||||
|
path-to-regexp@8.4.2: {}
|
||||||
|
|
||||||
|
path-type@4.0.0: {}
|
||||||
|
|
||||||
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
|
pause@0.0.1: {}
|
||||||
|
|
||||||
|
perfect-debounce@1.0.0: {}
|
||||||
|
|
||||||
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
|
picomatch@2.3.2: {}
|
||||||
|
|
||||||
|
picomatch@4.0.4: {}
|
||||||
|
|
||||||
|
pinia@2.3.1(typescript@5.7.3)(vue@3.5.35(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.6.3
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
vue-demi: 0.14.10(vue@3.5.35(typescript@5.7.3))
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
|
||||||
|
pirates@4.0.7: {}
|
||||||
|
|
||||||
|
pkg-dir@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
find-up: 4.1.0
|
||||||
|
|
||||||
|
pkg-types@2.3.1:
|
||||||
|
dependencies:
|
||||||
|
confbox: 0.2.4
|
||||||
|
exsolve: 1.0.8
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
pluralize@8.0.0: {}
|
||||||
|
|
||||||
|
postcss@8.5.15:
|
||||||
|
dependencies:
|
||||||
|
nanoid: 3.3.12
|
||||||
|
picocolors: 1.1.1
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
pretty-format@29.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/schemas': 29.0.0
|
||||||
|
ansi-styles: 5.2.0
|
||||||
|
react-is: 18.0.0
|
||||||
|
|
||||||
|
pretty-format@29.7.0:
|
||||||
|
dependencies:
|
||||||
|
'@jest/schemas': 29.6.3
|
||||||
|
ansi-styles: 5.2.0
|
||||||
|
react-is: 18.0.0
|
||||||
|
|
||||||
|
prisma@6.19.3(typescript@5.7.3):
|
||||||
|
dependencies:
|
||||||
|
'@prisma/config': 6.19.3
|
||||||
|
'@prisma/engines': 6.19.3
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- magicast
|
||||||
|
|
||||||
|
prompts@2.4.2:
|
||||||
|
dependencies:
|
||||||
|
kleur: 3.0.3
|
||||||
|
sisteransi: 1.0.5
|
||||||
|
|
||||||
|
proxy-addr@2.0.7:
|
||||||
|
dependencies:
|
||||||
|
forwarded: 0.2.0
|
||||||
|
ipaddr.js: 1.9.1
|
||||||
|
|
||||||
|
proxy-from-env@2.1.0: {}
|
||||||
|
|
||||||
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
|
pure-rand@6.0.0: {}
|
||||||
|
|
||||||
|
pure-rand@6.1.0: {}
|
||||||
|
|
||||||
|
qs@6.15.2:
|
||||||
|
dependencies:
|
||||||
|
side-channel: 1.1.0
|
||||||
|
|
||||||
|
range-parser@1.2.1: {}
|
||||||
|
|
||||||
|
raw-body@3.0.2:
|
||||||
|
dependencies:
|
||||||
|
bytes: 3.1.2
|
||||||
|
http-errors: 2.0.1
|
||||||
|
iconv-lite: 0.7.2
|
||||||
|
unpipe: 1.0.0
|
||||||
|
|
||||||
|
rc9@2.1.2:
|
||||||
|
dependencies:
|
||||||
|
defu: 6.1.7
|
||||||
|
destr: 2.0.5
|
||||||
|
|
||||||
|
react-is@18.0.0: {}
|
||||||
|
|
||||||
|
readable-stream@3.6.2:
|
||||||
|
dependencies:
|
||||||
|
inherits: 2.0.4
|
||||||
|
string_decoder: 1.3.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
|
readdirp@4.1.2: {}
|
||||||
|
|
||||||
|
redis-errors@1.2.0: {}
|
||||||
|
|
||||||
|
redis-parser@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
redis-errors: 1.2.0
|
||||||
|
|
||||||
|
reflect-metadata@0.2.2: {}
|
||||||
|
|
||||||
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
|
resolve-cwd@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
resolve-from: 5.0.0
|
||||||
|
|
||||||
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
|
resolve-from@5.0.0: {}
|
||||||
|
|
||||||
|
resolve.exports@2.0.3: {}
|
||||||
|
|
||||||
|
resolve@1.22.12:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
is-core-module: 2.16.2
|
||||||
|
path-parse: 1.0.7
|
||||||
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
|
restore-cursor@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
onetime: 5.1.0
|
||||||
|
signal-exit: 3.0.7
|
||||||
|
|
||||||
|
rollup@4.61.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.9
|
||||||
|
optionalDependencies:
|
||||||
|
'@rollup/rollup-android-arm-eabi': 4.61.0
|
||||||
|
'@rollup/rollup-android-arm64': 4.61.0
|
||||||
|
'@rollup/rollup-darwin-arm64': 4.61.0
|
||||||
|
'@rollup/rollup-darwin-x64': 4.61.0
|
||||||
|
'@rollup/rollup-freebsd-arm64': 4.61.0
|
||||||
|
'@rollup/rollup-freebsd-x64': 4.61.0
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf': 4.61.0
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf': 4.61.0
|
||||||
|
'@rollup/rollup-linux-arm64-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-linux-arm64-musl': 4.61.0
|
||||||
|
'@rollup/rollup-linux-loong64-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-linux-loong64-musl': 4.61.0
|
||||||
|
'@rollup/rollup-linux-ppc64-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-linux-ppc64-musl': 4.61.0
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-linux-riscv64-musl': 4.61.0
|
||||||
|
'@rollup/rollup-linux-s390x-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-linux-x64-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-linux-x64-musl': 4.61.0
|
||||||
|
'@rollup/rollup-openbsd-x64': 4.61.0
|
||||||
|
'@rollup/rollup-openharmony-arm64': 4.61.0
|
||||||
|
'@rollup/rollup-win32-arm64-msvc': 4.61.0
|
||||||
|
'@rollup/rollup-win32-ia32-msvc': 4.61.0
|
||||||
|
'@rollup/rollup-win32-x64-gnu': 4.61.0
|
||||||
|
'@rollup/rollup-win32-x64-msvc': 4.61.0
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
router@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
depd: 2.0.0
|
||||||
|
is-promise: 4.0.0
|
||||||
|
parseurl: 1.3.3
|
||||||
|
path-to-regexp: 8.4.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
rxjs@7.8.1:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
rxjs@7.8.2:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
safe-buffer@5.2.1: {}
|
||||||
|
|
||||||
|
safer-buffer@2.1.2: {}
|
||||||
|
|
||||||
|
schema-utils@3.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
ajv: 6.15.0
|
||||||
|
ajv-keywords: 3.5.2(ajv@6.15.0)
|
||||||
|
|
||||||
|
schema-utils@4.3.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
ajv: 8.20.0
|
||||||
|
ajv-formats: 2.1.1(ajv@8.20.0)
|
||||||
|
ajv-keywords: 5.1.0(ajv@8.20.0)
|
||||||
|
|
||||||
|
semver@6.3.0: {}
|
||||||
|
|
||||||
|
semver@6.3.1: {}
|
||||||
|
|
||||||
|
semver@7.8.1: {}
|
||||||
|
|
||||||
|
send@1.2.1:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
etag: 1.8.1
|
||||||
|
fresh: 2.0.0
|
||||||
|
http-errors: 2.0.1
|
||||||
|
mime-types: 3.0.2
|
||||||
|
ms: 2.1.3
|
||||||
|
on-finished: 2.4.1
|
||||||
|
range-parser: 1.2.1
|
||||||
|
statuses: 2.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
serve-static@2.2.1:
|
||||||
|
dependencies:
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
parseurl: 1.3.3
|
||||||
|
send: 1.2.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
setprototypeof@1.2.0: {}
|
||||||
|
|
||||||
|
shebang-command@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
shebang-regex: 3.0.0
|
||||||
|
|
||||||
|
shebang-regex@3.0.0: {}
|
||||||
|
|
||||||
|
side-channel-list@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
object-inspect: 1.13.4
|
||||||
|
|
||||||
|
side-channel-map@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
call-bound: 1.0.4
|
||||||
|
es-errors: 1.3.0
|
||||||
|
get-intrinsic: 1.3.0
|
||||||
|
object-inspect: 1.13.4
|
||||||
|
|
||||||
|
side-channel-weakmap@1.0.2:
|
||||||
|
dependencies:
|
||||||
|
call-bound: 1.0.4
|
||||||
|
es-errors: 1.3.0
|
||||||
|
get-intrinsic: 1.3.0
|
||||||
|
object-inspect: 1.13.4
|
||||||
|
side-channel-map: 1.0.1
|
||||||
|
|
||||||
|
side-channel@1.1.0:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
object-inspect: 1.13.4
|
||||||
|
side-channel-list: 1.0.1
|
||||||
|
side-channel-map: 1.0.1
|
||||||
|
side-channel-weakmap: 1.0.2
|
||||||
|
|
||||||
|
signal-exit@3.0.7: {}
|
||||||
|
|
||||||
|
signal-exit@4.1.0: {}
|
||||||
|
|
||||||
|
sisteransi@1.0.5: {}
|
||||||
|
|
||||||
|
slash@3.0.0: {}
|
||||||
|
|
||||||
|
source-map-js@1.2.1: {}
|
||||||
|
|
||||||
|
source-map-support@0.5.13:
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
|
||||||
|
source-map-support@0.5.21:
|
||||||
|
dependencies:
|
||||||
|
buffer-from: 1.1.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
|
||||||
|
source-map@0.6.1: {}
|
||||||
|
|
||||||
|
source-map@0.7.4: {}
|
||||||
|
|
||||||
|
sprintf-js@1.0.3: {}
|
||||||
|
|
||||||
|
stack-utils@2.0.6:
|
||||||
|
dependencies:
|
||||||
|
escape-string-regexp: 2.0.0
|
||||||
|
|
||||||
|
standard-as-callback@2.1.0: {}
|
||||||
|
|
||||||
|
statuses@2.0.2: {}
|
||||||
|
|
||||||
|
streamsearch@1.1.0: {}
|
||||||
|
|
||||||
|
string-length@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
char-regex: 1.0.2
|
||||||
|
strip-ansi: 6.0.0
|
||||||
|
|
||||||
|
string-width@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 8.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
strip-ansi: 5.2.0
|
||||||
|
|
||||||
|
string-width@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 8.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
strip-ansi: 6.0.0
|
||||||
|
|
||||||
|
string-width@4.2.3:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 8.0.0
|
||||||
|
is-fullwidth-code-point: 3.0.0
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
string_decoder@1.3.0:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
strip-ansi@5.2.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 4.1.1
|
||||||
|
|
||||||
|
strip-ansi@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 5.0.1
|
||||||
|
|
||||||
|
strip-ansi@6.0.1:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 5.0.1
|
||||||
|
|
||||||
|
strip-bom@3.0.0: {}
|
||||||
|
|
||||||
|
strip-bom@4.0.0: {}
|
||||||
|
|
||||||
|
strip-final-newline@2.0.0: {}
|
||||||
|
|
||||||
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
||||||
|
strtok3@10.3.5:
|
||||||
|
dependencies:
|
||||||
|
'@tokenizer/token': 0.3.0
|
||||||
|
|
||||||
|
supports-color@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
|
||||||
|
supports-color@8.1.1:
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
|
||||||
|
supports-preserve-symlinks-flag@1.0.0: {}
|
||||||
|
|
||||||
|
swagger-ui-dist@5.32.6:
|
||||||
|
dependencies:
|
||||||
|
'@scarf/scarf': 1.4.0
|
||||||
|
|
||||||
|
symbol-observable@4.0.0: {}
|
||||||
|
|
||||||
|
tapable@2.3.3: {}
|
||||||
|
|
||||||
|
terser-webpack-plugin@5.6.1(webpack@5.106.0):
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
jest-worker: 27.4.5
|
||||||
|
schema-utils: 4.3.3
|
||||||
|
terser: 5.48.0
|
||||||
|
webpack: 5.106.0
|
||||||
|
|
||||||
|
terser@5.48.0:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/source-map': 0.3.11
|
||||||
|
acorn: 8.16.0
|
||||||
|
commander: 2.20.0
|
||||||
|
source-map-support: 0.5.21
|
||||||
|
|
||||||
|
test-exclude@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@istanbuljs/schema': 0.1.6
|
||||||
|
glob: 7.2.0
|
||||||
|
minimatch: 3.1.5
|
||||||
|
|
||||||
|
tinyexec@1.2.4: {}
|
||||||
|
|
||||||
|
tinyglobby@0.2.17:
|
||||||
|
dependencies:
|
||||||
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
|
picomatch: 4.0.4
|
||||||
|
|
||||||
|
tmp@0.0.33:
|
||||||
|
dependencies:
|
||||||
|
os-tmpdir: 1.0.2
|
||||||
|
|
||||||
|
tmpl@1.0.5: {}
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
dependencies:
|
||||||
|
is-number: 7.0.0
|
||||||
|
|
||||||
|
toidentifier@1.0.1: {}
|
||||||
|
|
||||||
|
token-types@6.1.2:
|
||||||
|
dependencies:
|
||||||
|
'@borewit/text-codec': 0.2.2
|
||||||
|
'@tokenizer/token': 0.3.0
|
||||||
|
ieee754: 1.2.1
|
||||||
|
|
||||||
|
ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3)))(typescript@5.7.3):
|
||||||
|
dependencies:
|
||||||
|
bs-logger: 0.2.6
|
||||||
|
fast-json-stable-stringify: 2.1.0
|
||||||
|
handlebars: 4.7.9
|
||||||
|
jest: 29.7.0(@types/node@22.19.19)(ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3))
|
||||||
|
json5: 2.2.3
|
||||||
|
lodash.memoize: 4.1.2
|
||||||
|
make-error: 1.3.6
|
||||||
|
semver: 7.8.1
|
||||||
|
type-fest: 4.41.0
|
||||||
|
typescript: 5.7.3
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@babel/core': 7.29.7
|
||||||
|
'@jest/transform': 29.7.0
|
||||||
|
'@jest/types': 29.6.3
|
||||||
|
babel-jest: 29.7.0(@babel/core@7.29.7)
|
||||||
|
jest-util: 29.7.0
|
||||||
|
|
||||||
|
ts-node@10.9.2(@types/node@22.19.19)(typescript@5.7.3):
|
||||||
|
dependencies:
|
||||||
|
'@cspotcode/source-map-support': 0.8.1
|
||||||
|
'@tsconfig/node10': 1.0.12
|
||||||
|
'@tsconfig/node12': 1.0.11
|
||||||
|
'@tsconfig/node14': 1.0.3
|
||||||
|
'@tsconfig/node16': 1.0.4
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
acorn: 8.16.0
|
||||||
|
acorn-walk: 8.3.5
|
||||||
|
arg: 4.1.3
|
||||||
|
create-require: 1.1.1
|
||||||
|
diff: 4.0.4
|
||||||
|
make-error: 1.3.6
|
||||||
|
typescript: 5.7.3
|
||||||
|
v8-compile-cache-lib: 3.0.1
|
||||||
|
yn: 3.1.1
|
||||||
|
|
||||||
|
tsconfig-paths-webpack-plugin@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
enhanced-resolve: 5.22.1
|
||||||
|
tapable: 2.3.3
|
||||||
|
tsconfig-paths: 4.2.0
|
||||||
|
|
||||||
|
tsconfig-paths@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
json5: 2.2.3
|
||||||
|
minimist: 1.2.8
|
||||||
|
strip-bom: 3.0.0
|
||||||
|
|
||||||
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
type-detect@4.0.8: {}
|
||||||
|
|
||||||
|
type-fest@0.21.3: {}
|
||||||
|
|
||||||
|
type-fest@0.5.2: {}
|
||||||
|
|
||||||
|
type-fest@4.41.0: {}
|
||||||
|
|
||||||
|
type-is@1.6.18:
|
||||||
|
dependencies:
|
||||||
|
media-typer: 0.3.0
|
||||||
|
mime-types: 2.1.35
|
||||||
|
|
||||||
|
type-is@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
content-type: 2.0.0
|
||||||
|
media-typer: 1.1.0
|
||||||
|
mime-types: 3.0.2
|
||||||
|
|
||||||
|
typedarray@0.0.6: {}
|
||||||
|
|
||||||
|
typescript@5.7.3: {}
|
||||||
|
|
||||||
|
typescript@5.9.3: {}
|
||||||
|
|
||||||
|
uglify-js@3.19.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
uid@2.0.2:
|
||||||
|
dependencies:
|
||||||
|
'@lukeed/csprng': 1.1.0
|
||||||
|
|
||||||
|
uint8array-extras@1.5.0: {}
|
||||||
|
|
||||||
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
|
universalify@2.0.1: {}
|
||||||
|
|
||||||
|
unpipe@1.0.0: {}
|
||||||
|
|
||||||
|
update-browserslist-db@1.2.3(browserslist@4.28.2):
|
||||||
|
dependencies:
|
||||||
|
browserslist: 4.28.2
|
||||||
|
escalade: 3.2.0
|
||||||
|
picocolors: 1.1.1
|
||||||
|
|
||||||
|
uri-js@4.4.1:
|
||||||
|
dependencies:
|
||||||
|
punycode: 2.3.1
|
||||||
|
|
||||||
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
|
utils-merge@1.0.1: {}
|
||||||
|
|
||||||
|
uuid@11.1.1: {}
|
||||||
|
|
||||||
|
v8-compile-cache-lib@3.0.1: {}
|
||||||
|
|
||||||
|
v8-to-istanbul@9.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
'@types/istanbul-lib-coverage': 2.0.6
|
||||||
|
convert-source-map: 2.0.0
|
||||||
|
|
||||||
|
validator@13.15.35: {}
|
||||||
|
|
||||||
|
vary@1.1.2: {}
|
||||||
|
|
||||||
|
vite@6.4.2(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0):
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.25.0
|
||||||
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
|
picomatch: 4.0.4
|
||||||
|
postcss: 8.5.15
|
||||||
|
rollup: 4.61.0
|
||||||
|
tinyglobby: 0.2.17
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 22.19.19
|
||||||
|
fsevents: 2.3.3
|
||||||
|
jiti: 2.7.0
|
||||||
|
terser: 5.48.0
|
||||||
|
|
||||||
|
vscode-uri@3.1.0: {}
|
||||||
|
|
||||||
|
vue-component-type-helpers@3.3.3: {}
|
||||||
|
|
||||||
|
vue-demi@0.14.10(vue@3.5.35(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
vue-i18n@11.4.4(vue@3.5.35(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@intlify/core-base': 11.4.4
|
||||||
|
'@intlify/devtools-types': 11.4.4
|
||||||
|
'@intlify/shared': 11.4.4
|
||||||
|
'@vue/devtools-api': 6.5.0
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
vue-router@4.6.4(vue@3.5.35(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.6.4
|
||||||
|
vue: 3.5.35(typescript@5.7.3)
|
||||||
|
|
||||||
|
vue-tsc@2.2.0(typescript@5.7.3):
|
||||||
|
dependencies:
|
||||||
|
'@volar/typescript': 2.4.28
|
||||||
|
'@vue/language-core': 2.2.0(typescript@5.7.3)
|
||||||
|
typescript: 5.7.3
|
||||||
|
|
||||||
|
vue@3.5.35(typescript@5.7.3):
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-dom': 3.5.35
|
||||||
|
'@vue/compiler-sfc': 3.5.35
|
||||||
|
'@vue/runtime-dom': 3.5.35
|
||||||
|
'@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@5.7.3))
|
||||||
|
'@vue/shared': 3.5.35
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.3
|
||||||
|
|
||||||
|
walker@1.0.8:
|
||||||
|
dependencies:
|
||||||
|
makeerror: 1.0.12
|
||||||
|
|
||||||
|
watchpack@2.5.1:
|
||||||
|
dependencies:
|
||||||
|
glob-to-regexp: 0.4.1
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
|
||||||
|
wcwidth@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
defaults: 1.0.4
|
||||||
|
|
||||||
|
webpack-node-externals@3.0.0: {}
|
||||||
|
|
||||||
|
webpack-sources@3.5.0: {}
|
||||||
|
|
||||||
|
webpack@5.106.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/eslint-scope': 3.7.7
|
||||||
|
'@types/estree': 1.0.9
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
'@webassemblyjs/ast': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-edit': 1.14.1
|
||||||
|
'@webassemblyjs/wasm-parser': 1.14.1
|
||||||
|
acorn: 8.16.0
|
||||||
|
acorn-import-phases: 1.0.4(acorn@8.16.0)
|
||||||
|
browserslist: 4.28.2
|
||||||
|
chrome-trace-event: 1.0.4
|
||||||
|
enhanced-resolve: 5.22.1
|
||||||
|
es-module-lexer: 2.1.0
|
||||||
|
eslint-scope: 5.1.1
|
||||||
|
events: 3.3.0
|
||||||
|
glob-to-regexp: 0.4.1
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
json-parse-even-better-errors: 2.3.1
|
||||||
|
loader-runner: 4.3.2
|
||||||
|
mime-types: 2.1.35
|
||||||
|
neo-async: 2.6.2
|
||||||
|
schema-utils: 4.3.3
|
||||||
|
tapable: 2.3.3
|
||||||
|
terser-webpack-plugin: 5.6.1(webpack@5.106.0)
|
||||||
|
watchpack: 2.5.1
|
||||||
|
webpack-sources: 3.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@minify-html/node'
|
||||||
|
- '@swc/core'
|
||||||
|
- '@swc/css'
|
||||||
|
- '@swc/html'
|
||||||
|
- clean-css
|
||||||
|
- cssnano
|
||||||
|
- csso
|
||||||
|
- esbuild
|
||||||
|
- html-minifier-terser
|
||||||
|
- lightningcss
|
||||||
|
- postcss
|
||||||
|
- uglify-js
|
||||||
|
|
||||||
|
which@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
isexe: 2.0.0
|
||||||
|
|
||||||
|
wordwrap@1.0.0: {}
|
||||||
|
|
||||||
|
wrap-ansi@6.2.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
string-width: 4.1.0
|
||||||
|
strip-ansi: 6.0.0
|
||||||
|
|
||||||
|
wrap-ansi@7.0.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
write-file-atomic@4.0.2:
|
||||||
|
dependencies:
|
||||||
|
imurmurhash: 0.1.4
|
||||||
|
signal-exit: 3.0.7
|
||||||
|
|
||||||
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
|
yallist@3.1.1: {}
|
||||||
|
|
||||||
|
yargs-parser@21.1.1: {}
|
||||||
|
|
||||||
|
yargs@17.7.2:
|
||||||
|
dependencies:
|
||||||
|
cliui: 8.0.1
|
||||||
|
escalade: 3.2.0
|
||||||
|
get-caller-file: 2.0.5
|
||||||
|
require-directory: 2.1.1
|
||||||
|
string-width: 4.2.3
|
||||||
|
y18n: 5.0.8
|
||||||
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
|
yn@3.1.1: {}
|
||||||
|
|
||||||
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
yoctocolors-cjs@2.1.3: {}
|
||||||
11
pnpm-workspace.yaml
Normal file
11
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
packages:
|
||||||
|
- 'apps/*'
|
||||||
|
- 'packages/*'
|
||||||
|
allowBuilds:
|
||||||
|
'@nestjs/core': set this to true or false
|
||||||
|
'@prisma/client': set this to true or false
|
||||||
|
'@prisma/engines': set this to true or false
|
||||||
|
'@scarf/scarf': set this to true or false
|
||||||
|
esbuild: set this to true or false
|
||||||
|
prisma: set this to true or false
|
||||||
|
vue-demi: set this to true or false
|
||||||
1650
足球投注平台产品需求与MVP实施计划_PRD_v1.2.md
Normal file
1650
足球投注平台产品需求与MVP实施计划_PRD_v1.2.md
Normal file
@@ -0,0 +1,1650 @@
|
|||||||
|
# 足球投注平台产品需求与 MVP 实施计划(PRD)v1.2
|
||||||
|
|
||||||
|
> 文档日期:2026-06-02
|
||||||
|
> 文档状态:访谈结论定稿版
|
||||||
|
> 适用对象:产品、研发、测试、UI/UX、运营、财务、代理运营
|
||||||
|
> 业务前提:面向赌博合法化国家/地区的合法运营场景。本文不展开法律合规风险论证,只描述产品、业务、账务、权限、交互、技术实现和质量控制。
|
||||||
|
> 第一版目标:**尽量简单、快速上线、优先保证下注、结算、账务和代理额度正确。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 本版结论摘要
|
||||||
|
|
||||||
|
本版根据访谈确认,采用以下默认方案作为第一版开发依据。
|
||||||
|
|
||||||
|
| 模块 | 第一版定稿方案 |
|
||||||
|
|---|---|
|
||||||
|
| 体育项目 | 只做足球,数据结构预留其他体育项目。 |
|
||||||
|
| 下注模式 | 只做赛前盘,不做滚球 / Live / In-Play。 |
|
||||||
|
| 赔率来源 | 后台手动创建赛事、盘口、赔率,不接自动赔率源。 |
|
||||||
|
| 开奖方式 | 后台手动录入半场比分、全场比分,系统生成结算预览,管理员确认后入账。 |
|
||||||
|
| 前台语言 | 简体中文 `zh-CN`、马来语 / Bahasa Melayu `ms-MY`、英文 `en-US`。 |
|
||||||
|
| UI 方向 | 参考成熟体育投注站的信息密度和 Bet Slip 交互:赛事列表、盘口分组、赔率按钮、投注单抽屉;不复制任何品牌素材。 |
|
||||||
|
| 支持玩法 | 波胆、上半场波胆、下半场波胆、全场让球、全场大/小、全场独赢盘、全场单/双、半场让球、半场大/小、半场独赢盘、冠军竞猜。 |
|
||||||
|
| 串关 | 支持基础 `2 串 1` 到 `5 串 1`;不做系统串关;不允许同场串关;冠军竞猜不允许串关;四分之一让球/大小球第一版不允许进入串关。 |
|
||||||
|
| 让球/大小球 | 单关支持整数、半球、四分之一球;串关只允许整数和半球盘口,降低结算复杂度。 |
|
||||||
|
| 代理层级 | 第一版最多 2 级代理:平台 → 一级代理 → 二级代理 → 玩家。 |
|
||||||
|
| 代理额度 | 采用代理信用额度池。玩家余额、玩家冻结投注金额、下级代理占用都会消耗代理额度;玩家输掉或下分释放额度。 |
|
||||||
|
| 玩家钱包 | 可用余额 + 冻结金额;下注冻结本金,结算后释放并派彩或扣除。 |
|
||||||
|
| 返水 | 第一版只做玩家返水;按有效投注额计算;后台生成报表,人工确认发放。 |
|
||||||
|
| 暂不做 | 滚球、系统串关、同场串关、自动数据源、自动赛果源、Cash Out、Edit Bet、Bet Builder、原生 App、无限代理层级、代理佣金自动结算。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 项目目标与范围
|
||||||
|
|
||||||
|
### 1.1 产品目标
|
||||||
|
|
||||||
|
搭建一个足球投注平台,支持平台后台、代理后台、玩家前台三类核心端口。
|
||||||
|
|
||||||
|
核心业务流程:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[平台创建代理/玩家] --> B[平台给代理授信额度]
|
||||||
|
B --> C[代理创建下级代理/玩家]
|
||||||
|
C --> D[代理给直属玩家上分]
|
||||||
|
D --> E[玩家前台登录]
|
||||||
|
E --> F[查看足球赛事与盘口]
|
||||||
|
F --> G[单关或串关下注]
|
||||||
|
G --> H[系统冻结玩家本金]
|
||||||
|
H --> I[后台录入半场/全场比分]
|
||||||
|
I --> J[系统生成结算预览]
|
||||||
|
J --> K[管理员确认结算]
|
||||||
|
K --> L[系统派彩/扣款/退水]
|
||||||
|
L --> M[玩家查看注单/账变/返水]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 第一版产品原则
|
||||||
|
|
||||||
|
1. **先稳定后丰富**:第一版不追求 Bet365 的完整功能,只参考其成熟的信息架构与下注体验。
|
||||||
|
2. **账务不可直接改余额**:所有余额变化必须来自账变流水,不允许直接编辑钱包余额。
|
||||||
|
3. **赔率必须快照化**:玩家确认下注时锁定赔率、盘口、玩法、选项、赔率版本。
|
||||||
|
4. **结算必须可回溯**:每张注单的结算依据、比分、操作人、时间、重结算记录都必须留存。
|
||||||
|
5. **代理必须受额度控制**:代理不能无限给下级或玩家上分,额度可为负展示,但负数状态禁止继续放款。
|
||||||
|
6. **多语言从底层设计**:界面文本、赛事名称、球队名称、Banner、公告、规则说明均支持多语言字段和 fallback。
|
||||||
|
7. **第一版以移动端 H5 为主**:桌面端可用即可,移动端体验优先。
|
||||||
|
|
||||||
|
### 1.3 MVP 必须交付
|
||||||
|
|
||||||
|
| 模块 | 必须交付内容 |
|
||||||
|
|---|---|
|
||||||
|
| 玩家前台 | 登录、改密码、语言切换、赛事列表、赛事详情、投注单、单关下注、串关下注、我的投注、账变记录、返水记录、公告/Banner/走马灯。 |
|
||||||
|
| 代理后台 | 创建直属玩家、创建下级代理、给直属下级上分/下分、查看额度、查看下级玩家注单和报表。 |
|
||||||
|
| 平台后台 | 用户管理、代理管理、额度管理、账务流水、赛事管理、盘口赔率管理、结算预览、确认开奖、返水批次、报表、内容管理、多语言管理、操作日志。 |
|
||||||
|
| 注单引擎 | 支持指定 10 类足球玩法 + 冠军竞猜;支持单关和基础 2-5 串 1;赔率快照;余额冻结;幂等提交。 |
|
||||||
|
| 结算引擎 | 支持半场/全场比分结算、下半场净比分结算、亚洲让球、大小球、单双、波胆、串关结算、取消退款、重结算冲正。 |
|
||||||
|
| 账务系统 | 玩家钱包、代理额度池、账变流水、额度流水、返水入账、人工上分/下分。 |
|
||||||
|
| 系统安全 | 后台角色权限、关键操作二次确认、操作日志、软删除、登录失败锁定、接口幂等。 |
|
||||||
|
|
||||||
|
### 1.4 第一版明确不做
|
||||||
|
|
||||||
|
| 暂不做功能 | 原因 |
|
||||||
|
|---|---|
|
||||||
|
| 滚球 / Live / In-Play | 涉及实时比分、暂停下注、赔率频繁变化、延迟确认,第一版风险高。 |
|
||||||
|
| 系统串关,如 3 串 4、4 串 11 | 结算复杂,测试量大。 |
|
||||||
|
| 同场串关 | 强相关投注,风控与结算争议较多。 |
|
||||||
|
| 四分之一球串关 | 会产生半赢/半输拆注,第一版先限制。 |
|
||||||
|
| 自动赔率源 / 自动赛果源 | 先验证业务闭环,避免接口成本。 |
|
||||||
|
| Cash Out / Edit Bet / Bet Builder | 复杂度高,作为二期体验增强。 |
|
||||||
|
| 无限代理层级 | 报表、额度和权限复杂度过高。 |
|
||||||
|
| 代理佣金自动结算 | 第一版先做玩家返水和代理报表。 |
|
||||||
|
| 原生 iOS/Android App | 第一版先做移动端 H5。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 用户角色与权限
|
||||||
|
|
||||||
|
### 2.1 角色定义
|
||||||
|
|
||||||
|
| 角色 | 入口 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| 超级管理员 | 平台后台 | 拥有全部权限,管理系统配置、代理、玩家、账务、赛事、开奖、返水、报表。 |
|
||||||
|
| 赛事管理员 | 平台后台 | 创建赛事、联赛、球队、盘口、赔率、封盘、录入赛果、发起结算预览。 |
|
||||||
|
| 财务管理员 | 平台后台 | 玩家上分/下分、代理额度调整、账变查询、返水发放、财务报表。 |
|
||||||
|
| 客服/查询员 | 平台后台 | 查询玩家资料、投注历史、账变、重置密码,无账务执行权限。 |
|
||||||
|
| 一级代理 | 代理后台 | 创建二级代理和直属玩家,给直属下级分配额度或上分/下分,查看组织树报表。 |
|
||||||
|
| 二级代理 | 代理后台 | 创建直属玩家,给直属玩家上分/下分,查看直属玩家报表。 |
|
||||||
|
| 玩家 | 玩家前台 | 登录、查看赛事、下注、查看注单、查看账单、查看返水、修改密码。 |
|
||||||
|
|
||||||
|
### 2.2 权限矩阵
|
||||||
|
|
||||||
|
| 功能 | 超级管理员 | 赛事管理员 | 财务管理员 | 客服 | 一级代理 | 二级代理 | 玩家 |
|
||||||
|
|---|---:|---:|---:|---:|---:|---:|---:|
|
||||||
|
| 创建后台账号 | 是 | 否 | 否 | 否 | 否 | 否 | 否 |
|
||||||
|
| 创建一级代理 | 是 | 否 | 否 | 否 | 否 | 否 | 否 |
|
||||||
|
| 创建二级代理 | 是 | 否 | 否 | 否 | 是 | 否 | 否 |
|
||||||
|
| 创建玩家 | 是 | 否 | 可选 | 可选 | 是 | 是 | 否 |
|
||||||
|
| 设置代理额度 | 是 | 否 | 是 | 否 | 仅下级 | 否 | 否 |
|
||||||
|
| 玩家上分/下分 | 是 | 否 | 是 | 否 | 直属下级 | 直属下级 | 否 |
|
||||||
|
| 赛事维护 | 是 | 是 | 否 | 查看 | 查看 | 查看 | 查看 |
|
||||||
|
| 赔率维护 | 是 | 是 | 否 | 查看 | 查看 | 查看 | 查看 |
|
||||||
|
| 赛事封盘 | 是 | 是 | 否 | 否 | 否 | 否 | 否 |
|
||||||
|
| 录入赛果 | 是 | 是 | 否 | 否 | 否 | 否 | 否 |
|
||||||
|
| 确认结算 | 是 | 是/可选 | 否 | 否 | 否 | 否 | 否 |
|
||||||
|
| 重结算 | 是 | 否 | 否 | 否 | 否 | 否 | 否 |
|
||||||
|
| 返水批次 | 是 | 否 | 是 | 查看 | 查看下级 | 查看下级 | 查看自己 |
|
||||||
|
| 报表 | 全量 | 赛事报表 | 财务报表 | 查询报表 | 组织树 | 直属玩家 | 自己 |
|
||||||
|
| Banner/公告/走马灯 | 是 | 是 | 否 | 否 | 否 | 否 | 查看 |
|
||||||
|
|
||||||
|
### 2.3 代理权限边界
|
||||||
|
|
||||||
|
1. 代理只能创建和操作直属下级。
|
||||||
|
2. 一级代理可以创建二级代理和直属玩家。
|
||||||
|
3. 二级代理只能创建直属玩家。
|
||||||
|
4. 一级代理不能直接给二级代理名下玩家上分,除非该玩家是一级代理直属玩家。
|
||||||
|
5. 下级代理的额度、回水比例、单笔限额、日限额不能超过上级授权范围。
|
||||||
|
6. 代理不能修改赛事、盘口、赔率、开奖结果。
|
||||||
|
7. 代理不能撤销玩家已结算注单。
|
||||||
|
8. 代理账号被禁用后,其下级玩家可以继续登录还是一起冻结,需要后台配置;MVP 默认只禁用该代理操作权限,不自动冻结玩家。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 多语言与本地化
|
||||||
|
|
||||||
|
### 3.1 支持语言
|
||||||
|
|
||||||
|
| 语言 | Locale | 前台显示 | 备注 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 简体中文 | `zh-CN` | 中文 | 默认候选语言。 |
|
||||||
|
| 马来语 / Bahasa Melayu | `ms-MY` | Bahasa Melayu | 用户所说“马来西亚语”按马来语处理。 |
|
||||||
|
| 英文 | `en-US` | English | 默认 fallback 语言。 |
|
||||||
|
|
||||||
|
### 3.2 语言切换规则
|
||||||
|
|
||||||
|
1. 登录页、前台首页、玩家中心均提供语言切换。
|
||||||
|
2. 未登录用户:语言选择保存到浏览器 localStorage / cookie。
|
||||||
|
3. 已登录用户:语言选择保存到用户偏好 `user_preferences.locale`。
|
||||||
|
4. 后台也支持同三种语言,但第一版后台可以优先中文 + 英文,马来语可后补。
|
||||||
|
5. 内容缺失时 fallback 顺序:当前语言 → 英文 → 中文。
|
||||||
|
6. 后台维护赛事、球队、联赛时,至少录入一个主语言名称,其他语言可为空。
|
||||||
|
|
||||||
|
### 3.3 多语言内容范围
|
||||||
|
|
||||||
|
| 内容 | 是否多语言 | 说明 |
|
||||||
|
|---|---:|---|
|
||||||
|
| 系统菜单、按钮、错误提示 | 是 | 使用语言包 key。 |
|
||||||
|
| 玩法名称、盘口名称 | 是 | 使用系统内置翻译。 |
|
||||||
|
| 联赛、球队、赛事标题 | 是 | 后台可录入多语言名称,缺失时 fallback。 |
|
||||||
|
| Banner 标题、图片、跳转 | 是 | 不同语言可使用不同图片。 |
|
||||||
|
| 公告/走马灯 | 是 | 后台按语言维护。 |
|
||||||
|
| 投注规则说明 | 是 | 必须三语,减少争议。 |
|
||||||
|
| 注单状态 | 是 | 使用统一枚举翻译。 |
|
||||||
|
|
||||||
|
### 3.4 关键翻译 Key 示例
|
||||||
|
|
||||||
|
| Key | 中文 | Bahasa Melayu | English |
|
||||||
|
|---|---|---|---|
|
||||||
|
| nav.home | 首页 | Laman Utama | Home |
|
||||||
|
| nav.football | 足球 | Bola Sepak | Football |
|
||||||
|
| nav.my_bets | 我的投注 | Pertaruhan Saya | My Bets |
|
||||||
|
| auth.login | 登录 | Log Masuk | Login |
|
||||||
|
| auth.username | 账号 | Nama Pengguna | Username |
|
||||||
|
| wallet.balance | 余额 | Baki | Balance |
|
||||||
|
| bet.bet_slip | 投注单 | Slip Pertaruhan | Bet Slip |
|
||||||
|
| bet.stake | 投注金额 | Jumlah Pertaruhan | Stake |
|
||||||
|
| bet.place_bet | 确认下注 | Letak Pertaruhan | Place Bet |
|
||||||
|
| bet.parlay | 串关 | Pertaruhan Berganda | Accumulator |
|
||||||
|
| market.correct_score | 波胆 | Skor Tepat | Correct Score |
|
||||||
|
| market.ft_handicap | 全场让球 | Handicap Masa Penuh | Full Time Handicap |
|
||||||
|
| market.ft_over_under | 全场大/小 | Atas/Bawah Masa Penuh | Full Time Over/Under |
|
||||||
|
| market.ft_1x2 | 全场独赢盘 | 1X2 Masa Penuh | Full Time 1X2 |
|
||||||
|
| market.ft_odd_even | 全场单/双 | Ganjil/Genap Masa Penuh | Full Time Odd/Even |
|
||||||
|
| market.ht_handicap | 半场让球 | Handicap Separuh Masa | Half Time Handicap |
|
||||||
|
| market.ht_over_under | 半场大/小 | Atas/Bawah Separuh Masa | Half Time Over/Under |
|
||||||
|
| market.ht_1x2 | 半场独赢盘 | 1X2 Separuh Masa | Half Time 1X2 |
|
||||||
|
| error.odds_changed | 赔率已变化,请重新确认 | Odds telah berubah, sila sahkan semula | Odds changed, please confirm again |
|
||||||
|
| error.market_closed | 盘口已关闭 | Pasaran telah ditutup | Market closed |
|
||||||
|
| error.insufficient_balance | 余额不足 | Baki tidak mencukupi | Insufficient balance |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 前台 UI / UX 设计
|
||||||
|
|
||||||
|
### 4.1 总体风格
|
||||||
|
|
||||||
|
界面可参考成熟体育投注平台的信息架构:深色或高对比背景、清晰赛事分组、赔率按钮突出、右侧或底部投注单、快速查看余额、热门赛事与公告入口。
|
||||||
|
|
||||||
|
需要注意:参考的是**交互模式和信息组织方式**,不是复制 Bet365 的商标、Logo、图标、图片、文案或视觉资产。
|
||||||
|
|
||||||
|
### 4.2 移动端优先布局
|
||||||
|
|
||||||
|
第一版建议优先实现移动端 H5。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[顶部栏: Logo/语言/余额] --> B[Banner]
|
||||||
|
B --> C[公告走马灯]
|
||||||
|
C --> D[体育类型: 足球]
|
||||||
|
D --> E[联赛筛选]
|
||||||
|
E --> F[赛事列表]
|
||||||
|
F --> G[点击赔率加入投注单]
|
||||||
|
G --> H[底部投注单浮层]
|
||||||
|
H --> I[确认下注]
|
||||||
|
I --> J[下注成功页]
|
||||||
|
```
|
||||||
|
|
||||||
|
底部导航建议:
|
||||||
|
|
||||||
|
| Tab | 内容 |
|
||||||
|
|---|---|
|
||||||
|
| 首页 | Banner、公告、热门赛事、今日赛事。 |
|
||||||
|
| 足球 | 联赛筛选、赛事列表、赛事详情。 |
|
||||||
|
| 投注单 | 当前已选择投注项,支持单关/串关。 |
|
||||||
|
| 我的投注 | 未结算、已结算、已取消注单。 |
|
||||||
|
| 我的 | 余额、账单、返水、改密码、语言。 |
|
||||||
|
|
||||||
|
### 4.3 桌面端布局
|
||||||
|
|
||||||
|
桌面端采用三栏结构:
|
||||||
|
|
||||||
|
| 区域 | 内容 |
|
||||||
|
|---|---|
|
||||||
|
| 左侧 | 体育类型、联赛、日期筛选。 |
|
||||||
|
| 中间 | 赛事列表、盘口赔率、赛事详情。 |
|
||||||
|
| 右侧 | 投注单、余额、快捷投注金额、最近投注。 |
|
||||||
|
|
||||||
|
### 4.4 首页模块
|
||||||
|
|
||||||
|
| 模块 | 第一版要求 |
|
||||||
|
|---|---|
|
||||||
|
| 顶部栏 | Logo、语言切换、登录/余额、我的投注入口。 |
|
||||||
|
| Banner | 后台配置图片、标题、跳转链接、排序、语言。 |
|
||||||
|
| 公告走马灯 | 后台配置,支持多语言,点击可查看详情。 |
|
||||||
|
| 热门赛事 | 后台可设为热门;按开赛时间排序。 |
|
||||||
|
| 今日赛事 | 默认展示当天可下注赛事。 |
|
||||||
|
| 联赛筛选 | 按联赛折叠/展开。 |
|
||||||
|
| 投注单入口 | 移动端固定底部浮层,显示已选数量。 |
|
||||||
|
|
||||||
|
### 4.5 赛事列表页
|
||||||
|
|
||||||
|
列表页只展示核心盘口,避免页面过长。
|
||||||
|
|
||||||
|
| 列表页展示 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| 全场独赢盘 | 主胜 / 平 / 客胜。 |
|
||||||
|
| 全场让球 | 主队 / 客队,两项。 |
|
||||||
|
| 全场大/小 | 大 / 小,两项。 |
|
||||||
|
| 更多盘口入口 | 点击进入赛事详情页展示全部玩法。 |
|
||||||
|
|
||||||
|
### 4.6 赛事详情页
|
||||||
|
|
||||||
|
赛事详情页按盘口分组展示:
|
||||||
|
|
||||||
|
1. 全场独赢盘
|
||||||
|
2. 全场让球
|
||||||
|
3. 全场大/小
|
||||||
|
4. 全场单/双
|
||||||
|
5. 半场独赢盘
|
||||||
|
6. 半场让球
|
||||||
|
7. 半场大/小
|
||||||
|
8. 波胆
|
||||||
|
9. 上半场波胆
|
||||||
|
10. 下半场波胆
|
||||||
|
|
||||||
|
每个盘口组支持折叠/展开。赔率按钮显示:选项名、盘口线、赔率。
|
||||||
|
|
||||||
|
### 4.7 投注单交互
|
||||||
|
|
||||||
|
| 场景 | 交互 |
|
||||||
|
|---|---|
|
||||||
|
| 用户点击赔率 | 加入投注单;同一场同一盘口重复点击则取消选择。 |
|
||||||
|
| 单个选择项 | 展示单关投注金额输入框。 |
|
||||||
|
| 多个选择项 | 展示每项单关输入 + 串关区域。MVP 可默认只打开串关输入。 |
|
||||||
|
| 同场多个选项 | 投注单提示“同一场比赛不能串关”,但可分别作为单关投注。 |
|
||||||
|
| 赔率变化 | 提交时校验赔率版本;变化则提示重新确认。 |
|
||||||
|
| 盘口封盘 | 提交时提示盘口已关闭并移除该选择项。 |
|
||||||
|
| 余额不足 | 禁止提交并提示余额不足。 |
|
||||||
|
| 下注成功 | 展示注单号、投注金额、赔率、预计返还、投注时间。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 赛事、盘口与赔率管理
|
||||||
|
|
||||||
|
### 5.1 赛事结构
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[体育类型: 足球] --> B[联赛]
|
||||||
|
B --> C[比赛 Match]
|
||||||
|
C --> D[盘口 Market]
|
||||||
|
D --> E[选项 Selection]
|
||||||
|
E --> F[赔率 Odds]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 比赛字段
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| match_id | 比赛 ID。 |
|
||||||
|
| sport_type | 第一版固定 `FOOTBALL`。 |
|
||||||
|
| league_id | 联赛 ID。 |
|
||||||
|
| home_team_id | 主队。 |
|
||||||
|
| away_team_id | 客队。 |
|
||||||
|
| start_time | 开赛时间。 |
|
||||||
|
| status | 比赛状态。 |
|
||||||
|
| is_hot | 是否热门。 |
|
||||||
|
| display_order | 排序。 |
|
||||||
|
| publish_time | 发布时间。 |
|
||||||
|
| close_time | 封盘时间。 |
|
||||||
|
| created_by / updated_by | 操作人。 |
|
||||||
|
|
||||||
|
### 5.3 比赛状态流
|
||||||
|
|
||||||
|
| 状态 | 说明 | 可操作 |
|
||||||
|
|---|---|---|
|
||||||
|
| DRAFT 草稿 | 后台创建中,前台不可见。 | 编辑赛事、赔率。 |
|
||||||
|
| PUBLISHED 已发布 | 前台可见且可下注。 | 编辑赔率、手动封盘。 |
|
||||||
|
| CLOSED 已封盘 | 前台可见但不可下注。 | 录入赛果。 |
|
||||||
|
| PENDING_SETTLEMENT 待结算 | 已录入比分,等待结算确认。 | 查看结算预览、确认结算。 |
|
||||||
|
| SETTLED 已结算 | 已派彩/扣款。 | 超级管理员可发起重结算。 |
|
||||||
|
| CANCELLED 已取消 | 比赛取消,相关未结算注单退款。 | 查看退款流水。 |
|
||||||
|
| VOID 作废 | 异常赛事,不参与结算。 | 查看操作记录。 |
|
||||||
|
|
||||||
|
### 5.4 自动封盘
|
||||||
|
|
||||||
|
1. 默认在 `start_time` 到达时自动封盘。
|
||||||
|
2. 后台可提前手动封盘。
|
||||||
|
3. 投注提交时必须二次校验比赛状态、盘口状态、选项状态、赔率版本。
|
||||||
|
4. 后台开赛时间修改后,需要重新计算封盘任务。
|
||||||
|
|
||||||
|
### 5.5 赔率管理规则
|
||||||
|
|
||||||
|
1. 所有赔率使用十进制赔率 Decimal Odds,例如 `1.85`、`2.10`。
|
||||||
|
2. 后台修改赔率时生成新的 `odds_version`。
|
||||||
|
3. 已下注订单使用投注时快照,不受后续赔率修改影响。
|
||||||
|
4. 赔率最低值、最高值后台配置,避免误填。
|
||||||
|
5. 赔率必须大于 `1.00`。
|
||||||
|
6. 已发布赛事修改赔率需二次确认。
|
||||||
|
7. 支持批量编辑波胆赔率,类似表格/Excel 输入。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 第一版支持的下注类型
|
||||||
|
|
||||||
|
### 6.1 玩法总表
|
||||||
|
|
||||||
|
| 编码 | 中文名称 | 英文建议名 | 结算依据 | 是否支持单关 | 是否支持串关 |
|
||||||
|
|---|---|---|---|---:|---:|
|
||||||
|
| `FT_CORRECT_SCORE` | 波胆 | Full Time Correct Score | 全场比分 | 是 | 是 |
|
||||||
|
| `HT_CORRECT_SCORE` | 上半场波胆 | Half Time Correct Score | 半场比分 | 是 | 是 |
|
||||||
|
| `SH_CORRECT_SCORE` | 下半场波胆 | Second Half Correct Score | 下半场净比分 | 是 | 是 |
|
||||||
|
| `FT_HANDICAP` | 全场让球 | Full Time Asian Handicap | 全场比分 + 让球 | 是 | 条件支持 |
|
||||||
|
| `FT_OVER_UNDER` | 全场大/小 | Full Time Over/Under | 全场总进球 | 是 | 条件支持 |
|
||||||
|
| `FT_1X2` | 全场独赢盘 | Full Time 1X2 | 全场胜平负 | 是 | 是 |
|
||||||
|
| `FT_ODD_EVEN` | 全场单/双 | Full Time Odd/Even | 全场总进球单双 | 是 | 是 |
|
||||||
|
| `HT_HANDICAP` | 半场让球 | Half Time Asian Handicap | 半场比分 + 让球 | 是 | 条件支持 |
|
||||||
|
| `HT_OVER_UNDER` | 半场大/小 | Half Time Over/Under | 半场总进球 | 是 | 条件支持 |
|
||||||
|
| `HT_1X2` | 半场独赢盘 | Half Time 1X2 | 半场胜平负 | 是 | 是 |
|
||||||
|
| `OUTRIGHT_WINNER` | 冠军竞猜 | Outright Winner | 后台选择冠军 | 是 | 否 |
|
||||||
|
|
||||||
|
串关条件支持说明:
|
||||||
|
|
||||||
|
1. 只支持 `2 串 1` 到 `5 串 1`。
|
||||||
|
2. 不允许同一场比赛的多个选项进入同一串关单。
|
||||||
|
3. 四分之一让球 / 四分之一大小球不允许进入串关,例如 `-0.25`、`-0.75`、`2.25`、`2.75`。
|
||||||
|
4. 冠军竞猜不允许进入串关。
|
||||||
|
5. 已封盘、暂停、赔率变化未确认的选项不能提交。
|
||||||
|
|
||||||
|
### 6.2 波胆
|
||||||
|
|
||||||
|
波胆即预测准确比分。
|
||||||
|
|
||||||
|
| 类型 | 结算依据 | 示例 |
|
||||||
|
|---|---|---|
|
||||||
|
| 全场波胆 | 全场比分,90 分钟 + 全场伤停补时,不含加时和点球。 | 2-1、1-1、其他主胜。 |
|
||||||
|
| 上半场波胆 | 上半场比分,45 分钟 + 上半场伤停补时。 | 1-0、0-0、2-1。 |
|
||||||
|
| 下半场波胆 | 下半场净比分,即全场比分减去半场比分。 | 半场 1-1,全场 3-1,则下半场比分为 2-0。 |
|
||||||
|
|
||||||
|
#### 6.2.1 波胆比分模板
|
||||||
|
|
||||||
|
为降低后台录入成本,第一版使用固定模板。
|
||||||
|
|
||||||
|
全场波胆建议模板:
|
||||||
|
|
||||||
|
| 分类 | 选项 |
|
||||||
|
|---|---|
|
||||||
|
| 平局 | 0-0、1-1、2-2、3-3、4-4、其他平局 |
|
||||||
|
| 主胜 | 1-0、2-0、2-1、3-0、3-1、3-2、4-0、4-1、4-2、4-3、其他主胜 |
|
||||||
|
| 客胜 | 0-1、0-2、1-2、0-3、1-3、2-3、0-4、1-4、2-4、3-4、其他客胜 |
|
||||||
|
|
||||||
|
半场 / 下半场波胆建议模板:
|
||||||
|
|
||||||
|
| 分类 | 选项 |
|
||||||
|
|---|---|
|
||||||
|
| 平局 | 0-0、1-1、2-2、其他平局 |
|
||||||
|
| 主胜 | 1-0、2-0、2-1、3-0、其他主胜 |
|
||||||
|
| 客胜 | 0-1、0-2、1-2、0-3、其他客胜 |
|
||||||
|
|
||||||
|
#### 6.2.2 “其他比分”结算规则
|
||||||
|
|
||||||
|
1. 如果最终比分命中模板中的具体比分,具体比分选项赢,其他比分选项输。
|
||||||
|
2. 如果最终比分不在模板具体比分中,则按结果归类:
|
||||||
|
- 主队胜且不在具体比分中:其他主胜赢。
|
||||||
|
- 客队胜且不在具体比分中:其他客胜赢。
|
||||||
|
- 平局且不在具体比分中:其他平局赢。
|
||||||
|
|
||||||
|
### 6.3 全场让球 / 半场让球
|
||||||
|
|
||||||
|
让球采用亚洲让球两项盘。
|
||||||
|
|
||||||
|
| 项目 | 规则 |
|
||||||
|
|---|---|
|
||||||
|
| 全场让球 | 使用全场比分结算。 |
|
||||||
|
| 半场让球 | 使用半场比分结算。 |
|
||||||
|
| 盘口值 | 支持整数、半球、四分之一球。 |
|
||||||
|
| 单关 | 支持全赢、半赢、走水、半输、全输。 |
|
||||||
|
| 串关 | 第一版只允许整数和半球盘口进入串关;四分之一球不允许串关。 |
|
||||||
|
|
||||||
|
盘口值示例:`0`、`-0.25`、`-0.5`、`-0.75`、`-1`、`+0.25`、`+0.5`、`+0.75`、`+1`。
|
||||||
|
|
||||||
|
让球结算公式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
adjusted_score = selected_team_goals + handicap - opponent_goals
|
||||||
|
|
||||||
|
adjusted_score > 0 => 赢
|
||||||
|
adjusted_score = 0 => 走水
|
||||||
|
adjusted_score < 0 => 输
|
||||||
|
```
|
||||||
|
|
||||||
|
四分之一球拆分规则:
|
||||||
|
|
||||||
|
| 盘口 | 拆分为 |
|
||||||
|
|---|---|
|
||||||
|
| -0.25 | 0 和 -0.5 |
|
||||||
|
| +0.25 | 0 和 +0.5 |
|
||||||
|
| -0.75 | -0.5 和 -1 |
|
||||||
|
| +0.75 | +0.5 和 +1 |
|
||||||
|
| -1.25 | -1 和 -1.5 |
|
||||||
|
| +1.25 | +1 和 +1.5 |
|
||||||
|
| -1.75 | -1.5 和 -2 |
|
||||||
|
| +1.75 | +1.5 和 +2 |
|
||||||
|
|
||||||
|
### 6.4 全场大/小 / 半场大/小
|
||||||
|
|
||||||
|
| 项目 | 规则 |
|
||||||
|
|---|---|
|
||||||
|
| 全场大/小 | 使用全场总进球数。 |
|
||||||
|
| 半场大/小 | 使用半场总进球数。 |
|
||||||
|
| 盘口值 | 支持整数、半球、四分之一球。 |
|
||||||
|
| 单关 | 支持全赢、半赢、走水、半输、全输。 |
|
||||||
|
| 串关 | 第一版只允许整数和半球盘口进入串关;四分之一球不允许串关。 |
|
||||||
|
|
||||||
|
大小球结算示例:
|
||||||
|
|
||||||
|
| 盘口 | 选择 | 实际总进球 | 结果 |
|
||||||
|
|---|---|---:|---|
|
||||||
|
| 2.5 | 大 | 3 | 赢 |
|
||||||
|
| 2.5 | 小 | 3 | 输 |
|
||||||
|
| 2 | 大 | 2 | 走水 |
|
||||||
|
| 2.25 | 大 | 2 | 半输 |
|
||||||
|
| 2.75 | 大 | 3 | 半赢 |
|
||||||
|
|
||||||
|
四分之一大小球拆分规则:
|
||||||
|
|
||||||
|
| 盘口 | 拆分为 |
|
||||||
|
|---|---|
|
||||||
|
| 2.25 | 2 和 2.5 |
|
||||||
|
| 2.75 | 2.5 和 3 |
|
||||||
|
| 3.25 | 3 和 3.5 |
|
||||||
|
| 3.75 | 3.5 和 4 |
|
||||||
|
|
||||||
|
### 6.5 全场独赢盘 / 半场独赢盘
|
||||||
|
|
||||||
|
独赢盘采用足球三项盘:
|
||||||
|
|
||||||
|
| 选项 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| 主胜 | 主队进球数大于客队。 |
|
||||||
|
| 平局 | 主队进球数等于客队。 |
|
||||||
|
| 客胜 | 主队进球数小于客队。 |
|
||||||
|
|
||||||
|
全场独赢盘使用全场比分;半场独赢盘使用半场比分。
|
||||||
|
|
||||||
|
### 6.6 全场单/双
|
||||||
|
|
||||||
|
全场单/双按全场总进球数判断。
|
||||||
|
|
||||||
|
| 总进球 | 结果 |
|
||||||
|
|---:|---|
|
||||||
|
| 0 | 双 |
|
||||||
|
| 1 | 单 |
|
||||||
|
| 2 | 双 |
|
||||||
|
| 3 | 单 |
|
||||||
|
| 4 | 双 |
|
||||||
|
|
||||||
|
### 6.7 冠军竞猜
|
||||||
|
|
||||||
|
冠军竞猜作为特殊长期盘实现。
|
||||||
|
|
||||||
|
| 项目 | 第一版规则 |
|
||||||
|
|---|---|
|
||||||
|
| 类型 | 特殊赛事 / 长期盘。 |
|
||||||
|
| 玩法 | 只支持选择冠军队伍。 |
|
||||||
|
| 赔率 | 后台手动维护。 |
|
||||||
|
| 封盘 | 后台手动封盘或到达配置时间自动封盘。 |
|
||||||
|
| 开奖 | 后台选择冠军队伍,生成结算预览,确认后派彩。 |
|
||||||
|
| 串关 | 第一版不允许串关。 |
|
||||||
|
| 取消 | 后台可整盘取消,未结算注单退还本金。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 单关下注逻辑
|
||||||
|
|
||||||
|
### 7.1 单关注单流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as 玩家
|
||||||
|
participant FE as 前台
|
||||||
|
participant API as 后端API
|
||||||
|
participant DB as 数据库
|
||||||
|
|
||||||
|
U->>FE: 选择赔率
|
||||||
|
FE->>FE: 加入投注单
|
||||||
|
U->>FE: 输入金额并确认下注
|
||||||
|
FE->>API: 提交投注(selection_id, odds_version, stake, request_id)
|
||||||
|
API->>DB: 开启事务
|
||||||
|
API->>DB: 校验用户状态/余额/盘口状态/赔率版本/限额
|
||||||
|
API->>DB: 冻结投注本金
|
||||||
|
API->>DB: 创建注单和账变流水
|
||||||
|
API->>DB: 提交事务
|
||||||
|
API-->>FE: 返回下注成功
|
||||||
|
FE-->>U: 显示注单号和详情
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 投注校验
|
||||||
|
|
||||||
|
提交下注时后端必须校验:
|
||||||
|
|
||||||
|
1. 玩家账号状态为正常。
|
||||||
|
2. 玩家所属代理链状态正常。
|
||||||
|
3. 比赛状态为已发布且未封盘。
|
||||||
|
4. 盘口状态为开放。
|
||||||
|
5. 选项状态为开放。
|
||||||
|
6. 前端提交的 `odds_version` 与数据库当前版本一致。
|
||||||
|
7. 投注金额满足最小/最大单注限制。
|
||||||
|
8. 玩家可用余额足够。
|
||||||
|
9. 不超过玩家每日投注限额。
|
||||||
|
10. 不超过盘口单项最大投注额。
|
||||||
|
11. 不超过平台单注最高预计派彩。
|
||||||
|
12. 请求幂等号 `request_id` 未被使用。
|
||||||
|
|
||||||
|
### 7.3 赔率变化处理
|
||||||
|
|
||||||
|
第一版采用严格校验方式:
|
||||||
|
|
||||||
|
| 情况 | 处理 |
|
||||||
|
|---|---|
|
||||||
|
| 赔率未变化 | 正常下注。 |
|
||||||
|
| 赔率已变化 | 拒绝下注,返回最新赔率,提示玩家重新确认。 |
|
||||||
|
| 盘口已封盘 | 拒绝下注,从投注单移除。 |
|
||||||
|
| 选项已暂停 | 拒绝下注,提示该选项不可下注。 |
|
||||||
|
|
||||||
|
### 7.4 投注金额与预计返还
|
||||||
|
|
||||||
|
十进制赔率下:
|
||||||
|
|
||||||
|
```text
|
||||||
|
预计返还 = 投注金额 × 赔率
|
||||||
|
预计盈利 = 投注金额 × (赔率 - 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:投注 100,赔率 1.85。
|
||||||
|
|
||||||
|
```text
|
||||||
|
预计返还 = 100 × 1.85 = 185
|
||||||
|
预计盈利 = 100 × 0.85 = 85
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 串关玩法设计
|
||||||
|
|
||||||
|
### 8.1 第一版支持范围
|
||||||
|
|
||||||
|
| 项目 | 规则 |
|
||||||
|
|---|---|
|
||||||
|
| 串关类型 | 只支持 `2 串 1`、`3 串 1`、`4 串 1`、`5 串 1`。 |
|
||||||
|
| 系统串关 | 不支持。 |
|
||||||
|
| 同场串关 | 不支持。 |
|
||||||
|
| 最大串关场数 | 5 场。 |
|
||||||
|
| 最小串关场数 | 2 场。 |
|
||||||
|
| 冠军竞猜 | 不允许进入串关。 |
|
||||||
|
| 四分之一让球/大小球 | 不允许进入串关。 |
|
||||||
|
| 赔率 | 下单时锁定每个选项赔率。 |
|
||||||
|
| 结算 | 所有关联赛事结算后,系统自动结算串关。 |
|
||||||
|
|
||||||
|
### 8.2 串关可选盘口
|
||||||
|
|
||||||
|
| 玩法 | 是否可串 | 备注 |
|
||||||
|
|---|---:|---|
|
||||||
|
| 全场独赢盘 | 是 | 主胜/平/客胜。 |
|
||||||
|
| 半场独赢盘 | 是 | 主胜/平/客胜。 |
|
||||||
|
| 全场单/双 | 是 | 单/双。 |
|
||||||
|
| 波胆 | 是 | 高赔率,需受最高派彩限制。 |
|
||||||
|
| 上半场波胆 | 是 | 高赔率,需受最高派彩限制。 |
|
||||||
|
| 下半场波胆 | 是 | 高赔率,需受最高派彩限制。 |
|
||||||
|
| 全场让球 | 条件支持 | 仅整数、半球盘口可串。 |
|
||||||
|
| 半场让球 | 条件支持 | 仅整数、半球盘口可串。 |
|
||||||
|
| 全场大/小 | 条件支持 | 仅整数、半球盘口可串。 |
|
||||||
|
| 半场大/小 | 条件支持 | 仅整数、半球盘口可串。 |
|
||||||
|
| 冠军竞猜 | 否 | 长期盘,不进入第一版串关。 |
|
||||||
|
|
||||||
|
### 8.3 同场串关限制
|
||||||
|
|
||||||
|
同一张串关单中,不允许出现相同 `match_id` 的多个选择项。
|
||||||
|
|
||||||
|
示例:以下组合不允许作为串关:
|
||||||
|
|
||||||
|
| 赛事 | 选择项 |
|
||||||
|
|---|---|
|
||||||
|
| 曼联 vs 切尔西 | 曼联胜 |
|
||||||
|
| 曼联 vs 切尔西 | 全场大 2.5 |
|
||||||
|
|
||||||
|
原因:同一场比赛的不同盘口存在强相关性,第一版先禁止。
|
||||||
|
|
||||||
|
### 8.4 串关计算
|
||||||
|
|
||||||
|
如果所有选择项赢:
|
||||||
|
|
||||||
|
```text
|
||||||
|
串关返还 = 投注金额 × 赔率1 × 赔率2 × ... × 赔率N
|
||||||
|
串关盈利 = 串关返还 - 投注金额
|
||||||
|
```
|
||||||
|
|
||||||
|
如果任意一关输:
|
||||||
|
|
||||||
|
```text
|
||||||
|
串关注单 = 输
|
||||||
|
返还 = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
如果某一关走水 / 取消 / 作废:
|
||||||
|
|
||||||
|
```text
|
||||||
|
该关赔率按 1.00 计算,其余关继续结算
|
||||||
|
```
|
||||||
|
|
||||||
|
如果所有关都走水 / 取消 / 作废:
|
||||||
|
|
||||||
|
```text
|
||||||
|
整张串关注单退还本金
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
| 关数 | 原赔率 | 结果 | 有效赔率 |
|
||||||
|
|---:|---:|---|---:|
|
||||||
|
| 1 | 1.80 | 赢 | 1.80 |
|
||||||
|
| 2 | 2.00 | 走水 | 1.00 |
|
||||||
|
| 3 | 1.90 | 赢 | 1.90 |
|
||||||
|
|
||||||
|
投注 100:
|
||||||
|
|
||||||
|
```text
|
||||||
|
返还 = 100 × 1.80 × 1.00 × 1.90 = 342
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.5 串关限制配置
|
||||||
|
|
||||||
|
后台需支持以下配置:
|
||||||
|
|
||||||
|
| 配置项 | 建议默认值 |
|
||||||
|
|---|---:|
|
||||||
|
| 最小串关数量 | 2 |
|
||||||
|
| 最大串关数量 | 5 |
|
||||||
|
| 单张串关最低投注额 | 1 |
|
||||||
|
| 单张串关最高投注额 | 视币种配置 |
|
||||||
|
| 单张串关最高派彩金额 | 视币种配置 |
|
||||||
|
| 玩家每日串关投注上限 | 视运营配置 |
|
||||||
|
| 玩家每日串关派彩上限 | 视运营配置 |
|
||||||
|
| 是否允许波胆串关 | 默认允许,但受派彩上限限制 |
|
||||||
|
| 是否允许同场串关 | 默认关闭 |
|
||||||
|
| 是否允许四分之一球串关 | 默认关闭 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 结算规则
|
||||||
|
|
||||||
|
### 9.1 比分口径
|
||||||
|
|
||||||
|
| 类型 | 口径 |
|
||||||
|
|---|---|
|
||||||
|
| 全场比分 | 90 分钟 + 全场伤停补时,不含加时赛和点球大战。 |
|
||||||
|
| 半场比分 | 上半场 45 分钟 + 上半场伤停补时。 |
|
||||||
|
| 下半场比分 | 全场比分 - 半场比分。 |
|
||||||
|
| 冠军竞猜 | 后台选择的最终冠军队伍。 |
|
||||||
|
|
||||||
|
### 9.2 单关结算状态
|
||||||
|
|
||||||
|
| 状态 | 说明 | 返还公式 |
|
||||||
|
|---|---|---|
|
||||||
|
| WIN 全赢 | 下注选项完全赢 | `stake × odds` |
|
||||||
|
| HALF_WIN 半赢 | 四分之一球出现半赢 | `stake/2 × odds + stake/2` |
|
||||||
|
| PUSH 走水 | 盘口打平 | `stake` |
|
||||||
|
| HALF_LOSE 半输 | 四分之一球出现半输 | `stake/2` |
|
||||||
|
| LOSE 全输 | 下注选项输 | `0` |
|
||||||
|
| VOID 作废 | 比赛/盘口取消 | `stake` |
|
||||||
|
|
||||||
|
### 9.3 结算流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[管理员进入赛事结算页] --> B[录入半场比分]
|
||||||
|
B --> C[录入全场比分]
|
||||||
|
C --> D[点击生成结算预览]
|
||||||
|
D --> E[系统计算所有相关单关选择项]
|
||||||
|
E --> F[系统计算受影响串关]
|
||||||
|
F --> G[展示注单数量/应扣/应派/退款/异常]
|
||||||
|
G --> H{管理员确认?}
|
||||||
|
H -- 否 --> I[返回修改比分或取消]
|
||||||
|
H -- 是 --> J[系统事务入账]
|
||||||
|
J --> K[更新注单状态]
|
||||||
|
K --> L[生成账变流水]
|
||||||
|
L --> M[更新玩家钱包]
|
||||||
|
M --> N[更新代理额度占用]
|
||||||
|
N --> O[生成结算日志]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.4 结算预览
|
||||||
|
|
||||||
|
确认入账前必须展示:
|
||||||
|
|
||||||
|
| 指标 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| 比赛信息 | 联赛、主队、客队、开赛时间。 |
|
||||||
|
| 录入比分 | 半场比分、全场比分、自动计算下半场比分。 |
|
||||||
|
| 待结算单关注单数 | 本场相关单关数量。 |
|
||||||
|
| 待更新串关注单数 | 因本场结果影响的串关数量。 |
|
||||||
|
| 全赢金额 | 预计派彩金额。 |
|
||||||
|
| 输单金额 | 平台扣除本金金额。 |
|
||||||
|
| 走水/作废退款金额 | 需返还本金。 |
|
||||||
|
| 半赢/半输金额 | 四分之一盘口影响。 |
|
||||||
|
| 异常项 | 无赔率快照、注单状态异常、重复结算风险。 |
|
||||||
|
| 操作人 | 当前管理员。 |
|
||||||
|
|
||||||
|
### 9.5 重结算与冲正
|
||||||
|
|
||||||
|
如果比分录错或结算错误,允许超级管理员重结算。
|
||||||
|
|
||||||
|
重结算规则:
|
||||||
|
|
||||||
|
1. 不允许直接改余额。
|
||||||
|
2. 必须生成反向冲正账变。
|
||||||
|
3. 必须保留原结算记录和新结算记录。
|
||||||
|
4. 必须记录操作原因。
|
||||||
|
5. 重结算前生成影响预览:影响玩家数、注单数、需扣回金额、需补发金额。
|
||||||
|
6. 如果玩家余额不足以扣回,应形成负余额或异常待处理;MVP 默认允许负余额展示并冻结提现/下分。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 钱包、账变与代理额度
|
||||||
|
|
||||||
|
### 10.1 玩家钱包
|
||||||
|
|
||||||
|
玩家钱包字段:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| available_balance | 可用余额,可下注金额。 |
|
||||||
|
| frozen_balance | 冻结金额,未结算投注本金。 |
|
||||||
|
| total_balance | 总余额 = 可用余额 + 冻结金额。 |
|
||||||
|
| currency | 币种。 |
|
||||||
|
| status | 正常、冻结、禁用。 |
|
||||||
|
|
||||||
|
下注时:
|
||||||
|
|
||||||
|
```text
|
||||||
|
available_balance -= stake
|
||||||
|
frozen_balance += stake
|
||||||
|
```
|
||||||
|
|
||||||
|
结算赢:
|
||||||
|
|
||||||
|
```text
|
||||||
|
frozen_balance -= stake
|
||||||
|
available_balance += payout
|
||||||
|
```
|
||||||
|
|
||||||
|
结算输:
|
||||||
|
|
||||||
|
```text
|
||||||
|
frozen_balance -= stake
|
||||||
|
available_balance += 0
|
||||||
|
```
|
||||||
|
|
||||||
|
走水/作废:
|
||||||
|
|
||||||
|
```text
|
||||||
|
frozen_balance -= stake
|
||||||
|
available_balance += stake
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 账变流水类型
|
||||||
|
|
||||||
|
| 类型 | 说明 | 余额影响 |
|
||||||
|
|---|---|---|
|
||||||
|
| MANUAL_DEPOSIT | 后台/代理上分 | 增加可用余额。 |
|
||||||
|
| MANUAL_WITHDRAW | 后台/代理下分 | 减少可用余额。 |
|
||||||
|
| BET_FREEZE | 投注冻结 | 可用减少、冻结增加。 |
|
||||||
|
| BET_SETTLE_WIN | 赢单派彩 | 冻结减少、可用增加派彩金额。 |
|
||||||
|
| BET_SETTLE_LOSE | 输单扣除 | 冻结减少。 |
|
||||||
|
| BET_SETTLE_PUSH | 走水退回 | 冻结减少、可用增加本金。 |
|
||||||
|
| BET_VOID_REFUND | 作废退款 | 冻结减少、可用增加本金。 |
|
||||||
|
| CASHBACK | 玩家返水 | 增加可用余额。 |
|
||||||
|
| RESETTLE_REVERSE | 重结算冲正 | 按冲正方向变化。 |
|
||||||
|
| MANUAL_ADJUST | 人工调整 | 仅超级管理员,必须备注。 |
|
||||||
|
|
||||||
|
账变流水必须包含:
|
||||||
|
|
||||||
|
- `transaction_id`
|
||||||
|
- `user_id`
|
||||||
|
- `wallet_id`
|
||||||
|
- `transaction_type`
|
||||||
|
- `amount`
|
||||||
|
- `balance_before`
|
||||||
|
- `balance_after`
|
||||||
|
- `frozen_before`
|
||||||
|
- `frozen_after`
|
||||||
|
- `reference_type`
|
||||||
|
- `reference_id`
|
||||||
|
- `operator_id`
|
||||||
|
- `remark`
|
||||||
|
- `created_at`
|
||||||
|
|
||||||
|
### 10.3 代理额度模型
|
||||||
|
|
||||||
|
第一版采用信用额度池,不把代理额度等同于真实资金账户。
|
||||||
|
|
||||||
|
核心字段:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| credit_limit | 上级授予该代理的总额度。 |
|
||||||
|
| used_credit | 已占用额度。 |
|
||||||
|
| available_credit | 可用额度 = credit_limit - used_credit。 |
|
||||||
|
| direct_player_liability | 直属玩家总余额占用。 |
|
||||||
|
| child_agent_exposure | 下级代理占用。 |
|
||||||
|
| status | 正常、额度不足、冻结、禁用。 |
|
||||||
|
|
||||||
|
推荐公式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
agent_used_credit = direct_player_total_balance + child_agent_exposure
|
||||||
|
agent_available_credit = agent_credit_limit - agent_used_credit
|
||||||
|
|
||||||
|
child_agent_exposure = max(child_agent_credit_limit, child_agent_used_credit)
|
||||||
|
```
|
||||||
|
|
||||||
|
解释:
|
||||||
|
|
||||||
|
1. 代理给玩家上分,玩家总余额增加,代理额度被占用。
|
||||||
|
2. 玩家下注时,可用余额变成冻结金额,总余额不变,因此代理额度不变。
|
||||||
|
3. 玩家输掉,玩家总余额减少,代理额度释放。
|
||||||
|
4. 玩家赢了,玩家总余额增加,代理额度进一步占用。
|
||||||
|
5. 代理给下级代理分配额度,会占用自己的额度。
|
||||||
|
6. 如果下级代理因玩家赢钱导致实际占用超过其授信额度,上级看到的下级占用按更大的实际占用计算。
|
||||||
|
7. 代理额度可以显示为负数,但负数时禁止继续上分或继续给下级分配额度。
|
||||||
|
|
||||||
|
### 10.4 代理额度示例
|
||||||
|
|
||||||
|
平台给一级代理 A 授信 10,000。
|
||||||
|
|
||||||
|
| 动作 | A 可用额度变化 |
|
||||||
|
|---|---:|
|
||||||
|
| 初始 | 10,000 |
|
||||||
|
| A 给玩家 P 上分 1,000 | 9,000 |
|
||||||
|
| A 给二级代理 B 分配额度 3,000 | 6,000 |
|
||||||
|
| P 下注 500 未结算 | 6,000,总余额不变 |
|
||||||
|
| P 输掉 500 | 6,500,玩家余额减少 |
|
||||||
|
| P 赢 2,000 | 4,500,玩家余额增加 |
|
||||||
|
| B 名下玩家赢钱导致 B 实际占用 4,000 | A 可用额度减少为 3,500,因为 B 实际占用超过授信 3,000 |
|
||||||
|
|
||||||
|
### 10.5 上分/下分规则
|
||||||
|
|
||||||
|
| 场景 | 规则 |
|
||||||
|
|---|---|
|
||||||
|
| 平台给玩家上分 | 超级管理员/财务管理员可操作,直接入账并生成流水。 |
|
||||||
|
| 代理给直属玩家上分 | 受代理可用额度、单笔限额、日限额限制。 |
|
||||||
|
| 代理给玩家下分 | 只能操作直属玩家;玩家可用余额必须足够。 |
|
||||||
|
| 平台给代理授信 | 增加代理 `credit_limit`。 |
|
||||||
|
| 代理给下级代理分配额度 | 增加下级 `credit_limit`,占用本级额度。 |
|
||||||
|
| 回收下级代理额度 | 下级实际占用不能超过回收后的额度,否则禁止回收或提示会导致负额度。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 返水机制
|
||||||
|
|
||||||
|
### 11.1 第一版返水策略
|
||||||
|
|
||||||
|
第一版只做玩家返水,不做自动代理佣金。
|
||||||
|
|
||||||
|
| 项目 | 规则 |
|
||||||
|
|---|---|
|
||||||
|
| 计算方式 | 按有效投注额 × 返水比例。 |
|
||||||
|
| 发放方式 | 后台生成返水报表,管理员确认后入账。 |
|
||||||
|
| 配置维度 | 可按玩家、代理线、玩法类型设置返水比例。MVP 可先按玩家或代理线配置。 |
|
||||||
|
| 发放周期 | 日结或周结,后台可选时间范围。 |
|
||||||
|
| 入账方式 | 生成 `CASHBACK` 账变流水,增加玩家可用余额。 |
|
||||||
|
|
||||||
|
### 11.2 有效投注额口径
|
||||||
|
|
||||||
|
计入有效投注额:
|
||||||
|
|
||||||
|
1. 已结算且非作废注单。
|
||||||
|
2. 赢单、输单、半赢、半输均按原始投注金额计入。
|
||||||
|
3. 串关注单按串关本金计入一次。
|
||||||
|
|
||||||
|
不计入有效投注额:
|
||||||
|
|
||||||
|
1. 未结算注单。
|
||||||
|
2. 已取消注单。
|
||||||
|
3. 作废退款注单。
|
||||||
|
4. 全走水注单。
|
||||||
|
5. 重结算冲正流水本身。
|
||||||
|
|
||||||
|
### 11.3 返水批次流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[选择周期] --> B[系统统计有效投注额]
|
||||||
|
B --> C[生成返水预览]
|
||||||
|
C --> D[财务确认]
|
||||||
|
D --> E[生成返水批次]
|
||||||
|
E --> F[逐玩家入账]
|
||||||
|
F --> G[生成账变流水]
|
||||||
|
G --> H[玩家前台查看返水记录]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 后台管理功能
|
||||||
|
|
||||||
|
### 12.1 平台后台模块
|
||||||
|
|
||||||
|
| 模块 | 功能 |
|
||||||
|
|---|---|
|
||||||
|
| 控制台 | 今日投注额、派彩额、平台盈亏、待结算赛事、异常注单、代理额度预警。 |
|
||||||
|
| 用户管理 | 玩家列表、新增玩家、禁用/启用、重置密码、查看钱包、查看注单、查看账变。 |
|
||||||
|
| 代理管理 | 新增代理、代理层级树、授信额度、回收额度、代理报表、禁用代理。 |
|
||||||
|
| 财务管理 | 上分、下分、账变流水、额度流水、人工调整、导出报表。 |
|
||||||
|
| 赛事管理 | 联赛、球队、比赛、开赛时间、热门标记、发布/封盘/取消。 |
|
||||||
|
| 盘口管理 | 为比赛启用玩法模板、录入盘口值、录入赔率、批量修改。 |
|
||||||
|
| 开奖结算 | 录入半场/全场比分、生成预览、确认结算、查看结算日志、重结算。 |
|
||||||
|
| 注单管理 | 单关注单、串关注单、按玩家/代理/赛事/状态筛选。 |
|
||||||
|
| 返水管理 | 返水比例配置、生成返水批次、确认发放、返水记录。 |
|
||||||
|
| 内容管理 | Banner、公告、走马灯、多语言内容、排序、上下架。 |
|
||||||
|
| 多语言管理 | 语言包 key、翻译缺失检查、导入导出。 |
|
||||||
|
| 系统设置 | 限额配置、赔率范围、串关规则、币种、小数位、时区。 |
|
||||||
|
| 操作日志 | 查询所有后台、代理后台关键操作记录。 |
|
||||||
|
|
||||||
|
### 12.2 赛事创建流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[创建联赛/选择联赛] --> B[创建比赛]
|
||||||
|
B --> C[选择主队/客队]
|
||||||
|
C --> D[设置开赛时间]
|
||||||
|
D --> E[选择玩法模板]
|
||||||
|
E --> F[系统生成盘口结构]
|
||||||
|
F --> G[批量录入赔率]
|
||||||
|
G --> H[保存草稿]
|
||||||
|
H --> I[发布比赛]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.3 盘口模板
|
||||||
|
|
||||||
|
后台创建比赛时,建议提供一键生成模板:
|
||||||
|
|
||||||
|
| 模板 | 自动生成内容 |
|
||||||
|
|---|---|
|
||||||
|
| 全场独赢盘 | 主胜、平局、客胜。 |
|
||||||
|
| 半场独赢盘 | 主胜、平局、客胜。 |
|
||||||
|
| 全场让球 | 主队、客队,需填写盘口值和赔率。 |
|
||||||
|
| 半场让球 | 主队、客队,需填写盘口值和赔率。 |
|
||||||
|
| 全场大/小 | 大、小,需填写盘口线和赔率。 |
|
||||||
|
| 半场大/小 | 大、小,需填写盘口线和赔率。 |
|
||||||
|
| 全场单/双 | 单、双。 |
|
||||||
|
| 波胆 | 按固定比分模板生成选项。 |
|
||||||
|
| 上半场波胆 | 按半场比分模板生成选项。 |
|
||||||
|
| 下半场波胆 | 按半场比分模板生成选项。 |
|
||||||
|
| 冠军竞猜 | 批量添加参赛队伍。 |
|
||||||
|
|
||||||
|
### 12.4 高危操作二次确认
|
||||||
|
|
||||||
|
以下操作必须二次确认并写入操作日志:
|
||||||
|
|
||||||
|
1. 大额上分/下分。
|
||||||
|
2. 代理额度调整。
|
||||||
|
3. 已发布赛事修改开赛时间。
|
||||||
|
4. 已发布赛事修改赔率。
|
||||||
|
5. 手动封盘。
|
||||||
|
6. 取消赛事。
|
||||||
|
7. 确认结算。
|
||||||
|
8. 发起重结算。
|
||||||
|
9. 发放返水批次。
|
||||||
|
10. 禁用代理或玩家。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 数据库设计建议
|
||||||
|
|
||||||
|
### 13.1 核心表清单
|
||||||
|
|
||||||
|
| 表名 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| `users` | 用户基础表,包含玩家、代理、后台用户。 |
|
||||||
|
| `user_auth` | 登录账号、密码哈希、登录失败次数、最后登录。 |
|
||||||
|
| `roles` / `permissions` | 后台角色权限。 |
|
||||||
|
| `agent_profiles` | 代理资料、层级、上级、额度。 |
|
||||||
|
| `agent_closure` | 代理树闭包表,便于查询下级。 |
|
||||||
|
| `wallets` | 玩家钱包。 |
|
||||||
|
| `wallet_transactions` | 玩家账变流水。 |
|
||||||
|
| `agent_credit_transactions` | 代理额度流水。 |
|
||||||
|
| `leagues` | 联赛。 |
|
||||||
|
| `teams` | 球队。 |
|
||||||
|
| `matches` | 比赛。 |
|
||||||
|
| `match_scores` | 半场、全场、下半场比分。 |
|
||||||
|
| `market_types` | 玩法类型配置。 |
|
||||||
|
| `markets` | 比赛下的盘口。 |
|
||||||
|
| `market_selections` | 盘口选项。 |
|
||||||
|
| `odds_change_logs` | 赔率变更日志。 |
|
||||||
|
| `bets` | 注单主表,单关和串关。 |
|
||||||
|
| `bet_selections` | 注单明细,每个选择项一行。 |
|
||||||
|
| `settlement_batches` | 结算批次。 |
|
||||||
|
| `settlement_items` | 结算明细。 |
|
||||||
|
| `cashback_rules` | 返水规则。 |
|
||||||
|
| `cashback_batches` | 返水批次。 |
|
||||||
|
| `cashback_items` | 玩家返水明细。 |
|
||||||
|
| `contents` | Banner、公告、走马灯。 |
|
||||||
|
| `content_translations` | 内容多语言。 |
|
||||||
|
| `entity_translations` | 联赛、球队、赛事多语言名称。 |
|
||||||
|
| `i18n_messages` | 系统语言包。 |
|
||||||
|
| `audit_logs` | 操作日志。 |
|
||||||
|
|
||||||
|
### 13.2 注单主表关键字段
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE bets (
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
bet_no VARCHAR(64) NOT NULL UNIQUE,
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
agent_id BIGINT,
|
||||||
|
bet_type VARCHAR(20) NOT NULL, -- SINGLE / PARLAY
|
||||||
|
stake DECIMAL(18, 4) NOT NULL,
|
||||||
|
total_odds DECIMAL(18, 6),
|
||||||
|
potential_return DECIMAL(18, 4),
|
||||||
|
actual_return DECIMAL(18, 4) DEFAULT 0,
|
||||||
|
status VARCHAR(32) NOT NULL, -- PENDING / WON / LOST / PUSH / VOID / CANCELLED / SETTLED
|
||||||
|
settlement_status VARCHAR(32),
|
||||||
|
currency VARCHAR(16) NOT NULL,
|
||||||
|
request_id VARCHAR(128) NOT NULL,
|
||||||
|
placed_at TIMESTAMP NOT NULL,
|
||||||
|
settled_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP NOT NULL,
|
||||||
|
updated_at TIMESTAMP NOT NULL,
|
||||||
|
UNIQUE(user_id, request_id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.3 注单明细表关键字段
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE bet_selections (
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
bet_id BIGINT NOT NULL,
|
||||||
|
match_id BIGINT,
|
||||||
|
market_id BIGINT NOT NULL,
|
||||||
|
selection_id BIGINT NOT NULL,
|
||||||
|
market_type VARCHAR(64) NOT NULL,
|
||||||
|
period VARCHAR(16), -- FT / HT / SH / OUTRIGHT
|
||||||
|
selection_name_snapshot VARCHAR(255) NOT NULL,
|
||||||
|
handicap_line DECIMAL(8, 2),
|
||||||
|
total_line DECIMAL(8, 2),
|
||||||
|
odds DECIMAL(18, 6) NOT NULL,
|
||||||
|
odds_version BIGINT NOT NULL,
|
||||||
|
result_status VARCHAR(32), -- WIN / HALF_WIN / PUSH / HALF_LOSE / LOSE / VOID
|
||||||
|
effective_odds DECIMAL(18, 6),
|
||||||
|
sort_order INT,
|
||||||
|
created_at TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 13.4 市场与选项字段
|
||||||
|
|
||||||
|
`markets`:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| id | 盘口 ID。 |
|
||||||
|
| match_id | 所属比赛。 |
|
||||||
|
| market_type | 玩法类型。 |
|
||||||
|
| period | FT / HT / SH / OUTRIGHT。 |
|
||||||
|
| line_value | 让球线或大小球线,可为空。 |
|
||||||
|
| status | OPEN / SUSPENDED / CLOSED。 |
|
||||||
|
| allow_single | 是否允许单关。 |
|
||||||
|
| allow_parlay | 是否允许串关。 |
|
||||||
|
| sort_order | 排序。 |
|
||||||
|
|
||||||
|
`market_selections`:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| id | 选项 ID。 |
|
||||||
|
| market_id | 所属盘口。 |
|
||||||
|
| selection_code | HOME / DRAW / AWAY / OVER / UNDER / ODD / EVEN / SCORE_2_1 等。 |
|
||||||
|
| selection_name | 默认名称。 |
|
||||||
|
| odds | 当前赔率。 |
|
||||||
|
| odds_version | 当前赔率版本。 |
|
||||||
|
| status | OPEN / SUSPENDED / CLOSED。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. API 范围建议
|
||||||
|
|
||||||
|
### 14.1 玩家前台 API
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/api/player/auth/login` | 玩家登录。 |
|
||||||
|
| POST | `/api/player/auth/logout` | 退出登录。 |
|
||||||
|
| POST | `/api/player/auth/change-password` | 修改密码。 |
|
||||||
|
| GET | `/api/player/profile` | 玩家资料、余额、语言。 |
|
||||||
|
| POST | `/api/player/language` | 切换语言。 |
|
||||||
|
| GET | `/api/player/home` | 首页 Banner、公告、热门赛事。 |
|
||||||
|
| GET | `/api/player/matches` | 赛事列表。 |
|
||||||
|
| GET | `/api/player/matches/{id}` | 赛事详情和全部盘口。 |
|
||||||
|
| POST | `/api/player/bets/single` | 提交单关注单。 |
|
||||||
|
| POST | `/api/player/bets/parlay` | 提交串关注单。 |
|
||||||
|
| GET | `/api/player/bets` | 我的投注列表。 |
|
||||||
|
| GET | `/api/player/bets/{bet_no}` | 注单详情。 |
|
||||||
|
| GET | `/api/player/wallet/transactions` | 账变流水。 |
|
||||||
|
| GET | `/api/player/cashbacks` | 返水记录。 |
|
||||||
|
|
||||||
|
### 14.2 平台后台 API
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/api/admin/auth/login` | 后台登录。 |
|
||||||
|
| GET | `/api/admin/dashboard` | 控制台指标。 |
|
||||||
|
| GET/POST | `/api/admin/users` | 玩家/用户管理。 |
|
||||||
|
| POST | `/api/admin/users/{id}/reset-password` | 重置密码。 |
|
||||||
|
| GET/POST | `/api/admin/agents` | 代理管理。 |
|
||||||
|
| POST | `/api/admin/agents/{id}/credit` | 调整代理额度。 |
|
||||||
|
| POST | `/api/admin/wallet/deposit` | 玩家上分。 |
|
||||||
|
| POST | `/api/admin/wallet/withdraw` | 玩家下分。 |
|
||||||
|
| GET | `/api/admin/wallet/transactions` | 账变查询。 |
|
||||||
|
| GET/POST | `/api/admin/leagues` | 联赛管理。 |
|
||||||
|
| GET/POST | `/api/admin/teams` | 球队管理。 |
|
||||||
|
| GET/POST | `/api/admin/matches` | 比赛管理。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/publish` | 发布比赛。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/close` | 封盘。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/cancel` | 取消比赛。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/markets/templates` | 生成盘口模板。 |
|
||||||
|
| PUT | `/api/admin/markets/{id}` | 更新盘口。 |
|
||||||
|
| PUT | `/api/admin/selections/{id}/odds` | 更新赔率。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/settlement/preview` | 生成结算预览。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/settlement/confirm` | 确认结算。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/resettle/preview` | 重结算预览。 |
|
||||||
|
| POST | `/api/admin/matches/{id}/resettle/confirm` | 确认重结算。 |
|
||||||
|
| GET | `/api/admin/bets` | 注单查询。 |
|
||||||
|
| POST | `/api/admin/cashbacks/preview` | 返水预览。 |
|
||||||
|
| POST | `/api/admin/cashbacks/confirm` | 确认发放返水。 |
|
||||||
|
| GET/POST | `/api/admin/contents` | Banner、公告、走马灯。 |
|
||||||
|
| GET/POST | `/api/admin/i18n/messages` | 语言包管理。 |
|
||||||
|
| GET | `/api/admin/audit-logs` | 操作日志。 |
|
||||||
|
|
||||||
|
### 14.3 代理后台 API
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/api/agent/auth/login` | 代理登录。 |
|
||||||
|
| GET | `/api/agent/profile` | 代理资料和额度。 |
|
||||||
|
| GET/POST | `/api/agent/players` | 直属玩家管理。 |
|
||||||
|
| GET/POST | `/api/agent/agents` | 下级代理管理,一级代理可用。 |
|
||||||
|
| POST | `/api/agent/players/{id}/deposit` | 给直属玩家上分。 |
|
||||||
|
| POST | `/api/agent/players/{id}/withdraw` | 给直属玩家下分。 |
|
||||||
|
| POST | `/api/agent/agents/{id}/credit` | 给直属下级代理分配额度。 |
|
||||||
|
| GET | `/api/agent/bets` | 下级注单查询。 |
|
||||||
|
| GET | `/api/agent/reports/summary` | 代理汇总报表。 |
|
||||||
|
| GET | `/api/agent/wallet-transactions` | 直属玩家账变查询。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. 幂等、事务与并发控制
|
||||||
|
|
||||||
|
### 15.1 必须幂等的操作
|
||||||
|
|
||||||
|
| 操作 | 幂等 Key |
|
||||||
|
|---|---|
|
||||||
|
| 单关下注 | `user_id + request_id` |
|
||||||
|
| 串关下注 | `user_id + request_id` |
|
||||||
|
| 玩家上分 | `operator_id + external_ref/request_id` |
|
||||||
|
| 玩家下分 | `operator_id + external_ref/request_id` |
|
||||||
|
| 代理额度调整 | `operator_id + request_id` |
|
||||||
|
| 确认结算 | `match_id + settlement_batch_id` |
|
||||||
|
| 重结算 | `match_id + resettle_batch_id` |
|
||||||
|
| 发放返水 | `cashback_batch_id` |
|
||||||
|
|
||||||
|
### 15.2 下注事务要求
|
||||||
|
|
||||||
|
下注必须在单个数据库事务内完成:
|
||||||
|
|
||||||
|
1. 锁定玩家钱包行。
|
||||||
|
2. 校验余额。
|
||||||
|
3. 校验比赛/盘口/选项状态。
|
||||||
|
4. 校验赔率版本。
|
||||||
|
5. 创建注单。
|
||||||
|
6. 创建注单明细。
|
||||||
|
7. 扣减可用余额、增加冻结金额。
|
||||||
|
8. 创建账变流水。
|
||||||
|
9. 提交事务。
|
||||||
|
|
||||||
|
### 15.3 结算事务要求
|
||||||
|
|
||||||
|
确认结算建议按批处理执行,但每批必须保证一致性:
|
||||||
|
|
||||||
|
1. 锁定待结算注单。
|
||||||
|
2. 防止重复结算。
|
||||||
|
3. 计算返还金额。
|
||||||
|
4. 更新注单状态。
|
||||||
|
5. 更新钱包余额。
|
||||||
|
6. 生成账变流水。
|
||||||
|
7. 更新代理额度统计。
|
||||||
|
8. 写入结算日志。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. 报表需求
|
||||||
|
|
||||||
|
### 16.1 平台报表
|
||||||
|
|
||||||
|
| 报表 | 维度 |
|
||||||
|
|---|---|
|
||||||
|
| 平台投注汇总 | 日期、玩法、联赛、代理线、币种。 |
|
||||||
|
| 平台盈亏报表 | 投注额、派彩额、返水额、平台净盈亏。 |
|
||||||
|
| 待结算报表 | 比赛、开赛时间、待结算注单数、预估风险。 |
|
||||||
|
| 玩家报表 | 玩家余额、投注额、输赢、返水、上分/下分。 |
|
||||||
|
| 代理报表 | 代理额度、可用额度、下级余额、下级投注额、下级输赢。 |
|
||||||
|
| 盘口报表 | 每场比赛每个盘口投注分布。 |
|
||||||
|
| 串关报表 | 串关投注额、派彩额、最高风险。 |
|
||||||
|
| 账变报表 | 所有余额变动流水。 |
|
||||||
|
|
||||||
|
### 16.2 代理报表
|
||||||
|
|
||||||
|
代理后台至少展示:
|
||||||
|
|
||||||
|
1. 当前总额度。
|
||||||
|
2. 已用额度。
|
||||||
|
3. 可用额度。
|
||||||
|
4. 直属玩家总余额。
|
||||||
|
5. 下级代理额度占用。
|
||||||
|
6. 今日投注额。
|
||||||
|
7. 今日派彩额。
|
||||||
|
8. 今日玩家输赢。
|
||||||
|
9. 下级玩家注单列表。
|
||||||
|
10. 上分/下分记录。
|
||||||
|
|
||||||
|
### 16.3 玩家报表
|
||||||
|
|
||||||
|
玩家前台展示:
|
||||||
|
|
||||||
|
1. 当前余额。
|
||||||
|
2. 未结算投注。
|
||||||
|
3. 已结算投注。
|
||||||
|
4. 赢/输/走水/作废状态。
|
||||||
|
5. 账变流水。
|
||||||
|
6. 返水记录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. 内容管理
|
||||||
|
|
||||||
|
### 17.1 Banner
|
||||||
|
|
||||||
|
字段:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| title | 标题,多语言。 |
|
||||||
|
| image_url | 图片,多语言可不同。 |
|
||||||
|
| link_type | 无跳转、赛事、公告、外链。 |
|
||||||
|
| link_target | 跳转目标。 |
|
||||||
|
| start_time / end_time | 展示时间。 |
|
||||||
|
| status | 草稿、启用、停用。 |
|
||||||
|
| sort_order | 排序。 |
|
||||||
|
|
||||||
|
### 17.2 走马灯 / 公告
|
||||||
|
|
||||||
|
| 类型 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| 走马灯 | 首页顶部滚动短文案。 |
|
||||||
|
| 公告 | 可点击查看详情的完整内容。 |
|
||||||
|
| 系统通知 | 维护、封盘、活动等提示。 |
|
||||||
|
|
||||||
|
公告必须支持三语内容。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. 系统配置
|
||||||
|
|
||||||
|
### 18.1 投注限额配置
|
||||||
|
|
||||||
|
| 配置 | 维度 |
|
||||||
|
|---|---|
|
||||||
|
| 最小单注金额 | 全局 / 玩家等级 / 玩法。 |
|
||||||
|
| 最大单注金额 | 全局 / 玩家等级 / 玩法。 |
|
||||||
|
| 单张最高派彩 | 全局 / 玩法 / 串关。 |
|
||||||
|
| 玩家每日投注上限 | 玩家 / 代理线。 |
|
||||||
|
| 玩家每日派彩上限 | 玩家 / 代理线。 |
|
||||||
|
| 串关最小/最大场数 | 全局。 |
|
||||||
|
| 波胆最高派彩 | 玩法级。 |
|
||||||
|
| 赔率最小/最大值 | 全局。 |
|
||||||
|
|
||||||
|
### 18.2 账号安全配置
|
||||||
|
|
||||||
|
| 配置 | 建议默认值 |
|
||||||
|
|---|---:|
|
||||||
|
| 玩家登录失败锁定次数 | 5 次。 |
|
||||||
|
| 玩家锁定时长 | 15 分钟。 |
|
||||||
|
| 后台登录失败锁定次数 | 5 次。 |
|
||||||
|
| 后台二次验证 | 建议开启 Google Authenticator 或 IP 白名单。 |
|
||||||
|
| 密码最小长度 | 8 位。 |
|
||||||
|
| 密码复杂度 | 至少字母 + 数字。 |
|
||||||
|
| 会话过期 | 玩家 24 小时,后台 2 小时。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 19. 测试用例重点
|
||||||
|
|
||||||
|
### 19.1 下注测试
|
||||||
|
|
||||||
|
| 编号 | 场景 | 预期 |
|
||||||
|
|---|---|---|
|
||||||
|
| B001 | 正常单关下注 | 创建注单、冻结本金、生成账变。 |
|
||||||
|
| B002 | 余额不足下注 | 拒绝下注,不生成注单。 |
|
||||||
|
| B003 | 赔率变化后提交 | 拒绝下注,提示重新确认。 |
|
||||||
|
| B004 | 封盘后提交 | 拒绝下注。 |
|
||||||
|
| B005 | 重复点击提交 | 只生成一张注单。 |
|
||||||
|
| B006 | 串关 2 串 1 正常提交 | 创建串关注单和多个明细。 |
|
||||||
|
| B007 | 串关包含同场多个选择项 | 拒绝提交。 |
|
||||||
|
| B008 | 串关包含四分之一球 | 拒绝提交。 |
|
||||||
|
| B009 | 串关超过 5 场 | 拒绝提交。 |
|
||||||
|
| B010 | 冠军竞猜加入串关 | 拒绝提交。 |
|
||||||
|
|
||||||
|
### 19.2 结算测试
|
||||||
|
|
||||||
|
| 编号 | 场景 | 预期 |
|
||||||
|
|---|---|---|
|
||||||
|
| S001 | 全场独赢主胜 | 主胜注单赢,平/客输。 |
|
||||||
|
| S002 | 全场独赢平局 | 平局注单赢。 |
|
||||||
|
| S003 | 半场独赢 | 按半场比分结算。 |
|
||||||
|
| S004 | 全场单双 0-0 | 双赢。 |
|
||||||
|
| S005 | 全场波胆命中 2-1 | 2-1 赢,其他输。 |
|
||||||
|
| S006 | 全场波胆其他主胜 | 具体比分未列且主胜,其他主胜赢。 |
|
||||||
|
| S007 | 上半场波胆 | 按半场比分结算。 |
|
||||||
|
| S008 | 下半场波胆 | 按全场减半场后的净比分结算。 |
|
||||||
|
| S009 | 让球全赢 | 返还 stake × odds。 |
|
||||||
|
| S010 | 让球走水 | 退回本金。 |
|
||||||
|
| S011 | 让球半赢 | 返还 stake/2 × odds + stake/2。 |
|
||||||
|
| S012 | 让球半输 | 返还 stake/2。 |
|
||||||
|
| S013 | 大小球全赢 | 正确派彩。 |
|
||||||
|
| S014 | 大小球走水 | 退回本金。 |
|
||||||
|
| S015 | 大小球半赢/半输 | 正确拆分。 |
|
||||||
|
| S016 | 串关全中 | 按赔率连乘派彩。 |
|
||||||
|
| S017 | 串关一关输 | 整单输。 |
|
||||||
|
| S018 | 串关一关走水 | 该关赔率按 1.00。 |
|
||||||
|
| S019 | 串关全部走水 | 退回本金。 |
|
||||||
|
| S020 | 比赛取消 | 未结算注单退款。 |
|
||||||
|
| S021 | 重结算 | 生成冲正流水并更新新结果。 |
|
||||||
|
|
||||||
|
### 19.3 代理额度测试
|
||||||
|
|
||||||
|
| 编号 | 场景 | 预期 |
|
||||||
|
|---|---|---|
|
||||||
|
| A001 | 代理给玩家上分 | 玩家余额增加,代理可用额度减少。 |
|
||||||
|
| A002 | 玩家下注未结算 | 玩家总余额不变,代理额度不变。 |
|
||||||
|
| A003 | 玩家输单 | 玩家总余额减少,代理额度释放。 |
|
||||||
|
| A004 | 玩家赢单 | 玩家总余额增加,代理额度占用增加。 |
|
||||||
|
| A005 | 代理额度为负 | 禁止继续上分和分配下级额度。 |
|
||||||
|
| A006 | 一级代理给二级代理分额 | 一级代理可用额度减少。 |
|
||||||
|
| A007 | 非直属玩家上分 | 拒绝操作。 |
|
||||||
|
|
||||||
|
### 19.4 多语言测试
|
||||||
|
|
||||||
|
| 编号 | 场景 | 预期 |
|
||||||
|
|---|---|---|
|
||||||
|
| L001 | 切换中文 | 所有系统文案显示中文。 |
|
||||||
|
| L002 | 切换马来语 | 前台文案显示 Bahasa Melayu。 |
|
||||||
|
| L003 | 切换英文 | 前台文案显示英文。 |
|
||||||
|
| L004 | 球队名缺少马来语 | fallback 到英文或中文。 |
|
||||||
|
| L005 | Banner 不同语言图片 | 按当前语言展示正确图片。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 20. BUG 风险与控制方案
|
||||||
|
|
||||||
|
| 风险 | 可能后果 | 控制方案 |
|
||||||
|
|---|---|---|
|
||||||
|
| 赔率变化未校验 | 玩家按旧赔率下注,引发争议。 | 提交时校验 `odds_version`。 |
|
||||||
|
| 重复提交下注 | 重复扣款。 | `user_id + request_id` 幂等唯一约束。 |
|
||||||
|
| 结算重复执行 | 重复派彩。 | 结算批次唯一、注单状态锁、事务处理。 |
|
||||||
|
| 半赢/半输算错 | 钱包金额错误。 | 结算引擎单元测试覆盖四分之一盘口。 |
|
||||||
|
| 下半场波胆口径错 | 结算争议。 | 明确下半场净比分 = 全场 - 半场。 |
|
||||||
|
| 串关包含同场选项 | 强相关风险和规则争议。 | 后端强制校验 `match_id` 唯一。 |
|
||||||
|
| 玩家余额并发扣款 | 余额负数或超额下注。 | 钱包行级锁 / 乐观锁。 |
|
||||||
|
| 代理额度超用 | 代理无限放款。 | 每次上分、分额、派彩后重新计算额度。 |
|
||||||
|
| 人工改余额 | 账务不可追踪。 | 禁止直接改余额,只能账变。 |
|
||||||
|
| 比赛开始未封盘 | 开赛后仍可下注。 | 自动封盘任务 + 提交下注二次校验。 |
|
||||||
|
| 多语言缺失 | 页面显示混乱。 | fallback 机制 + 后台缺失翻译检查。 |
|
||||||
|
| 重结算扣回失败 | 玩家余额不足。 | 允许负余额并冻结下分,生成异常报表。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 21. 开发实施路径
|
||||||
|
|
||||||
|
### 21.1 推荐里程碑
|
||||||
|
|
||||||
|
#### 阶段 0:项目基础
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 前后台项目结构。
|
||||||
|
2. 数据库初始化。
|
||||||
|
3. 登录鉴权。
|
||||||
|
4. RBAC 权限框架。
|
||||||
|
5. 多语言框架。
|
||||||
|
6. 操作日志基础能力。
|
||||||
|
7. 钱包和账变基础表。
|
||||||
|
|
||||||
|
#### 阶段 1:账号、代理、钱包
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 平台后台创建玩家。
|
||||||
|
2. 平台后台创建一级代理。
|
||||||
|
3. 一级代理创建二级代理和玩家。
|
||||||
|
4. 二级代理创建玩家。
|
||||||
|
5. 代理额度池。
|
||||||
|
6. 玩家上分/下分。
|
||||||
|
7. 钱包流水。
|
||||||
|
8. 代理额度流水。
|
||||||
|
|
||||||
|
验收重点:代理权限隔离、额度计算、账变一致性。
|
||||||
|
|
||||||
|
#### 阶段 2:赛事、盘口、赔率
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 联赛管理。
|
||||||
|
2. 球队管理。
|
||||||
|
3. 比赛管理。
|
||||||
|
4. 盘口模板。
|
||||||
|
5. 波胆批量赔率编辑。
|
||||||
|
6. 发布/封盘/取消。
|
||||||
|
7. 前台赛事列表。
|
||||||
|
8. 前台赛事详情。
|
||||||
|
|
||||||
|
验收重点:赔率版本、封盘状态、多语言名称。
|
||||||
|
|
||||||
|
#### 阶段 3:下注引擎
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 投注单 UI。
|
||||||
|
2. 单关下注。
|
||||||
|
3. 串关下注。
|
||||||
|
4. 投注限额。
|
||||||
|
5. 赔率变化提示。
|
||||||
|
6. 注单列表和详情。
|
||||||
|
7. 钱包冻结。
|
||||||
|
|
||||||
|
验收重点:幂等、余额冻结、同场串关限制、四分之一球串关限制。
|
||||||
|
|
||||||
|
#### 阶段 4:结算引擎
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 录入半场/全场比分。
|
||||||
|
2. 结算预览。
|
||||||
|
3. 单关结算。
|
||||||
|
4. 串关结算。
|
||||||
|
5. 作废退款。
|
||||||
|
6. 重结算冲正。
|
||||||
|
7. 结算日志。
|
||||||
|
|
||||||
|
验收重点:所有玩法结算准确、重复结算防护、冲正账变。
|
||||||
|
|
||||||
|
#### 阶段 5:返水、报表、内容
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 返水规则。
|
||||||
|
2. 返水预览。
|
||||||
|
3. 返水确认发放。
|
||||||
|
4. 平台报表。
|
||||||
|
5. 代理报表。
|
||||||
|
6. 玩家账单。
|
||||||
|
7. Banner。
|
||||||
|
8. 公告和走马灯。
|
||||||
|
9. 多语言内容管理。
|
||||||
|
|
||||||
|
验收重点:返水有效投注额口径、报表与账变一致。
|
||||||
|
|
||||||
|
#### 阶段 6:UAT 与上线准备
|
||||||
|
|
||||||
|
交付内容:
|
||||||
|
|
||||||
|
1. 测试账号。
|
||||||
|
2. 测试赛事。
|
||||||
|
3. 全玩法测试用例。
|
||||||
|
4. 压测下注接口。
|
||||||
|
5. 压测结算批次。
|
||||||
|
6. 数据备份策略。
|
||||||
|
7. 上线回滚方案。
|
||||||
|
8. 后台操作培训文档。
|
||||||
|
|
||||||
|
### 21.2 MVP 上线验收清单
|
||||||
|
|
||||||
|
| 验收项 | 是否必须 |
|
||||||
|
|---|---:|
|
||||||
|
| 玩家可登录、改密码、切换语言 | 是 |
|
||||||
|
| 代理可创建直属玩家 | 是 |
|
||||||
|
| 代理可给直属玩家上分/下分 | 是 |
|
||||||
|
| 代理额度正确扣减和释放 | 是 |
|
||||||
|
| 后台可创建比赛和盘口 | 是 |
|
||||||
|
| 后台可批量录入波胆赔率 | 是 |
|
||||||
|
| 前台可展示赛事和盘口 | 是 |
|
||||||
|
| 玩家可单关下注 | 是 |
|
||||||
|
| 玩家可 2-5 串 1 | 是 |
|
||||||
|
| 同场串关被禁止 | 是 |
|
||||||
|
| 四分之一盘口不能进入串关 | 是 |
|
||||||
|
| 开赛自动封盘 | 是 |
|
||||||
|
| 后台可录入比分并生成预览 | 是 |
|
||||||
|
| 确认结算后钱包正确变化 | 是 |
|
||||||
|
| 玩家可查看注单和账变 | 是 |
|
||||||
|
| 后台可生成并发放返水 | 是 |
|
||||||
|
| Banner/公告/走马灯可配置 | 是 |
|
||||||
|
| 所有关键操作有日志 | 是 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 22. 二期规划
|
||||||
|
|
||||||
|
第一版稳定后,再考虑以下能力:
|
||||||
|
|
||||||
|
| 二期功能 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| 滚球 / Live | 实时盘口、赔率变化、下注延迟确认、危险进攻暂停。 |
|
||||||
|
| 系统串关 | 3 串 4、4 串 11、5 串 26 等组合。 |
|
||||||
|
| 同场组合投注 | 类似 Bet Builder 的同场多条件组合。 |
|
||||||
|
| Cash Out | 提前兑现。 |
|
||||||
|
| Edit Bet | 修改已下注串关。 |
|
||||||
|
| 自动赔率接口 | 接入第三方数据源。 |
|
||||||
|
| 自动赛果接口 | 自动获取比分和赛果。 |
|
||||||
|
| 多赔率格式 | Decimal、Malay、Hong Kong、Indonesian、American。 |
|
||||||
|
| 更多体育项目 | 篮球、网球、电竞等。 |
|
||||||
|
| 原生 App | iOS / Android。 |
|
||||||
|
| 代理佣金自动结算 | 按流水、净输赢、分层比例自动计算。 |
|
||||||
|
| 更复杂返水 | 按等级、玩法、时间段、活动自动返水。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 23. 研发交付建议
|
||||||
|
|
||||||
|
### 23.1 后端建议
|
||||||
|
|
||||||
|
1. 第一版采用单体后端,不建议微服务。
|
||||||
|
2. 下注、结算、钱包、额度必须使用数据库事务。
|
||||||
|
3. Redis 可用于会话、缓存、接口限流、封盘任务锁,但不能作为唯一账务来源。
|
||||||
|
4. 账务金额使用 `DECIMAL(18,4)` 或更高精度,禁止使用浮点数。
|
||||||
|
5. 赔率使用 `DECIMAL(18,6)`。
|
||||||
|
6. 所有核心表使用软删除或状态字段,不物理删除。
|
||||||
|
7. 所有账务接口必须写操作日志。
|
||||||
|
|
||||||
|
### 23.2 前端建议
|
||||||
|
|
||||||
|
1. 玩家前台优先移动端 H5。
|
||||||
|
2. 投注单做成全局状态,切换页面不丢失。
|
||||||
|
3. 投注提交后必须刷新余额和注单状态。
|
||||||
|
4. 赔率变化时清晰提示,不自动替玩家接受新赔率。
|
||||||
|
5. 多语言使用统一 i18n key,不把文案写死在组件里。
|
||||||
|
6. 波胆区域需要折叠和分组,否则页面过长。
|
||||||
|
|
||||||
|
### 23.3 测试建议
|
||||||
|
|
||||||
|
1. 结算引擎必须做单元测试。
|
||||||
|
2. 钱包和代理额度必须做集成测试。
|
||||||
|
3. 串关必须做全流程测试。
|
||||||
|
4. 上线前用固定比分回归全部玩法。
|
||||||
|
5. 重结算必须单独测试冲正逻辑。
|
||||||
|
6. 并发下注必须压测。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 24. 最终 MVP 业务规则定稿
|
||||||
|
|
||||||
|
以下规则作为第一版不可变更的默认规则,除非重新评审:
|
||||||
|
|
||||||
|
1. 足球普通赛事只按常规时间结算,即 90 分钟 + 伤停补时,不含加时和点球。
|
||||||
|
2. 半场玩法只按上半场比分结算。
|
||||||
|
3. 下半场波胆按下半场净比分结算。
|
||||||
|
4. 全场 0-0 的单/双结果为双。
|
||||||
|
5. 所有下注按下注时赔率快照结算。
|
||||||
|
6. 赔率变化必须重新确认,不能默认接受。
|
||||||
|
7. 开赛时间到达后自动封盘。
|
||||||
|
8. 后台录入比分后必须先生成结算预览,再确认入账。
|
||||||
|
9. 结算错误只能通过重结算和冲正账变修正,不能直接改余额。
|
||||||
|
10. 串关只支持 2-5 串 1。
|
||||||
|
11. 串关不允许同场。
|
||||||
|
12. 串关不允许冠军竞猜。
|
||||||
|
13. 串关不允许四分之一让球/大小球。
|
||||||
|
14. 串关中走水或作废的关卡按赔率 1.00 处理。
|
||||||
|
15. 玩家下注时冻结本金,结算后释放冻结并派彩/扣除/退款。
|
||||||
|
16. 代理采用信用额度池。
|
||||||
|
17. 玩家余额变化会影响所属代理额度占用。
|
||||||
|
18. 代理额度可以为负展示,但负数时禁止继续上分和分配额度。
|
||||||
|
19. 玩家返水按有效投注额计算,后台确认后发放。
|
||||||
|
20. 第一版不做滚球、系统串关、自动赔率源、自动赛果源、Cash Out、Edit Bet、Bet Builder。
|
||||||
Reference in New Issue
Block a user