Compare commits

...

4 Commits

Author SHA1 Message Date
78a2fb4fce feat(排行榜): 重写全服 Top100,补上「我的排名」,后台那条挪进 admin
起因是 Top100 的「已解决」「提交数」两列一直空白:列的 key 还是
snake_case(accepted_number / submission_number),而数据早在 c2a8120 拆掉
转换层后就是 camelCase 了。naive-ui 按 row[key] 取值,取到 undefined 就渲染
空白、不报错 —— 和 d3348f9 是同一个病根。

顺着这条线把整个端点重写了:

**上限不再由调用方传。** `top` 参数原来有三个调用方各传各的(100 / 10 / 0),
而它会覆盖 limit 与 offset、total 却按全量算,正是 36e4ac2 那个「每页都是同样
100 条」的成因。现在 100 写死在服务端,参数只剩 limit / offset。
「全服 Top10」不需要另一个上限,它就是这个榜的第一页。

**排序补了第三档 asc(user.id)。** 前两个键完全相同的学生在真实数据里成片存在
(都是 0/0),没有稳定兜底键时 postgres 每次返回的顺序可以不同,翻页会看到重复
或漏掉的人。老代码缺这一档。

**新增 me(我的全服名次)。** 名次 = 排在我前面的人数 + 1,三个排序键逐级比较,
与列表的 orderBy 逐字对应 —— 少比一级就会出现「显示第 7 名、实际排在表格第 9 行」。
榜上高亮我那一行,名次超出 100 时在 footer 单独给一行。未登录、教师/超管返回 null。

**后台那条搬去 /api/admin/rankings/users**(requireSuperAdmin,无上限)。
原来它走的是公开端点的 top=0 分支,也就是任何匿名请求都能 ?top=0&limit=250
翻走全校学生名单和个性签名 —— 而 /profiles/:username 恰恰为了收紧枚举面才做了
「匿名一律返回空」,注释里还专门点了 /rankings/users 的名。这条页面本来就是
requiresSuperAdmin,它调的另外两个接口也都是 requireSuperAdmin,守卫对得上。

顺手去掉恒真条件 gte(acceptedNumber, 0):该列是 notNull default 0。

同一次扫了全仓 218 个表格列定义,筛出 70 个没有 render 的(只有这些才靠 key
直接取值),比对全部类型定义里的字段名 —— 除这两处外没有漏网的。`_id` 和
`test_case` 是真字段名,不能改。另外收掉两处同类的雷:
admin/setting/config.vue 手写的 `interface Testcase` 字段名和类型都是错的
(真实数据是 createTime: number,不是 create_time: string),改用契约的
OrphanTestCase;serverColumns 里 last_heartbeat / create_time 两个残留 key
有 render 兜着没出事,一并改正。

实测(造 120 个探针用户,含 3 个 AC 与提交数完全相同的并列,验完已清库):
122 人时 total=100;offset=95 末页 5 条;offset=100 越界返回空且不发 SQL;
并列三人稳定占据前三;student(ac=2) 拿到 rank=121 走「不在榜上」分支;
把 ac 调到 450 时 rank=4 且表格第 4 行正是 student(名次与行号对得上);
升成超管后 me 变 null;后台端点 total=121 无上限、keyword=probe_01 命中 10 条、
未登录 401;传 top=1 已被忽略。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 17:57:55 -06:00
6e905085a1 chore(依赖): 后端升到 TypeScript 7,前端被 vue-tsc 挡住留在 5.9
apps/api 单独嵌套 typescript ^7.0.2,`bun run --filter '@oj2/api' typecheck`
从此跑的是原生版;根和 apps/web 留在 5.9.3。api 在 TS 7 下 0 error,
另外塞了个故意写错的文件复检过,确认它真在报错、不是静默跳过。

前端升不了:vue-tsc 启动时要 require.resolve("typescript/lib/tsc"),
而 TS 7 的 exports 里已经没有这个子路径,直接
ERR_PACKAGE_PATH_NOT_EXPORTED。vue-tsc@latest 就是仓库里的 3.3.11,
还没有支持 TS 7 的版本 —— 它 peer 写的是 typescript >=5.0.0,范围太松,
装上去不报冲突、一跑就炸。读源码看到 resolveTscPath 里唯一的适配分支是认
@typescript/typescript6,可见 Vue 侧目前给的路只到 6。硬上的代价是丢掉
刚清零的前端类型门禁,不值得。

顺带清掉一处假配置:apps/web 原本声明着 "typescript": "^7.0.2",是
ae1fb32 从 ojnext 原样搬过来的,**从来没生效过** —— vue-tsc 被 hoist 在根
node_modules/,解析 typescript 时看的是根上那份 5.9.3,压根看不见
apps/web 里嵌套的 7.0.2。留着它有两个坏处:白拉 20 个
@typescript/typescript-* 平台包;哪天 bun 换个 hoist 策略把它提到根上,
vue-tsc 就会以那个看不懂的 ERR_PACKAGE_PATH_NOT_EXPORTED 挂掉。改成显式
跟随根版本。

所以现在的分工是:**vue-tsc 把根上的 typescript 钉死在 5.x**,谁要升根
版本谁先解决 vue-tsc。等 vue-tsc 支持 TS 7,把 apps/api 那行删掉、
根上升到 7 即可。

验证(删空 node_modules 重装后):apps/api tsc 7.0.2 下 0 error、
check:routes 167 条无遮蔽、apps/web vue-tsc 0 error、vite build 通过、
drizzle-kit v0.31.10 正常。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 17:39:50 -06:00
0e1e024fa4 chore(依赖): 升一批 patch/minor,锁死 mermaid-legacy 和 TypeScript 大版本
后端:@node-rs/jieba 2.0.1→2.0.2(含精确 pin 的 -linux-x64-gnu)、
bullmq 6.0.9→6.2.2、postgres→3.4.9、sql.js→1.14.2、web-tree-sitter→0.26.13;
hono ^4.0.0→^4.13.4 与 zod ^4.0.0→^4.4.3 只是把范围收紧到实装版本。

前端:@codemirror/view→6.43.9、highlight.js→11.12.0、mermaid→11.17.2、
naive-ui→2.45.2、pinia→4.0.3、y-codemirror.next→0.3.6、vite→8.2.2、
@types/node→26.3.0。

刻意没动两个:

- mermaid-legacy(npm:mermaid@^9.4.3)保持 9.4.3,vite 的 build target
  也没碰 —— 机房 Chrome < 94 的兜底,legacy polyfill 产物照常生成。
- typescript 停在 5.9.3(只把根上的范围从 ^5.7.0 收到 ^5.9.3)。
  7.0.2 是 Go 原生重写版,另开一个 commit 处理。

踩到的坑记一笔:`bun install` 不清理旧的嵌套副本。升完 @codemirror/view 后
md-editor-v3/node_modules/ 下留着一份 6.43.8,vue-tsc 立刻报
dispatchTransactions 私有属性冲突 —— 看起来像版本不兼容,其实
md-editor-v3 要的是 ^6.38.2,6.43.9 完全满足。当时全仓有十几处这种残留。
**升完依赖必须 rm -rf node_modules && bun install 再验类型**,否则会
对着一堆假的类型冲突查半天。

验证(删空 node_modules 重装后):apps/api tsc 0 error、check:routes 167 条
无遮蔽、apps/web vue-tsc 0 error、vite build 通过、单二进制在仓库外起得来
并打库返回真数据、jieba 原生模块 dev 与编译两种形态都验过、BullMQ 两个
worker 正常 ready。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 17:39:06 -06:00
a03e22e60f chore(前端): 删掉三个从 ojnext 搬来就没人用的文件
阶段 1 是「原样搬入、不改业务代码」,所以 ojnext 里已经死掉的东西也一起
搬了过来。这三个文件全仓零引用:

- utils/permissions.ts(usePermissions / checkRoutePermission)
- oj/ai/components/StreakStats.vue
- shared/components/IconButton.vue

确认方式:在 apps/web 全部 .ts / .vue / .json / .html / .js 里搜这五个名字
(排除文件自身)零命中;两个组件也不在自动生成的 components.d.ts 里,
permissions.ts 的两个导出同样不在 auto-imports.d.ts 里 —— 也就是说
它们连自动导入的注册都没拿到,不存在「模板里隐式用到」的可能。

permissions.ts 在 ojnext 里也是死的,不是搬运过程产生的。

vue-tsc 0 error,vite build 通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 17:37:58 -06:00
18 changed files with 344 additions and 465 deletions

View File

@@ -16,23 +16,24 @@
"db:pull": "drizzle-kit pull"
},
"dependencies": {
"@node-rs/jieba": "^2.0.1",
"@node-rs/jieba-linux-x64-gnu": "2.0.1",
"@node-rs/jieba": "^2.0.2",
"@node-rs/jieba-linux-x64-gnu": "2.0.2",
"@oj2/contract": "workspace:*",
"bullmq": "^6.0.9",
"bullmq": "^6.2.2",
"drizzle-orm": "^0.45.2",
"fflate": "^0.8.3",
"hono": "^4.0.0",
"hono": "^4.13.4",
"ioredis": "^6.0.0",
"postgres": "^3.4.0",
"sql.js": "^1.14.1",
"postgres": "^3.4.9",
"sql.js": "^1.14.2",
"tree-sitter-c": "^0.24.1",
"tree-sitter-python": "^0.25.0",
"web-tree-sitter": "^0.26.11",
"zod": "^4.0.0"
"web-tree-sitter": "^0.26.13",
"zod": "^4.4.3"
},
"devDependencies": {
"@types/sql.js": "^1.4.11",
"drizzle-kit": "^0.31.10"
"drizzle-kit": "^0.31.10",
"typescript": "^7.0.2"
}
}

View File

@@ -5,6 +5,7 @@ import {
activityRankItemSchema,
metricsSchema,
problemRankSchema,
myRankSchema,
rankProfileSchema,
registerRequestSchema,
updateProfileRequestSchema,
@@ -17,10 +18,11 @@ import {
countDistinct,
desc,
eq,
gt,
gte,
ilike,
inArray,
isNull,
lt,
lte,
min,
or,
@@ -145,36 +147,108 @@ accountRoutes.get("/users/:id/metrics", async (c) => {
return success(c, metricsSchema.parse({ now: new Date().toISOString(), first: row.first, latest: row.latest }))
})
accountRoutes.get("/rankings/users", async (c) => {
const limit = queryInteger(c.req.query("limit"), 10, { min: 1, max: 250 })
/**
* 全服榜单的大小。**写死在服务端,不接受调用方传** —— 上限是这个端点的属性,
* 不是调用方的选择。
*
* 之前它是个 `top` 查询参数三个调用方各传各的100 / 10 / 0
* 而 top 又会覆盖 limit 与 offsettotal 却按全量人数算 —— 于是分页器算出几十页、
* 页页内容相同36e4ac2。「全服 Top10」不需要另一个上限取 limit=10&offset=0 即可;
* 后台那个「不限量」的用法搬去了 /admin/rankings/users。
*/
const LEADERBOARD_SIZE = 100
/** 入榜人群:正常状态的学生与学生管理员。教师和超管不参与排名。 */
const leaderboardWhere = and(
inArray(schema.user.adminType, ["Regular User", "Student Admin"]),
eq(schema.user.isDisabled, false),
)
/**
* 榜单排序AC 多的在前 → 同 AC 时提交少的在前 → 再同就按 id。
*
* 第三档不是凑数:前两个键完全相同的学生在真实数据里成片存在(都是 0/0
* 没有稳定的兜底键时 postgres 每次返回的顺序可以不同,翻页会看到重复或漏掉的人。
*/
const leaderboardOrder = [
desc(schema.userProfile.acceptedNumber),
asc(schema.userProfile.submissionNumber),
asc(schema.user.id),
]
accountRoutes.get("/rankings/users", optionalAuth, async (c) => {
const limit = queryInteger(c.req.query("limit"), 10, { min: 1, max: LEADERBOARD_SIZE })
const offset = queryInteger(c.req.query("offset"), 0, { min: 0 })
const top = queryInteger(c.req.query("top"), 0, { min: 0, max: 10_000 })
const username = c.req.query("username")?.trim() ?? ""
const where = and(
inArray(schema.user.adminType, ["Regular User", "Student Admin"]),
eq(schema.user.isDisabled, false),
gte(schema.userProfile.acceptedNumber, 0),
username ? ilike(schema.user.username, `%${username}%`) : undefined,
)
const [totalRow] = await db.select({ value: count() }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id)).where(where)
// top 只是「榜单取前 N 名」的上限,分页仍要在这 N 条之内生效:
// 否则 top=100 时每页都返回同样的 100 条,而 total 又是全量人数,翻页翻不动
const total = top > 0 ? Math.min(totalRow?.value ?? 0, top) : (totalRow?.value ?? 0)
const pageLimit = top > 0 ? Math.max(0, Math.min(limit, top - offset)) : limit
const rows = pageLimit === 0 ? [] : await db.select({ profile: schema.userProfile, user: schema.user }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id)).where(where)
.orderBy(desc(schema.userProfile.acceptedNumber), asc(schema.userProfile.submissionNumber))
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id))
.where(leaderboardWhere)
const total = Math.min(totalRow?.value ?? 0, LEADERBOARD_SIZE)
// 末页可能只剩不足 limit 条,越界页一条不剩 —— 后者直接不发 SQL
const pageLimit = Math.max(0, Math.min(limit, total - offset))
const rows = pageLimit === 0 ? [] : await db
.select({ profile: schema.userProfile, user: schema.user }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id))
.where(leaderboardWhere).orderBy(...leaderboardOrder)
.limit(pageLimit).offset(offset)
const results = rows.map(({ profile, user }) => rankProfileSchema.parse({
return success(c, userRankSchema.parse({
results: rows.map(serializeRankRow),
total,
me: await myLeaderboardRank(c.get("user")?.id),
}))
})
function serializeRankRow({ profile, user }: {
profile: typeof schema.userProfile.$inferSelect
user: typeof schema.user.$inferSelect
}) {
return rankProfileSchema.parse({
id: profile.id,
user: sampleUser(user, profile.realName),
acceptedNumber: profile.acceptedNumber,
submissionNumber: profile.submissionNumber,
mood: profile.mood,
}))
return success(c, userRankSchema.parse({ results, total }))
})
})
}
/**
* 「我」的全服名次,登录且身份入榜时才有。
*
* 名次 = 排在我前面的人数 + 1三个排序键**逐级**比较,与列表的 orderBy 逐字对应 ——
* 少比一级就会出现「显示第 7 名、实际排在表格第 9 行」这种对不上的情况。
* 三个键全等才算并列,此时名次相同。
*/
async function myLeaderboardRank(userId: number | undefined) {
if (!userId) return null
const [mine] = await db
.select({ profile: schema.userProfile, user: schema.user }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id))
.where(and(leaderboardWhere, eq(schema.user.id, userId))).limit(1)
if (!mine) return null
const { acceptedNumber, submissionNumber } = mine.profile
const [ahead] = await db.select({ value: count() }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id))
.where(and(leaderboardWhere, or(
gt(schema.userProfile.acceptedNumber, acceptedNumber),
and(
eq(schema.userProfile.acceptedNumber, acceptedNumber),
lt(schema.userProfile.submissionNumber, submissionNumber),
),
and(
eq(schema.userProfile.acceptedNumber, acceptedNumber),
eq(schema.userProfile.submissionNumber, submissionNumber),
lt(schema.user.id, userId),
),
)))
return myRankSchema.parse({
...serializeRankRow(mine),
rank: (ahead?.value ?? 0) + 1,
})
}
accountRoutes.get("/rankings/activity", async (c) => {
const start = c.req.query("start")

View File

@@ -1,8 +1,10 @@
import {
adminUserListSchema,
adminUserRankSchema,
adminUserSchema,
deleteUsersRequestSchema,
importUsersRequestSchema,
rankProfileSchema,
resetPasswordResponseSchema,
updateUserRequestSchema,
} from "@oj2/contract"
@@ -13,7 +15,7 @@ import { Hono } from "hono"
import { requireSuperAdmin, type AppEnv } from "../../auth/middleware"
import { db, schema } from "../../db"
import { failure, success } from "../../http"
import { queryInteger } from "../helpers"
import { queryInteger, sampleUser } from "../helpers"
export const adminAccountRoutes = new Hono<AppEnv>()
@@ -79,6 +81,51 @@ function selectUser(id: number) {
.where(eq(schema.user.id, id)).limit(1)
}
/**
* 后台的用户排名:老师按班级前缀翻学生,**不设 100 名上限**。
*
* 这份逻辑原来是公开榜单 `/rankings/users` 的 `top=0` 分支,搬过来是因为那意味着
* 任何匿名请求都能 `?top=0&limit=250` 翻走全校学生名单和个性签名 ——
* 而 `/profiles/:username` 恰恰为了收紧枚举面才做了「匿名一律返回空」。
*
* 排序口径与公开榜单一致(见 routes/account.ts 的 leaderboardOrder
* AC 降序 → 提交数升序 → id 升序,第三档保证翻页稳定。
*/
adminAccountRoutes.get("/rankings/users", requireSuperAdmin, async (c) => {
const limit = queryInteger(c.req.query("limit"), 10, { min: 1, max: 250 })
const offset = queryInteger(c.req.query("offset"), 0, { min: 0 })
const keyword = c.req.query("keyword")?.trim()
const where = and(
inArray(schema.user.adminType, ["Regular User", "Student Admin"]),
eq(schema.user.isDisabled, false),
keyword ? ilike(schema.user.username, `%${keyword}%`) : undefined,
)
const [totalRows, rows] = await Promise.all([
db.select({ value: count() }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id)).where(where),
db.select({ profile: schema.userProfile, user: schema.user }).from(schema.userProfile)
.innerJoin(schema.user, eq(schema.userProfile.userId, schema.user.id)).where(where)
.orderBy(
desc(schema.userProfile.acceptedNumber),
asc(schema.userProfile.submissionNumber),
asc(schema.user.id),
)
.limit(limit).offset(offset),
])
return success(c, adminUserRankSchema.parse({
results: rows.map(({ profile, user }) => rankProfileSchema.parse({
id: profile.id,
user: sampleUser(user, profile.realName),
acceptedNumber: profile.acceptedNumber,
submissionNumber: profile.submissionNumber,
mood: profile.mood,
})),
total: totalRows[0]?.value ?? 0,
}))
})
adminAccountRoutes.get("/users", requireSuperAdmin, async (c) => {
const limit = queryInteger(c.req.query("limit"), 10, { min: 1, max: 250 })
const offset = queryInteger(c.req.query("offset"), 0, { min: 0 })

View File

@@ -18,7 +18,7 @@
"@codemirror/lang-sql": "^6.10.0",
"@codemirror/language": "^6.12.4",
"@codemirror/state": "^6.7.1",
"@codemirror/view": "^6.43.8",
"@codemirror/view": "^6.43.9",
"@lezer/highlight": "^1.2.3",
"@oj2/contract": "workspace:*",
"@vue-flow/background": "^1.3.2",
@@ -39,35 +39,34 @@
"copy-text-to-clipboard": "^3.2.2",
"date-fns": "^4.4.0",
"fflate": "^0.8.3",
"highlight.js": "^11.11.1",
"highlight.js": "^11.12.0",
"md-editor-v3": "^6.5.6",
"mermaid": "^11.16.1",
"mermaid": "^11.17.2",
"mermaid-legacy": "npm:mermaid@^9.4.3",
"naive-ui": "^2.44.1",
"naive-ui": "^2.45.2",
"nanoid": "^6.0.1",
"normalize.css": "^8.0.1",
"pinia": "^4.0.2",
"pinia": "^4.0.3",
"skulpt": "^1.2.0",
"vue": "^3.5.41",
"vue-chartjs": "^5.3.4",
"vue-codemirror": "^6.1.1",
"vue-router": "^5.2.0",
"y-codemirror.next": "^0.3.5",
"y-codemirror.next": "^0.3.6",
"y-webrtc": "^10.3.0",
"yjs": "^13.6.32"
},
"devDependencies": {
"@iconify/vue": "^5.0.1",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^26.1.2",
"@types/node": "^26.3.0",
"@vitejs/plugin-legacy": "^8.2.3",
"@vitejs/plugin-vue": "^6.0.8",
"@vue/tsconfig": "^0.9.1",
"prettier": "^3.9.6",
"typescript": "^7.0.2",
"unplugin-auto-import": "^21.1.0",
"unplugin-vue-components": "^32.1.0",
"vite": "^8.2.1",
"vite": "^8.2.2",
"vue-tsc": "^3.3.11"
}
}

View File

@@ -139,6 +139,13 @@ export function batchTagProblems(
})
}
// 用户排名(后台版,无 100 名上限;公开榜单是 oj/api.ts 的 getRank
export function getAdminUserRank(offset: number, limit: number, keyword: string) {
return api2.get<AdminUserRank>("admin/rankings/users", {
params: { offset, limit, keyword },
})
}
// 用户列表
export function getUserList(
offset = 0,
@@ -707,6 +714,7 @@ export function getPinnedAIReports() {
import type {
AdminAchievement,
AchievementMetric as MetricOption,
AdminUserRank,
} from "@oj2/contract"
export type { AdminAchievement, MetricOption }

View File

@@ -19,11 +19,6 @@ import {
} from "../api"
import { useUserStore } from "shared/store/user"
interface Testcase {
id: string
create_time: string
}
const message = useMessage()
const configStore = useConfigStore()
const userStore = useUserStore()
@@ -41,7 +36,7 @@ watch(
{ immediate: true },
)
const testcaseColumns: DataTableColumn<Testcase>[] = [
const testcaseColumns: DataTableColumn<OrphanTestCase>[] = [
{ title: "测试用例 ID", key: "id" },
{
title: "选项",
@@ -102,13 +97,13 @@ const serverColumns: DataTableColumn<Server>[] = [
{ title: "服务器 URL", key: "serviceUrl", width: 200 },
{
title: "上一次心跳",
key: "last_heartbeat",
key: "lastHeartbeat",
render: (row) => parseTime(row.lastHeartbeat, "YYYY-MM-DD HH:mm:ss"),
width: 120,
},
{
title: "创建时间",
key: "create_time",
key: "createTime",
render: (row) => parseTime(row.createTime, "YYYY-MM-DD HH:mm:ss"),
width: 120,
},

View File

@@ -2,12 +2,11 @@
import { h, onMounted, reactive, ref, watch } from "vue"
import { useRouter } from "vue-router"
import { NButton } from "naive-ui"
import { getRank } from "oj/api"
import Pagination from "shared/components/Pagination.vue"
import { useUserStore } from "shared/store/user"
import { getACRate } from "utils/functions"
import type { Rank } from "utils/types"
import { getBaseInfo, randomUser10 } from "../api"
import { getAdminUserRank, getBaseInfo, randomUser10 } from "../api"
const userCount = ref(0)
const submissionCount = ref(0)
@@ -72,7 +71,7 @@ onMounted(async () => {
async function listRanks() {
const offset = (query.page - 1) * query.limit
const res = await getRank(offset, query.limit, 0, query.classroom)
const res = await getAdminUserRank(offset, query.limit, query.classroom)
data.value = res.data.results
total.value = res.data.total
}

View File

@@ -1,166 +0,0 @@
<template>
<n-card title="连续做题统计" size="small">
<template #header-extra>
<n-text depth="3" style="font-size: 12px">激励持续学习</n-text>
</template>
<n-spin :show="aiStore.loading.heatmap" :delay="50">
<n-grid :cols="2" :x-gap="12" :y-gap="12">
<n-gi>
<n-statistic label="当前连续" :value="currentStreak">
<template #suffix>
<span style="font-size: 14px"></span>
<span
v-if="currentStreak > 0"
style="font-size: 20px; margin-left: 4px"
>
🔥
</span>
</template>
</n-statistic>
</n-gi>
<n-gi>
<n-statistic label="最长连续" :value="maxStreak">
<template #suffix>
<span style="font-size: 14px"></span>
<span
v-if="maxStreak >= 7"
style="font-size: 20px; margin-left: 4px"
>
</span>
</template>
</n-statistic>
</n-gi>
<n-gi>
<n-statistic label="本周做题" :value="weekCount">
<template #suffix>
<span style="font-size: 14px"></span>
</template>
</n-statistic>
</n-gi>
<n-gi>
<n-statistic label="本月做题" :value="monthCount">
<template #suffix>
<span style="font-size: 14px"></span>
</template>
</n-statistic>
</n-gi>
</n-grid>
<n-divider style="margin: 12px 0" />
<n-flex vertical size="small">
<n-text depth="2" style="font-size: 12px">
<span v-if="currentStreak === 0"> 开始做题建立学习连续记录 </span>
<span v-else-if="currentStreak < 3"> 继续保持争取连续3天 </span>
<span v-else-if="currentStreak < 7">
很棒继续保持一周连续记录
</span>
<span v-else-if="currentStreak < 30">
太棒了坚持满30天将获得持之以恒成就
</span>
<span v-else>
🎉 恭喜你你已经连续学习 {{ currentStreak }} 真的非常厉害
</span>
</n-text>
</n-flex>
</n-spin>
</n-card>
</template>
<script setup lang="ts">
import { useAIStore } from "oj/store/ai"
const aiStore = useAIStore()
// 计算连续天数
const streakData = computed(() => {
const heatmap = aiStore.heatmapData
if (!heatmap || heatmap.length === 0) {
return {
currentStreak: 0,
maxStreak: 0,
weekCount: 0,
monthCount: 0,
}
}
// 按时间戳排序
const sortedData = [...heatmap].sort((a, b) => a.timestamp - b.timestamp)
let currentStreak = 0
let maxStreak = 0
let tempStreak = 0
let lastDate: Date | null = null
const now = new Date()
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
let weekCount = 0
let monthCount = 0
// 遍历数据计算连续天数
for (const item of sortedData) {
if (item.value > 0) {
const currentDate = new Date(item.timestamp)
// 统计本周和本月
if (currentDate >= weekAgo) {
weekCount++
}
if (currentDate >= monthAgo) {
monthCount++
}
if (lastDate === null) {
tempStreak = 1
} else {
const dayDiff = Math.floor(
(currentDate.getTime() - lastDate.getTime()) / (24 * 60 * 60 * 1000),
)
if (dayDiff === 1) {
tempStreak++
} else {
maxStreak = Math.max(maxStreak, tempStreak)
tempStreak = 1
}
}
lastDate = currentDate
}
}
maxStreak = Math.max(maxStreak, tempStreak)
// 计算当前连续天数(必须包含今天或昨天)
if (lastDate) {
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
const lastDateOnly = new Date(
lastDate.getFullYear(),
lastDate.getMonth(),
lastDate.getDate(),
)
if (
lastDateOnly.getTime() === today.getTime() ||
lastDateOnly.getTime() === yesterday.getTime()
) {
currentStreak = tempStreak
} else {
currentStreak = 0
}
}
return {
currentStreak,
maxStreak,
weekCount,
monthCount,
}
})
const currentStreak = computed(() => streakData.value.currentStreak)
const maxStreak = computed(() => streakData.value.maxStreak)
const weekCount = computed(() => streakData.value.weekCount)
const monthCount = computed(() => streakData.value.monthCount)
</script>

View File

@@ -164,15 +164,12 @@ export function getSubmissionStatistics(
})
}
export function getRank(
offset: number,
limit: number,
n: number,
username?: string,
) {
return api2.get<UserRank>("rankings/users", {
params: { offset, limit, username, top: n },
})
/**
* 全服榜单。上限100 名)由服务端定,调用方只管翻页 ——
* 「全服 Top10」就是这个榜的第一页取 limit=10 即可,不需要另一个上限参数。
*/
export function getRank(offset: number, limit: number) {
return api2.get<UserRank>("rankings/users", { params: { offset, limit } })
}
export function getActivityRank(start: string) {

View File

@@ -3,6 +3,7 @@ import type {
ClassComparison,
ClassRankItem as ClassRank,
ClassUserRank,
MyRank,
Rank,
} from "utils/types"
import { formatISO, sub, type Duration } from "date-fns"
@@ -40,6 +41,10 @@ const userStore = useUserStore()
const { isDesktop } = useBreakpoints()
const data = ref<Rank[]>([])
const total = ref(0)
/** 我的全服名次;未登录、教师/超管不入榜时为 null */
const me = ref<MyRank | null>(null)
/** 我在前 100 名之外 —— 榜上高亮不到我,另起一行显示 */
const meOffBoard = computed(() => !!me.value && me.value.rank > total.value)
const query = reactive({
limit: 10,
page: 1,
@@ -145,12 +150,22 @@ async function analyzeSingleClassWithAI() {
async function init() {
const offset = (query.page - 1) * query.limit
const res = await getRank(offset, query.limit, 100)
const res = await getRank(offset, query.limit)
data.value = res.data.results
total.value = res.data.total
me.value = res.data.me
return res.data.results
}
function isMe(row: Rank) {
return !!me.value && row.user.id === me.value.user.id
}
// 高亮我那一行。用 id 比对而不是用户名用户名会重名到大小写差异上id 不会
function rowClassName(row: Rank) {
return isMe(row) ? "me-row" : ""
}
const columns: DataTableColumn<Rank>[] = [
{
title: renderTableTitle("排名", "streamline-emojis:flexed-biceps-1"),
@@ -178,6 +193,9 @@ const columns: DataTableColumn<Rank>[] = [
},
() => row.user.username,
),
isMe(row)
? h(Icon, { width: 20, icon: "fluent-emoji:person-raising-hand" })
: null,
h(
NButton,
{
@@ -201,7 +219,7 @@ const columns: DataTableColumn<Rank>[] = [
},
{
title: renderTableTitle("已解决", "streamline-emojis:raised-fist-1"),
key: "accepted_number",
key: "acceptedNumber",
width: 120,
align: "center",
},
@@ -210,7 +228,7 @@ const columns: DataTableColumn<Rank>[] = [
"提交数",
"streamline-ultimate-color:space-rocket-earth",
),
key: "submission_number",
key: "submissionNumber",
width: 120,
align: "center",
},
@@ -247,8 +265,9 @@ async function listActivity() {
}))
}
// 「全服 Top10」就是同一个榜的第一页 —— 上限由服务端定,这里只要前 10 条
async function listRank() {
const res = await getRank(0, 10, 10)
const res = await getRank(0, 10)
rankChart.value = res.data.results
}
@@ -509,13 +528,29 @@ watch(
</n-grid>
<n-card>
<template #header>全服 Top100</template>
<n-data-table :data="data" :columns="columns" />
<n-data-table
:data="data"
:columns="columns"
:row-class-name="rowClassName"
/>
<template #footer>
<Pagination
:total="total"
v-model:page="query.page"
v-model:limit="query.limit"
/>
<n-flex align="center" justify="space-between" :wrap="false">
<!-- 100 名之外的学生榜上找不到自己这里单独给一行 -->
<n-tag v-if="meOffBoard" type="info" round :bordered="false">
<template #icon>
<Icon width="18" icon="fluent-emoji:person-raising-hand" />
</template>
我的排名 {{ me!.rank }} · 已解决 {{ me!.acceptedNumber }} ·
提交 {{ me!.submissionNumber }} · 正确率
{{ getACRate(me!.acceptedNumber, me!.submissionNumber) }}
</n-tag>
<span v-else />
<Pagination
:total="total"
v-model:page="query.page"
v-model:limit="query.limit"
/>
</n-flex>
</template>
</n-card>
<n-grid :cols="isDesktop ? 2 : 1" :x-gap="20" :y-gap="20">
@@ -800,6 +835,10 @@ watch(
</template>
<style scoped>
:deep(.me-row > td) {
background-color: rgba(24, 160, 88, 0.12) !important;
}
.stat-total-ac :deep(.n-statistic-value),
.stat-total-ac :deep(.n-statistic-value__content),
.stat-total-ac :deep(.n-number-animation),

View File

@@ -1,29 +0,0 @@
<template>
<n-tooltip>
<template #trigger>
<n-button round :type="type ?? 'default'" @click="$emit('click')">
<template #icon>
<Icon :icon="icon" />
</template>
</n-button>
</template>
{{ tip }}
</n-tooltip>
</template>
<script lang="ts" setup>
import { Icon } from "@iconify/vue"
defineProps<{
tip: string
icon: string
type?:
| "default"
| "tertiary"
| "primary"
| "info"
| "success"
| "warning"
| "error"
}>()
defineEmits(["click"])
</script>

View File

@@ -1,108 +0,0 @@
import { useUserStore } from "shared/store/user"
export function usePermissions() {
const userStore = useUserStore()
return {
isAuthenticated: computed(() => userStore.isAuthed),
isAdminRole: computed(() => userStore.isAdminRole),
isTeacherOrAbove: computed(() => userStore.isTeacherOrAbove),
isSuperAdmin: computed(() => userStore.isSuperAdmin),
hasProblemPermission: computed(() => userStore.hasProblemPermission),
canManageUsers: computed(() => userStore.isSuperAdmin),
canManageAnnouncements: computed(() => userStore.isSuperAdmin),
canManageTutorials: computed(() => userStore.isSuperAdmin),
canManageSystemConfig: computed(() => userStore.isSuperAdmin),
canSendMessages: computed(() => userStore.isSuperAdmin),
canManageProblems: computed(() => userStore.hasProblemPermission),
canManageContests: computed(() => userStore.isTeacherOrAbove),
canManageProblemsets: computed(() => userStore.isTeacherOrAbove),
canViewClassroomData: computed(() => userStore.isTeacherOrAbove),
canManageAllProblems: computed(
() =>
userStore.user?.problemPermission === "All" || userStore.isSuperAdmin,
),
canManageOwnProblems: computed(
() =>
userStore.user?.problemPermission === "Own" && !userStore.isSuperAdmin,
),
getUserPermissionLevel: computed(() => {
if (userStore.isSuperAdmin) return "超级管理员"
if (userStore.isTeacherAdmin) return "教师管理员"
if (userStore.isStudentAdmin) return "学生管理员"
return "普通用户"
}),
getProblemPermissionLevel: computed(() => {
if (!userStore.user) return "无权限"
switch (userStore.user.problemPermission) {
case "All":
return "管理所有题目"
case "Own":
return "管理自己的题目"
case "None":
return "无题目权限"
default:
return "无权限"
}
}),
}
}
export function checkRoutePermission(routeName: string): boolean {
const userStore = useUserStore()
const superAdminRoutes = [
"admin home",
"admin config",
"admin user list",
"admin user generate",
"admin announcement list",
"admin announcement create",
"admin announcement edit",
"admin message list",
"admin tutorial list",
"admin tutorial create",
"admin tutorial edit",
]
const teacherAdminRoutes = [
"admin contest list",
"admin contest create",
"admin contest edit",
"admin contest problem list",
"admin contest problem create",
"admin contest problem edit",
"admin contest helper",
"admin problemset list",
"admin problemset create",
"admin problemset edit",
"admin problemset detail",
"admin stuck problems",
"admin top ac trend",
]
const problemPermissionRoutes = [
"admin problem list",
"admin problem create",
"admin problem edit",
]
if (superAdminRoutes.includes(routeName)) {
return userStore.isSuperAdmin
}
if (teacherAdminRoutes.includes(routeName)) {
return userStore.isTeacherOrAbove
}
if (problemPermissionRoutes.includes(routeName)) {
return userStore.hasProblemPermission
}
return true
}

View File

@@ -380,6 +380,9 @@ export type { SessionUser } from "@oj2/contract"
export type Rank = RankProfile
/** 榜单里「我」的位置:比 Rank 多一个全服名次 */
export type { MyRank } from "@oj2/contract"
export type {
ClassComparison,
ClassRankItem,

168
bun.lock
View File

@@ -6,31 +6,32 @@
"name": "oj2",
"devDependencies": {
"@types/bun": "latest",
"typescript": "^5.7.0",
"typescript": "^5.9.3",
},
},
"apps/api": {
"name": "@oj2/api",
"version": "0.0.0",
"dependencies": {
"@node-rs/jieba": "^2.0.1",
"@node-rs/jieba-linux-x64-gnu": "2.0.1",
"@node-rs/jieba": "^2.0.2",
"@node-rs/jieba-linux-x64-gnu": "2.0.2",
"@oj2/contract": "workspace:*",
"bullmq": "^6.0.9",
"bullmq": "^6.2.2",
"drizzle-orm": "^0.45.2",
"fflate": "^0.8.3",
"hono": "^4.0.0",
"hono": "^4.13.4",
"ioredis": "^6.0.0",
"postgres": "^3.4.0",
"sql.js": "^1.14.1",
"postgres": "^3.4.9",
"sql.js": "^1.14.2",
"tree-sitter-c": "^0.24.1",
"tree-sitter-python": "^0.25.0",
"web-tree-sitter": "^0.26.11",
"zod": "^4.0.0",
"web-tree-sitter": "^0.26.13",
"zod": "^4.4.3",
},
"devDependencies": {
"@types/sql.js": "^1.4.11",
"drizzle-kit": "^0.31.10",
"typescript": "^7.0.2",
},
},
"apps/web": {
@@ -43,7 +44,7 @@
"@codemirror/lang-sql": "^6.10.0",
"@codemirror/language": "^6.12.4",
"@codemirror/state": "^6.7.1",
"@codemirror/view": "^6.43.8",
"@codemirror/view": "^6.43.9",
"@lezer/highlight": "^1.2.3",
"@oj2/contract": "workspace:*",
"@vue-flow/background": "^1.3.2",
@@ -64,35 +65,34 @@
"copy-text-to-clipboard": "^3.2.2",
"date-fns": "^4.4.0",
"fflate": "^0.8.3",
"highlight.js": "^11.11.1",
"highlight.js": "^11.12.0",
"md-editor-v3": "^6.5.6",
"mermaid": "^11.16.1",
"mermaid": "^11.17.2",
"mermaid-legacy": "npm:mermaid@^9.4.3",
"naive-ui": "^2.44.1",
"naive-ui": "^2.45.2",
"nanoid": "^6.0.1",
"normalize.css": "^8.0.1",
"pinia": "^4.0.2",
"pinia": "^4.0.3",
"skulpt": "^1.2.0",
"vue": "^3.5.41",
"vue-chartjs": "^5.3.4",
"vue-codemirror": "^6.1.1",
"vue-router": "^5.2.0",
"y-codemirror.next": "^0.3.5",
"y-codemirror.next": "^0.3.6",
"y-webrtc": "^10.3.0",
"yjs": "^13.6.32",
},
"devDependencies": {
"@iconify/vue": "^5.0.1",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^26.1.2",
"@types/node": "^26.3.0",
"@vitejs/plugin-legacy": "^8.2.3",
"@vitejs/plugin-vue": "^6.0.8",
"@vue/tsconfig": "^0.9.1",
"prettier": "^3.9.6",
"typescript": "^7.0.2",
"unplugin-auto-import": "^21.1.0",
"unplugin-vue-components": "^32.1.0",
"vite": "^8.2.1",
"vite": "^8.2.2",
"vue-tsc": "^3.3.11",
},
},
@@ -100,7 +100,7 @@
"name": "@oj2/contract",
"version": "0.0.0",
"dependencies": {
"zod": "^4.0.0",
"zod": "^4.4.3",
},
},
},
@@ -351,7 +351,7 @@
"@codemirror/state": ["@codemirror/state@6.7.1", "https://registry.npmjs.com/@codemirror/state/-/state-6.7.1.tgz", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A=="],
"@codemirror/view": ["@codemirror/view@6.43.8", "https://registry.npmjs.com/@codemirror/view/-/view-6.43.8.tgz", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-qtItTDssZ/5GFfi94hrILu9j/VUeFPDPkhovEfmWFj2ipTxnzPB8DdHgfbb8HYTzLTYhrndKmyQxXUz/PDLenw=="],
"@codemirror/view": ["@codemirror/view@6.43.9", "https://registry.npmjs.com/@codemirror/view/-/view-6.43.9.tgz", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-sTuUzTpPMFebRhg6dawChoKKgndIwfjmJgKVxBefPElcU2NwQ6AFroupk0SFqEerQyZOGRfDNnSN8Dw/lMAsXw=="],
"@css-render/plugin-bem": ["@css-render/plugin-bem@0.15.14", "https://registry.npmjs.com/@css-render/plugin-bem/-/plugin-bem-0.15.14.tgz", { "peerDependencies": { "css-render": "~0.15.14" } }, "sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg=="],
@@ -359,12 +359,6 @@
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "https://registry.npmjs.com/@drizzle-team/brocli/-/brocli-0.10.2.tgz", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
"@emnapi/core": ["@emnapi/core@1.11.3", "https://registry.npmjs.com/@emnapi/core/-/core-1.11.3.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" } }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="],
"@emnapi/runtime": ["@emnapi/runtime@1.11.3", "https://registry.npmjs.com/@emnapi/runtime/-/runtime-1.11.3.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "https://registry.npmjs.com/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="],
"@emotion/hash": ["@emotion/hash@0.8.0", "https://registry.npmjs.com/@emotion/hash/-/hash-0.8.0.tgz", {}, "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="],
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "https://registry.npmjs.com/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
@@ -485,7 +479,7 @@
"@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.3", "https://registry.npmjs.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", {}, "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA=="],
"@mermaid-js/parser": ["@mermaid-js/parser@1.2.0", "https://registry.npmjs.com/@mermaid-js/parser/-/parser-1.2.0.tgz", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA=="],
"@mermaid-js/parser": ["@mermaid-js/parser@1.2.1", "https://registry.npmjs.com/@mermaid-js/parser/-/parser-1.2.1.tgz", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw=="],
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4", "https://registry.npmjs.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ=="],
@@ -499,37 +493,33 @@
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "https://registry.npmjs.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "https://registry.npmjs.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
"@node-rs/jieba": ["@node-rs/jieba@2.0.2", "https://registry.npmjs.com/@node-rs/jieba/-/jieba-2.0.2.tgz", { "optionalDependencies": { "@node-rs/jieba-android-arm-eabi": "2.0.2", "@node-rs/jieba-android-arm64": "2.0.2", "@node-rs/jieba-darwin-arm64": "2.0.2", "@node-rs/jieba-darwin-x64": "2.0.2", "@node-rs/jieba-freebsd-x64": "2.0.2", "@node-rs/jieba-linux-arm-gnueabihf": "2.0.2", "@node-rs/jieba-linux-arm64-gnu": "2.0.2", "@node-rs/jieba-linux-arm64-musl": "2.0.2", "@node-rs/jieba-linux-x64-gnu": "2.0.2", "@node-rs/jieba-linux-x64-musl": "2.0.2", "@node-rs/jieba-win32-arm64-msvc": "2.0.2", "@node-rs/jieba-win32-ia32-msvc": "2.0.2", "@node-rs/jieba-win32-x64-msvc": "2.0.2" } }, "sha512-aONN6nwpbwHKenEzCcYUbm6ZFHWEs7N5eas7zwWFs3c4MmEdN79m9Si4PvOxCp285I2M+g4MfLyUm9WcYaQi7Q=="],
"@node-rs/jieba": ["@node-rs/jieba@2.0.1", "https://registry.npmjs.com/@node-rs/jieba/-/jieba-2.0.1.tgz", { "optionalDependencies": { "@node-rs/jieba-android-arm-eabi": "2.0.1", "@node-rs/jieba-android-arm64": "2.0.1", "@node-rs/jieba-darwin-arm64": "2.0.1", "@node-rs/jieba-darwin-x64": "2.0.1", "@node-rs/jieba-freebsd-x64": "2.0.1", "@node-rs/jieba-linux-arm-gnueabihf": "2.0.1", "@node-rs/jieba-linux-arm64-gnu": "2.0.1", "@node-rs/jieba-linux-arm64-musl": "2.0.1", "@node-rs/jieba-linux-x64-gnu": "2.0.1", "@node-rs/jieba-linux-x64-musl": "2.0.1", "@node-rs/jieba-wasm32-wasi": "2.0.1", "@node-rs/jieba-win32-arm64-msvc": "2.0.1", "@node-rs/jieba-win32-ia32-msvc": "2.0.1", "@node-rs/jieba-win32-x64-msvc": "2.0.1" } }, "sha512-tnfzXOMqzVQF2dSKMhPC9HrHzzWmN6KheL/zYtGenhOpq/bCKHJWVASSggEnHlkmHgXGeIJHR2N/IuPzewz1BQ=="],
"@node-rs/jieba-android-arm-eabi": ["@node-rs/jieba-android-arm-eabi@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-2.0.2.tgz", { "os": "android", "cpu": "arm" }, "sha512-5w+349/6X+0MkW0DMCLmtmjbCx9YXKMqMzSGH9A/XMP3sSy+MgzgMQ5UwEteu2YFnRve8V0mJzKQpG0R7TmT7A=="],
"@node-rs/jieba-android-arm-eabi": ["@node-rs/jieba-android-arm-eabi@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-2.0.1.tgz", { "os": "android", "cpu": "arm" }, "sha512-tavsIaxybnlA9tRbJ+oc3NW3zhx0d5rNiCGdpIdGWjflwS7HyeUTVAZmAFDlg58Mc6EjTdVKZH+RolBbAJtgcQ=="],
"@node-rs/jieba-android-arm64": ["@node-rs/jieba-android-arm64@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-android-arm64/-/jieba-android-arm64-2.0.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-tqNVsZ6VVzkbwWZAQ7zcOwJLtRKANW+Oa+uj8R+PWQSVXjy8Xs6KzfpDC5r+euFdNOiSjKPuZv4VbYSSTXed8g=="],
"@node-rs/jieba-android-arm64": ["@node-rs/jieba-android-arm64@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-android-arm64/-/jieba-android-arm64-2.0.1.tgz", { "os": "android", "cpu": "arm64" }, "sha512-AwdyqKvVNuSDnDq3anUfq+nJ5J/kzXjkfbr/1WY6TfaAlTNuuGVskuQv72/wIx/jn7NoXfm/UPuJrWYG16NC6w=="],
"@node-rs/jieba-darwin-arm64": ["@node-rs/jieba-darwin-arm64@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-2.0.2.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-JIpC+9p3E67OPzvvLcxI9TUHfQL6Xuti+e4zbio8Kc7gUZ3EYJ+USRHGS6EixhsWDDZKFhNThl3ztTk9ZqBjzg=="],
"@node-rs/jieba-darwin-arm64": ["@node-rs/jieba-darwin-arm64@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-2.0.1.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-10+nwGQ6KzXXJlIL/sELA6Fi6m7eJ7xJksBiKuw1kxKUgaJwtVfAG0iqRF+NRQv0Sdq7r3k5ew9K9y0+IYaEcA=="],
"@node-rs/jieba-darwin-x64": ["@node-rs/jieba-darwin-x64@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-2.0.2.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-E7xPjd3L4oSPl9VSZJC6yN8niFYYL9NmmxKRHW14kAoa9TOr07xeiXtfRWajq95bgfvxHB9ymMkYKHCzGlTR4Q=="],
"@node-rs/jieba-darwin-x64": ["@node-rs/jieba-darwin-x64@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-2.0.1.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-IJ5RK0X/uPQa1XRmTvwKSieya+w1IJeiKLw0EekoBFJKybXQdvo8/uqM/8z2eVJ8vQxW9X6K2vkVGFvYQa9dYA=="],
"@node-rs/jieba-freebsd-x64": ["@node-rs/jieba-freebsd-x64@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-2.0.2.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-FHfveI/E/uLNgVGWLobFhWLrh09+DM2g3UBq8YuDJ22tnBefjGaakoTIjd9NHQ03sxAAkhlucR8QLTrFN6cMUg=="],
"@node-rs/jieba-freebsd-x64": ["@node-rs/jieba-freebsd-x64@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-2.0.1.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-yg7vyhqzP2weJu5DJ3q9q4pb0b4GWWRwcv54zK7MSSA6KNJ/uQv2a4R9/qmptLU/fZv14gWuJBEMFdL7y1Dv2w=="],
"@node-rs/jieba-linux-arm-gnueabihf": ["@node-rs/jieba-linux-arm-gnueabihf@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-2.0.2.tgz", { "os": "linux", "cpu": "arm" }, "sha512-Hl2+3GOff5WmUkialLs0HXwUycacxIJlygMTvGnl3au6UnAxdj9KyF9+n+HQpRRLZqE0aExPQEBxvnshlwl/mg=="],
"@node-rs/jieba-linux-arm-gnueabihf": ["@node-rs/jieba-linux-arm-gnueabihf@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-2.0.1.tgz", { "os": "linux", "cpu": "arm" }, "sha512-fxQYunS7w2tv8XV9GigkWJPzHnbcw6tjrUdDu5/qU0FdQVEzGuEYG85DjlNf8lZTDGSUKHBVyAQs7bBIvq8yqg=="],
"@node-rs/jieba-linux-arm64-gnu": ["@node-rs/jieba-linux-arm64-gnu@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-2.0.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-tM0Gdh37ZhHpol8O3REGaWILdPYYoXfDuw+XfXhq1ppNbfwG5fm+DmoW7n4qg4nQM7jGM2t67BbmlENwxiAlCQ=="],
"@node-rs/jieba-linux-arm64-gnu": ["@node-rs/jieba-linux-arm64-gnu@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-2.0.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-VnLU630hQIyO/fwyxh2vqZi72mO+hXkVUC3jVLPfOAlppinmsGX9N81tpTPUK3840hbV8WLtbYTWN1XodI38eg=="],
"@node-rs/jieba-linux-arm64-musl": ["@node-rs/jieba-linux-arm64-musl@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-2.0.2.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-7BrJjtsiuHdKzaVedWusiMzS9dUJXPTZBAunuRly/dyTvQyJqRJ3/N6RdgNRXaF49zuEz8JrtA9+aCn0t+ajTg=="],
"@node-rs/jieba-linux-arm64-musl": ["@node-rs/jieba-linux-arm64-musl@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-2.0.1.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-K4EDyNixSLVdTNYnHwD+7I/ytvzpo7tt+vdCLqwQViiek2PMpL/FFRvA39uU2tk99jXIxvkczdxARG20BRZppg=="],
"@node-rs/jieba-linux-x64-gnu": ["@node-rs/jieba-linux-x64-gnu@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-2.0.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-514+0NFGCZp2e9lrnVRmyfe1/Cd+zUV3IUD0pWYB2gHfmowNrHPHL95WZosJMDp96C6LYbJZuNTRKifm0PyVBA=="],
"@node-rs/jieba-linux-x64-gnu": ["@node-rs/jieba-linux-x64-gnu@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-2.0.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-sq3J6L2ANTE25I9eVFq/nb57OtXcvUIeUD1CTKJxwgTKIVmcB2LyOZpWf20AjHRUfbMER9Klqg5dgyyO+Six+w=="],
"@node-rs/jieba-linux-x64-musl": ["@node-rs/jieba-linux-x64-musl@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-2.0.2.tgz", { "os": "linux", "cpu": "x64" }, "sha512-KFlbnGoGoX58qLKeWutPyupTbbYPGgC4DAGVjEu8ctYVh4UyjeRHCxlY7ipC7NIW8I3YzKQRZ/yFMzmiDYX7UQ=="],
"@node-rs/jieba-linux-x64-musl": ["@node-rs/jieba-linux-x64-musl@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-2.0.1.tgz", { "os": "linux", "cpu": "x64" }, "sha512-0zfP9Qy68yEXrhBFknfhF6WUJDPU/8eRuyIrkMGdMjfRpxhpSbr2fMfnsqhOQLvhuK4w3iDFvTy4t5d0s6JKMA=="],
"@node-rs/jieba-win32-arm64-msvc": ["@node-rs/jieba-win32-arm64-msvc@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-2.0.2.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-EI3JLL01kf6pP3mAUoYnDrt8S+FY2W2nWpjtiK7ucs46tITLkYDx9wmAwDsOnoCkxs4CETzLytPhLZ12lzR5Qw=="],
"@node-rs/jieba-wasm32-wasi": ["@node-rs/jieba-wasm32-wasi@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-wasm32-wasi/-/jieba-wasm32-wasi-2.0.1.tgz", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.5" }, "cpu": "none" }, "sha512-7I5rJya5rlQNJIhv8PvPzIVT1/gVc0vFzHmlfRGwCPGDJ3tHVxkSPW34dDx3OgDmbIeadNpmgIyC1RaS9djPJg=="],
"@node-rs/jieba-win32-ia32-msvc": ["@node-rs/jieba-win32-ia32-msvc@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-2.0.2.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-FtTx1cth53zZqdZTCsTDTlg0rTGDvFZyaMCLEa3FcjrDD8DeWNavNcJbLmProS9YlZPdCvArPzH/etaoiTKimA=="],
"@node-rs/jieba-win32-arm64-msvc": ["@node-rs/jieba-win32-arm64-msvc@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-2.0.1.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Aj/2EwYSaPgAbKnSl+vKM/2kOaZNMZWnShiZzbSNyzlLy3eIOyOYVLbYRDno4547KngRxer8uzROhIQIwXwkvw=="],
"@node-rs/jieba-win32-ia32-msvc": ["@node-rs/jieba-win32-ia32-msvc@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-2.0.1.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-tpJt3uuBlGrcOInQLTYvcgamQgfadl5cwExLYU+CX9rXKpXLDO31dIujUDBgNWoiQq3tOiU1/AKbT7ZdNd4lBQ=="],
"@node-rs/jieba-win32-x64-msvc": ["@node-rs/jieba-win32-x64-msvc@2.0.1", "https://registry.npmjs.com/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-2.0.1.tgz", { "os": "win32", "cpu": "x64" }, "sha512-LDOyo2/2CO8UnpSGLJdgqtH8mOnsABPhNxkfIky7UT9cyLEzOaU44nbA5YzPGpBI3qzMbWcwJYQsjBcgK2VqAg=="],
"@node-rs/jieba-win32-x64-msvc": ["@node-rs/jieba-win32-x64-msvc@2.0.2", "https://registry.npmjs.com/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-2.0.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-9c08mSvOoluteKy0AiyAy+x4uvWB45Q3fheg51gcIxYGxl+Lk0+Xq8JnQxs/f6nLH+mNaOWAmX4DtORQMWabxA=="],
"@oj2/api": ["@oj2/api@workspace:apps/api"],
@@ -537,43 +527,43 @@
"@oj2/web": ["@oj2/web@workspace:apps/web"],
"@oxc-project/types": ["@oxc-project/types@0.143.0", "https://registry.npmjs.com/@oxc-project/types/-/types-0.143.0.tgz", {}, "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA=="],
"@oxc-project/types": ["@oxc-project/types@0.146.0", "https://registry.npmjs.com/@oxc-project/types/-/types-0.146.0.tgz", {}, "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.3", "https://registry.npmjs.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", { "os": "android", "cpu": "arm64" }, "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw=="],
"@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.5", "https://registry.npmjs.com/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.3", "https://registry.npmjs.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.5", "https://registry.npmjs.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.3", "https://registry.npmjs.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.5", "https://registry.npmjs.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.3", "https://registry.npmjs.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.5", "https://registry.npmjs.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", { "os": "linux", "cpu": "arm" }, "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.5", "https://registry.npmjs.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", { "os": "linux", "cpu": "x64" }, "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.3", "https://registry.npmjs.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", { "os": "linux", "cpu": "x64" }, "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.3", "https://registry.npmjs.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", { "os": "none", "cpu": "arm64" }, "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.5", "https://registry.npmjs.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.3", "https://registry.npmjs.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.5", "https://registry.npmjs.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", { "os": "none", "cpu": "arm64" }, "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.3", "https://registry.npmjs.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", { "os": "win32", "cpu": "x64" }, "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.5", "https://registry.npmjs.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.5", "https://registry.npmjs.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "https://registry.npmjs.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
"@transloadit/prettier-bytes": ["@transloadit/prettier-bytes@0.3.5", "https://registry.npmjs.com/@transloadit/prettier-bytes/-/prettier-bytes-0.3.5.tgz", {}, "sha512-xF4A3d/ZyX2LJWeQZREZQw+qFX4TGQ8bGVP97OLRt6sPO6T0TNHBFTuRHOJh7RNmYOBmQ9MHxpolD9bXihpuVA=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "https://registry.npmjs.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
"@types/bun": ["@types/bun@1.3.14", "https://registry.npmjs.com/@types/bun/-/bun-1.3.14.tgz", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
"@types/bun": ["@types/bun@1.4.0", "https://registry.npmjs.com/@types/bun/-/bun-1.4.0.tgz", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="],
"@types/canvas-confetti": ["@types/canvas-confetti@1.9.0", "https://registry.npmjs.com/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz", {}, "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg=="],
@@ -661,7 +651,7 @@
"@types/mdurl": ["@types/mdurl@2.0.0", "https://registry.npmjs.com/@types/mdurl/-/mdurl-2.0.0.tgz", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
"@types/node": ["@types/node@26.1.2", "https://registry.npmjs.com/@types/node/-/node-26.1.2.tgz", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg=="],
"@types/node": ["@types/node@26.3.0", "https://registry.npmjs.com/@types/node/-/node-26.3.0.tgz", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw=="],
"@types/retry": ["@types/retry@0.12.2", "https://registry.npmjs.com/@types/retry/-/retry-0.12.2.tgz", {}, "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow=="],
@@ -733,11 +723,11 @@
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.8", "https://registry.npmjs.com/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew=="],
"@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="],
"@volar/language-core": ["@volar/language-core@2.4.28", "https://registry.npmjs.com/@volar/language-core/-/language-core-2.4.28.tgz", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="],
"@volar/source-map": ["@volar/source-map@2.4.28", "", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="],
"@volar/source-map": ["@volar/source-map@2.4.28", "https://registry.npmjs.com/@volar/source-map/-/source-map-2.4.28.tgz", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="],
"@volar/typescript": ["@volar/typescript@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="],
"@volar/typescript": ["@volar/typescript@2.4.28", "https://registry.npmjs.com/@volar/typescript/-/typescript-2.4.28.tgz", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="],
"@vue-flow/background": ["@vue-flow/background@1.3.2", "https://registry.npmjs.com/@vue-flow/background/-/background-1.3.2.tgz", { "peerDependencies": { "@vue-flow/core": "^1.23.0", "vue": "^3.3.0" } }, "sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w=="],
@@ -767,7 +757,7 @@
"@vue/devtools-shared": ["@vue/devtools-shared@8.2.1", "https://registry.npmjs.com/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", {}, "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g=="],
"@vue/language-core": ["@vue/language-core@3.3.11", "", { "dependencies": { "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.2.1", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.4" } }, "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw=="],
"@vue/language-core": ["@vue/language-core@3.3.11", "https://registry.npmjs.com/@vue/language-core/-/language-core-3.3.11.tgz", { "dependencies": { "@volar/language-core": "2.4.28", "@vue/compiler-dom": "^3.5.0", "@vue/shared": "^3.5.0", "alien-signals": "^3.2.1", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1", "picomatch": "^4.0.4" } }, "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw=="],
"@vue/reactivity": ["@vue/reactivity@3.5.41", "https://registry.npmjs.com/@vue/reactivity/-/reactivity-3.5.41.tgz", { "dependencies": { "@vue/shared": "3.5.41" } }, "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA=="],
@@ -811,7 +801,7 @@
"agent-base": ["agent-base@6.0.2", "https://registry.npmjs.com/agent-base/-/agent-base-6.0.2.tgz", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
"alien-signals": ["alien-signals@3.2.1", "", {}, "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g=="],
"alien-signals": ["alien-signals@3.2.1", "https://registry.npmjs.com/alien-signals/-/alien-signals-3.2.1.tgz", {}, "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g=="],
"argparse": ["argparse@2.0.1", "https://registry.npmjs.com/argparse/-/argparse-2.0.1.tgz", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
@@ -845,9 +835,9 @@
"buffer-from": ["buffer-from@1.1.2", "https://registry.npmjs.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bullmq": ["bullmq@6.0.9", "https://registry.npmjs.com/bullmq/-/bullmq-6.0.9.tgz", { "dependencies": { "cron-parser": "5.7.0", "msgpackr": "2.0.5", "node-abort-controller": "3.1.1", "semver": "7.8.5", "tslib": "2.8.1" }, "peerDependencies": { "bullmq-otel": ">=2.0.0", "ioredis": ">=5.0.0", "pg": ">=8.0.0", "redis": ">=5.0.0" }, "optionalPeers": ["bullmq-otel", "ioredis", "pg", "redis"] }, "sha512-l7kNlirauwdZrqsViO6owHUAhSSN/aCDkrUwZQ2futIItlbJGAxgU+6IOQtTl5Re39GZJEPU6jelGuY1gQD28w=="],
"bullmq": ["bullmq@6.2.2", "https://registry.npmjs.com/bullmq/-/bullmq-6.2.2.tgz", { "dependencies": { "cron-parser": "5.10.0", "msgpackr": "2.0.5", "node-abort-controller": "3.1.1", "semver": "7.8.5", "tslib": "2.8.1" }, "peerDependencies": { "bullmq-otel": ">=2.0.0", "ioredis": ">=5.0.0", "pg": ">=8.0.0", "redis": ">=5.0.0" }, "optionalPeers": ["bullmq-otel", "ioredis", "pg", "redis"] }, "sha512-dwpI14djPYpG15C6Wc7c14/rw8zXXbsGWRgVLR/x8TUi/UBl5+WI6xiBVQH1jnxa3Rdf+T7nylqNui0gysMqQg=="],
"bun-types": ["bun-types@1.3.14", "https://registry.npmjs.com/bun-types/-/bun-types-1.3.14.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
"bun-types": ["bun-types@1.4.0", "https://registry.npmjs.com/bun-types/-/bun-types-1.4.0.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "https://registry.npmjs.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
@@ -885,7 +875,7 @@
"crelt": ["crelt@1.0.7", "https://registry.npmjs.com/crelt/-/crelt-1.0.7.tgz", {}, "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA=="],
"cron-parser": ["cron-parser@5.7.0", "https://registry.npmjs.com/cron-parser/-/cron-parser-5.7.0.tgz", { "dependencies": { "luxon": "^3.7.2" } }, "sha512-iSpDHpwwW/GhIg4JVODYlWUEpMNSimaHvqOhHpOz1W+Y97z1lL1nf+dpcF17cNwFRpTtKN9devgi1fxflp3Phw=="],
"cron-parser": ["cron-parser@5.10.0", "https://registry.npmjs.com/cron-parser/-/cron-parser-5.10.0.tgz", { "dependencies": { "luxon": "^3.7.2" } }, "sha512-izNAxJyRWUP8ljBoDSub5WyrVOUlT4SLGShswE7eoRBpp6QUsSycYxLBMJlbshgPBMcPT/nrfgjNY2918ayv2A=="],
"css-render": ["css-render@0.15.14", "https://registry.npmjs.com/css-render/-/css-render-0.15.14.tgz", { "dependencies": { "@emotion/hash": "~0.8.0", "csstype": "~3.0.5" } }, "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg=="],
@@ -1039,6 +1029,8 @@
"ext": ["ext@1.7.0", "https://registry.npmjs.com/ext/-/ext-1.7.0.tgz", { "dependencies": { "type": "^2.7.2" } }, "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw=="],
"fastdom": ["fastdom@1.0.12", "https://registry.npmjs.com/fastdom/-/fastdom-1.0.12.tgz", { "dependencies": { "strictdom": "^1.0.1" } }, "sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg=="],
"fdir": ["fdir@6.5.0", "https://registry.npmjs.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fflate": ["fflate@0.8.3", "https://registry.npmjs.com/fflate/-/fflate-0.8.3.tgz", {}, "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="],
@@ -1071,9 +1063,9 @@
"hasown": ["hasown@2.0.4", "https://registry.npmjs.com/hasown/-/hasown-2.0.4.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
"highlight.js": ["highlight.js@11.11.1", "https://registry.npmjs.com/highlight.js/-/highlight.js-11.11.1.tgz", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
"highlight.js": ["highlight.js@11.12.0", "https://registry.npmjs.com/highlight.js/-/highlight.js-11.12.0.tgz", {}, "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg=="],
"hono": ["hono@4.13.0", "https://registry.npmjs.com/hono/-/hono-4.13.0.tgz", {}, "sha512-jhunvfHWxd7J5EFfSgH4xsYJzSe/lfqbUCxiyyeaQasUsXeEHXtzVid+7EOGByc5JnFa23SSFL3Y2RV/z1T+eQ=="],
"hono": ["hono@4.13.4", "https://registry.npmjs.com/hono/-/hono-4.13.4.tgz", {}, "sha512-AGEwKIyRMHRv1t8Wjwa3LHxQ61X5CqrdFT+4BRNTpqS5aJNnpl5WLjADb7vFlJzI/8uK7T5QLVApCMQKNa3LgQ=="],
"hookable": ["hookable@5.5.3", "https://registry.npmjs.com/hookable/-/hookable-5.5.3.tgz", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
@@ -1193,7 +1185,7 @@
"meow": ["meow@13.2.0", "https://registry.npmjs.com/meow/-/meow-13.2.0.tgz", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="],
"mermaid": ["mermaid@11.16.1", "https://registry.npmjs.com/mermaid/-/mermaid-11.16.1.tgz", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.20", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "katex": "^0.16.45", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g=="],
"mermaid": ["mermaid@11.17.2", "https://registry.npmjs.com/mermaid/-/mermaid-11.17.2.tgz", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.34.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.21", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "fastdom": "1.0.12", "katex": "^0.16.47", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg=="],
"mermaid-legacy": ["mermaid@9.4.3", "https://registry.npmjs.com/mermaid/-/mermaid-9.4.3.tgz", { "dependencies": { "@braintree/sanitize-url": "^6.0.0", "cytoscape": "^3.23.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.1.0", "d3": "^7.4.0", "dagre-d3-es": "7.0.9", "dayjs": "^1.11.7", "dompurify": "2.4.3", "elkjs": "^0.8.2", "khroma": "^2.0.0", "lodash-es": "^4.17.21", "non-layered-tidy-tree-layout": "^2.0.2", "stylis": "^4.1.2", "ts-dedent": "^2.2.0", "uuid": "^9.0.0", "web-worker": "^1.2.0" } }, "sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw=="],
@@ -1213,7 +1205,7 @@
"muggle-string": ["muggle-string@0.4.1", "https://registry.npmjs.com/muggle-string/-/muggle-string-0.4.1.tgz", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
"naive-ui": ["naive-ui@2.44.1", "https://registry.npmjs.com/naive-ui/-/naive-ui-2.44.1.tgz", { "dependencies": { "@css-render/plugin-bem": "^0.15.14", "@css-render/vue3-ssr": "^0.15.14", "@types/lodash": "^4.17.20", "@types/lodash-es": "^4.17.12", "async-validator": "^4.2.5", "css-render": "^0.15.14", "csstype": "^3.1.3", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "evtd": "^0.2.4", "highlight.js": "^11.8.0", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "seemly": "^0.3.10", "treemate": "^0.3.11", "vdirs": "^0.1.8", "vooks": "^0.2.12", "vueuc": "^0.4.65" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-reo8Esw0p58liZwbUutC7meW24Xbn3EwNv91zReWKm2W4JPu+zfgJRn/F7aO0BFmvN+h2brA2M5lRvYqLq4kuA=="],
"naive-ui": ["naive-ui@2.45.2", "https://registry.npmjs.com/naive-ui/-/naive-ui-2.45.2.tgz", { "dependencies": { "@css-render/plugin-bem": "^0.15.14", "@css-render/vue3-ssr": "^0.15.14", "@types/lodash": "^4.17.20", "@types/lodash-es": "^4.17.12", "async-validator": "^4.2.5", "css-render": "^0.15.14", "csstype": "^3.1.3", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "evtd": "^0.2.4", "highlight.js": "^11.8.0", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "seemly": "^0.3.10", "treemate": "^0.3.11", "vdirs": "^0.1.8", "vooks": "^0.2.12", "vueuc": "^0.4.66" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-KshetbFOX/uZ/Pe+60hJoUAo47x5QO1JpZaUVPQCQkNhFfJ7hKsX55A8oMFQHccEpLuQUMPkJ41cX94R4nWUjg=="],
"namespace-emitter": ["namespace-emitter@2.0.1", "https://registry.npmjs.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz", {}, "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="],
@@ -1243,7 +1235,7 @@
"package-manager-detector": ["package-manager-detector@1.8.0", "https://registry.npmjs.com/package-manager-detector/-/package-manager-detector-1.8.0.tgz", {}, "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A=="],
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
"path-browserify": ["path-browserify@1.0.1", "https://registry.npmjs.com/path-browserify/-/path-browserify-1.0.1.tgz", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
"path-data-parser": ["path-data-parser@0.1.0", "https://registry.npmjs.com/path-data-parser/-/path-data-parser-0.1.0.tgz", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="],
@@ -1257,7 +1249,7 @@
"picomatch": ["picomatch@4.0.5", "https://registry.npmjs.com/picomatch/-/picomatch-4.0.5.tgz", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
"pinia": ["pinia@4.0.2", "https://registry.npmjs.com/pinia/-/pinia-4.0.2.tgz", { "dependencies": { "nostics": "^1.1.4" }, "peerDependencies": { "@vue/devtools-api": "^8.1.5", "typescript": ">=5.6.0", "vue": "^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-yKVVA7bSj5oRZFp/Ab9wLlmyb5gPUYEiIm4ryiWTe/xe7PtkRdMVOp1X1ggvq0c6Uj7Q0Du1HnV2mtAwM0Ks1g=="],
"pinia": ["pinia@4.0.3", "https://registry.npmjs.com/pinia/-/pinia-4.0.3.tgz", { "dependencies": { "nostics": "^1.1.4" }, "peerDependencies": { "@vue/devtools-api": "^8.1.5", "typescript": ">=5.6.0", "vue": "^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA=="],
"pkg-types": ["pkg-types@2.3.1", "https://registry.npmjs.com/pkg-types/-/pkg-types-2.3.1.tgz", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="],
@@ -1311,7 +1303,7 @@
"robust-predicates": ["robust-predicates@3.0.3", "https://registry.npmjs.com/robust-predicates/-/robust-predicates-3.0.3.tgz", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
"rolldown": ["rolldown@1.2.3", "https://registry.npmjs.com/rolldown/-/rolldown-1.2.3.tgz", { "dependencies": { "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.3", "@rolldown/binding-darwin-arm64": "1.2.3", "@rolldown/binding-darwin-x64": "1.2.3", "@rolldown/binding-freebsd-x64": "1.2.3", "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", "@rolldown/binding-linux-arm64-gnu": "1.2.3", "@rolldown/binding-linux-arm64-musl": "1.2.3", "@rolldown/binding-linux-ppc64-gnu": "1.2.3", "@rolldown/binding-linux-s390x-gnu": "1.2.3", "@rolldown/binding-linux-x64-gnu": "1.2.3", "@rolldown/binding-linux-x64-musl": "1.2.3", "@rolldown/binding-openharmony-arm64": "1.2.3", "@rolldown/binding-win32-arm64-msvc": "1.2.3", "@rolldown/binding-win32-x64-msvc": "1.2.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A=="],
"rolldown": ["rolldown@1.2.5", "https://registry.npmjs.com/rolldown/-/rolldown-1.2.5.tgz", { "dependencies": { "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.5", "@rolldown/binding-android-arm64": "1.2.5", "@rolldown/binding-darwin-arm64": "1.2.5", "@rolldown/binding-darwin-x64": "1.2.5", "@rolldown/binding-freebsd-x64": "1.2.5", "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", "@rolldown/binding-linux-arm64-gnu": "1.2.5", "@rolldown/binding-linux-arm64-musl": "1.2.5", "@rolldown/binding-linux-ppc64-gnu": "1.2.5", "@rolldown/binding-linux-s390x-gnu": "1.2.5", "@rolldown/binding-linux-x64-gnu": "1.2.5", "@rolldown/binding-linux-x64-musl": "1.2.5", "@rolldown/binding-openharmony-arm64": "1.2.5", "@rolldown/binding-win32-arm64-msvc": "1.2.5", "@rolldown/binding-win32-x64-msvc": "1.2.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA=="],
"roughjs": ["roughjs@4.6.6", "https://registry.npmjs.com/roughjs/-/roughjs-4.6.6.tgz", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
@@ -1345,12 +1337,14 @@
"source-map-support": ["source-map-support@0.5.21", "https://registry.npmjs.com/source-map-support/-/source-map-support-0.5.21.tgz", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"sql.js": ["sql.js@1.14.1", "https://registry.npmjs.com/sql.js/-/sql.js-1.14.1.tgz", {}, "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A=="],
"sql.js": ["sql.js@1.14.2", "https://registry.npmjs.com/sql.js/-/sql.js-1.14.2.tgz", {}, "sha512-3ZGPovObMFrdw79zrUHbfdE/DLIsy8jdNdssmMSQuRAymedU6q84asPt0kgiqrdMYlPegDItiIMfmIXzZnYFcw=="],
"ssr-window": ["ssr-window@4.0.2", "https://registry.npmjs.com/ssr-window/-/ssr-window-4.0.2.tgz", {}, "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="],
"standard-as-callback": ["standard-as-callback@2.1.0", "https://registry.npmjs.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
"strictdom": ["strictdom@1.0.1", "https://registry.npmjs.com/strictdom/-/strictdom-1.0.1.tgz", {}, "sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg=="],
"string_decoder": ["string_decoder@1.3.0", "https://registry.npmjs.com/string_decoder/-/string_decoder-1.3.0.tgz", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"strip-literal": ["strip-literal@4.0.0", "https://registry.npmjs.com/strip-literal/-/strip-literal-4.0.0.tgz", { "dependencies": { "js-tokens": "^10.0.0" } }, "sha512-PaqAvfUZKBwc/SLmNZtHmzK+v19Z4O4eS3cKPeGvbIv/U3pnyEq4Tuw3/4v/FwfM8VQaEawsyCcOQ0P+kpwWWw=="],
@@ -1417,11 +1411,11 @@
"vdirs": ["vdirs@0.1.8", "https://registry.npmjs.com/vdirs/-/vdirs-0.1.8.tgz", { "dependencies": { "evtd": "^0.2.2" }, "peerDependencies": { "vue": "^3.0.11" } }, "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw=="],
"vite": ["vite@8.2.1", "https://registry.npmjs.com/vite/-/vite-8.2.1.tgz", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.25", "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw=="],
"vite": ["vite@8.2.2", "https://registry.npmjs.com/vite/-/vite-8.2.2.tgz", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.26", "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q=="],
"vooks": ["vooks@0.2.12", "https://registry.npmjs.com/vooks/-/vooks-0.2.12.tgz", { "dependencies": { "evtd": "^0.2.2" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q=="],
"vscode-uri": ["vscode-uri@3.2.0", "", {}, "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg=="],
"vscode-uri": ["vscode-uri@3.2.0", "https://registry.npmjs.com/vscode-uri/-/vscode-uri-3.2.0.tgz", {}, "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg=="],
"vue": ["vue@3.5.41", "https://registry.npmjs.com/vue/-/vue-3.5.41.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.41", "@vue/compiler-sfc": "3.5.41", "@vue/runtime-dom": "3.5.41", "@vue/server-renderer": "3.5.41", "@vue/shared": "3.5.41" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg=="],
@@ -1433,13 +1427,13 @@
"vue-router": ["vue-router@5.2.0", "https://registry.npmjs.com/vue-router/-/vue-router-5.2.0.tgz", { "dependencies": { "@babel/generator": "^8.0.0", "@vue-macros/common": "^3.1.3", "@vue/devtools-api": "^8.1.5", "ast-walker-scope": "^0.9.0", "chokidar": "^5.0.0", "json5": "^2.2.3", "local-pkg": "^1.2.1", "magic-string": "^0.30.21", "mlly": "^1.8.2", "muggle-string": "^0.4.1", "nostics": "^1.1.4", "pathe": "^2.0.3", "picomatch": "^4.0.5", "scule": "^1.3.0", "tinyglobby": "^0.2.17", "unplugin": "^3.3.0", "unplugin-utils": "^0.3.2", "yaml": "^2.9.0" }, "peerDependencies": { "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", "pinia": "^3.0.4 || ^4.0.2", "vite": "^7.3.0 || ^8.0.0", "vue": "^3.5.34 || ^4.0.0" }, "optionalPeers": ["@pinia/colada", "@vue/compiler-sfc", "pinia", "vite"] }, "sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw=="],
"vue-tsc": ["vue-tsc@3.3.11", "", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.3.11" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ=="],
"vue-tsc": ["vue-tsc@3.3.11", "https://registry.npmjs.com/vue-tsc/-/vue-tsc-3.3.11.tgz", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.3.11" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ=="],
"vueuc": ["vueuc@0.4.65", "https://registry.npmjs.com/vueuc/-/vueuc-0.4.65.tgz", { "dependencies": { "@css-render/vue3-ssr": "^0.15.10", "@juggle/resize-observer": "^3.3.1", "css-render": "^0.15.10", "evtd": "^0.2.4", "seemly": "^0.3.6", "vdirs": "^0.1.4", "vooks": "^0.2.4" }, "peerDependencies": { "vue": "^3.0.11" } }, "sha512-lXuMl+8gsBmruudfxnMF9HW4be8rFziylXFu1VHVNbLVhRTXXV4njvpRuJapD/8q+oFEMSfQMH16E/85VoWRyQ=="],
"vueuc": ["vueuc@0.4.66", "https://registry.npmjs.com/vueuc/-/vueuc-0.4.66.tgz", { "dependencies": { "@css-render/vue3-ssr": "^0.15.10", "@juggle/resize-observer": "^3.3.1", "css-render": "^0.15.10", "evtd": "^0.2.4", "seemly": "^0.3.6", "vdirs": "^0.1.4", "vooks": "^0.2.4" }, "peerDependencies": { "vue": "^3.0.11" } }, "sha512-hHrZl+0AsrUvqYPBXSXQ2QaSrO3razjeUseLAhJWlOS/Hdfw/Y2MU05fOTP1qn1A3121gMxsHLaEz2V6CkWdTg=="],
"w3c-keyname": ["w3c-keyname@2.2.8", "https://registry.npmjs.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
"web-tree-sitter": ["web-tree-sitter@0.26.11", "https://registry.npmjs.com/web-tree-sitter/-/web-tree-sitter-0.26.11.tgz", {}, "sha512-Q5Dm3YTIXSXuH6FxX6RuzX2Qwpc4DPGiYMU87Wg5Z8OIStiQFiUex4zMDc0vBTw78EphaYJacncJghCHzbZptg=="],
"web-tree-sitter": ["web-tree-sitter@0.26.13", "https://registry.npmjs.com/web-tree-sitter/-/web-tree-sitter-0.26.13.tgz", {}, "sha512-5bUZ7vbQ1kcondet96wzP974+JfCZDeQ7bTpacICm2nnvHpa5cO0ByRsoMcAhUP+743vpkb4m0BFlVSm+Ye9VA=="],
"web-worker": ["web-worker@1.5.0", "https://registry.npmjs.com/web-worker/-/web-worker-1.5.0.tgz", {}, "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw=="],
@@ -1451,7 +1445,7 @@
"xss": ["xss@1.0.15", "https://registry.npmjs.com/xss/-/xss-1.0.15.tgz", { "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" } }, "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg=="],
"y-codemirror.next": ["y-codemirror.next@0.3.5", "https://registry.npmjs.com/y-codemirror.next/-/y-codemirror.next-0.3.5.tgz", { "dependencies": { "lib0": "^0.2.42" }, "peerDependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "yjs": "^13.5.6" } }, "sha512-VluNu3e5HfEXybnypnsGwKAj+fKLd4iAnR7JuX1Sfyydmn1jCBS5wwEL/uS04Ch2ib0DnMAOF6ZRR/8kK3wyGw=="],
"y-codemirror.next": ["y-codemirror.next@0.3.6", "https://registry.npmjs.com/y-codemirror.next/-/y-codemirror.next-0.3.6.tgz", { "dependencies": { "lib0": "^0.2.42" }, "peerDependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "yjs": "^13.5.6" } }, "sha512-GnmVXhTe+UtoFbbaSdwhq6gdAQZdIY9az0Xj6HR2J4PO6Yw4w3xaaSssU+6T72WhXAI0wf9FbuY2s0Ms+WrexA=="],
"y-protocols": ["y-protocols@1.0.7", "https://registry.npmjs.com/y-protocols/-/y-protocols-1.0.7.tgz", { "dependencies": { "lib0": "^0.2.85" }, "peerDependencies": { "yjs": "^13.0.0" } }, "sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw=="],
@@ -1485,7 +1479,7 @@
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "https://registry.npmjs.com/esbuild/-/esbuild-0.18.20.tgz", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
"@oj2/web/typescript": ["typescript@7.0.2", "https://registry.npmjs.com/typescript/-/typescript-7.0.2.tgz", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="],
"@oj2/api/typescript": ["typescript@7.0.2", "https://registry.npmjs.com/typescript/-/typescript-7.0.2.tgz", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="],
"@types/d3-cloud/@types/d3": ["@types/d3@3.5.53", "https://registry.npmjs.com/@types/d3/-/d3-3.5.53.tgz", {}, "sha512-8yKQA9cAS6+wGsJpBysmnhlaaxlN42Qizqkw+h2nILSlS+MAG2z4JdO6p+PJrJ+ACvimkmLJL281h157e52psQ=="],

View File

@@ -13,7 +13,7 @@
"db:down": "docker compose -f docker/compose.dev.yml down"
},
"devDependencies": {
"typescript": "^5.7.0",
"typescript": "^5.9.3",
"@types/bun": "latest"
}
}

View File

@@ -7,6 +7,6 @@
"types": "./src/index.ts",
"exports": { ".": "./src/index.ts" },
"dependencies": {
"zod": "^4.0.0"
"zod": "^4.4.3"
}
}

View File

@@ -29,7 +29,21 @@ export const rankProfileSchema = z.object({
mood: z.string().nullable(),
})
export const userRankSchema = paginatedSchema(rankProfileSchema)
/**
* 榜单里「我」的位置。`rank` 是**全服名次**,与当前翻到第几页无关 ——
* 前 100 名之外的学生也拿得到,页面靠它单独显示一行。
*
* 名次口径与列表的排序完全一致AC 降序 → 提交数升序 → id 升序),
* 所以「我的名次」和「我在表格里的行号」永远对得上;三个键都相同才算并列。
*/
export const myRankSchema = rankProfileSchema.extend({
rank: z.number().int().positive(),
})
export const userRankSchema = paginatedSchema(rankProfileSchema).extend({
/** 未登录、或身份不入榜(教师/超管)时为 null */
me: myRankSchema.nullable(),
})
export const activityRankItemSchema = z.object({
username: z.string(),
@@ -50,5 +64,6 @@ export type UpdateProfileRequest = z.infer<typeof updateProfileRequestSchema>
export type ProblemRank = z.infer<typeof problemRankSchema>
export type RankProfile = z.infer<typeof rankProfileSchema>
export type UserRank = z.infer<typeof userRankSchema>
export type MyRank = z.infer<typeof myRankSchema>
export type ActivityRankItem = z.infer<typeof activityRankItemSchema>
export type Metrics = z.infer<typeof metricsSchema>

View File

@@ -1,6 +1,7 @@
import { z } from "zod"
import { achievementRaritySchema } from "./achievement"
import { rankProfileSchema } from "./account"
import { paginatedSchema, sampleUserSchema } from "./common"
import { problemDifficultySchema } from "./problem"
@@ -269,6 +270,15 @@ export const orphanTestCaseSchema = z.object({
createTime: z.number(),
})
/**
* 后台的用户排名列表。行的形状与 oj 侧榜单相同,但**没有 100 名的上限**
* 这里不是榜单,是老师按班级前缀翻全班学生的管理视图,要能一直翻到底。
*
* 独立成一条端点而不是给公开榜单加个「不限量」开关:那个开关一旦存在,
* 匿名请求就能拉走全校学生名单和个性签名。
*/
export const adminUserRankSchema = paginatedSchema(rankProfileSchema)
export const dashboardInfoSchema = z.object({
userCount: z.number().int(),
recentContestCount: z.number().int(),
@@ -644,6 +654,7 @@ export type JudgeServer = z.infer<typeof judgeServerSchema>
export type DashboardInfo = z.infer<typeof dashboardInfoSchema>
export type JudgeServerList = z.infer<typeof judgeServerListSchema>
export type OrphanTestCase = z.infer<typeof orphanTestCaseSchema>
export type AdminUserRank = z.infer<typeof adminUserRankSchema>
export type AdminAiReportListItem = z.infer<typeof adminAiReportListItemSchema>
export type AdminAiReport = z.infer<typeof adminAiReportSchema>