test(api): add unit and integration tests for game service and endpoints

- Unit tests for createGameSession and evaluateAnswer (14 tests)
- Endpoint tests for POST /game/start and /game/answer via supertest (8 tests)
- Mock @glossa/db — no real database dependency
This commit is contained in:
lila 2026-04-12 09:04:41 +02:00
parent 48457936e8
commit e320f43d8e
5 changed files with 559 additions and 21 deletions

View file

@ -6,7 +6,8 @@
"scripts": { "scripts": {
"dev": "tsx watch src/server.ts", "dev": "tsx watch src/server.ts",
"build": "tsc", "build": "tsc",
"start": "node dist/server.js" "start": "node dist/server.js",
"test": "vitest"
}, },
"dependencies": { "dependencies": {
"@glossa/db": "workspace:*", "@glossa/db": "workspace:*",
@ -15,6 +16,8 @@
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/supertest": "^7.2.0",
"supertest": "^7.2.2",
"tsx": "^4.21.0" "tsx": "^4.21.0"
} }
} }

View file

@ -0,0 +1,139 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import request from "supertest";
vi.mock("@glossa/db", () => ({
getGameTerms: vi.fn(),
getDistractors: vi.fn(),
}));
import { getGameTerms, getDistractors } from "@glossa/db";
import { createApp } from "../app.js";
const app = createApp();
const mockGetGameTerms = vi.mocked(getGameTerms);
const mockGetDistractors = vi.mocked(getDistractors);
const validBody = {
source_language: "en",
target_language: "it",
pos: "noun",
difficulty: "easy",
rounds: "3",
};
const fakeTerms = [
{ termId: "t1", sourceText: "dog", targetText: "cane", sourceGloss: null },
{ termId: "t2", sourceText: "cat", targetText: "gatto", sourceGloss: null },
{ termId: "t3", sourceText: "house", targetText: "casa", sourceGloss: null },
];
beforeEach(() => {
vi.clearAllMocks();
mockGetGameTerms.mockResolvedValue(fakeTerms);
mockGetDistractors.mockResolvedValue(["wrong1", "wrong2", "wrong3"]);
});
describe("POST /api/v1/game/start", () => {
it("returns 200 with a valid game session", async () => {
const res = await request(app).post("/api/v1/game/start").send(validBody);
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
expect(res.body.data.sessionId).toBeDefined();
expect(res.body.data.questions).toHaveLength(3);
});
it("returns 400 when the body is empty", async () => {
const res = await request(app).post("/api/v1/game/start").send({});
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
expect(res.body.error).toBeDefined();
});
it("returns 400 when required fields are missing", async () => {
const res = await request(app)
.post("/api/v1/game/start")
.send({ source_language: "en" });
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
});
it("returns 400 when a field has an invalid value", async () => {
const res = await request(app)
.post("/api/v1/game/start")
.send({ ...validBody, difficulty: "impossible" });
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
});
});
describe("POST /api/v1/game/answer", () => {
it("returns 200 with an answer result for a valid submission", async () => {
// Start a game first
const startRes = await request(app)
.post("/api/v1/game/start")
.send(validBody);
const { sessionId, questions } = startRes.body.data;
const question = questions[0];
const res = await request(app)
.post("/api/v1/game/answer")
.send({
sessionId,
questionId: question.questionId,
selectedOptionId: 0,
});
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
expect(res.body.data.questionId).toBe(question.questionId);
expect(typeof res.body.data.isCorrect).toBe("boolean");
expect(typeof res.body.data.correctOptionId).toBe("number");
expect(res.body.data.selectedOptionId).toBe(0);
});
it("returns 400 when the body is empty", async () => {
const res = await request(app).post("/api/v1/game/answer").send({});
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
});
it("returns 404 when the session does not exist", async () => {
const res = await request(app)
.post("/api/v1/game/answer")
.send({
sessionId: "00000000-0000-0000-0000-000000000000",
questionId: "00000000-0000-0000-0000-000000000000",
selectedOptionId: 0,
});
expect(res.status).toBe(404);
expect(res.body.success).toBe(false);
expect(res.body.error).toContain("Game session not found");
});
it("returns 404 when the question does not exist in the session", async () => {
const startRes = await request(app)
.post("/api/v1/game/start")
.send(validBody);
const { sessionId } = startRes.body.data;
const res = await request(app)
.post("/api/v1/game/answer")
.send({
sessionId,
questionId: "00000000-0000-0000-0000-000000000000",
selectedOptionId: 0,
});
expect(res.status).toBe(404);
expect(res.body.success).toBe(false);
expect(res.body.error).toContain("Question not found");
});
});

View file

@ -0,0 +1,195 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import type { GameRequest, AnswerSubmission } from "@glossa/shared";
vi.mock("@glossa/db", () => ({
getGameTerms: vi.fn(),
getDistractors: vi.fn(),
}));
import { getGameTerms, getDistractors } from "@glossa/db";
import { createGameSession, evaluateAnswer } from "./gameService.js";
const mockGetGameTerms = vi.mocked(getGameTerms);
const mockGetDistractors = vi.mocked(getDistractors);
const validRequest: GameRequest = {
source_language: "en",
target_language: "it",
pos: "noun",
difficulty: "easy",
rounds: "3",
};
const fakeTerms = [
{ termId: "t1", sourceText: "dog", targetText: "cane", sourceGloss: null },
{ termId: "t2", sourceText: "cat", targetText: "gatto", sourceGloss: null },
{
termId: "t3",
sourceText: "house",
targetText: "casa",
sourceGloss: "a building for living in",
},
];
beforeEach(() => {
vi.clearAllMocks();
mockGetGameTerms.mockResolvedValue(fakeTerms);
mockGetDistractors.mockResolvedValue(["wrong1", "wrong2", "wrong3"]);
});
describe("createGameSession", () => {
it("returns a session with the correct number of questions", async () => {
const session = await createGameSession(validRequest);
expect(session.sessionId).toBeDefined();
expect(session.questions).toHaveLength(3);
});
it("each question has exactly 4 options", async () => {
const session = await createGameSession(validRequest);
for (const question of session.questions) {
expect(question.options).toHaveLength(4);
}
});
it("each question has a unique questionId", async () => {
const session = await createGameSession(validRequest);
const ids = session.questions.map((q) => q.questionId);
expect(new Set(ids).size).toBe(ids.length);
});
it("options have sequential optionIds 0-3", async () => {
const session = await createGameSession(validRequest);
for (const question of session.questions) {
const optionIds = question.options.map((o) => o.optionId);
expect(optionIds).toEqual([0, 1, 2, 3]);
}
});
it("the correct answer is always among the options", async () => {
const session = await createGameSession(validRequest);
for (let i = 0; i < session.questions.length; i++) {
const question = session.questions[i]!;
const correctText = fakeTerms[i]!.targetText;
const optionTexts = question.options.map((o) => o.text);
expect(optionTexts).toContain(correctText);
}
});
it("distractors are never the correct answer", async () => {
const session = await createGameSession(validRequest);
for (let i = 0; i < session.questions.length; i++) {
const question = session.questions[i]!;
const correctText = fakeTerms[i]!.targetText;
const distractorTexts = question.options
.map((o) => o.text)
.filter((t) => t !== correctText);
for (const text of distractorTexts) {
expect(text).not.toBe(correctText);
}
}
});
it("sets the prompt from the source text", async () => {
const session = await createGameSession(validRequest);
expect(session.questions[0]!.prompt).toBe("dog");
expect(session.questions[1]!.prompt).toBe("cat");
expect(session.questions[2]!.prompt).toBe("house");
});
it("passes gloss through (null or string)", async () => {
const session = await createGameSession(validRequest);
expect(session.questions[0]!.gloss).toBeNull();
expect(session.questions[2]!.gloss).toBe("a building for living in");
});
it("calls getGameTerms with the correct arguments", async () => {
await createGameSession(validRequest);
expect(mockGetGameTerms).toHaveBeenCalledWith(
"en",
"it",
"noun",
"easy",
3,
);
});
it("calls getDistractors once per question", async () => {
await createGameSession(validRequest);
expect(mockGetDistractors).toHaveBeenCalledTimes(3);
});
});
describe("evaluateAnswer", () => {
it("returns isCorrect: true when the correct option is selected", async () => {
const session = await createGameSession(validRequest);
const question = session.questions[0]!;
const correctText = fakeTerms[0]!.targetText;
const correctOption = question.options.find((o) => o.text === correctText)!;
const result = await evaluateAnswer({
sessionId: session.sessionId,
questionId: question.questionId,
selectedOptionId: correctOption.optionId,
});
expect(result.isCorrect).toBe(true);
expect(result.correctOptionId).toBe(correctOption.optionId);
expect(result.selectedOptionId).toBe(correctOption.optionId);
});
it("returns isCorrect: false when a wrong option is selected", async () => {
const session = await createGameSession(validRequest);
const question = session.questions[0]!;
const correctText = fakeTerms[0]!.targetText;
const correctOption = question.options.find((o) => o.text === correctText)!;
const wrongOption = question.options.find((o) => o.text !== correctText)!;
const result = await evaluateAnswer({
sessionId: session.sessionId,
questionId: question.questionId,
selectedOptionId: wrongOption.optionId,
});
expect(result.isCorrect).toBe(false);
expect(result.correctOptionId).toBe(correctOption.optionId);
expect(result.selectedOptionId).toBe(wrongOption.optionId);
});
it("throws NotFoundError for a non-existent session", async () => {
const submission: AnswerSubmission = {
sessionId: "00000000-0000-0000-0000-000000000000",
questionId: "00000000-0000-0000-0000-000000000000",
selectedOptionId: 0,
};
await expect(evaluateAnswer(submission)).rejects.toThrow(
"Game session not found",
);
});
it("throws NotFoundError for a non-existent question", async () => {
const session = await createGameSession(validRequest);
const submission: AnswerSubmission = {
sessionId: session.sessionId,
questionId: "00000000-0000-0000-0000-000000000000",
selectedOptionId: 0,
};
await expect(evaluateAnswer(submission)).rejects.toThrow(
"Question not found",
);
});
});

View file

@ -313,7 +313,7 @@ Required before: implementing the double join for source language prompt.
- Central error middleware in `app.ts` - Central error middleware in `app.ts`
- Remove temporary `safeParse` error handling from controllers - Remove temporary `safeParse` error handling from controllers
### Step 7 — Tests ### Step 7 — Tests - done
- Unit tests for `QuizService` — correct POS filtering, distractor never equals correct answer - Unit tests for `QuizService` — correct POS filtering, distractor never equals correct answer
- Unit tests for `evaluateAnswer` — correct and incorrect cases - Unit tests for `evaluateAnswer` — correct and incorrect cases
@ -339,3 +339,10 @@ Required before: implementing the double join for source language prompt.
word in the definition text (e.g. "Padre" appearing in the English gloss for word in the definition text (e.g. "Padre" appearing in the English gloss for
"father"). Address during the post-MVP data enrichment pass — either clean the "father"). Address during the post-MVP data enrichment pass — either clean the
glosses, replace them with custom definitions, or filter at the service layer. => resolved glosses, replace them with custom definitions, or filter at the service layer. => resolved
- WARN 2 deprecated subdependencies found: @esbuild-kit/core-utils@3.3.2, @esbuild-kit/esm-loader@2.6.5
../.. | Progress: resolved 556, reused 0, downloaded 0, added 0, done
WARN Issues with peer dependencies found
.
└─┬ eslint-plugin-react-hooks 7.0.1
└── ✕ unmet peer eslint@"^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0": found 10.0.3

232
pnpm-lock.yaml generated
View file

@ -16,7 +16,7 @@ importers:
version: 1.161.6(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) version: 1.161.6(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0(vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))) version: 4.1.0(vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@1.8.0))(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)))
concurrently: concurrently:
specifier: ^9.2.1 specifier: ^9.2.1
version: 9.2.1 version: 9.2.1
@ -43,7 +43,7 @@ importers:
version: 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) version: 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
vitest: vitest:
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)) version: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@1.8.0))(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))
apps/api: apps/api:
dependencies: dependencies:
@ -60,6 +60,12 @@ importers:
'@types/express': '@types/express':
specifier: ^5.0.6 specifier: ^5.0.6
version: 5.0.6 version: 5.0.6
'@types/supertest':
specifier: ^7.2.0
version: 7.2.0
supertest:
specifier: ^7.2.2
version: 7.2.2
tsx: tsx:
specifier: ^4.21.0 specifier: ^4.21.0
version: 4.21.0 version: 4.21.0
@ -105,7 +111,7 @@ importers:
version: 6.0.1(vite@8.0.1(@types/node@24.12.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)) version: 6.0.1(vite@8.0.1(@types/node@24.12.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))
jsdom: jsdom:
specifier: ^29.0.1 specifier: ^29.0.1
version: 29.0.1 version: 29.0.1(@noble/hashes@1.8.0)
vite: vite:
specifier: ^8.0.1 specifier: ^8.0.1
version: 8.0.1(@types/node@24.12.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0) version: 8.0.1(@types/node@24.12.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)
@ -828,9 +834,16 @@ packages:
'@napi-rs/wasm-runtime@1.1.1': '@napi-rs/wasm-runtime@1.1.1':
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
'@oxc-project/types@0.120.0': '@oxc-project/types@0.120.0':
resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==}
'@paralleldrive/cuid2@2.3.1':
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
'@rolldown/binding-android-arm64@1.0.0-rc.10': '@rolldown/binding-android-arm64@1.0.0-rc.10':
resolution: {integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==} resolution: {integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@ -1128,6 +1141,9 @@ packages:
'@types/connect@3.4.38': '@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
'@types/cookiejar@2.1.5':
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
'@types/deep-eql@4.0.2': '@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
@ -1149,6 +1165,9 @@ packages:
'@types/json-schema@7.0.15': '@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/methods@1.1.4':
resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
'@types/node@24.12.0': '@types/node@24.12.0':
resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==}
@ -1178,6 +1197,12 @@ packages:
'@types/serve-static@2.2.0': '@types/serve-static@2.2.0':
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
'@types/superagent@8.1.9':
resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==}
'@types/supertest@7.2.0':
resolution: {integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==}
'@typescript-eslint/eslint-plugin@8.57.1': '@typescript-eslint/eslint-plugin@8.57.1':
resolution: {integrity: sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==} resolution: {integrity: sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -1325,6 +1350,9 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
assertion-error@2.0.1: assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -1336,6 +1364,9 @@ packages:
ast-v8-to-istanbul@1.0.0: ast-v8-to-istanbul@1.0.0:
resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
babel-dead-code-elimination@1.0.12: babel-dead-code-elimination@1.0.12:
resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==}
@ -1425,6 +1456,13 @@ packages:
color-name@1.1.4: color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
component-emitter@1.3.1:
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
concurrently@9.2.1: concurrently@9.2.1:
resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -1452,6 +1490,9 @@ packages:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
cookiejar@2.1.4:
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
crc-32@1.2.2: crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'} engines: {node: '>=0.8'}
@ -1487,6 +1528,10 @@ packages:
deep-is@0.1.4: deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
depd@2.0.0: depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -1495,6 +1540,9 @@ packages:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
diff@8.0.3: diff@8.0.3:
resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==}
engines: {node: '>=0.3.1'} engines: {node: '>=0.3.1'}
@ -1639,6 +1687,10 @@ packages:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
esbuild@0.18.20: esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -1753,6 +1805,9 @@ packages:
fast-levenshtein@2.0.6: fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
fdir@6.5.0: fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
@ -1785,6 +1840,14 @@ packages:
flatted@3.4.2: flatted@3.4.2:
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
form-data@4.0.5:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
formidable@3.5.4:
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
engines: {node: '>=14.0.0'}
forwarded@0.2.0: forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
@ -1852,6 +1915,10 @@ packages:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2: hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -2096,14 +2163,31 @@ packages:
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
engines: {node: '>=18'} engines: {node: '>=18'}
methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-db@1.54.0: mime-db@1.54.0:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'} 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: mime-types@3.0.2:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'} engines: {node: '>=18'}
mime@2.6.0:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
engines: {node: '>=4.0.0'}
hasBin: true
minimatch@10.2.4: minimatch@10.2.4:
resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==}
engines: {node: 18 || 20 || >=22} engines: {node: 18 || 20 || >=22}
@ -2423,6 +2507,14 @@ packages:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'} engines: {node: '>=8'}
superagent@10.3.0:
resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==}
engines: {node: '>=14.18.0'}
supertest@7.2.2:
resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
engines: {node: '>=14.18.0'}
supports-color@7.2.0: supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -3178,7 +3270,9 @@ snapshots:
'@eslint/core': 1.1.1 '@eslint/core': 1.1.1
levn: 0.4.1 levn: 0.4.1
'@exodus/bytes@1.15.0': {} '@exodus/bytes@1.15.0(@noble/hashes@1.8.0)':
optionalDependencies:
'@noble/hashes': 1.8.0
'@humanfs/core@0.19.1': {} '@humanfs/core@0.19.1': {}
@ -3217,8 +3311,14 @@ snapshots:
'@tybys/wasm-util': 0.10.1 '@tybys/wasm-util': 0.10.1
optional: true optional: true
'@noble/hashes@1.8.0': {}
'@oxc-project/types@0.120.0': {} '@oxc-project/types@0.120.0': {}
'@paralleldrive/cuid2@2.3.1':
dependencies:
'@noble/hashes': 1.8.0
'@rolldown/binding-android-arm64@1.0.0-rc.10': '@rolldown/binding-android-arm64@1.0.0-rc.10':
optional: true optional: true
@ -3468,6 +3568,8 @@ snapshots:
dependencies: dependencies:
'@types/node': 24.12.0 '@types/node': 24.12.0
'@types/cookiejar@2.1.5': {}
'@types/deep-eql@4.0.2': {} '@types/deep-eql@4.0.2': {}
'@types/esrecurse@4.3.1': {} '@types/esrecurse@4.3.1': {}
@ -3491,6 +3593,8 @@ snapshots:
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/methods@1.1.4': {}
'@types/node@24.12.0': '@types/node@24.12.0':
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
@ -3527,6 +3631,18 @@ snapshots:
'@types/http-errors': 2.0.5 '@types/http-errors': 2.0.5
'@types/node': 24.12.0 '@types/node': 24.12.0
'@types/superagent@8.1.9':
dependencies:
'@types/cookiejar': 2.1.5
'@types/methods': 1.1.4
'@types/node': 24.12.0
form-data: 4.0.5
'@types/supertest@7.2.0':
dependencies:
'@types/methods': 1.1.4
'@types/superagent': 8.1.9
'@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
@ -3623,7 +3739,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-rc.7 '@rolldown/pluginutils': 1.0.0-rc.7
vite: 8.0.1(@types/node@24.12.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0) vite: 8.0.1(@types/node@24.12.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)
'@vitest/coverage-v8@4.1.0(vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)))': '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@1.8.0))(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)))':
dependencies: dependencies:
'@bcoe/v8-coverage': 1.0.2 '@bcoe/v8-coverage': 1.0.2
'@vitest/utils': 4.1.0 '@vitest/utils': 4.1.0
@ -3635,7 +3751,7 @@ snapshots:
obug: 2.1.1 obug: 2.1.1
std-env: 4.0.0 std-env: 4.0.0
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
vitest: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)) vitest: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@1.8.0))(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))
'@vitest/expect@4.1.0': '@vitest/expect@4.1.0':
dependencies: dependencies:
@ -3711,6 +3827,8 @@ snapshots:
normalize-path: 3.0.0 normalize-path: 3.0.0
picomatch: 2.3.1 picomatch: 2.3.1
asap@2.0.6: {}
assertion-error@2.0.1: {} assertion-error@2.0.1: {}
ast-types@0.16.1: ast-types@0.16.1:
@ -3723,6 +3841,8 @@ snapshots:
estree-walker: 3.0.3 estree-walker: 3.0.3
js-tokens: 10.0.0 js-tokens: 10.0.0
asynckit@0.4.0: {}
babel-dead-code-elimination@1.0.12: babel-dead-code-elimination@1.0.12:
dependencies: dependencies:
'@babel/core': 7.29.0 '@babel/core': 7.29.0
@ -3828,6 +3948,12 @@ snapshots:
color-name@1.1.4: {} color-name@1.1.4: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
component-emitter@1.3.1: {}
concurrently@9.2.1: concurrently@9.2.1:
dependencies: dependencies:
chalk: 4.1.2 chalk: 4.1.2
@ -3849,6 +3975,8 @@ snapshots:
cookie@0.7.2: {} cookie@0.7.2: {}
cookiejar@2.1.4: {}
crc-32@1.2.2: {} crc-32@1.2.2: {}
cross-spawn@7.0.6: cross-spawn@7.0.6:
@ -3864,10 +3992,10 @@ snapshots:
csstype@3.2.3: {} csstype@3.2.3: {}
data-urls@7.0.0: data-urls@7.0.0(@noble/hashes@1.8.0):
dependencies: dependencies:
whatwg-mimetype: 5.0.0 whatwg-mimetype: 5.0.0
whatwg-url: 16.0.1 whatwg-url: 16.0.1(@noble/hashes@1.8.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@noble/hashes' - '@noble/hashes'
@ -3879,10 +4007,17 @@ snapshots:
deep-is@0.1.4: {} deep-is@0.1.4: {}
delayed-stream@1.0.0: {}
depd@2.0.0: {} depd@2.0.0: {}
detect-libc@2.1.2: {} detect-libc@2.1.2: {}
dezalgo@1.0.4:
dependencies:
asap: 2.0.6
wrappy: 1.0.2
diff@8.0.3: {} diff@8.0.3: {}
dotenv@17.3.1: {} dotenv@17.3.1: {}
@ -3930,6 +4065,13 @@ snapshots:
dependencies: dependencies:
es-errors: 1.3.0 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.2
esbuild@0.18.20: esbuild@0.18.20:
optionalDependencies: optionalDependencies:
'@esbuild/android-arm': 0.18.20 '@esbuild/android-arm': 0.18.20
@ -4153,6 +4295,8 @@ snapshots:
fast-levenshtein@2.0.6: {} fast-levenshtein@2.0.6: {}
fast-safe-stringify@2.1.1: {}
fdir@6.5.0(picomatch@4.0.3): fdir@6.5.0(picomatch@4.0.3):
optionalDependencies: optionalDependencies:
picomatch: 4.0.3 picomatch: 4.0.3
@ -4188,6 +4332,20 @@ snapshots:
flatted@3.4.2: {} flatted@3.4.2: {}
form-data@4.0.5:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
hasown: 2.0.2
mime-types: 2.1.35
formidable@3.5.4:
dependencies:
'@paralleldrive/cuid2': 2.3.1
dezalgo: 1.0.4
once: 1.4.0
forwarded@0.2.0: {} forwarded@0.2.0: {}
frac@1.1.2: {} frac@1.1.2: {}
@ -4245,6 +4403,10 @@ snapshots:
has-symbols@1.1.0: {} has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2: hasown@2.0.2:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
@ -4255,9 +4417,9 @@ snapshots:
dependencies: dependencies:
hermes-estree: 0.25.1 hermes-estree: 0.25.1
html-encoding-sniffer@6.0.0: html-encoding-sniffer@6.0.0(@noble/hashes@1.8.0):
dependencies: dependencies:
'@exodus/bytes': 1.15.0 '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@noble/hashes' - '@noble/hashes'
@ -4326,17 +4488,17 @@ snapshots:
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
jsdom@29.0.1: jsdom@29.0.1(@noble/hashes@1.8.0):
dependencies: dependencies:
'@asamuzakjp/css-color': 5.0.1 '@asamuzakjp/css-color': 5.0.1
'@asamuzakjp/dom-selector': 7.0.4 '@asamuzakjp/dom-selector': 7.0.4
'@bramus/specificity': 2.4.2 '@bramus/specificity': 2.4.2
'@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1)
'@exodus/bytes': 1.15.0 '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0)
css-tree: 3.2.1 css-tree: 3.2.1
data-urls: 7.0.0 data-urls: 7.0.0(@noble/hashes@1.8.0)
decimal.js: 10.6.0 decimal.js: 10.6.0
html-encoding-sniffer: 6.0.0 html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0)
is-potential-custom-element-name: 1.0.1 is-potential-custom-element-name: 1.0.1
lru-cache: 11.2.7 lru-cache: 11.2.7
parse5: 8.0.0 parse5: 8.0.0
@ -4347,7 +4509,7 @@ snapshots:
w3c-xmlserializer: 5.0.0 w3c-xmlserializer: 5.0.0
webidl-conversions: 8.0.1 webidl-conversions: 8.0.1
whatwg-mimetype: 5.0.0 whatwg-mimetype: 5.0.0
whatwg-url: 16.0.1 whatwg-url: 16.0.1(@noble/hashes@1.8.0)
xml-name-validator: 5.0.0 xml-name-validator: 5.0.0
transitivePeerDependencies: transitivePeerDependencies:
- '@noble/hashes' - '@noble/hashes'
@ -4452,12 +4614,22 @@ snapshots:
merge-descriptors@2.0.0: {} merge-descriptors@2.0.0: {}
methods@1.1.2: {}
mime-db@1.52.0: {}
mime-db@1.54.0: {} mime-db@1.54.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
mime-types@3.0.2: mime-types@3.0.2:
dependencies: dependencies:
mime-db: 1.54.0 mime-db: 1.54.0
mime@2.6.0: {}
minimatch@10.2.4: minimatch@10.2.4:
dependencies: dependencies:
brace-expansion: 5.0.4 brace-expansion: 5.0.4
@ -4774,6 +4946,28 @@ snapshots:
dependencies: dependencies:
ansi-regex: 5.0.1 ansi-regex: 5.0.1
superagent@10.3.0:
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
debug: 4.4.3
fast-safe-stringify: 2.1.1
form-data: 4.0.5
formidable: 3.5.4
methods: 1.1.2
mime: 2.6.0
qs: 6.15.0
transitivePeerDependencies:
- supports-color
supertest@7.2.2:
dependencies:
cookie-signature: 1.2.2
methods: 1.1.2
superagent: 10.3.0
transitivePeerDependencies:
- supports-color
supports-color@7.2.0: supports-color@7.2.0:
dependencies: dependencies:
has-flag: 4.0.0 has-flag: 4.0.0
@ -4921,7 +5115,7 @@ snapshots:
jiti: 2.6.1 jiti: 2.6.1
tsx: 4.21.0 tsx: 4.21.0
vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)): vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@1.8.0))(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)):
dependencies: dependencies:
'@vitest/expect': 4.1.0 '@vitest/expect': 4.1.0
'@vitest/mocker': 4.1.0(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)) '@vitest/mocker': 4.1.0(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))
@ -4945,7 +5139,7 @@ snapshots:
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 25.5.0 '@types/node': 25.5.0
jsdom: 29.0.1 jsdom: 29.0.1(@noble/hashes@1.8.0)
transitivePeerDependencies: transitivePeerDependencies:
- msw - msw
@ -4959,9 +5153,9 @@ snapshots:
whatwg-mimetype@5.0.0: {} whatwg-mimetype@5.0.0: {}
whatwg-url@16.0.1: whatwg-url@16.0.1(@noble/hashes@1.8.0):
dependencies: dependencies:
'@exodus/bytes': 1.15.0 '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0)
tr46: 6.0.0 tr46: 6.0.0
webidl-conversions: 8.0.1 webidl-conversions: 8.0.1
transitivePeerDependencies: transitivePeerDependencies: