chore: barebones testing setup
This commit is contained in:
parent
2789b5e46a
commit
bae86750e0
15 changed files with 2099 additions and 46 deletions
|
|
@ -39,7 +39,6 @@ export const EXTERNAL_DEPENDENCIES = [
|
|||
"@libsql/win32-x64-msvc",
|
||||
"libsql",
|
||||
"onnxruntime-node",
|
||||
"workerpool",
|
||||
"@amical/whisper-wrapper",
|
||||
// Add any other native modules you need here
|
||||
];
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@
|
|||
"lint": "eslint --ext .ts,.tsx .",
|
||||
"format:check": "prettier --check \"**/*.{ts,tsx,md,json,mjs,mts,css,mdx}\" --cache --ignore-path=../../.prettierignore",
|
||||
"ts:check": "tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
|
|
@ -63,8 +67,10 @@
|
|||
"@tailwindcss/vite": "^4.1.6",
|
||||
"@tanstack/react-router-devtools": "^1.131.41",
|
||||
"@tanstack/router-plugin": "^1.131.41",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@vitest/ui": "^4.0.8",
|
||||
"bumpp": "^10.2.3",
|
||||
"electron": "38.1.0",
|
||||
"eslint": "^9.26.0",
|
||||
|
|
@ -72,17 +78,19 @@
|
|||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.4.0",
|
||||
"flora-colossus": "^2.0.0",
|
||||
"fs-extra": "^11.3.2",
|
||||
"prettier": "^3.5.3",
|
||||
"tailwindcss": "^4.1.6",
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.1.5"
|
||||
"vite": "^7.1.5",
|
||||
"vitest": "^4.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^1.3.22",
|
||||
"@amical/eslint-config": "workspace:*",
|
||||
"@amical/whisper-wrapper": "workspace:*",
|
||||
"@amical/types": "workspace:*",
|
||||
"@amical/whisper-wrapper": "workspace:*",
|
||||
"@amical/y-libsql": "workspace:*",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
|
|
@ -170,7 +178,6 @@
|
|||
"update-electron-app": "^3.1.1",
|
||||
"uuid": "^11.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"workerpool": "^9.3.3",
|
||||
"yjs": "^13.6.27",
|
||||
"zod": "^3.25.24"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { eq, desc, asc, and, ilike, count, gte, lte } from "drizzle-orm";
|
||||
import { eq, desc, asc, and, ilike, count, gte, lte, sql, like } from "drizzle-orm";
|
||||
import { db } from ".";
|
||||
import {
|
||||
transcriptions,
|
||||
|
|
@ -55,7 +55,7 @@ export async function getTranscriptions(
|
|||
return await db
|
||||
.select()
|
||||
.from(transcriptions)
|
||||
.where(ilike(transcriptions.text, `%${search}%`))
|
||||
.where(sql`${transcriptions.text} LIKE ${`%${search}%`} COLLATE NOCASE`)
|
||||
.orderBy(orderFn(sortColumn))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
|
@ -113,7 +113,7 @@ export async function getTranscriptionsCount(search?: string) {
|
|||
const result = await db
|
||||
.select({ count: count() })
|
||||
.from(transcriptions)
|
||||
.where(ilike(transcriptions.text, `%${search}%`));
|
||||
.where(sql`${transcriptions.text} LIKE ${`%${search}%`} COLLATE NOCASE`);
|
||||
return result[0]?.count || 0;
|
||||
} else {
|
||||
const result = await db.select({ count: count() }).from(transcriptions);
|
||||
|
|
@ -152,7 +152,7 @@ export async function searchTranscriptions(searchTerm: string, limit = 20) {
|
|||
return await db
|
||||
.select()
|
||||
.from(transcriptions)
|
||||
.where(ilike(transcriptions.text, `%${searchTerm}%`))
|
||||
.where(sql`${transcriptions.text} LIKE ${`%${searchTerm}%`} COLLATE NOCASE`)
|
||||
.orderBy(desc(transcriptions.timestamp))
|
||||
.limit(limit);
|
||||
}
|
||||
|
|
|
|||
121
apps/desktop/tests/README.md
Normal file
121
apps/desktop/tests/README.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# Testing Guide
|
||||
|
||||
This directory contains the test setup for the Amical Desktop application's main process.
|
||||
|
||||
## Overview
|
||||
|
||||
We use **Vitest** to test the Electron main process, specifically:
|
||||
- **tRPC router procedures** - Direct testing by calling router methods
|
||||
- **Service business logic** - Testing services with different database states
|
||||
- **App initialization** - Testing how the app initializes with various database conditions
|
||||
|
||||
## Architecture
|
||||
|
||||
### Test Database
|
||||
- Uses real SQLite databases (not mocked)
|
||||
- Each test gets an isolated database in a temporary directory
|
||||
- Migrations are applied automatically
|
||||
- Fixtures for seeding test data
|
||||
|
||||
### Mocking Strategy
|
||||
- **Electron APIs** - Fully mocked (app, ipcMain, BrowserWindow, Menu, etc.)
|
||||
- **Native Modules** - Mocked (onnxruntime, whisper, keytar, etc.)
|
||||
- **Database** - Real SQLite with test fixtures
|
||||
- **tRPC** - Called directly, bypassing IPC layer
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pnpm test
|
||||
|
||||
# Watch mode
|
||||
pnpm test:watch
|
||||
|
||||
# UI mode
|
||||
pnpm test:ui
|
||||
|
||||
# With coverage
|
||||
pnpm test:coverage
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
### Testing tRPC Procedures
|
||||
|
||||
```typescript
|
||||
import { createTestDatabase } from '../helpers/test-db';
|
||||
import { initializeTestServices } from '../helpers/test-app';
|
||||
import { seedDatabase } from '../helpers/fixtures';
|
||||
|
||||
describe('My Service', () => {
|
||||
let testDb;
|
||||
let trpcCaller;
|
||||
let cleanup;
|
||||
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'my-test' });
|
||||
await seedDatabase(testDb, 'withTranscriptions'); // or 'empty', 'full', etc.
|
||||
|
||||
const result = await initializeTestServices(testDb);
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (cleanup) await cleanup();
|
||||
if (testDb) await testDb.close();
|
||||
});
|
||||
|
||||
it('should do something', async () => {
|
||||
const result = await trpcCaller.myRouter.myProcedure({ input });
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Available Fixtures
|
||||
|
||||
- `empty` - Empty database with default settings
|
||||
- `withTranscriptions` - Database with sample transcriptions
|
||||
- `withVocabulary` - Database with vocabulary items
|
||||
- `withModels` - Database with downloaded models
|
||||
- `withNotes` - Database with notes
|
||||
- `withAuth` - Database with authenticated user
|
||||
- `full` - Database with all types of data
|
||||
|
||||
### Custom Fixtures
|
||||
|
||||
```typescript
|
||||
await fixtures.withCustomSettings(testDb, {
|
||||
ui: { theme: 'dark' },
|
||||
transcription: { language: 'es' }
|
||||
});
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Full AppManager initialization** - Currently has issues with ServiceManager initialization. Use `initializeTestServices` instead for testing service business logic.
|
||||
|
||||
2. **Some modules require additional mocking** - If you encounter errors about missing modules, add mocks to `tests/setup.ts`.
|
||||
|
||||
3. **Database mocking** - The dynamic database mocking via `vi.doMock` doesn't work well with the existing module resolution. Tests work best when testing services directly rather than full app initialization.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "ServiceManager not initialized"
|
||||
This means you're trying to use AppManager which requires more complex initialization. Use `initializeTestServices` to test services directly.
|
||||
|
||||
### "No procedure found on path"
|
||||
Check that the tRPC procedure name matches the actual router definition. Refer to `src/trpc/routers/` for available procedures.
|
||||
|
||||
### "ENOENT: no such file or directory"
|
||||
The test database or migrations folder might not be found. Ensure migrations exist at `src/db/migrations/`.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
- Fix AppManager initialization for full integration tests
|
||||
- Add more comprehensive fixtures
|
||||
- Add test coverage reporting
|
||||
- Add database state assertions helpers
|
||||
- Create mock factories for complex objects
|
||||
384
apps/desktop/tests/helpers/electron-mocks.ts
Normal file
384
apps/desktop/tests/helpers/electron-mocks.ts
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
import { vi } from 'vitest';
|
||||
import { EventEmitter } from 'events';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
|
||||
// Create a fake BrowserWindow class
|
||||
class FakeBrowserWindow extends EventEmitter {
|
||||
id: number;
|
||||
webContents: any;
|
||||
private _isDestroyed = false;
|
||||
private _bounds = { x: 0, y: 0, width: 800, height: 600 };
|
||||
private _isVisible = false;
|
||||
private _isMinimized = false;
|
||||
private _isMaximized = false;
|
||||
private _isFocused = false;
|
||||
private _isFullScreen = false;
|
||||
|
||||
constructor(options?: any) {
|
||||
super();
|
||||
this.id = Math.floor(Math.random() * 1000000);
|
||||
|
||||
// Mock webContents
|
||||
this.webContents = {
|
||||
send: vi.fn(),
|
||||
on: vi.fn(),
|
||||
once: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
session: {
|
||||
clearCache: vi.fn().mockResolvedValue(undefined),
|
||||
clearStorageData: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
openDevTools: vi.fn(),
|
||||
closeDevTools: vi.fn(),
|
||||
isDevToolsOpened: vi.fn().mockReturnValue(false),
|
||||
executeJavaScript: vi.fn().mockResolvedValue(undefined),
|
||||
setWindowOpenHandler: vi.fn(),
|
||||
setBackgroundThrottling: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
loadURL(url: string) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
loadFile(filePath: string) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
show() {
|
||||
this._isVisible = true;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this._isVisible = false;
|
||||
}
|
||||
|
||||
close() {
|
||||
this._isDestroyed = true;
|
||||
this.emit('closed');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._isDestroyed = true;
|
||||
}
|
||||
|
||||
isDestroyed() {
|
||||
return this._isDestroyed;
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
isMinimized() {
|
||||
return this._isMinimized;
|
||||
}
|
||||
|
||||
isMaximized() {
|
||||
return this._isMaximized;
|
||||
}
|
||||
|
||||
isFocused() {
|
||||
return this._isFocused;
|
||||
}
|
||||
|
||||
isFullScreen() {
|
||||
return this._isFullScreen;
|
||||
}
|
||||
|
||||
focus() {
|
||||
this._isFocused = true;
|
||||
}
|
||||
|
||||
blur() {
|
||||
this._isFocused = false;
|
||||
}
|
||||
|
||||
minimize() {
|
||||
this._isMinimized = true;
|
||||
}
|
||||
|
||||
maximize() {
|
||||
this._isMaximized = true;
|
||||
}
|
||||
|
||||
restore() {
|
||||
this._isMinimized = false;
|
||||
this._isMaximized = false;
|
||||
}
|
||||
|
||||
setFullScreen(flag: boolean) {
|
||||
this._isFullScreen = flag;
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
return { ...this._bounds };
|
||||
}
|
||||
|
||||
setBounds(bounds: Partial<typeof this._bounds>) {
|
||||
this._bounds = { ...this._bounds, ...bounds };
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
this._bounds.width = width;
|
||||
this._bounds.height = height;
|
||||
}
|
||||
|
||||
setPosition(x: number, y: number) {
|
||||
this._bounds.x = x;
|
||||
this._bounds.y = y;
|
||||
}
|
||||
|
||||
center() {
|
||||
// Mock centering
|
||||
}
|
||||
|
||||
setResizable(resizable: boolean) {}
|
||||
setMovable(movable: boolean) {}
|
||||
setMinimizable(minimizable: boolean) {}
|
||||
setMaximizable(maximizable: boolean) {}
|
||||
setFullScreenable(fullscreenable: boolean) {}
|
||||
setClosable(closable: boolean) {}
|
||||
setAlwaysOnTop(flag: boolean, level?: string) {}
|
||||
setVisibleOnAllWorkspaces(visible: boolean) {}
|
||||
setIgnoreMouseEvents(ignore: boolean) {}
|
||||
setContentProtection(enable: boolean) {}
|
||||
setFocusable(focusable: boolean) {}
|
||||
setParentWindow(parent: any) {}
|
||||
setTitle(title: string) {}
|
||||
setTitleBarOverlay(options: any) {}
|
||||
setOpacity(opacity: number) {}
|
||||
setShape(rects: any[]) {}
|
||||
setSkipTaskbar(skip: boolean) {}
|
||||
setMenu(menu: any) {}
|
||||
setAutoHideMenuBar(hide: boolean) {}
|
||||
setMenuBarVisibility(visible: boolean) {}
|
||||
setAspectRatio(aspectRatio: number) {}
|
||||
setBackgroundColor(color: string) {}
|
||||
setHasShadow(hasShadow: boolean) {}
|
||||
setRepresentedFilename(filename: string) {}
|
||||
setDocumentEdited(edited: boolean) {}
|
||||
setIcon(icon: any) {}
|
||||
setProgressBar(progress: number) {}
|
||||
setOverlayIcon(overlay: any, description: string) {}
|
||||
setThumbarButtons(buttons: any[]) {}
|
||||
setThumbnailClip(region: any) {}
|
||||
setThumbnailToolTip(toolTip: string) {}
|
||||
setAppDetails(options: any) {}
|
||||
setVibrancy(type: string) {}
|
||||
setWindowButtonVisibility(visible: boolean) {}
|
||||
setTrafficLightPosition(position: { x: number; y: number }) {}
|
||||
|
||||
// Mock methods that return values
|
||||
getTitle() { return 'Test Window'; }
|
||||
getNativeWindowHandle() { return Buffer.from('test'); }
|
||||
getMediaSourceId() { return 'test-id'; }
|
||||
}
|
||||
|
||||
// Create test directories
|
||||
const testUserDataPath = path.join(os.tmpdir(), 'amical-test-' + Date.now());
|
||||
const testAppPath = process.cwd();
|
||||
|
||||
// Mock app object
|
||||
const mockApp = {
|
||||
getPath: vi.fn((name: string) => {
|
||||
const paths: Record<string, string> = {
|
||||
userData: testUserDataPath,
|
||||
appData: testUserDataPath,
|
||||
temp: os.tmpdir(),
|
||||
home: os.homedir(),
|
||||
documents: path.join(os.homedir(), 'Documents'),
|
||||
downloads: path.join(os.homedir(), 'Downloads'),
|
||||
desktop: path.join(os.homedir(), 'Desktop'),
|
||||
music: path.join(os.homedir(), 'Music'),
|
||||
pictures: path.join(os.homedir(), 'Pictures'),
|
||||
videos: path.join(os.homedir(), 'Videos'),
|
||||
logs: path.join(testUserDataPath, 'logs'),
|
||||
crashDumps: path.join(testUserDataPath, 'crashDumps'),
|
||||
};
|
||||
return paths[name] || testUserDataPath;
|
||||
}),
|
||||
getName: vi.fn(() => 'Amical'),
|
||||
getVersion: vi.fn(() => '0.1.0-test'),
|
||||
isPackaged: false,
|
||||
isReady: vi.fn(() => true),
|
||||
whenReady: vi.fn(() => Promise.resolve()),
|
||||
quit: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
relaunch: vi.fn(),
|
||||
focus: vi.fn(),
|
||||
hide: vi.fn(),
|
||||
show: vi.fn(),
|
||||
setName: vi.fn(),
|
||||
setPath: vi.fn(),
|
||||
getLocale: vi.fn(() => 'en-US'),
|
||||
getLocaleCountryCode: vi.fn(() => 'US'),
|
||||
getSystemLocale: vi.fn(() => 'en-US'),
|
||||
on: vi.fn(),
|
||||
once: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
removeAllListeners: vi.fn(),
|
||||
emit: vi.fn(),
|
||||
setLoginItemSettings: vi.fn(),
|
||||
getLoginItemSettings: vi.fn(() => ({ openAtLogin: false })),
|
||||
};
|
||||
|
||||
// Mock ipcMain
|
||||
const mockIpcMain = {
|
||||
handle: vi.fn(),
|
||||
on: vi.fn(),
|
||||
once: vi.fn(),
|
||||
removeHandler: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
removeAllListeners: vi.fn(),
|
||||
emit: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock screen
|
||||
const mockScreen = {
|
||||
getPrimaryDisplay: vi.fn(() => ({
|
||||
id: 1,
|
||||
bounds: { x: 0, y: 0, width: 1920, height: 1080 },
|
||||
workArea: { x: 0, y: 0, width: 1920, height: 1040 },
|
||||
size: { width: 1920, height: 1080 },
|
||||
workAreaSize: { width: 1920, height: 1040 },
|
||||
scaleFactor: 1,
|
||||
rotation: 0,
|
||||
internal: false,
|
||||
})),
|
||||
getAllDisplays: vi.fn(() => [
|
||||
{
|
||||
id: 1,
|
||||
bounds: { x: 0, y: 0, width: 1920, height: 1080 },
|
||||
workArea: { x: 0, y: 0, width: 1920, height: 1040 },
|
||||
size: { width: 1920, height: 1080 },
|
||||
workAreaSize: { width: 1920, height: 1040 },
|
||||
scaleFactor: 1,
|
||||
rotation: 0,
|
||||
internal: false,
|
||||
},
|
||||
]),
|
||||
getCursorScreenPoint: vi.fn(() => ({ x: 100, y: 100 })),
|
||||
getDisplayNearestPoint: vi.fn(() => ({
|
||||
id: 1,
|
||||
bounds: { x: 0, y: 0, width: 1920, height: 1080 },
|
||||
workArea: { x: 0, y: 0, width: 1920, height: 1040 },
|
||||
size: { width: 1920, height: 1080 },
|
||||
workAreaSize: { width: 1920, height: 1040 },
|
||||
scaleFactor: 1,
|
||||
rotation: 0,
|
||||
internal: false,
|
||||
})),
|
||||
on: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock systemPreferences
|
||||
const mockSystemPreferences = {
|
||||
getMediaAccessStatus: vi.fn(() => 'granted'),
|
||||
askForMediaAccess: vi.fn(() => Promise.resolve(true)),
|
||||
isTrustedAccessibilityClient: vi.fn(() => true),
|
||||
getColor: vi.fn(() => '#000000'),
|
||||
on: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock nativeTheme
|
||||
const mockNativeTheme = {
|
||||
shouldUseDarkColors: false,
|
||||
themeSource: 'system' as const,
|
||||
on: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock Menu
|
||||
const mockMenu = {
|
||||
buildFromTemplate: vi.fn(() => ({})),
|
||||
setApplicationMenu: vi.fn(),
|
||||
getApplicationMenu: vi.fn(() => null),
|
||||
};
|
||||
|
||||
// Mock Tray
|
||||
class FakeTray extends EventEmitter {
|
||||
constructor(image: any) {
|
||||
super();
|
||||
}
|
||||
setToolTip(toolTip: string) {}
|
||||
setTitle(title: string) {}
|
||||
setImage(image: any) {}
|
||||
setContextMenu(menu: any) {}
|
||||
destroy() {}
|
||||
isDestroyed() { return false; }
|
||||
}
|
||||
|
||||
// Mock dialog
|
||||
const mockDialog = {
|
||||
showOpenDialog: vi.fn(() => Promise.resolve({ canceled: false, filePaths: [] })),
|
||||
showSaveDialog: vi.fn(() => Promise.resolve({ canceled: false, filePath: '' })),
|
||||
showMessageBox: vi.fn(() => Promise.resolve({ response: 0 })),
|
||||
showErrorBox: vi.fn(),
|
||||
showCertificateTrustDialog: vi.fn(() => Promise.resolve()),
|
||||
};
|
||||
|
||||
// Mock shell
|
||||
const mockShell = {
|
||||
openExternal: vi.fn(() => Promise.resolve()),
|
||||
openPath: vi.fn(() => Promise.resolve('')),
|
||||
showItemInFolder: vi.fn(),
|
||||
openItem: vi.fn(() => Promise.resolve(true)),
|
||||
moveItemToTrash: vi.fn(() => Promise.resolve(true)),
|
||||
beep: vi.fn(),
|
||||
writeShortcutLink: vi.fn(() => true),
|
||||
readShortcutLink: vi.fn(() => ({})),
|
||||
};
|
||||
|
||||
// Mock globalShortcut
|
||||
const mockGlobalShortcut = {
|
||||
register: vi.fn(() => true),
|
||||
registerAll: vi.fn(),
|
||||
isRegistered: vi.fn(() => false),
|
||||
unregister: vi.fn(),
|
||||
unregisterAll: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock clipboard
|
||||
const mockClipboard = {
|
||||
readText: vi.fn(() => ''),
|
||||
writeText: vi.fn(),
|
||||
readHTML: vi.fn(() => ''),
|
||||
writeHTML: vi.fn(),
|
||||
readImage: vi.fn(() => ({})),
|
||||
writeImage: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
availableFormats: vi.fn(() => []),
|
||||
};
|
||||
|
||||
// Mock nativeImage
|
||||
const mockNativeImage = {
|
||||
createEmpty: vi.fn(() => ({})),
|
||||
createFromPath: vi.fn(() => ({})),
|
||||
createFromBuffer: vi.fn(() => ({})),
|
||||
createFromDataURL: vi.fn(() => ({})),
|
||||
};
|
||||
|
||||
export function createElectronMocks() {
|
||||
return {
|
||||
app: mockApp,
|
||||
ipcMain: mockIpcMain,
|
||||
BrowserWindow: FakeBrowserWindow as any,
|
||||
screen: mockScreen,
|
||||
systemPreferences: mockSystemPreferences,
|
||||
nativeTheme: mockNativeTheme,
|
||||
Menu: mockMenu,
|
||||
Tray: FakeTray as any,
|
||||
dialog: mockDialog,
|
||||
shell: mockShell,
|
||||
globalShortcut: mockGlobalShortcut,
|
||||
clipboard: mockClipboard,
|
||||
nativeImage: mockNativeImage,
|
||||
};
|
||||
}
|
||||
|
||||
// Export test user data path for cleanup
|
||||
export const TEST_USER_DATA_PATH = testUserDataPath;
|
||||
276
apps/desktop/tests/helpers/fixtures.ts
Normal file
276
apps/desktop/tests/helpers/fixtures.ts
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
import type { TestDatabase } from './test-db';
|
||||
import * as schema from '@db/schema';
|
||||
import type {
|
||||
NewTranscription,
|
||||
NewVocabulary,
|
||||
NewModel,
|
||||
NewAppSettings,
|
||||
NewNote,
|
||||
AppSettingsData,
|
||||
} from '@db/schema';
|
||||
|
||||
/**
|
||||
* Default app settings for testing
|
||||
*/
|
||||
export const defaultAppSettings: AppSettingsData = {
|
||||
formatterConfig: {
|
||||
model: 'gpt-4o-mini',
|
||||
enabled: false,
|
||||
},
|
||||
ui: {
|
||||
theme: 'system',
|
||||
},
|
||||
transcription: {
|
||||
language: 'en',
|
||||
autoTranscribe: true,
|
||||
confidenceThreshold: 0.7,
|
||||
enablePunctuation: true,
|
||||
enableTimestamps: false,
|
||||
preloadWhisperModel: false,
|
||||
},
|
||||
recording: {
|
||||
defaultFormat: 'wav',
|
||||
sampleRate: 16000,
|
||||
autoStopSilence: true,
|
||||
silenceThreshold: -45,
|
||||
maxRecordingDuration: 600,
|
||||
},
|
||||
shortcuts: {
|
||||
pushToTalk: 'CommandOrControl+Shift+Space',
|
||||
toggleRecording: 'CommandOrControl+Shift+R',
|
||||
toggleWindow: 'CommandOrControl+Shift+W',
|
||||
},
|
||||
modelProvidersConfig: {
|
||||
defaultSpeechModel: 'local-whisper:ggml-base.en',
|
||||
},
|
||||
dictation: {
|
||||
autoDetectEnabled: true,
|
||||
selectedLanguage: 'en',
|
||||
},
|
||||
preferences: {
|
||||
launchAtLogin: false,
|
||||
minimizeToTray: true,
|
||||
showWidgetWhileInactive: true,
|
||||
},
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
auth: {
|
||||
isAuthenticated: false,
|
||||
idToken: null,
|
||||
refreshToken: null,
|
||||
expiresAt: null,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Sample transcriptions for testing
|
||||
*/
|
||||
export const sampleTranscriptions: NewTranscription[] = [
|
||||
{
|
||||
text: 'This is a test transcription',
|
||||
language: 'en',
|
||||
confidence: 0.95,
|
||||
duration: 5,
|
||||
speechModel: 'whisper-base',
|
||||
formattingModel: null,
|
||||
},
|
||||
{
|
||||
text: 'Another test transcription with more content',
|
||||
language: 'en',
|
||||
confidence: 0.88,
|
||||
duration: 8,
|
||||
speechModel: 'whisper-base',
|
||||
formattingModel: 'gpt-4o-mini',
|
||||
},
|
||||
{
|
||||
text: 'A third transcription for comprehensive testing',
|
||||
language: 'en',
|
||||
confidence: 0.92,
|
||||
duration: 6,
|
||||
speechModel: 'whisper-large',
|
||||
formattingModel: null,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Sample vocabulary items for testing
|
||||
*/
|
||||
export const sampleVocabulary: NewVocabulary[] = [
|
||||
{
|
||||
word: 'Amical',
|
||||
replacementWord: null,
|
||||
isReplacement: false,
|
||||
usageCount: 5,
|
||||
},
|
||||
{
|
||||
word: 'API',
|
||||
replacementWord: null,
|
||||
isReplacement: false,
|
||||
usageCount: 3,
|
||||
},
|
||||
{
|
||||
word: 'teh',
|
||||
replacementWord: 'the',
|
||||
isReplacement: true,
|
||||
usageCount: 2,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Sample models for testing
|
||||
*/
|
||||
export const sampleModels: NewModel[] = [
|
||||
{
|
||||
id: 'ggml-base.en',
|
||||
provider: 'local-whisper',
|
||||
name: 'Whisper Base English',
|
||||
type: 'speech',
|
||||
size: '~147 MB',
|
||||
description: 'Optimized for English transcription',
|
||||
localPath: '/test/models/ggml-base.en.bin',
|
||||
sizeBytes: 147964211,
|
||||
checksum: 'test-checksum-base',
|
||||
downloadedAt: new Date(),
|
||||
speed: 4,
|
||||
accuracy: 3,
|
||||
},
|
||||
{
|
||||
id: 'gpt-4o-mini',
|
||||
provider: 'openrouter',
|
||||
name: 'GPT-4o Mini',
|
||||
type: 'language',
|
||||
context: '128k',
|
||||
description: 'Fast and efficient language model',
|
||||
speed: 5,
|
||||
accuracy: 4,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Sample notes for testing
|
||||
*/
|
||||
export const sampleNotes: NewNote[] = [
|
||||
{
|
||||
title: 'Test Note 1',
|
||||
content: 'This is the first test note',
|
||||
icon: '📝',
|
||||
},
|
||||
{
|
||||
title: 'Test Note 2',
|
||||
content: 'This is the second test note with more content',
|
||||
icon: '📄',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Fixture presets
|
||||
*/
|
||||
export const fixtures = {
|
||||
/**
|
||||
* Empty database with only default settings
|
||||
*/
|
||||
empty: async (testDb: TestDatabase) => {
|
||||
// Clear existing settings first
|
||||
await testDb.db.delete(schema.appSettings);
|
||||
// Insert default settings
|
||||
await testDb.db.insert(schema.appSettings).values({
|
||||
id: 1,
|
||||
data: defaultAppSettings,
|
||||
version: 1,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Database with existing transcriptions
|
||||
*/
|
||||
withTranscriptions: async (testDb: TestDatabase) => {
|
||||
await fixtures.empty(testDb);
|
||||
await testDb.db.insert(schema.transcriptions).values(sampleTranscriptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Database with vocabulary items
|
||||
*/
|
||||
withVocabulary: async (testDb: TestDatabase) => {
|
||||
await fixtures.empty(testDb);
|
||||
await testDb.db.insert(schema.vocabulary).values(sampleVocabulary);
|
||||
},
|
||||
|
||||
/**
|
||||
* Database with downloaded models
|
||||
*/
|
||||
withModels: async (testDb: TestDatabase) => {
|
||||
await fixtures.empty(testDb);
|
||||
await testDb.db.insert(schema.models).values(sampleModels);
|
||||
},
|
||||
|
||||
/**
|
||||
* Database with notes
|
||||
*/
|
||||
withNotes: async (testDb: TestDatabase) => {
|
||||
await fixtures.empty(testDb);
|
||||
await testDb.db.insert(schema.notes).values(sampleNotes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Full database with all types of data
|
||||
*/
|
||||
full: async (testDb: TestDatabase) => {
|
||||
await fixtures.empty(testDb);
|
||||
await testDb.db.insert(schema.transcriptions).values(sampleTranscriptions);
|
||||
await testDb.db.insert(schema.vocabulary).values(sampleVocabulary);
|
||||
await testDb.db.insert(schema.models).values(sampleModels);
|
||||
await testDb.db.insert(schema.notes).values(sampleNotes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Database with custom settings
|
||||
*/
|
||||
withCustomSettings: async (
|
||||
testDb: TestDatabase,
|
||||
settings: Partial<AppSettingsData>
|
||||
) => {
|
||||
// Clear existing settings first
|
||||
await testDb.db.delete(schema.appSettings);
|
||||
// Insert custom settings
|
||||
await testDb.db.insert(schema.appSettings).values({
|
||||
id: 1,
|
||||
data: { ...defaultAppSettings, ...settings },
|
||||
version: 1,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Database with authenticated user
|
||||
*/
|
||||
withAuth: async (testDb: TestDatabase) => {
|
||||
await fixtures.withCustomSettings(testDb, {
|
||||
auth: {
|
||||
isAuthenticated: true,
|
||||
idToken: 'test-id-token',
|
||||
refreshToken: 'test-refresh-token',
|
||||
expiresAt: Date.now() + 3600000, // 1 hour from now
|
||||
userInfo: {
|
||||
sub: 'test-user-123',
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to seed specific data
|
||||
*/
|
||||
export async function seedDatabase(
|
||||
testDb: TestDatabase,
|
||||
fixture: keyof typeof fixtures | ((testDb: TestDatabase) => Promise<void>)
|
||||
): Promise<void> {
|
||||
if (typeof fixture === 'function') {
|
||||
await fixture(testDb);
|
||||
} else {
|
||||
await fixtures[fixture](testDb);
|
||||
}
|
||||
}
|
||||
172
apps/desktop/tests/helpers/native-mocks.ts
Normal file
172
apps/desktop/tests/helpers/native-mocks.ts
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
import { vi } from 'vitest';
|
||||
|
||||
// Mock onnxruntime-node
|
||||
export const mockOnnxRuntime = {
|
||||
InferenceSession: {
|
||||
create: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
run: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
output: {
|
||||
data: new Float32Array([0.5, 0.5, 0.5]),
|
||||
dims: [1, 3],
|
||||
},
|
||||
})
|
||||
),
|
||||
release: vi.fn(),
|
||||
})
|
||||
),
|
||||
},
|
||||
Tensor: vi.fn(),
|
||||
env: {
|
||||
wasm: {
|
||||
numThreads: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Mock @amical/whisper-wrapper
|
||||
export const mockWhisperWrapper = {
|
||||
WhisperModel: vi.fn().mockImplementation(() => ({
|
||||
transcribe: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
text: 'Test transcription',
|
||||
segments: [
|
||||
{
|
||||
start: 0,
|
||||
end: 1.5,
|
||||
text: 'Test transcription',
|
||||
},
|
||||
],
|
||||
})
|
||||
),
|
||||
dispose: vi.fn(),
|
||||
})),
|
||||
downloadModel: vi.fn(() => Promise.resolve()),
|
||||
getModelPath: vi.fn(() => '/mock/model/path'),
|
||||
};
|
||||
|
||||
// Mock keytar (credential storage)
|
||||
export const mockKeytar = {
|
||||
getPassword: vi.fn((service: string, account: string) =>
|
||||
Promise.resolve(null)
|
||||
),
|
||||
setPassword: vi.fn((service: string, account: string, password: string) =>
|
||||
Promise.resolve()
|
||||
),
|
||||
deletePassword: vi.fn((service: string, account: string) =>
|
||||
Promise.resolve(true)
|
||||
),
|
||||
findPassword: vi.fn((service: string) => Promise.resolve(null)),
|
||||
findCredentials: vi.fn((service: string) => Promise.resolve([])),
|
||||
};
|
||||
|
||||
// Mock libsql native module
|
||||
export const mockLibsql = {
|
||||
createClient: vi.fn(() => ({
|
||||
execute: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
rows: [],
|
||||
columns: [],
|
||||
rowsAffected: 0,
|
||||
})
|
||||
),
|
||||
batch: vi.fn(() => Promise.resolve([])),
|
||||
close: vi.fn(() => Promise.resolve()),
|
||||
sync: vi.fn(() => Promise.resolve()),
|
||||
})),
|
||||
};
|
||||
|
||||
// Mock native helper modules
|
||||
export const mockSwiftHelper = {
|
||||
checkAccessibilityPermission: vi.fn(() => true),
|
||||
checkMicrophonePermission: vi.fn(() => true),
|
||||
requestMicrophonePermission: vi.fn(() => Promise.resolve(true)),
|
||||
getSystemAudioLevel: vi.fn(() => 0.5),
|
||||
setSystemAudioMuted: vi.fn(() => true),
|
||||
isSystemAudioMuted: vi.fn(() => false),
|
||||
writeToClipboard: vi.fn(() => true),
|
||||
readFromClipboard: vi.fn(() => ''),
|
||||
isRunning: vi.fn(() => true),
|
||||
};
|
||||
|
||||
export const mockWindowsHelper = {
|
||||
checkMicrophonePermission: vi.fn(() => true),
|
||||
requestMicrophonePermission: vi.fn(() => Promise.resolve(true)),
|
||||
registerGlobalShortcut: vi.fn(() => true),
|
||||
unregisterGlobalShortcut: vi.fn(() => true),
|
||||
isKeyPressed: vi.fn(() => false),
|
||||
getSystemAudioLevel: vi.fn(() => 0.5),
|
||||
isRunning: vi.fn(() => true),
|
||||
};
|
||||
|
||||
// Mock node-machine-id
|
||||
export const mockMachineId = {
|
||||
machineIdSync: vi.fn(() => 'test-machine-id-12345'),
|
||||
machineId: vi.fn(() => Promise.resolve('test-machine-id-12345')),
|
||||
};
|
||||
|
||||
// Mock systeminformation
|
||||
export const mockSystemInformation = {
|
||||
system: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
manufacturer: 'Test Manufacturer',
|
||||
model: 'Test Model',
|
||||
version: '1.0',
|
||||
serial: 'TEST123',
|
||||
uuid: 'test-uuid',
|
||||
sku: 'TEST-SKU',
|
||||
})
|
||||
),
|
||||
cpu: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
manufacturer: 'Test CPU',
|
||||
brand: 'Test Brand',
|
||||
speed: 2.5,
|
||||
cores: 4,
|
||||
})
|
||||
),
|
||||
mem: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
total: 16000000000,
|
||||
free: 8000000000,
|
||||
used: 8000000000,
|
||||
})
|
||||
),
|
||||
osInfo: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
platform: 'darwin',
|
||||
distro: 'macOS',
|
||||
release: '14.0',
|
||||
arch: 'arm64',
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
// Mock posthog-node
|
||||
export const mockPostHog = {
|
||||
PostHog: vi.fn().mockImplementation(() => ({
|
||||
capture: vi.fn(),
|
||||
identify: vi.fn(),
|
||||
alias: vi.fn(),
|
||||
shutdown: vi.fn(() => Promise.resolve()),
|
||||
})),
|
||||
};
|
||||
|
||||
// Mock update-electron-app
|
||||
export const mockUpdateElectronApp = vi.fn();
|
||||
|
||||
export function createNativeMocks() {
|
||||
return {
|
||||
'onnxruntime-node': mockOnnxRuntime,
|
||||
'@amical/whisper-wrapper': mockWhisperWrapper,
|
||||
keytar: mockKeytar,
|
||||
libsql: mockLibsql,
|
||||
'@amical/swift-helper': mockSwiftHelper,
|
||||
'@amical/windows-helper': mockWindowsHelper,
|
||||
'node-machine-id': mockMachineId,
|
||||
systeminformation: mockSystemInformation,
|
||||
'posthog-node': mockPostHog,
|
||||
'update-electron-app': mockUpdateElectronApp,
|
||||
};
|
||||
}
|
||||
121
apps/desktop/tests/helpers/test-app.ts
Normal file
121
apps/desktop/tests/helpers/test-app.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { vi } from 'vitest';
|
||||
import type { TestDatabase } from './test-db';
|
||||
import { AppManager } from '@main/core/app-manager';
|
||||
import { ServiceManager } from '@main/managers/service-manager';
|
||||
import { router } from '@trpc/router';
|
||||
import { createContext } from '@trpc/context';
|
||||
|
||||
/**
|
||||
* Test wrapper for AppManager
|
||||
*/
|
||||
export interface TestApp {
|
||||
appManager: AppManager;
|
||||
serviceManager: ServiceManager;
|
||||
trpcCaller: ReturnType<typeof router.createCaller>;
|
||||
cleanup: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a test instance of AppManager with mocked database
|
||||
*/
|
||||
export async function initializeTestApp(
|
||||
testDb: TestDatabase,
|
||||
options: {
|
||||
skipOnboarding?: boolean;
|
||||
skipWindows?: boolean;
|
||||
} = {}
|
||||
): Promise<TestApp> {
|
||||
const { skipOnboarding = true, skipWindows = false } = options;
|
||||
|
||||
// Mock the database module to use our test database
|
||||
vi.doMock('@db', () => ({
|
||||
db: testDb.db,
|
||||
dbPath: testDb.dbPath,
|
||||
initializeDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
closeDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
// Mock onboarding check to skip it
|
||||
if (skipOnboarding) {
|
||||
process.env.FORCE_ONBOARDING = 'false';
|
||||
}
|
||||
|
||||
// Create AppManager instance
|
||||
const appManager = new AppManager();
|
||||
|
||||
// Initialize the app
|
||||
// Note: This will try to create windows, which are mocked
|
||||
try {
|
||||
await appManager.initialize();
|
||||
} catch (error) {
|
||||
// Some initialization errors are expected in test environment
|
||||
console.warn('AppManager initialization warning:', error);
|
||||
}
|
||||
|
||||
// Get service manager
|
||||
const serviceManager = ServiceManager.getInstance()!;
|
||||
|
||||
// Create tRPC caller for testing
|
||||
const ctx = createContext(serviceManager);
|
||||
const trpcCaller = router.createCaller(ctx);
|
||||
|
||||
return {
|
||||
appManager,
|
||||
serviceManager,
|
||||
trpcCaller,
|
||||
cleanup: async () => {
|
||||
// Clean up services
|
||||
// Note: Add cleanup logic as needed
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tRPC caller without initializing the full AppManager
|
||||
* Useful for testing specific service methods in isolation
|
||||
*/
|
||||
export function createTestTRPCCaller(serviceManager: ServiceManager) {
|
||||
const ctx = createContext(serviceManager);
|
||||
return router.createCaller(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize just the ServiceManager without AppManager
|
||||
* Useful for testing services in isolation
|
||||
*/
|
||||
export async function initializeTestServices(
|
||||
testDb: TestDatabase
|
||||
): Promise<{
|
||||
serviceManager: ServiceManager;
|
||||
trpcCaller: ReturnType<typeof router.createCaller>;
|
||||
cleanup: () => Promise<void>;
|
||||
}> {
|
||||
// Mock the database module
|
||||
vi.doMock('@db', () => ({
|
||||
db: testDb.db,
|
||||
dbPath: testDb.dbPath,
|
||||
initializeDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
closeDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
// Create and initialize ServiceManager
|
||||
const serviceManager = ServiceManager.createInstance();
|
||||
|
||||
try {
|
||||
await serviceManager.initialize();
|
||||
} catch (error) {
|
||||
console.warn('ServiceManager initialization warning:', error);
|
||||
}
|
||||
|
||||
// Create tRPC caller
|
||||
const ctx = createContext(serviceManager);
|
||||
const trpcCaller = router.createCaller(ctx);
|
||||
|
||||
return {
|
||||
serviceManager,
|
||||
trpcCaller,
|
||||
cleanup: async () => {
|
||||
// Cleanup logic
|
||||
},
|
||||
};
|
||||
}
|
||||
116
apps/desktop/tests/helpers/test-db.ts
Normal file
116
apps/desktop/tests/helpers/test-db.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { migrate } from 'drizzle-orm/libsql/migrator';
|
||||
import * as schema from '@db/schema';
|
||||
import path from 'node:path';
|
||||
import fs from 'fs-extra';
|
||||
import { TEST_USER_DATA_PATH } from './electron-mocks';
|
||||
|
||||
let dbCounter = 0;
|
||||
|
||||
export interface TestDatabase {
|
||||
db: ReturnType<typeof drizzle>;
|
||||
dbPath: string;
|
||||
close: () => Promise<void>;
|
||||
clear: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an isolated test database with migrations applied
|
||||
*/
|
||||
export async function createTestDatabase(
|
||||
options: {
|
||||
name?: string;
|
||||
skipMigrations?: boolean;
|
||||
} = {}
|
||||
): Promise<TestDatabase> {
|
||||
const { name, skipMigrations = false } = options;
|
||||
|
||||
// Create unique database path
|
||||
const dbName = name || `test-${dbCounter++}-${Date.now()}.db`;
|
||||
const dbPath = path.join(TEST_USER_DATA_PATH, 'databases', dbName);
|
||||
|
||||
// Ensure directory exists
|
||||
await fs.ensureDir(path.dirname(dbPath));
|
||||
|
||||
// Create drizzle instance
|
||||
const db = drizzle(`file:${dbPath}`, {
|
||||
schema: {
|
||||
...schema,
|
||||
},
|
||||
});
|
||||
|
||||
// Run migrations if not skipped
|
||||
if (!skipMigrations) {
|
||||
const migrationsPath = path.join(process.cwd(), 'src', 'db', 'migrations');
|
||||
|
||||
// Check if migrations exist
|
||||
if (!fs.existsSync(migrationsPath)) {
|
||||
console.warn(
|
||||
'Migrations folder not found at:',
|
||||
migrationsPath,
|
||||
'- skipping migrations'
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await migrate(db, {
|
||||
migrationsFolder: migrationsPath,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to run migrations:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
db,
|
||||
dbPath,
|
||||
close: async () => {
|
||||
db.$client.close();
|
||||
},
|
||||
clear: async () => {
|
||||
// Clear all tables
|
||||
await db.delete(schema.transcriptions);
|
||||
await db.delete(schema.vocabulary);
|
||||
await db.delete(schema.models);
|
||||
await db.delete(schema.appSettings);
|
||||
await db.delete(schema.yjsUpdates);
|
||||
await db.delete(schema.notes);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a test database file
|
||||
*/
|
||||
export async function deleteTestDatabase(dbPath: string): Promise<void> {
|
||||
try {
|
||||
await fs.remove(dbPath);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete test database:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all test databases
|
||||
*/
|
||||
export async function clearAllTestDatabases(): Promise<void> {
|
||||
const dbDir = path.join(TEST_USER_DATA_PATH, 'databases');
|
||||
try {
|
||||
await fs.emptyDir(dbDir);
|
||||
} catch (error) {
|
||||
console.error('Failed to clear test databases:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get database instance for testing
|
||||
* This bypasses the singleton pattern used in production
|
||||
*/
|
||||
export function createMockDb(dbPath: string) {
|
||||
return drizzle(`file:${dbPath}`, {
|
||||
schema: {
|
||||
...schema,
|
||||
},
|
||||
});
|
||||
}
|
||||
219
apps/desktop/tests/services/transcriptions.test.ts
Normal file
219
apps/desktop/tests/services/transcriptions.test.ts
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { createTestDatabase, type TestDatabase } from '../helpers/test-db';
|
||||
import { seedDatabase, fixtures, sampleTranscriptions } from '../helpers/fixtures';
|
||||
import { initializeTestServices } from '../helpers/test-app';
|
||||
import { setTestDatabase } from '../setup';
|
||||
|
||||
describe('Transcriptions Service', () => {
|
||||
let testDb: TestDatabase;
|
||||
let serviceManager: any;
|
||||
let trpcCaller: any;
|
||||
let cleanup: () => Promise<void>;
|
||||
|
||||
afterEach(async () => {
|
||||
if (cleanup) {
|
||||
await cleanup();
|
||||
}
|
||||
if (testDb) {
|
||||
await testDb.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe('Get Transcriptions', () => {
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'get-transcriptions-test' });
|
||||
setTestDatabase(testDb.db);
|
||||
await seedDatabase(testDb, 'withTranscriptions');
|
||||
const result = await initializeTestServices(testDb);
|
||||
serviceManager = result.serviceManager;
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
it('should return all transcriptions', async () => {
|
||||
const transcriptions = await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
expect(transcriptions).toHaveLength(sampleTranscriptions.length);
|
||||
expect(transcriptions[0]).toHaveProperty('id');
|
||||
expect(transcriptions[0]).toHaveProperty('text');
|
||||
expect(transcriptions[0]).toHaveProperty('language');
|
||||
});
|
||||
|
||||
it('should respect limit parameter', async () => {
|
||||
const transcriptions = await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 2,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
expect(transcriptions).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should respect offset parameter', async () => {
|
||||
const allTranscriptions =
|
||||
await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
const offsetTranscriptions =
|
||||
await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 10,
|
||||
offset: 1,
|
||||
});
|
||||
|
||||
expect(offsetTranscriptions).toHaveLength(allTranscriptions.length - 1);
|
||||
expect(offsetTranscriptions[0].id).not.toBe(allTranscriptions[0].id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get Transcription by ID', () => {
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'get-by-id-test' });
|
||||
setTestDatabase(testDb.db);
|
||||
await seedDatabase(testDb, 'withTranscriptions');
|
||||
const result = await initializeTestServices(testDb);
|
||||
serviceManager = result.serviceManager;
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
it('should return transcription by id', async () => {
|
||||
const transcriptions = await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 1,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
const transcription = await trpcCaller.transcriptions.getTranscriptionById({
|
||||
id: transcriptions[0].id,
|
||||
});
|
||||
|
||||
expect(transcription).toBeDefined();
|
||||
expect(transcription.id).toBe(transcriptions[0].id);
|
||||
expect(transcription.text).toBe(transcriptions[0].text);
|
||||
});
|
||||
|
||||
it('should return null for non-existent id', async () => {
|
||||
const result = await trpcCaller.transcriptions.getTranscriptionById({
|
||||
id: 99999,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delete Transcription', () => {
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'delete-test' });
|
||||
setTestDatabase(testDb.db);
|
||||
await seedDatabase(testDb, 'withTranscriptions');
|
||||
const result = await initializeTestServices(testDb);
|
||||
serviceManager = result.serviceManager;
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
it('should delete transcription by id', async () => {
|
||||
const transcriptions = await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
const initialCount = transcriptions.length;
|
||||
const idToDelete = transcriptions[0].id;
|
||||
|
||||
await trpcCaller.transcriptions.deleteTranscription({ id: idToDelete });
|
||||
|
||||
const afterDelete = await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
expect(afterDelete).toHaveLength(initialCount - 1);
|
||||
expect(afterDelete.find((t: any) => t.id === idToDelete)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search Transcriptions', () => {
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'search-test' });
|
||||
setTestDatabase(testDb.db);
|
||||
await seedDatabase(testDb, 'withTranscriptions');
|
||||
const result = await initializeTestServices(testDb);
|
||||
serviceManager = result.serviceManager;
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
it('should search transcriptions by text', async () => {
|
||||
const results = await trpcCaller.transcriptions.searchTranscriptions({
|
||||
searchTerm: 'test',
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
results.forEach((result: any) => {
|
||||
expect(result.text.toLowerCase()).toContain('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array for no matches', async () => {
|
||||
const results = await trpcCaller.transcriptions.searchTranscriptions({
|
||||
searchTerm: 'nonexistentquerystring',
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(results).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Empty Database', () => {
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'empty-test' });
|
||||
setTestDatabase(testDb.db);
|
||||
await seedDatabase(testDb, 'empty');
|
||||
const result = await initializeTestServices(testDb);
|
||||
serviceManager = result.serviceManager;
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
it('should return empty array for empty database', async () => {
|
||||
const transcriptions = await trpcCaller.transcriptions.getTranscriptions({
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
expect(transcriptions).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle search on empty database', async () => {
|
||||
const results = await trpcCaller.transcriptions.searchTranscriptions({
|
||||
searchTerm: 'test',
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
expect(results).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get Count', () => {
|
||||
beforeEach(async () => {
|
||||
testDb = await createTestDatabase({ name: 'count-test' });
|
||||
setTestDatabase(testDb.db);
|
||||
await seedDatabase(testDb, 'withTranscriptions');
|
||||
const result = await initializeTestServices(testDb);
|
||||
serviceManager = result.serviceManager;
|
||||
trpcCaller = result.trpcCaller;
|
||||
cleanup = result.cleanup;
|
||||
});
|
||||
|
||||
it('should return total transcription count', async () => {
|
||||
const count = await trpcCaller.transcriptions.getTranscriptionsCount({});
|
||||
|
||||
expect(count).toBe(sampleTranscriptions.length);
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
235
apps/desktop/tests/setup.ts
Normal file
235
apps/desktop/tests/setup.ts
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
import { vi, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
|
||||
import { TEST_USER_DATA_PATH } from './helpers/electron-mocks';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
// Set test environment variable
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.VITEST = 'true';
|
||||
|
||||
// Global test database instance - will be set by each test
|
||||
let currentTestDb: any = null;
|
||||
|
||||
// Helper function to set the current test database
|
||||
export function setTestDatabase(db: any) {
|
||||
currentTestDb = db;
|
||||
}
|
||||
|
||||
// Helper function to get the current test database
|
||||
export function getTestDatabase() {
|
||||
if (!currentTestDb) {
|
||||
throw new Error('Test database not set. Call setTestDatabase() in beforeEach.');
|
||||
}
|
||||
return currentTestDb;
|
||||
}
|
||||
|
||||
// Mock the database module to return the current test database
|
||||
vi.mock('@db', () => ({
|
||||
get db() {
|
||||
return getTestDatabase();
|
||||
},
|
||||
get dbPath() {
|
||||
return '/test/db/path';
|
||||
},
|
||||
initializeDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
closeDatabase: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
// Mock electron module
|
||||
vi.mock('electron', async () => {
|
||||
const { createElectronMocks } = await import('./helpers/electron-mocks');
|
||||
return createElectronMocks();
|
||||
});
|
||||
|
||||
// Mock native modules
|
||||
vi.mock('onnxruntime-node', () => ({
|
||||
InferenceSession: {
|
||||
create: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
run: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
output: {
|
||||
data: new Float32Array([0.5, 0.5, 0.5]),
|
||||
dims: [1, 3],
|
||||
},
|
||||
});
|
||||
}),
|
||||
release: vi.fn(),
|
||||
});
|
||||
}),
|
||||
},
|
||||
Tensor: vi.fn(),
|
||||
env: {
|
||||
wasm: {
|
||||
numThreads: 1,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@amical/whisper-wrapper', () => ({
|
||||
WhisperModel: vi.fn().mockImplementation(function() {
|
||||
return {
|
||||
transcribe: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
text: 'Test transcription',
|
||||
segments: [
|
||||
{
|
||||
start: 0,
|
||||
end: 1.5,
|
||||
text: 'Test transcription',
|
||||
},
|
||||
],
|
||||
});
|
||||
}),
|
||||
dispose: vi.fn(),
|
||||
};
|
||||
}),
|
||||
downloadModel: vi.fn(function() { return Promise.resolve(); }),
|
||||
getModelPath: vi.fn(function() { return '/mock/model/path'; }),
|
||||
}));
|
||||
|
||||
vi.mock('keytar', () => ({
|
||||
getPassword: vi.fn(function(service: string, account: string) {
|
||||
return Promise.resolve(null);
|
||||
}),
|
||||
setPassword: vi.fn(function(service: string, account: string, password: string) {
|
||||
return Promise.resolve();
|
||||
}),
|
||||
deletePassword: vi.fn(function(service: string, account: string) {
|
||||
return Promise.resolve(true);
|
||||
}),
|
||||
findPassword: vi.fn(function(service: string) {
|
||||
return Promise.resolve(null);
|
||||
}),
|
||||
findCredentials: vi.fn(function(service: string) {
|
||||
return Promise.resolve([]);
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('node-machine-id', () => ({
|
||||
machineIdSync: vi.fn(function() { return 'test-machine-id-12345'; }),
|
||||
machineId: vi.fn(function() { return Promise.resolve('test-machine-id-12345'); }),
|
||||
}));
|
||||
|
||||
vi.mock('systeminformation', () => ({
|
||||
system: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
manufacturer: 'Test Manufacturer',
|
||||
model: 'Test Model',
|
||||
version: '1.0',
|
||||
serial: 'TEST123',
|
||||
uuid: 'test-uuid',
|
||||
sku: 'TEST-SKU',
|
||||
});
|
||||
}),
|
||||
cpu: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
manufacturer: 'Test CPU',
|
||||
brand: 'Test Brand',
|
||||
speed: 2.5,
|
||||
cores: 4,
|
||||
});
|
||||
}),
|
||||
mem: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
total: 16000000000,
|
||||
free: 8000000000,
|
||||
used: 8000000000,
|
||||
});
|
||||
}),
|
||||
osInfo: vi.fn(function() {
|
||||
return Promise.resolve({
|
||||
platform: 'darwin',
|
||||
distro: 'macOS',
|
||||
release: '14.0',
|
||||
arch: 'arm64',
|
||||
});
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('posthog-node', () => ({
|
||||
PostHog: vi.fn().mockImplementation(function() {
|
||||
return {
|
||||
capture: vi.fn(),
|
||||
identify: vi.fn(),
|
||||
alias: vi.fn(),
|
||||
shutdown: vi.fn(function() { return Promise.resolve(); }),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('update-electron-app', () => ({
|
||||
default: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock electron-log
|
||||
vi.mock('electron-log', () => ({
|
||||
default: {
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
verbose: vi.fn(),
|
||||
silly: vi.fn(),
|
||||
transports: {
|
||||
file: { level: 'info' },
|
||||
console: { level: 'info' },
|
||||
},
|
||||
scope: vi.fn(() => ({
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
})),
|
||||
},
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
verbose: vi.fn(),
|
||||
silly: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock electron-squirrel-startup
|
||||
vi.mock('electron-squirrel-startup', () => ({
|
||||
default: false,
|
||||
}));
|
||||
|
||||
// Mock electron-trpc-experimental
|
||||
vi.mock('electron-trpc-experimental/main', () => ({
|
||||
createIPCHandler: vi.fn(() => ({
|
||||
handle: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
// Global test setup
|
||||
beforeAll(async () => {
|
||||
// Create test user data directory
|
||||
await fs.ensureDir(TEST_USER_DATA_PATH);
|
||||
await fs.ensureDir(path.join(TEST_USER_DATA_PATH, 'databases'));
|
||||
await fs.ensureDir(path.join(TEST_USER_DATA_PATH, 'models'));
|
||||
await fs.ensureDir(path.join(TEST_USER_DATA_PATH, 'logs'));
|
||||
});
|
||||
|
||||
// Global test teardown
|
||||
afterAll(async () => {
|
||||
// Clean up test user data directory
|
||||
try {
|
||||
await fs.remove(TEST_USER_DATA_PATH);
|
||||
} catch (error) {
|
||||
console.error('Failed to clean up test directory:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset mocks between tests
|
||||
beforeEach(() => {
|
||||
// Clear all mock calls and instances
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Additional cleanup if needed
|
||||
});
|
||||
|
||||
// Export for use in tests
|
||||
export { TEST_USER_DATA_PATH };
|
||||
28
apps/desktop/vitest.config.ts
Normal file
28
apps/desktop/vitest.config.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
import { resolve } from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
include: ['tests/**/*.{test,spec}.{js,ts}'],
|
||||
exclude: ['node_modules', '.vite', 'out'],
|
||||
setupFiles: ['./tests/setup.ts'],
|
||||
testTimeout: 30000, // 30 seconds for full app initialization
|
||||
hookTimeout: 30000,
|
||||
// Run tests sequentially to avoid database conflicts
|
||||
threads: false,
|
||||
// Isolate environment for each test file
|
||||
isolate: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src'),
|
||||
'@db': resolve(__dirname, 'src/db'),
|
||||
'@main': resolve(__dirname, 'src/main'),
|
||||
'@services': resolve(__dirname, 'src/services'),
|
||||
'@utils': resolve(__dirname, 'src/utils'),
|
||||
'@trpc': resolve(__dirname, 'src/trpc'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -7,7 +7,10 @@
|
|||
"lint": "turbo run lint",
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md,json,mjs,mts,css,mdx}\"",
|
||||
"format:check": "turbo run format:check",
|
||||
"type:check": "turbo run type:check"
|
||||
"type:check": "turbo run type:check",
|
||||
"test": "turbo run test",
|
||||
"test:watch": "turbo run test:watch",
|
||||
"test:ui": "turbo run test:ui"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.5.3",
|
||||
|
|
|
|||
432
pnpm-lock.yaml
generated
432
pnpm-lock.yaml
generated
|
|
@ -200,7 +200,7 @@ importers:
|
|||
version: 0.31.4
|
||||
drizzle-orm:
|
||||
specifier: ^0.43.1
|
||||
version: 0.43.1(@libsql/client@0.15.12)(@opentelemetry/api@1.9.0)
|
||||
version: 0.43.1(@libsql/client@0.15.12)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1)
|
||||
electron-log:
|
||||
specifier: ^5.4.0
|
||||
version: 5.4.3
|
||||
|
|
@ -297,9 +297,6 @@ importers:
|
|||
vaul:
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
workerpool:
|
||||
specifier: ^9.3.3
|
||||
version: 9.3.3
|
||||
yjs:
|
||||
specifier: ^13.6.27
|
||||
version: 13.6.27
|
||||
|
|
@ -345,19 +342,25 @@ importers:
|
|||
version: 28.0.6(rollup@4.47.1)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.6
|
||||
version: 4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))
|
||||
version: 4.1.12(vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))
|
||||
'@tanstack/react-router-devtools':
|
||||
specifier: ^1.131.41
|
||||
version: 1.131.41(@tanstack/react-router@1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.41)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.9)(tiny-invariant@1.3.3)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: ^1.131.41
|
||||
version: 1.131.41(@tanstack/react-router@1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))
|
||||
version: 1.131.41(@tanstack/react-router@1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))
|
||||
'@types/node':
|
||||
specifier: ^24.10.1
|
||||
version: 24.10.1
|
||||
'@types/react':
|
||||
specifier: ^19.1.12
|
||||
version: 19.1.12
|
||||
'@types/react-dom':
|
||||
specifier: ^19.1.9
|
||||
version: 19.1.9(@types/react@19.1.12)
|
||||
'@vitest/ui':
|
||||
specifier: ^4.0.8
|
||||
version: 4.0.8(vitest@4.0.8)
|
||||
bumpp:
|
||||
specifier: ^10.2.3
|
||||
version: 10.2.3
|
||||
|
|
@ -379,6 +382,9 @@ importers:
|
|||
flora-colossus:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
fs-extra:
|
||||
specifier: ^11.3.2
|
||||
version: 11.3.2
|
||||
prettier:
|
||||
specifier: ^3.5.3
|
||||
version: 3.6.2
|
||||
|
|
@ -393,7 +399,10 @@ importers:
|
|||
version: 5.8.3
|
||||
vite:
|
||||
specifier: ^7.1.5
|
||||
version: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
version: 7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
vitest:
|
||||
specifier: ^4.0.8
|
||||
version: 4.0.8(@types/node@24.10.1)(@vitest/ui@4.0.8)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
|
||||
packages/eslint-config:
|
||||
devDependencies:
|
||||
|
|
@ -1521,6 +1530,9 @@ packages:
|
|||
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
'@posthog/core@1.0.2':
|
||||
resolution: {integrity: sha512-hWk3rUtJl2crQK0WNmwg13n82hnTwB99BT99/XI5gZSvIlYZ1TPmMZE8H2dhJJ98J/rm9vYJ/UXNzw3RV5HTpQ==}
|
||||
|
||||
|
|
@ -2264,6 +2276,9 @@ packages:
|
|||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@standard-schema/spec@1.0.0':
|
||||
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
||||
|
||||
'@standard-schema/utils@0.3.0':
|
||||
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
|
||||
|
||||
|
|
@ -2517,9 +2532,15 @@ packages:
|
|||
resolution: {integrity: sha512-TmY25GmxzgX+395Fwl/F0te6S4RHdJtYl1QjZr+wlxVvKJ0IBOACpnpAvnLM3dpTgXuQukGtSWcRz7Zi9mZqcQ==}
|
||||
hasBin: true
|
||||
|
||||
'@types/better-sqlite3@7.6.13':
|
||||
resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
|
||||
|
||||
'@types/cacheable-request@6.0.3':
|
||||
resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||
|
||||
'@types/d3-array@3.2.1':
|
||||
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||
|
||||
|
|
@ -2547,6 +2568,9 @@ packages:
|
|||
'@types/d3-timer@3.0.2':
|
||||
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
'@types/diff-match-patch@1.0.36':
|
||||
resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
|
||||
|
||||
|
|
@ -2602,6 +2626,9 @@ packages:
|
|||
'@types/node@22.15.12':
|
||||
resolution: {integrity: sha512-K0fpC/ZVeb8G9rm7bH7vI0KAec4XHEhBam616nVJCV51bKzJ6oA3luG4WdKoaztxe70QaNjS/xBmcDLmr4PiGw==}
|
||||
|
||||
'@types/node@24.10.1':
|
||||
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==}
|
||||
|
||||
'@types/node@24.3.0':
|
||||
resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==}
|
||||
|
||||
|
|
@ -2699,6 +2726,40 @@ packages:
|
|||
resolution: {integrity: sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@vitest/expect@4.0.8':
|
||||
resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==}
|
||||
|
||||
'@vitest/mocker@4.0.8':
|
||||
resolution: {integrity: sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^6.0.0 || ^7.0.0-0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@4.0.8':
|
||||
resolution: {integrity: sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==}
|
||||
|
||||
'@vitest/runner@4.0.8':
|
||||
resolution: {integrity: sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==}
|
||||
|
||||
'@vitest/snapshot@4.0.8':
|
||||
resolution: {integrity: sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==}
|
||||
|
||||
'@vitest/spy@4.0.8':
|
||||
resolution: {integrity: sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==}
|
||||
|
||||
'@vitest/ui@4.0.8':
|
||||
resolution: {integrity: sha512-F9jI5rSstNknPlTlPN2gcc4gpbaagowuRzw/OJzl368dvPun668Q182S8Q8P9PITgGCl5LAKXpzuue106eM4wA==}
|
||||
peerDependencies:
|
||||
vitest: 4.0.8
|
||||
|
||||
'@vitest/utils@4.0.8':
|
||||
resolution: {integrity: sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==}
|
||||
|
||||
'@watchable/unpromise@1.0.2':
|
||||
resolution: {integrity: sha512-yGCKYzCrAfJQ9yzm76r1bl4WUIWyqmh4vqidXn5LyOfPbgdiZrKOyvW2ivqIvtmsRVb7u3ModEpc4q901VRgXw==}
|
||||
|
||||
|
|
@ -2871,6 +2932,10 @@ packages:
|
|||
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ast-types@0.13.4:
|
||||
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -2926,10 +2991,17 @@ packages:
|
|||
before-after-hook@2.2.3:
|
||||
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
||||
|
||||
better-sqlite3@12.4.1:
|
||||
resolution: {integrity: sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==}
|
||||
engines: {node: 20.x || 22.x || 23.x || 24.x}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
bindings@1.5.0:
|
||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||
|
||||
bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
|
||||
|
|
@ -3027,6 +3099,10 @@ packages:
|
|||
caniuse-lite@1.0.30001736:
|
||||
resolution: {integrity: sha512-ImpN5gLEY8gWeqfLUyEF4b7mYWcYoR2Si1VhnrbM4JizRFmfGaAQ12PhNykq6nvI4XvKLrsp8Xde74D5phJOSw==}
|
||||
|
||||
chai@6.2.1:
|
||||
resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
chalk-template@0.4.0:
|
||||
resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -3351,6 +3427,15 @@ packages:
|
|||
supports-color:
|
||||
optional: true
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decimal.js-light@2.5.1:
|
||||
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
|
||||
|
||||
|
|
@ -3676,6 +3761,9 @@ packages:
|
|||
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-module-lexer@1.7.0:
|
||||
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -3855,6 +3943,9 @@ packages:
|
|||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
esutils@2.0.3:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -3885,6 +3976,10 @@ packages:
|
|||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
expect-type@1.2.2:
|
||||
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
exponential-backoff@3.1.2:
|
||||
resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==}
|
||||
|
||||
|
|
@ -3943,6 +4038,9 @@ packages:
|
|||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
|
||||
fflate@0.8.2:
|
||||
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||
|
||||
figures@3.2.0:
|
||||
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -3951,6 +4049,9 @@ packages:
|
|||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||
|
||||
filename-reserved-regex@2.0.0:
|
||||
resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -4045,6 +4146,10 @@ packages:
|
|||
resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==}
|
||||
engines: {node: '>=14.14'}
|
||||
|
||||
fs-extra@11.3.2:
|
||||
resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==}
|
||||
engines: {node: '>=14.14'}
|
||||
|
||||
fs-extra@7.0.1:
|
||||
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
|
||||
engines: {node: '>=6 <7 || >=8'}
|
||||
|
|
@ -4823,6 +4928,9 @@ packages:
|
|||
magic-string@0.30.17:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
make-error@1.3.6:
|
||||
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
||||
|
||||
|
|
@ -4964,6 +5072,10 @@ packages:
|
|||
motion-utils@12.23.6:
|
||||
resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==}
|
||||
|
||||
mrmime@2.0.1:
|
||||
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
ms@2.0.0:
|
||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||
|
||||
|
|
@ -5786,6 +5898,9 @@ packages:
|
|||
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
|
||||
|
|
@ -5795,6 +5910,10 @@ packages:
|
|||
simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
|
||||
sirv@3.0.2:
|
||||
resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -5869,6 +5988,12 @@ packages:
|
|||
resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
|
||||
stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
||||
std-env@3.10.0:
|
||||
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||
|
||||
stop-iteration-iterator@1.1.0:
|
||||
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -6046,9 +6171,15 @@ packages:
|
|||
tiny-warning@1.0.3:
|
||||
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinycolor2@1.6.0:
|
||||
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
|
||||
|
||||
tinyexec@0.3.2:
|
||||
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||
|
||||
tinyexec@1.0.1:
|
||||
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
|
||||
|
||||
|
|
@ -6063,6 +6194,10 @@ packages:
|
|||
tinygradient@1.1.5:
|
||||
resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==}
|
||||
|
||||
tinyrainbow@3.0.3:
|
||||
resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
title-case@2.1.1:
|
||||
resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==}
|
||||
|
||||
|
|
@ -6088,6 +6223,10 @@ packages:
|
|||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
totalist@3.0.1:
|
||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
|
|
@ -6254,6 +6393,9 @@ packages:
|
|||
undici-types@7.10.0:
|
||||
resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
|
||||
|
||||
undici-types@7.16.0:
|
||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||
|
||||
unicode-properties@1.4.1:
|
||||
resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==}
|
||||
|
||||
|
|
@ -6409,6 +6551,40 @@ packages:
|
|||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest@4.0.8:
|
||||
resolution: {integrity: sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==}
|
||||
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/debug': ^4.1.12
|
||||
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
|
||||
'@vitest/browser-playwright': 4.0.8
|
||||
'@vitest/browser-preview': 4.0.8
|
||||
'@vitest/browser-webdriverio': 4.0.8
|
||||
'@vitest/ui': 4.0.8
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@types/debug':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser-playwright':
|
||||
optional: true
|
||||
'@vitest/browser-preview':
|
||||
optional: true
|
||||
'@vitest/browser-webdriverio':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
wcwidth@1.0.1:
|
||||
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||
|
||||
|
|
@ -6454,6 +6630,11 @@ packages:
|
|||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
|
||||
|
|
@ -6468,9 +6649,6 @@ packages:
|
|||
resolution: {integrity: sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==}
|
||||
engines: {node: '>=12.17'}
|
||||
|
||||
workerpool@9.3.3:
|
||||
resolution: {integrity: sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -7193,7 +7371,7 @@ snapshots:
|
|||
debug: 4.4.1
|
||||
extract-zip: 2.0.1
|
||||
filenamify: 4.3.0
|
||||
fs-extra: 11.3.1
|
||||
fs-extra: 11.3.2
|
||||
galactus: 1.0.0
|
||||
get-package-info: 1.0.0
|
||||
junk: 3.1.0
|
||||
|
|
@ -7233,7 +7411,7 @@ snapshots:
|
|||
'@malept/cross-spawn-promise': 2.0.0
|
||||
debug: 4.4.1
|
||||
dir-compare: 4.2.0
|
||||
fs-extra: 11.3.1
|
||||
fs-extra: 11.3.2
|
||||
minimatch: 9.0.5
|
||||
plist: 3.1.0
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -7243,7 +7421,7 @@ snapshots:
|
|||
dependencies:
|
||||
cross-dirname: 0.1.0
|
||||
debug: 4.4.1
|
||||
fs-extra: 11.3.1
|
||||
fs-extra: 11.3.2
|
||||
minimist: 1.2.8
|
||||
postject: 1.0.0-alpha.6
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -7524,7 +7702,7 @@ snapshots:
|
|||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
'@types/yargs': 17.0.33
|
||||
chalk: 4.1.2
|
||||
|
||||
|
|
@ -7753,6 +7931,8 @@ snapshots:
|
|||
|
||||
'@pkgr/core@0.2.9': {}
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@posthog/core@1.0.2': {}
|
||||
|
||||
'@radix-ui/number@1.1.1': {}
|
||||
|
|
@ -8494,6 +8674,8 @@ snapshots:
|
|||
|
||||
'@sindresorhus/is@4.6.0': {}
|
||||
|
||||
'@standard-schema/spec@1.0.0': {}
|
||||
|
||||
'@standard-schema/utils@0.3.0': {}
|
||||
|
||||
'@szmarczak/http-timer@4.0.6':
|
||||
|
|
@ -8571,12 +8753,12 @@ snapshots:
|
|||
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.12
|
||||
'@tailwindcss/oxide-win32-x64-msvc': 4.1.12
|
||||
|
||||
'@tailwindcss/vite@4.1.12(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))':
|
||||
'@tailwindcss/vite@4.1.12(vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.1.12
|
||||
'@tailwindcss/oxide': 4.1.12
|
||||
tailwindcss: 4.1.12
|
||||
vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
|
||||
'@tanstack/history@1.131.2': {}
|
||||
|
||||
|
|
@ -8666,7 +8848,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/router-plugin@1.131.41(@tanstack/react-router@1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))':
|
||||
'@tanstack/router-plugin@1.131.41(@tanstack/react-router@1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3)
|
||||
|
|
@ -8684,7 +8866,7 @@ snapshots:
|
|||
zod: 3.25.76
|
||||
optionalDependencies:
|
||||
'@tanstack/react-router': 1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
vite: 7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
vite: 7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -8771,13 +8953,23 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
||||
'@types/better-sqlite3@7.6.13':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
optional: true
|
||||
|
||||
'@types/cacheable-request@6.0.3':
|
||||
dependencies:
|
||||
'@types/http-cache-semantics': 4.0.4
|
||||
'@types/keyv': 3.1.4
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
'@types/responselike': 1.0.3
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
assertion-error: 2.0.1
|
||||
|
||||
'@types/d3-array@3.2.1': {}
|
||||
|
||||
'@types/d3-color@3.1.3': {}
|
||||
|
|
@ -8802,13 +8994,15 @@ snapshots:
|
|||
|
||||
'@types/d3-timer@3.0.2': {}
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/diff-match-patch@1.0.36': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/fs-extra@9.0.13':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
optional: true
|
||||
|
||||
'@types/glob@7.2.0':
|
||||
|
|
@ -8839,7 +9033,7 @@ snapshots:
|
|||
|
||||
'@types/keyv@3.1.4':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
|
||||
'@types/minimatch@6.0.0':
|
||||
dependencies:
|
||||
|
|
@ -8847,7 +9041,7 @@ snapshots:
|
|||
|
||||
'@types/node-fetch@2.6.13':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
form-data: 4.0.4
|
||||
|
||||
'@types/node@16.18.126': {}
|
||||
|
|
@ -8864,6 +9058,10 @@ snapshots:
|
|||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@24.10.1':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
'@types/node@24.3.0':
|
||||
dependencies:
|
||||
undici-types: 7.10.0
|
||||
|
|
@ -8878,11 +9076,11 @@ snapshots:
|
|||
|
||||
'@types/responselike@1.0.3':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
|
||||
'@types/split2@4.2.3':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
|
||||
'@types/through@0.0.33':
|
||||
dependencies:
|
||||
|
|
@ -8894,7 +9092,7 @@ snapshots:
|
|||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
|
|
@ -8904,7 +9102,7 @@ snapshots:
|
|||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.33.0(jiti@2.5.1))(typescript@5.8.3)':
|
||||
|
|
@ -9000,6 +9198,56 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.40.0
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
'@vitest/expect@4.0.8':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.0.0
|
||||
'@types/chai': 5.2.3
|
||||
'@vitest/spy': 4.0.8
|
||||
'@vitest/utils': 4.0.8
|
||||
chai: 6.2.1
|
||||
tinyrainbow: 3.0.3
|
||||
|
||||
'@vitest/mocker@4.0.8(vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.0.8
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
|
||||
'@vitest/pretty-format@4.0.8':
|
||||
dependencies:
|
||||
tinyrainbow: 3.0.3
|
||||
|
||||
'@vitest/runner@4.0.8':
|
||||
dependencies:
|
||||
'@vitest/utils': 4.0.8
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@4.0.8':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.0.8
|
||||
magic-string: 0.30.21
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@4.0.8': {}
|
||||
|
||||
'@vitest/ui@4.0.8(vitest@4.0.8)':
|
||||
dependencies:
|
||||
'@vitest/utils': 4.0.8
|
||||
fflate: 0.8.2
|
||||
flatted: 3.3.3
|
||||
pathe: 2.0.3
|
||||
sirv: 3.0.2
|
||||
tinyglobby: 0.2.15
|
||||
tinyrainbow: 3.0.3
|
||||
vitest: 4.0.8(@types/node@24.10.1)(@vitest/ui@4.0.8)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
|
||||
'@vitest/utils@4.0.8':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 4.0.8
|
||||
tinyrainbow: 3.0.3
|
||||
|
||||
'@watchable/unpromise@1.0.2': {}
|
||||
|
||||
'@xmldom/xmldom@0.8.11': {}
|
||||
|
|
@ -9024,7 +9272,7 @@ snapshots:
|
|||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -9194,6 +9442,8 @@ snapshots:
|
|||
get-intrinsic: 1.3.0
|
||||
is-array-buffer: 3.0.5
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
ast-types@0.13.4:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
|
@ -9251,8 +9501,19 @@ snapshots:
|
|||
|
||||
before-after-hook@2.2.3: {}
|
||||
|
||||
better-sqlite3@12.4.1:
|
||||
dependencies:
|
||||
bindings: 1.5.0
|
||||
prebuild-install: 7.1.3
|
||||
optional: true
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
bindings@1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path: 1.0.0
|
||||
optional: true
|
||||
|
||||
bl@4.1.0:
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
|
|
@ -9400,6 +9661,8 @@ snapshots:
|
|||
|
||||
caniuse-lite@1.0.30001736: {}
|
||||
|
||||
chai@6.2.1: {}
|
||||
|
||||
chalk-template@0.4.0:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
|
|
@ -9729,6 +9992,10 @@ snapshots:
|
|||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decimal.js-light@2.5.1: {}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
|
|
@ -9837,10 +10104,12 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
drizzle-orm@0.43.1(@libsql/client@0.15.12)(@opentelemetry/api@1.9.0):
|
||||
drizzle-orm@0.43.1(@libsql/client@0.15.12)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1):
|
||||
optionalDependencies:
|
||||
'@libsql/client': 0.15.12
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/better-sqlite3': 7.6.13
|
||||
better-sqlite3: 12.4.1
|
||||
|
||||
ds-store@0.1.6:
|
||||
dependencies:
|
||||
|
|
@ -10079,6 +10348,8 @@ snapshots:
|
|||
iterator.prototype: 1.1.5
|
||||
safe-array-concat: 1.1.3
|
||||
|
||||
es-module-lexer@1.7.0: {}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
|
@ -10343,6 +10614,10 @@ snapshots:
|
|||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
|
|
@ -10377,6 +10652,8 @@ snapshots:
|
|||
|
||||
expand-template@2.0.3: {}
|
||||
|
||||
expect-type@1.2.2: {}
|
||||
|
||||
exponential-backoff@3.1.2: {}
|
||||
|
||||
exsolve@1.0.7: {}
|
||||
|
|
@ -10440,6 +10717,8 @@ snapshots:
|
|||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.3.3
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
figures@3.2.0:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
|
|
@ -10448,6 +10727,9 @@ snapshots:
|
|||
dependencies:
|
||||
flat-cache: 4.0.1
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
optional: true
|
||||
|
||||
filename-reserved-regex@2.0.0: {}
|
||||
|
||||
filenamify@4.3.0:
|
||||
|
|
@ -10544,6 +10826,12 @@ snapshots:
|
|||
jsonfile: 6.2.0
|
||||
universalify: 2.0.1
|
||||
|
||||
fs-extra@11.3.2:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
jsonfile: 6.2.0
|
||||
universalify: 2.0.1
|
||||
|
||||
fs-extra@7.0.1:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
|
@ -10847,7 +11135,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@tootallnate/once': 2.0.0
|
||||
agent-base: 6.0.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -10866,7 +11154,7 @@ snapshots:
|
|||
https-proxy-agent@5.0.1:
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -11164,7 +11452,7 @@ snapshots:
|
|||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
|
|
@ -11172,7 +11460,7 @@ snapshots:
|
|||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
jest-util: 29.7.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
|
@ -11394,6 +11682,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
make-error@1.3.6: {}
|
||||
|
||||
make-fetch-happen@10.2.1:
|
||||
|
|
@ -11536,6 +11828,8 @@ snapshots:
|
|||
|
||||
motion-utils@12.23.6: {}
|
||||
|
||||
mrmime@2.0.1: {}
|
||||
|
||||
ms@2.0.0: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
|
@ -12475,6 +12769,8 @@ snapshots:
|
|||
side-channel-map: 1.0.1
|
||||
side-channel-weakmap: 1.0.2
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
signal-exit@3.0.7: {}
|
||||
|
||||
simple-concat@1.0.1: {}
|
||||
|
|
@ -12485,6 +12781,12 @@ snapshots:
|
|||
once: 1.4.0
|
||||
simple-concat: 1.0.1
|
||||
|
||||
sirv@3.0.2:
|
||||
dependencies:
|
||||
'@polka/url': 1.0.0-next.29
|
||||
mrmime: 2.0.1
|
||||
totalist: 3.0.1
|
||||
|
||||
slash@3.0.0: {}
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
|
|
@ -12501,7 +12803,7 @@ snapshots:
|
|||
socks-proxy-agent@7.0.0:
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.4.1
|
||||
debug: 4.4.3
|
||||
socks: 2.8.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -12563,6 +12865,10 @@ snapshots:
|
|||
dependencies:
|
||||
minipass: 3.3.6
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
std-env@3.10.0: {}
|
||||
|
||||
stop-iteration-iterator@1.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
|
@ -12769,8 +13075,12 @@ snapshots:
|
|||
|
||||
tiny-warning@1.0.3: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinycolor2@1.6.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
||||
tinyexec@1.0.1: {}
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
|
|
@ -12788,6 +13098,8 @@ snapshots:
|
|||
'@types/tinycolor2': 1.4.6
|
||||
tinycolor2: 1.6.0
|
||||
|
||||
tinyrainbow@3.0.3: {}
|
||||
|
||||
title-case@2.1.1:
|
||||
dependencies:
|
||||
no-case: 2.3.2
|
||||
|
|
@ -12817,6 +13129,8 @@ snapshots:
|
|||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
trim-repeated@1.0.0:
|
||||
|
|
@ -12999,6 +13313,8 @@ snapshots:
|
|||
|
||||
undici-types@7.10.0: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
unicode-properties@1.4.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
|
|
@ -13126,7 +13442,7 @@ snapshots:
|
|||
d3-time: 3.1.0
|
||||
d3-timer: 3.0.1
|
||||
|
||||
vite@7.1.5(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1):
|
||||
vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1):
|
||||
dependencies:
|
||||
esbuild: 0.25.9
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
|
|
@ -13135,13 +13451,52 @@ snapshots:
|
|||
rollup: 4.47.1
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 24.3.0
|
||||
'@types/node': 24.10.1
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.5.1
|
||||
lightningcss: 1.30.1
|
||||
tsx: 4.20.4
|
||||
yaml: 2.8.1
|
||||
|
||||
vitest@4.0.8(@types/node@24.10.1)(@vitest/ui@4.0.8)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.0.8
|
||||
'@vitest/mocker': 4.0.8(vite@7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1))
|
||||
'@vitest/pretty-format': 4.0.8
|
||||
'@vitest/runner': 4.0.8
|
||||
'@vitest/snapshot': 4.0.8
|
||||
'@vitest/spy': 4.0.8
|
||||
'@vitest/utils': 4.0.8
|
||||
debug: 4.4.3
|
||||
es-module-lexer: 1.7.0
|
||||
expect-type: 1.2.2
|
||||
magic-string: 0.30.21
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.3
|
||||
std-env: 3.10.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 0.3.2
|
||||
tinyglobby: 0.2.15
|
||||
tinyrainbow: 3.0.3
|
||||
vite: 7.1.5(@types/node@24.10.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 24.10.1
|
||||
'@vitest/ui': 4.0.8(vitest@4.0.8)
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
wcwidth@1.0.1:
|
||||
dependencies:
|
||||
defaults: 1.0.4
|
||||
|
|
@ -13208,6 +13563,11 @@ snapshots:
|
|||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
stackback: 0.0.2
|
||||
|
||||
wide-align@1.1.5:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
|
@ -13218,8 +13578,6 @@ snapshots:
|
|||
|
||||
wordwrapjs@5.1.0: {}
|
||||
|
||||
workerpool@9.3.3: {}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
|
|
|||
14
turbo.json
14
turbo.json
|
|
@ -60,6 +60,20 @@
|
|||
"dependsOn": ["^build"],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": [],
|
||||
"cache": false
|
||||
},
|
||||
"test:watch": {
|
||||
"dependsOn": [],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"test:ui": {
|
||||
"dependsOn": [],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue