test: add vitest unit tests across packages

Cover shared zod schemas, extract traversal/refs/downloads, and tts filename/error handling. Document the test setup in the README and docs.
This commit is contained in:
2026-08-08 11:13:14 +08:00
parent 1de559c321
commit 92f11db415
15 changed files with 1185 additions and 3 deletions
+1
View File
@@ -40,6 +40,7 @@ Get a Steam Web API key at https://steamcommunity.com/dev/apikey (free).
pnpm dev # run the proxy (tsx watch)
pnpm build # compile all packages
pnpm typecheck # typecheck all packages
pnpm test # run the unit tests (vitest)
pnpm lint # lint all packages
```
+1
View File
@@ -89,6 +89,7 @@ packages/extract ──► packages/tts (traverseMod) ──► packages/shared
## Tooling dependencies
- `typescript` (strict), `tsx` (dev runner), `eslint`, `prettier`.
- `vitest` (unit tests), colocated as `*.test.ts` next to sources.
- `pnpm` workspaces for package management.
## Deployment / runtime shape
+2 -1
View File
@@ -192,7 +192,8 @@ packages/extract → flatten objects / extract refs / download assets
- TypeScript strict mode.
- `tsx` for dev, `tsc` for build.
- Root scripts: `pnpm dev`, `pnpm build`, `pnpm lint`.
- `vitest` for unit tests, colocated as `*.test.ts` next to sources.
- Root scripts: `pnpm dev`, `pnpm build`, `pnpm test`, `pnpm lint`.
## Build order
+4 -2
View File
@@ -7,10 +7,12 @@
"dev": "pnpm --filter @tts/proxy dev",
"build": "pnpm -r build",
"lint": "pnpm -r lint",
"typecheck": "pnpm -r typecheck"
"typecheck": "pnpm -r typecheck",
"test": "vitest run"
},
"packageManager": "pnpm@10.33.0",
"devDependencies": {
"typescript": "^5.7.2"
"typescript": "^5.7.2",
"vitest": "^4.1.10"
}
}
+1
View File
@@ -14,6 +14,7 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "vitest run",
"lint": "echo \"no lint configured\""
},
"dependencies": {
+92
View File
@@ -0,0 +1,92 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import type { AssetRef } from '@tts/shared';
import { downloadAll, downloadAsset, guessMimeType } from './download.js';
const ref = (url: string): AssetRef => ({
kind: 'image',
url,
ownerGuid: 'g1',
});
function blobResponse(): Response {
return new Response(new Blob(['data']), { status: 200 });
}
afterEach(() => {
vi.unstubAllGlobals();
});
describe('guessMimeType', () => {
it('maps common image extensions', () => {
expect(guessMimeType('https://example.com/a.png')).toBe('image/png');
expect(guessMimeType('https://example.com/a.jpg')).toBe('image/jpeg');
expect(guessMimeType('https://example.com/a.jpeg')).toBe('image/jpeg');
expect(guessMimeType('https://example.com/a.gif')).toBe('image/gif');
expect(guessMimeType('https://example.com/a.webp')).toBe('image/webp');
expect(guessMimeType('https://example.com/a.pdf')).toBe('application/pdf');
});
it('ignores query strings when reading the extension', () => {
expect(guessMimeType('https://example.com/a.png?v=2')).toBe('image/png');
});
it('falls back to octet-stream for unknown extensions', () => {
expect(guessMimeType('https://example.com/a.xyz')).toBe(
'application/octet-stream',
);
expect(guessMimeType('https://example.com/noext')).toBe(
'application/octet-stream',
);
});
});
describe('downloadAsset', () => {
it('returns a blob on success', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(blobResponse()));
const blob = await downloadAsset('https://example.com/a.png');
expect(blob).toBeInstanceOf(Blob);
});
it('throws on a non-OK response', async () => {
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue(new Response(null, { status: 404 })),
);
await expect(downloadAsset('https://example.com/a.png')).rejects.toThrow(
'Failed to download asset (404)',
);
});
});
describe('downloadAll', () => {
it('downloads every ref and reports progress', async () => {
vi.stubGlobal('fetch', vi.fn().mockImplementation(() => blobResponse()));
const refs = [ref('https://example.com/1.png'), ref('https://example.com/2.png')];
const progress: number[] = [];
const results = await downloadAll(refs, {
concurrency: 2,
onProgress: (p) => progress.push(p.done),
});
expect(results).toHaveLength(2);
expect(progress).toEqual([1, 2]);
});
it('respects the concurrency limit', async () => {
let inFlight = 0;
let maxInFlight = 0;
const fetchMock = vi.fn().mockImplementation(async () => {
inFlight += 1;
maxInFlight = Math.max(maxInFlight, inFlight);
await new Promise((r) => setTimeout(r, 5));
inFlight -= 1;
return blobResponse();
});
vi.stubGlobal('fetch', fetchMock);
const refs = Array.from({ length: 6 }, (_, i) =>
ref(`https://example.com/${i}.png`),
);
await downloadAll(refs, { concurrency: 2 });
expect(maxInFlight).toBeLessThanOrEqual(2);
});
});
+51
View File
@@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest';
import type { TTSMod, TTSObject } from '@tts/shared';
import { filterObjects, findObject, flattenObjects } from './objects.js';
function card(guid: string, name = 'Card'): TTSObject {
return { Name: name, GUID: guid, Description: '' };
}
function bag(guid: string, contained: TTSObject[]): TTSObject {
return { Name: 'Bag', GUID: guid, Description: '', ContainedObjects: contained };
}
const mod: TTSMod = {
GameMode: 'Tabletop',
Date: '2024-01-01',
ObjectStates: [
card('a', 'Deck'),
bag('b', [card('c', 'Card'), card('d', 'Token')]),
card('e', 'Deck'),
],
};
describe('flattenObjects', () => {
it('returns every object including those inside bags', () => {
const guids = flattenObjects(mod).map((o) => o.GUID);
expect(guids).toEqual(['a', 'c', 'd', 'e']);
});
it('sets Parent links before returning', () => {
const [a, c] = flattenObjects(mod);
expect(a!.Parent).toBeUndefined();
expect(c!.Parent!.GUID).toBe('b');
});
});
describe('filterObjects', () => {
it('filters by predicate', () => {
const decks = filterObjects(mod, (o) => o.Name === 'Deck');
expect(decks.map((o) => o.GUID)).toEqual(['a', 'e']);
});
});
describe('findObject', () => {
it('finds an object by GUID', () => {
expect(findObject(mod, 'd')?.Name).toBe('Token');
});
it('returns undefined for a missing GUID', () => {
expect(findObject(mod, 'zzz')).toBeUndefined();
});
});
+100
View File
@@ -0,0 +1,100 @@
import { describe, expect, it } from 'vitest';
import type { TTSMod, TTSObject } from '@tts/shared';
import { collectRefs, extractRefs } from './refs.js';
function card(guid: string): TTSObject {
return { Name: 'Card', GUID: guid, Description: '' };
}
describe('extractRefs', () => {
it('extracts a PDF reference', () => {
const o: TTSObject = {
...card('g1'),
CustomPDF: { PDFUrl: 'https://example.com/rules.pdf' },
};
expect(extractRefs(o)).toEqual([
{ kind: 'pdf', url: 'https://example.com/rules.pdf', ownerGuid: 'g1' },
]);
});
it('extracts deck face and back references', () => {
const o: TTSObject = {
...card('g2'),
CustomDeck: {
1: {
FaceURL: 'https://example.com/face.png',
BackURL: 'https://example.com/back.png',
UniqueBack: true,
NumHeight: 1,
NumWidth: 1,
},
},
};
expect(extractRefs(o)).toEqual([
{ kind: 'deckFace', url: 'https://example.com/face.png', ownerGuid: 'g2' },
{ kind: 'deckBack', url: 'https://example.com/back.png', ownerGuid: 'g2' },
]);
});
it('extracts image and secondary image references', () => {
const o: TTSObject = {
...card('g3'),
CustomImage: {
ImageURL: 'https://example.com/a.png',
ImageSecondaryURL: 'https://example.com/b.png',
},
};
expect(extractRefs(o)).toEqual([
{ kind: 'image', url: 'https://example.com/a.png', ownerGuid: 'g3' },
{
kind: 'imageSecondary',
url: 'https://example.com/b.png',
ownerGuid: 'g3',
},
]);
});
it('returns an empty array for an object with no refs', () => {
expect(extractRefs(card('g4'))).toEqual([]);
});
});
describe('collectRefs', () => {
it('collects refs across the whole save', () => {
const mod: TTSMod = {
GameMode: 'Tabletop',
Date: '2024-01-01',
ObjectStates: [
{ ...card('g1'), CustomPDF: { PDFUrl: 'https://example.com/a.pdf' } },
{
...card('g2'),
CustomImage: {
ImageURL: 'https://example.com/a.png',
ImageSecondaryURL: 'https://example.com/b.png',
},
},
],
};
const refs = collectRefs(mod);
expect(refs).toHaveLength(3);
expect(refs.map((r) => r.kind).sort()).toEqual([
'image',
'imageSecondary',
'pdf',
]);
});
it('dedupes refs by URL', () => {
const mod: TTSMod = {
GameMode: 'Tabletop',
Date: '2024-01-01',
ObjectStates: [
{ ...card('g1'), CustomPDF: { PDFUrl: 'https://example.com/a.pdf' } },
{ ...card('g2'), CustomPDF: { PDFUrl: 'https://example.com/a.pdf' } },
],
};
const refs = collectRefs(mod);
expect(refs).toHaveLength(1);
expect(refs[0]!.ownerGuid).toBe('g1');
});
});
+60
View File
@@ -0,0 +1,60 @@
import { describe, expect, it } from 'vitest';
import type { TTSMod, TTSObject } from '@tts/shared';
import { markParent, traverseMod } from './traverse.js';
function card(guid: string, name = 'Card'): TTSObject {
return { Name: name, GUID: guid, Description: '' };
}
function bag(guid: string, contained: TTSObject[]): TTSObject {
return { Name: 'Bag', GUID: guid, Description: '', ContainedObjects: contained };
}
const mod: TTSMod = {
GameMode: 'Tabletop',
Date: '2024-01-01',
ObjectStates: [
card('a'),
bag('b', [card('c'), bag('d', [card('e')])]),
card('f'),
],
};
describe('markParent', () => {
it('sets Parent on direct children', () => {
const root = bag('b', [card('c')]);
markParent(root);
expect(root.ContainedObjects![0]!.Parent).toBe(root);
});
it('sets Parent recursively', () => {
const root = bag('b', [bag('d', [card('e')])]);
markParent(root);
const d = root.ContainedObjects![0]!;
expect(d.Parent).toBe(root);
expect(d.ContainedObjects![0]!.Parent).toBe(d);
});
it('leaves leaf objects untouched', () => {
const leaf = card('a');
markParent(leaf);
expect(leaf.Parent).toBeUndefined();
});
});
describe('traverseMod', () => {
it('yields every non-bag object in order', () => {
const guids = [...traverseMod(mod)].map((o) => o.GUID);
expect(guids).toEqual(['a', 'c', 'e', 'f']);
});
it('descends into nested bags', () => {
const guids = [...traverseMod(mod)].map((o) => o.GUID);
expect(guids).toContain('e');
});
it('accepts a single object', () => {
const guids = [...traverseMod(bag('b', [card('c')]))].map((o) => o.GUID);
expect(guids).toEqual(['c']);
});
});
+1
View File
@@ -14,6 +14,7 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "vitest run",
"lint": "echo \"no lint configured\""
},
"dependencies": {
+135
View File
@@ -0,0 +1,135 @@
import { describe, expect, it } from 'vitest';
import {
assetRefSchema,
extractedObjectSchema,
itemIdSchema,
searchQuerySchema,
searchResultSchema,
workshopItemSchema,
} from './schemas.js';
describe('itemIdSchema', () => {
it('accepts a run of digits', () => {
expect(itemIdSchema.parse('123456')).toBe('123456');
});
it('rejects empty strings', () => {
expect(itemIdSchema.safeParse('').success).toBe(false);
});
it('rejects non-numeric input', () => {
expect(itemIdSchema.safeParse('abc').success).toBe(false);
expect(itemIdSchema.safeParse('12a34').success).toBe(false);
});
});
describe('searchQuerySchema', () => {
it('coerces page to a number and defaults it', () => {
expect(searchQuerySchema.parse({ q: 'cards' })).toEqual({
q: 'cards',
page: 1,
});
expect(searchQuerySchema.parse({ q: 'cards', page: '3' })).toEqual({
q: 'cards',
page: 3,
});
});
it('rejects a missing or blank query', () => {
expect(searchQuerySchema.safeParse({}).success).toBe(false);
expect(searchQuerySchema.safeParse({ q: ' ' }).success).toBe(false);
});
it('rejects a non-positive page', () => {
expect(searchQuerySchema.safeParse({ q: 'cards', page: 0 }).success).toBe(
false,
);
});
});
describe('assetRefSchema', () => {
it('accepts a valid ref', () => {
const ref = {
kind: 'pdf',
url: 'https://example.com/doc.pdf',
ownerGuid: 'abc',
};
expect(assetRefSchema.parse(ref)).toEqual(ref);
});
it('rejects an invalid URL', () => {
expect(
assetRefSchema.safeParse({
kind: 'pdf',
url: 'not-a-url',
ownerGuid: 'abc',
}).success,
).toBe(false);
});
it('rejects an unknown kind', () => {
expect(
assetRefSchema.safeParse({
kind: 'video',
url: 'https://example.com/a.mp4',
ownerGuid: 'abc',
}).success,
).toBe(false);
});
});
describe('extractedObjectSchema', () => {
it('accepts a minimal object', () => {
const obj = {
guid: 'g1',
name: 'Card',
type: 'Card',
childrenGuids: [],
refs: [],
};
expect(extractedObjectSchema.parse(obj)).toEqual(obj);
});
it('accepts optional parentGuid', () => {
const obj = {
guid: 'g1',
name: 'Card',
type: 'Card',
parentGuid: 'g0',
childrenGuids: ['g2'],
refs: [],
};
expect(extractedObjectSchema.parse(obj).parentGuid).toBe('g0');
});
});
describe('workshopItemSchema', () => {
it('accepts a full item', () => {
const item = {
id: '1',
title: 'Deck',
author: 'someone',
previewImageUrl: 'https://example.com/p.png',
tags: ['card'],
timeCreated: 123,
};
expect(workshopItemSchema.parse(item)).toEqual(item);
});
it('accepts a minimal item', () => {
const item = {
id: '1',
title: 'Deck',
author: 'someone',
previewImageUrl: 'https://example.com/p.png',
};
expect(workshopItemSchema.parse(item)).toEqual(item);
});
});
describe('searchResultSchema', () => {
it('accepts an empty result set', () => {
const result = { items: [], page: 1, hasMore: false };
expect(searchResultSchema.parse(result)).toEqual(result);
});
});
+1
View File
@@ -14,6 +14,7 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "vitest run",
"lint": "echo \"no lint configured\""
},
"dependencies": {
+56
View File
@@ -0,0 +1,56 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { getFileName } from './index.js';
import { ItemNotFoundError, NoFileError, SteamApiError, TtsError } from './errors.js';
afterEach(() => {
vi.unstubAllGlobals();
});
describe('getFileName', () => {
it('parses a quoted content-disposition filename', () => {
expect(
getFileName('https://example.com/save', 'attachment; filename="mod.json"'),
).toBe('mod.json');
});
it('parses an unquoted filename', () => {
expect(
getFileName('https://example.com/save', 'attachment; filename=mod.json'),
).toBe('mod.json');
});
it('falls back to the URL path when there is no disposition', () => {
expect(getFileName('https://example.com/files/mod.json', null)).toBe(
'mod.json',
);
});
it('falls back to a default when the URL has no path', () => {
expect(getFileName('https://example.com', null)).toBe('save.json');
});
});
describe('error classes', () => {
it('ItemNotFoundError carries a 404 status', () => {
const err = new ItemNotFoundError('123');
expect(err).toBeInstanceOf(TtsError);
expect(err.status).toBe(404);
expect(err.message).toContain('123');
});
it('NoFileError carries a 404 status', () => {
const err = new NoFileError('123');
expect(err.status).toBe(404);
expect(err.message).toContain('123');
});
it('SteamApiError defaults to a 502 status', () => {
const err = new SteamApiError('boom');
expect(err.status).toBe(502);
});
it('TtsError defaults to a 500 status', () => {
const err = new TtsError('boom');
expect(err.status).toBe(500);
});
});
+672
View File
@@ -11,6 +11,9 @@ importers:
typescript:
specifier: ^5.7.2
version: 5.9.3
vitest:
specifier: ^4.1.10
version: 4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11))
apps/proxy:
dependencies:
@@ -237,18 +240,192 @@ packages:
peerDependencies:
hono: ^4
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@oxc-project/types@0.143.0':
resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==}
'@rolldown/binding-android-arm64@1.2.3':
resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-darwin-arm64@1.2.3':
resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.2.3':
resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-freebsd-x64@1.2.3':
resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.2.3':
resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.2.3':
resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.2.3':
resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.2.3':
resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.2.3':
resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.2.3':
resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.2.3':
resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.2.3':
resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-win32-arm64-msvc@1.2.3':
resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.2.3':
resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
'@types/node@22.20.1':
resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==}
'@vitest/expect@4.1.10':
resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==}
'@vitest/mocker@4.1.10':
resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==}
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
'@vitest/pretty-format@4.1.10':
resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==}
'@vitest/runner@4.1.10':
resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==}
'@vitest/snapshot@4.1.10':
resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==}
'@vitest/spy@4.1.10':
resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==}
'@vitest/utils@4.1.10':
resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
bson@6.10.4:
resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==}
engines: {node: '>=16.20.1'}
chai@6.2.2:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
es-module-lexer@2.3.1:
resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==}
esbuild@0.28.1:
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
engines: {node: '>=18'}
hasBin: true
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
expect-type@1.4.0:
resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
engines: {node: '>=12.0.0'}
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -258,6 +435,139 @@ packages:
resolution: {integrity: sha512-kdJoFVv2xmayw6cY09H7AbMJMt8Jn5jdlEdXsP7AGBdF2DIptVlKlOLKXP41yPip4/a3yQPv9gVcJYI8YY04dw==}
engines: {node: '>=16.9.0'}
lightningcss-android-arm64@1.33.0:
resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
lightningcss-darwin-arm64@1.33.0:
resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.33.0:
resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.33.0:
resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.33.0:
resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm64-gnu@1.33.0:
resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.33.0:
resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.33.0:
resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.33.0:
resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.33.0:
resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-x64-msvc@1.33.0:
resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss@1.33.0:
resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
engines: {node: '>= 12.0.0'}
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
nanoid@3.3.18:
resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
obug@2.1.4:
resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==}
engines: {node: '>=12.20.0'}
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.5:
resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
engines: {node: '>=12'}
postcss@8.5.26:
resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
engines: {node: ^10 || ^12 || >=14}
rolldown@1.2.3:
resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
std-env@4.2.0:
resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
tinyexec@1.3.0:
resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
engines: {node: '>=18'}
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
tinyrainbow@3.1.1:
resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
engines: {node: '>=14.0.0'}
tsx@4.23.11:
resolution: {integrity: sha512-Ry2oTEUnhBdeEdWIztY8kf3/nBGnPnjMLVGL0YfdRXMORuPER5NlKmayqxtxRxwB1xBN+RivRaJfe7PM1rtiyw==}
engines: {node: '>=18.0.0'}
@@ -271,6 +581,95 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
vite@8.2.1:
resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
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
peerDependenciesMeta:
'@types/node':
optional: true
'@vitejs/devtools':
optional: true
esbuild:
optional: true
jiti:
optional: true
less:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
vitest@4.1.10:
resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@opentelemetry/api': ^1.9.0
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
'@vitest/browser-playwright': 4.1.10
'@vitest/browser-preview': 4.1.10
'@vitest/browser-webdriverio': 4.1.10
'@vitest/coverage-istanbul': 4.1.10
'@vitest/coverage-v8': 4.1.10
'@vitest/ui': 4.1.10
happy-dom: '*'
jsdom: '*'
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
'@opentelemetry/api':
optional: true
'@types/node':
optional: true
'@vitest/browser-playwright':
optional: true
'@vitest/browser-preview':
optional: true
'@vitest/browser-webdriverio':
optional: true
'@vitest/coverage-istanbul':
optional: true
'@vitest/coverage-v8':
optional: true
'@vitest/ui':
optional: true
happy-dom:
optional: true
jsdom:
optional: true
why-is-node-running@2.3.0:
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
engines: {node: '>=8'}
hasBin: true
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
@@ -358,12 +757,122 @@ snapshots:
dependencies:
hono: 4.13.1
'@jridgewell/sourcemap-codec@1.5.5': {}
'@oxc-project/types@0.143.0': {}
'@rolldown/binding-android-arm64@1.2.3':
optional: true
'@rolldown/binding-darwin-arm64@1.2.3':
optional: true
'@rolldown/binding-darwin-x64@1.2.3':
optional: true
'@rolldown/binding-freebsd-x64@1.2.3':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.2.3':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-arm64-musl@1.2.3':
optional: true
'@rolldown/binding-linux-ppc64-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-s390x-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-x64-gnu@1.2.3':
optional: true
'@rolldown/binding-linux-x64-musl@1.2.3':
optional: true
'@rolldown/binding-openharmony-arm64@1.2.3':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.3':
optional: true
'@rolldown/binding-win32-x64-msvc@1.2.3':
optional: true
'@rolldown/pluginutils@1.0.1': {}
'@standard-schema/spec@1.1.0': {}
'@types/chai@5.2.3':
dependencies:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
'@types/deep-eql@4.0.2': {}
'@types/estree@1.0.9': {}
'@types/node@22.20.1':
dependencies:
undici-types: 6.21.0
'@vitest/expect@4.1.10':
dependencies:
'@standard-schema/spec': 1.1.0
'@types/chai': 5.2.3
'@vitest/spy': 4.1.10
'@vitest/utils': 4.1.10
chai: 6.2.2
tinyrainbow: 3.1.1
'@vitest/mocker@4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11))':
dependencies:
'@vitest/spy': 4.1.10
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11)
'@vitest/pretty-format@4.1.10':
dependencies:
tinyrainbow: 3.1.1
'@vitest/runner@4.1.10':
dependencies:
'@vitest/utils': 4.1.10
pathe: 2.0.3
'@vitest/snapshot@4.1.10':
dependencies:
'@vitest/pretty-format': 4.1.10
'@vitest/utils': 4.1.10
magic-string: 0.30.21
pathe: 2.0.3
'@vitest/spy@4.1.10': {}
'@vitest/utils@4.1.10':
dependencies:
'@vitest/pretty-format': 4.1.10
convert-source-map: 2.0.0
tinyrainbow: 3.1.1
assertion-error@2.0.1: {}
bson@6.10.4: {}
chai@6.2.2: {}
convert-source-map@2.0.0: {}
detect-libc@2.1.2: {}
es-module-lexer@2.3.1: {}
esbuild@0.28.1:
optionalDependencies:
'@esbuild/aix-ppc64': 0.28.1
@@ -393,11 +902,129 @@ snapshots:
'@esbuild/win32-ia32': 0.28.1
'@esbuild/win32-x64': 0.28.1
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.9
expect-type@1.4.0: {}
fdir@6.5.0(picomatch@4.0.5):
optionalDependencies:
picomatch: 4.0.5
fsevents@2.3.3:
optional: true
hono@4.13.1: {}
lightningcss-android-arm64@1.33.0:
optional: true
lightningcss-darwin-arm64@1.33.0:
optional: true
lightningcss-darwin-x64@1.33.0:
optional: true
lightningcss-freebsd-x64@1.33.0:
optional: true
lightningcss-linux-arm-gnueabihf@1.33.0:
optional: true
lightningcss-linux-arm64-gnu@1.33.0:
optional: true
lightningcss-linux-arm64-musl@1.33.0:
optional: true
lightningcss-linux-x64-gnu@1.33.0:
optional: true
lightningcss-linux-x64-musl@1.33.0:
optional: true
lightningcss-win32-arm64-msvc@1.33.0:
optional: true
lightningcss-win32-x64-msvc@1.33.0:
optional: true
lightningcss@1.33.0:
dependencies:
detect-libc: 2.1.2
optionalDependencies:
lightningcss-android-arm64: 1.33.0
lightningcss-darwin-arm64: 1.33.0
lightningcss-darwin-x64: 1.33.0
lightningcss-freebsd-x64: 1.33.0
lightningcss-linux-arm-gnueabihf: 1.33.0
lightningcss-linux-arm64-gnu: 1.33.0
lightningcss-linux-arm64-musl: 1.33.0
lightningcss-linux-x64-gnu: 1.33.0
lightningcss-linux-x64-musl: 1.33.0
lightningcss-win32-arm64-msvc: 1.33.0
lightningcss-win32-x64-msvc: 1.33.0
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
nanoid@3.3.18: {}
obug@2.1.4: {}
pathe@2.0.3: {}
picocolors@1.1.1: {}
picomatch@4.0.5: {}
postcss@8.5.26:
dependencies:
nanoid: 3.3.18
picocolors: 1.1.1
source-map-js: 1.2.1
rolldown@1.2.3:
dependencies:
'@oxc-project/types': 0.143.0
'@rolldown/pluginutils': 1.0.1
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
siginfo@2.0.0: {}
source-map-js@1.2.1: {}
stackback@0.0.2: {}
std-env@4.2.0: {}
tinybench@2.9.0: {}
tinyexec@1.3.0: {}
tinyglobby@0.2.17:
dependencies:
fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.5
tinyrainbow@3.1.1: {}
tsx@4.23.11:
dependencies:
esbuild: 0.28.1
@@ -408,4 +1035,49 @@ snapshots:
undici-types@6.21.0: {}
vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11):
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.5
postcss: 8.5.26
rolldown: 1.2.3
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 22.20.1
esbuild: 0.28.1
fsevents: 2.3.3
tsx: 4.23.11
vitest@4.1.10(@types/node@22.20.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11)):
dependencies:
'@vitest/expect': 4.1.10
'@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11))
'@vitest/pretty-format': 4.1.10
'@vitest/runner': 4.1.10
'@vitest/snapshot': 4.1.10
'@vitest/spy': 4.1.10
'@vitest/utils': 4.1.10
es-module-lexer: 2.3.1
expect-type: 1.4.0
magic-string: 0.30.21
obug: 2.1.4
pathe: 2.0.3
picomatch: 4.0.5
std-env: 4.2.0
tinybench: 2.9.0
tinyexec: 1.3.0
tinyglobby: 0.2.17
tinyrainbow: 3.1.1
vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(tsx@4.23.11)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.20.1
transitivePeerDependencies:
- msw
why-is-node-running@2.3.0:
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
zod@3.25.76: {}
+8
View File
@@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
include: ['packages/*/src/**/*.test.ts', 'apps/*/src/**/*.test.ts'],
},
});