diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml new file mode 100644 index 0000000..af06c85 --- /dev/null +++ b/.github/workflows/desktop-build.yml @@ -0,0 +1,103 @@ +name: Build Desktop App + +on: + push: + tags: + - "desktop-v*" + workflow_dispatch: + +jobs: + build: + strategy: + matrix: + include: + - os: ubuntu-latest + platform: linux + - os: macos-latest + platform: mac + - os: windows-latest + platform: win + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install + + - name: Build workspace packages + run: pnpm --filter @hackclub/lapse-shared --filter @hackclub/lapse-api run build + + - name: Build desktop app + working-directory: apps/desktop + run: npx electron-vite build + + - name: Get Electron version + id: electron-ver + shell: bash + run: echo "version=$(node -e "console.log(require('electron/package.json').version)")" >> $GITHUB_OUTPUT + + - name: Build installer (Linux) + if: matrix.platform == 'linux' + working-directory: apps/desktop + run: npx electron-builder --linux AppImage deb --config electron-builder.yml -c.electronVersion=${{ steps.electron-ver.outputs.version }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Build installer (macOS) + if: matrix.platform == 'mac' + working-directory: apps/desktop + run: npx electron-builder --mac dmg zip --config electron-builder.yml -c.electronVersion=${{ steps.electron-ver.outputs.version }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Build installer (Windows) + if: matrix.platform == 'win' + working-directory: apps/desktop + run: npx electron-builder --win nsis --config electron-builder.yml -c.electronVersion=${{ steps.electron-ver.outputs.version }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: desktop-${{ matrix.platform }} + path: | + apps/desktop/release/*.exe + apps/desktop/release/*.dmg + apps/desktop/release/*.zip + apps/desktop/release/*.AppImage + apps/desktop/release/*.deb + if-no-files-found: warn + + release: + needs: build + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: artifacts/* + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..d67f374 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +node-linker=hoisted diff --git a/apps/desktop/.gitignore b/apps/desktop/.gitignore new file mode 100644 index 0000000..f063649 --- /dev/null +++ b/apps/desktop/.gitignore @@ -0,0 +1,3 @@ +dist/ +out/ +release/ diff --git a/apps/desktop/build/entitlements.mac.plist b/apps/desktop/build/entitlements.mac.plist new file mode 100644 index 0000000..fe8ec5d --- /dev/null +++ b/apps/desktop/build/entitlements.mac.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.device.camera + + com.apple.security.device.audio-input + + + diff --git a/apps/desktop/build/icon.ico b/apps/desktop/build/icon.ico new file mode 100644 index 0000000..07ed919 Binary files /dev/null and b/apps/desktop/build/icon.ico differ diff --git a/apps/desktop/build/icon.png b/apps/desktop/build/icon.png new file mode 100644 index 0000000..13d2640 Binary files /dev/null and b/apps/desktop/build/icon.png differ diff --git a/apps/desktop/build/icons/icon.png b/apps/desktop/build/icons/icon.png new file mode 100644 index 0000000..07ed919 Binary files /dev/null and b/apps/desktop/build/icons/icon.png differ diff --git a/apps/desktop/electron-builder.yml b/apps/desktop/electron-builder.yml new file mode 100644 index 0000000..8865d57 --- /dev/null +++ b/apps/desktop/electron-builder.yml @@ -0,0 +1,41 @@ +appId: com.hackclub.lapse +productName: Lapse +directories: + output: release + buildResources: build + +files: + - "out/**/*" + +mac: + category: public.app-category.developer-tools + icon: build/icon.png + target: + - target: dmg + arch: [x64, arm64] + - target: zip + arch: [x64, arm64] + +win: + icon: build/icon.png + target: + - target: nsis + arch: [x64] + +linux: + icon: build/icon.png + target: + - target: AppImage + arch: [x64] + - target: deb + arch: [x64] + +nsis: + oneClick: false + perMachine: false + allowToChangeInstallationDirectory: true + +publish: + provider: github + owner: claynicholson + repo: Clapse diff --git a/apps/desktop/electron.vite.config.ts b/apps/desktop/electron.vite.config.ts new file mode 100644 index 0000000..211035b --- /dev/null +++ b/apps/desktop/electron.vite.config.ts @@ -0,0 +1,46 @@ +import { defineConfig, externalizeDepsPlugin } from "electron-vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/postcss"; +import { resolve } from "node:path"; + +export default defineConfig({ + main: { + resolve: { + alias: { + "@": resolve(__dirname, "src") + } + }, + build: { + rollupOptions: { + input: { + index: resolve(__dirname, "src/main/index.ts"), + captureWindow: resolve(__dirname, "src/main/capture/captureWindow.ts") + } + } + } + }, + preload: { + plugins: [externalizeDepsPlugin()], + build: { + rollupOptions: { + input: { + index: resolve(__dirname, "src/preload/index.ts"), + capturePreload: resolve(__dirname, "src/main/capture/capturePreload.ts") + } + } + } + }, + renderer: { + plugins: [react()], + resolve: { + alias: { + "@": resolve(__dirname, "src") + } + }, + css: { + postcss: { + plugins: [tailwindcss()] + } + } + } +}); diff --git a/apps/desktop/package.json b/apps/desktop/package.json new file mode 100644 index 0000000..2fa5dfe --- /dev/null +++ b/apps/desktop/package.json @@ -0,0 +1,46 @@ +{ + "name": "@lapse/lapse-desktop", + "version": "2.0.0", + "description": "Lapse desktop client — timelapse recording for Hack Clubbers", + "private": true, + "type": "module", + "main": "out/main/index.js", + "scripts": { + "dev": "electron-vite dev", + "build": "tsc --noEmit && electron-vite build", + "build:mac": "pnpm build && electron-builder --mac", + "build:win": "pnpm build && electron-builder --win", + "build:linux": "pnpm build && electron-builder --linux", + "build:all": "pnpm build && electron-builder --mac --win --linux", + "check-types": "tsc --noEmit", + "preview": "electron-vite preview" + }, + "dependencies": { + "@hackclub/lapse-api": "workspace:*", + "@hackclub/lapse-shared": "workspace:*", + "@orpc/client": "^1.13.6", + "@orpc/contract": "^1.13.5", + "@orpc/openapi-client": "^1.13.5", + "electron-updater": "^6.6.2", + "react": "19.2.3", + "react-dom": "19.2.3", + "jsonrepair": "^3.13.3", + "react-router": "^7.6.3", + "tus-js-client": "^4.3.1", + "zod": "^4.2.1" + }, + "devDependencies": { + "@types/node": "^25.0.3", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@tailwindcss/postcss": "^4.1.18", + "@vitejs/plugin-react": "^5.1.2", + "electron": "^35.7.5", + "electron-builder": "^26.0.12", + "electron-vite": "^5.0.0", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "typescript": "^5.9.3", + "vite": "^6.3.5" + } +} diff --git a/apps/desktop/src/main/auth/apiClient.ts b/apps/desktop/src/main/auth/apiClient.ts new file mode 100644 index 0000000..347af8e --- /dev/null +++ b/apps/desktop/src/main/auth/apiClient.ts @@ -0,0 +1,85 @@ +import * as fs from "node:fs"; +import type { JsonifiedClient } from "@orpc/openapi-client"; +import type { ContractRouterClient } from "@orpc/contract"; +import { createORPCClient } from "@orpc/client"; +import { OpenAPILink } from "@orpc/openapi-client/fetch"; +import { compositeRouterContract } from "@hackclub/lapse-api"; +import * as tus from "tus-js-client"; +import { authService } from "./oauthFlow"; + +const API_URL = process.env.LAPSE_API_URL ?? "https://api.lapse.hackclub.com"; + +const link = new OpenAPILink(compositeRouterContract, { + url: `${API_URL}/api`, + headers: () => { + const token = authService.getToken(); + + return { + Authorization: token ? `Bearer ${token}` : undefined + }; + } +}); + +/** + * The main oRPC API client for the Electron main process. Uses the same + * contracts as the web client (`compositeRouterContract`) so that all + * type-safe route helpers are available. + */ +export const api: JsonifiedClient> = createORPCClient(link); + +/** + * Convenience wrapper that exposes common API helpers. + */ +export const apiClient = { + /** + * Fetches the currently authenticated user, or `null` if not signed in + * or the request fails. + */ + async getUser() { + const result = await api.user.myself({}); + if ("data" in result && result.data) { + return (result.data as { user: { id: string; handle: string; displayName: string; profilePictureUrl: string | null } | null }).user; + } + return null; + } +}; + +/** + * Handles resumable multipart uploads via `tus`, consuming a single-use + * upload `token`. Reads a file from disk using `fs.createReadStream` so + * that large recordings do not need to be buffered entirely in memory. + */ +export async function apiUpload( + token: string, + filePath: string, + onProgress?: (uploaded: number, total: number) => void +): Promise { + const stat = fs.statSync(filePath); + const stream = fs.createReadStream(filePath); + + return new Promise((resolve, reject) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const upload = new tus.Upload(stream as any, { + endpoint: `${API_URL}/upload`, + retryDelays: [0, 3000, 5000, 10000, 20000], + uploadSize: stat.size, + headers: { + authorization: `Bearer ${token}` + }, + onProgress: onProgress + ? (bytesUploaded: number, bytesTotal: number) => { + onProgress(bytesUploaded, bytesTotal); + } + : undefined, + onSuccess() { + resolve(); + }, + onError(error) { + console.error("(apiClient.ts) upload failed!", error); + reject(error); + } + }); + + upload.start(); + }); +} diff --git a/apps/desktop/src/main/auth/oauthFlow.ts b/apps/desktop/src/main/auth/oauthFlow.ts new file mode 100644 index 0000000..b38de9b --- /dev/null +++ b/apps/desktop/src/main/auth/oauthFlow.ts @@ -0,0 +1,195 @@ +import * as crypto from "node:crypto"; +import { app, shell } from "electron"; +import { saveToken, loadToken, clearToken } from "./tokenStore"; + +const API_URL = process.env.LAPSE_API_URL ?? "https://api.lapse.hackclub.com"; +const CLIENT_ID = process.env.LAPSE_OAUTH_CLIENT_ID ?? "lapse-desktop"; +const REDIRECT_URI = "lapse://auth/callback"; + +/** + * Generates a cryptographically random string of the given byte length, + * returned as a hex-encoded string. + */ +function randomString(bytes: number): string { + return crypto.randomBytes(bytes).toString("hex"); +} + +/** + * Derives a PKCE `code_challenge` from the given `code_verifier` using + * SHA-256 + base64url encoding (S256 method). + */ +function generateCodeChallenge(verifier: string): string { + const hash = crypto.createHash("sha256").update(verifier).digest(); + return hash + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); +} + +interface PendingAuth { + state: string; + codeVerifier: string; + resolve: (token: string) => void; + reject: (error: Error) => void; +} + +class OAuthService { + private token: string | null = null; + private pending: PendingAuth | null = null; + private initialized = false; + + /** + * Loads a previously persisted token from disk. Must be called once during + * app startup (after `app.whenReady()`). + */ + async initialize(): Promise { + if (this.initialized) return; + this.token = await loadToken(); + this.initialized = true; + } + + /** + * Opens the system browser to begin the OAuth2 PKCE authorization flow. + * Resolves with the access token once the callback is received and the + * authorization code has been exchanged. + */ + login(): Promise { + return new Promise((resolve, reject) => { + const state = randomString(16); + const codeVerifier = randomString(32); + const codeChallenge = generateCodeChallenge(codeVerifier); + + this.pending = { state, codeVerifier, resolve, reject }; + + const url = new URL(`${API_URL}/api/auth/authorize`); + url.searchParams.set("response_type", "code"); + url.searchParams.set("client_id", CLIENT_ID); + url.searchParams.set("redirect_uri", REDIRECT_URI); + url.searchParams.set("scope", "elevated"); + url.searchParams.set("state", state); + url.searchParams.set("code_challenge", codeChallenge); + url.searchParams.set("code_challenge_method", "S256"); + + shell.openExternal(url.href).catch(err => { + this.pending = null; + reject(err); + }); + }); + } + + /** + * Handles an incoming `lapse://` callback URL. This is called from the + * main process when either the `open-url` event fires (macOS) or from + * `second-instance` argv parsing (Windows/Linux). + */ + async handleCallback(callbackUrl: string): Promise { + if (!this.pending) { + console.warn("(oauthFlow.ts) received callback but no auth flow is pending"); + return; + } + + const { state, codeVerifier, resolve, reject } = this.pending; + this.pending = null; + + let parsed: URL; + try { + parsed = new URL(callbackUrl); + } catch { + reject(new Error("Invalid callback URL")); + return; + } + + const error = parsed.searchParams.get("error"); + if (error) { + reject(new Error(`OAuth error: ${error}`)); + return; + } + + const code = parsed.searchParams.get("code"); + const returnedState = parsed.searchParams.get("state"); + + if (!code) { + reject(new Error("No authorization code in callback")); + return; + } + + if (returnedState !== state) { + reject(new Error("State mismatch -- possible CSRF")); + return; + } + + try { + const token = await this.exchangeCode(code, codeVerifier); + this.token = token; + await saveToken(token); + resolve(token); + } catch (err) { + reject(err instanceof Error ? err : new Error(String(err))); + } + } + + /** + * Exchanges an authorization code for an access token via the token + * endpoint. + */ + private async exchangeCode(code: string, codeVerifier: string): Promise { + const response = await fetch(`${API_URL}/api/auth/token`, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "Accept": "application/json" + }, + body: new URLSearchParams({ + grant_type: "authorization_code", + code, + redirect_uri: REDIRECT_URI, + client_id: CLIENT_ID, + code_verifier: codeVerifier + }) + }); + + if (!response.ok) { + const body = await response.text(); + throw new Error(`Token exchange failed (${response.status}): ${body}`); + } + + const data = (await response.json()) as { access_token: string }; + return data.access_token; + } + + /** + * Returns the current access token, or `null` if not authenticated. + */ + getToken(): string | null { + return this.token; + } + + /** + * Returns `true` if the user currently has a stored access token. + */ + isAuthenticated(): boolean { + return this.token !== null; + } + + /** + * Manually set a token (for dev/testing — paste a token from the web client). + */ + async setToken(token: string): Promise { + this.token = token; + await saveToken(token); + } + + /** + * Clears the current token from memory and disk. + */ + async logout(): Promise { + this.token = null; + await clearToken(); + } +} + +/** + * Singleton OAuth service for the desktop app. + */ +export const authService = new OAuthService(); diff --git a/apps/desktop/src/main/auth/tokenStore.ts b/apps/desktop/src/main/auth/tokenStore.ts new file mode 100644 index 0000000..a628c27 --- /dev/null +++ b/apps/desktop/src/main/auth/tokenStore.ts @@ -0,0 +1,76 @@ +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { app, safeStorage } from "electron"; + +/** + * Returns the path to the encrypted auth token file. + */ +function getTokenPath(): string { + return path.join(app.getPath("userData"), "lapse", "auth.enc"); +} + +/** + * Persists an OAuth access token to disk. + * + * When Electron's `safeStorage` is available (Keychain on macOS, DPAPI on + * Windows, libsecret on Linux), the token is encrypted before writing. If + * `safeStorage` is not available, the token is stored as plain text so that + * the app still works on minimal environments. + */ +export async function saveToken(token: string): Promise { + const tokenPath = getTokenPath(); + await fs.mkdir(path.dirname(tokenPath), { recursive: true }); + + if (safeStorage.isEncryptionAvailable()) { + const encrypted = safeStorage.encryptString(token); + await fs.writeFile(tokenPath, encrypted); + } else { + console.warn("(tokenStore.ts) safeStorage unavailable -- storing token as plain text"); + await fs.writeFile(tokenPath, token, "utf-8"); + } +} + +/** + * Loads a previously persisted OAuth access token from disk. + * Returns `null` if no token has been saved or the file cannot be read. + */ +export async function loadToken(): Promise { + const tokenPath = getTokenPath(); + + let data: Buffer; + try { + data = await fs.readFile(tokenPath); + } catch { + return null; + } + + if (data.length === 0) { + return null; + } + + if (safeStorage.isEncryptionAvailable()) { + try { + return safeStorage.decryptString(data); + } catch { + // The file may have been written as plain text on a previous run where + // safeStorage was unavailable. Fall through to return as UTF-8. + console.warn("(tokenStore.ts) decryption failed -- attempting plain text read"); + return data.toString("utf-8"); + } + } + + return data.toString("utf-8"); +} + +/** + * Removes the persisted token from disk. + */ +export async function clearToken(): Promise { + const tokenPath = getTokenPath(); + + try { + await fs.unlink(tokenPath); + } catch { + // File may not exist -- that is fine. + } +} diff --git a/apps/desktop/src/main/capture/captureManager.ts b/apps/desktop/src/main/capture/captureManager.ts new file mode 100644 index 0000000..27152bc --- /dev/null +++ b/apps/desktop/src/main/capture/captureManager.ts @@ -0,0 +1,282 @@ +import { app, BrowserWindow, desktopCapturer, ipcMain } from "electron"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import type { CaptureStatus, DesktopSource } from "@/shared/ipc-channels"; +import { storageService } from "../storage/deviceStorage"; + +/** + * Manages the screen/webcam capture lifecycle for the desktop app. + * + * Uses a hidden BrowserWindow to run MediaRecorder (which requires a DOM + * context). The hidden window receives chunks via IPC and this manager + * persists them to disk through the storage service. + */ +class CaptureManager { + private status: CaptureStatus = { state: "idle" }; + private captureWindow: BrowserWindow | null = null; + private sessionId: number | null = null; + private snapshotCount = 0; + private sessionCount = 0; + + /** + * Enumerates available screens and windows via Electron's desktopCapturer. + */ + async getSources(): Promise { + const sources = await desktopCapturer.getSources({ + types: ["screen", "window"], + thumbnailSize: { width: 320, height: 180 } + }); + + return sources.map(source => ({ + id: source.id, + name: source.name, + thumbnailDataUrl: source.thumbnail.toDataURL() + })); + } + + /** + * Returns the current capture status. + */ + getStatus(): CaptureStatus { + return this.status; + } + + /** + * Starts a new capture session targeting the given source. + */ + async startCapture(sourceId: string): Promise { + if (this.status.state !== "idle") { + throw new Error(`Cannot start capture: current state is "${this.status.state}"`); + } + + // Resolve source name + const sources = await desktopCapturer.getSources({ types: ["screen", "window"] }); + const source = sources.find(s => s.id === sourceId); + const sourceName = source?.name ?? "Unknown"; + + // Ensure a timelapse exists in storage + const existing = await storageService.getTimelapse(); + if (!existing) { + await storageService.createTimelapse(); + } + + this.sessionId = Date.now(); + this.snapshotCount = 0; + this.sessionCount = (existing?.sessions.length ?? 0) + 1; + + // Register IPC listeners for chunks and snapshots from the capture window + this.registerCaptureIpc(); + + // Create the hidden capture window + const preloadPath = join(__dirname, "../preload/capturePreload.mjs"); + const captureScriptPath = join(__dirname, "captureWindow.js"); + + this.captureWindow = new BrowserWindow({ + show: false, + width: 1, + height: 1, + webPreferences: { + preload: preloadPath, + contextIsolation: true, + nodeIntegration: false, + sandbox: false // needed for desktopCapturer constraints + } + }); + + // Load a minimal inline HTML page. The actual capture logic is loaded as + // an external script from the built captureWindow.js bundle, with the + // preload providing the IPC bridge. + const captureScriptUrl = `file://${captureScriptPath.replace(/\\/g, "/")}`; + const html = [ + "", + "", + ``, + "", + ``, + "" + ].join(""); + + // loadURL with a data URI does not allow file:// script src due to CSP. + // Instead we write a temporary HTML file and load that. + const tmpHtmlPath = join(app.getPath("temp"), "lapse-capture.html"); + await writeFile(tmpHtmlPath, html, "utf-8"); + this.captureWindow.loadFile(tmpHtmlPath); + + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error("Capture window failed to load within 10 seconds")); + }, 10000); + + this.captureWindow!.webContents.once("did-finish-load", () => { + clearTimeout(timeout); + // Send the sourceId to the capture window so it can start recording + this.captureWindow!.webContents.send("capture:init", sourceId); + resolve(); + }); + }); + + this.setStatus({ + state: "recording", + sourceId, + sourceName, + startedAt: Date.now(), + sessionCount: this.sessionCount, + snapshotCount: this.snapshotCount + }); + } + + /** + * Stops the current capture session and tears down the hidden window. + */ + async stopCapture(): Promise { + if (this.status.state === "idle") { + return; + } + + if (this.captureWindow && !this.captureWindow.isDestroyed()) { + // Ask the capture window to stop gracefully + this.captureWindow.webContents.send("capture:stop-recording"); + + // Wait briefly for final chunks + await new Promise(resolve => { + const onStopped = () => { + ipcMain.removeListener("capture:stopped", onStopped); + resolve(); + }; + + ipcMain.once("capture:stopped", onStopped); + + // Fallback timeout -- don't wait forever + setTimeout(() => { + ipcMain.removeListener("capture:stopped", onStopped); + resolve(); + }, 3000); + }); + + this.captureWindow.destroy(); + this.captureWindow = null; + } + + this.unregisterCaptureIpc(); + this.sessionId = null; + this.setStatus({ state: "idle" }); + } + + /** + * Pauses the current capture session. + */ + async pauseCapture(): Promise { + if (this.status.state !== "recording") { + throw new Error(`Cannot pause: current state is "${this.status.state}"`); + } + + if (this.captureWindow && !this.captureWindow.isDestroyed()) { + this.captureWindow.webContents.send("capture:pause-recording"); + } + + this.setStatus({ + state: "paused", + sourceId: this.status.sourceId, + sourceName: this.status.sourceName, + startedAt: this.status.startedAt, + sessionCount: this.status.sessionCount, + snapshotCount: this.status.snapshotCount + }); + } + + /** + * Resumes a paused capture session. + */ + async resumeCapture(): Promise { + if (this.status.state !== "paused") { + throw new Error(`Cannot resume: current state is "${this.status.state}"`); + } + + if (this.captureWindow && !this.captureWindow.isDestroyed()) { + this.captureWindow.webContents.send("capture:resume-recording"); + } + + this.setStatus({ + state: "recording", + sourceId: this.status.sourceId, + sourceName: this.status.sourceName, + startedAt: this.status.startedAt, + sessionCount: this.status.sessionCount, + snapshotCount: this.status.snapshotCount + }); + } + + // --------------------------------------------------------------------------- + // Internal helpers + // --------------------------------------------------------------------------- + + private chunkHandler = async (_event: Electron.IpcMainEvent, buffer: Buffer) => { + if (this.sessionId == null) return; + + try { + await storageService.appendChunk(this.sessionId, Buffer.from(buffer)); + } catch (err) { + console.error("(captureManager.ts) failed to write chunk:", err); + } + }; + + private snapshotHandler = (_event: Electron.IpcMainEvent, timestamp: number) => { + this.snapshotCount++; + + // Persist the snapshot timestamp + storageService.appendSnapshot(timestamp).catch(err => { + console.error("(captureManager.ts) failed to write snapshot:", err); + }); + + // Update status with new snapshot count and broadcast + if (this.status.state === "recording" || this.status.state === "paused") { + this.setStatus({ + ...this.status, + snapshotCount: this.snapshotCount + }); + } + + // Notify renderers about the individual snapshot + this.broadcastEvent("capture:snapshot", { timestamp }); + }; + + private stoppedHandler = () => { + // Handled by the stopCapture await + }; + + private registerCaptureIpc() { + ipcMain.on("capture:chunk", this.chunkHandler); + ipcMain.on("capture:snapshot-taken", this.snapshotHandler); + ipcMain.on("capture:stopped", this.stoppedHandler); + } + + private unregisterCaptureIpc() { + ipcMain.removeListener("capture:chunk", this.chunkHandler); + ipcMain.removeListener("capture:snapshot-taken", this.snapshotHandler); + ipcMain.removeListener("capture:stopped", this.stoppedHandler); + } + + /** + * Updates the internal status and broadcasts the change to all renderer + * windows. + */ + private setStatus(newStatus: CaptureStatus) { + this.status = newStatus; + this.broadcastEvent("capture:status-changed", newStatus); + } + + /** + * Sends an event to every open BrowserWindow. + */ + private broadcastEvent(channel: string, data: T) { + for (const win of BrowserWindow.getAllWindows()) { + // Don't send to the hidden capture window + if (win === this.captureWindow) continue; + if (win.isDestroyed()) continue; + + win.webContents.send(channel, data); + } + } +} + +export const captureManager = new CaptureManager(); diff --git a/apps/desktop/src/main/capture/capturePreload.ts b/apps/desktop/src/main/capture/capturePreload.ts new file mode 100644 index 0000000..ca1e201 --- /dev/null +++ b/apps/desktop/src/main/capture/capturePreload.ts @@ -0,0 +1,56 @@ +import { contextBridge, ipcRenderer } from "electron"; + +/** + * Minimal preload script for the hidden capture BrowserWindow. + * + * Exposes only the IPC channels needed for capture -- the capture window + * does NOT get the full `window.lapse` API. + */ +const captureApi = { + /** Send a recorded video chunk (as an ArrayBuffer) to the main process. */ + sendChunk(buffer: ArrayBuffer): void { + ipcRenderer.send("capture:chunk", Buffer.from(buffer)); + }, + + /** Notify the main process that a snapshot (frame) was captured. */ + sendSnapshot(timestamp: number): void { + ipcRenderer.send("capture:snapshot-taken", timestamp); + }, + + /** Notify the main process that the MediaRecorder has fully stopped. */ + sendStopped(): void { + ipcRenderer.send("capture:stopped"); + }, + + /** Listen for the initial sourceId from the main process. */ + onInit(callback: (sourceId: string) => void): void { + ipcRenderer.on("capture:init", (_event, sourceId: string) => { + callback(sourceId); + }); + }, + + /** Listen for a stop command from the main process. */ + onStop(callback: () => void): void { + ipcRenderer.on("capture:stop-recording", () => { + callback(); + }); + }, + + /** Listen for a pause command from the main process. */ + onPause(callback: () => void): void { + ipcRenderer.on("capture:pause-recording", () => { + callback(); + }); + }, + + /** Listen for a resume command from the main process. */ + onResume(callback: () => void): void { + ipcRenderer.on("capture:resume-recording", () => { + callback(); + }); + } +}; + +contextBridge.exposeInMainWorld("captureApi", captureApi); + +export type CaptureApi = typeof captureApi; diff --git a/apps/desktop/src/main/capture/captureWindow.html b/apps/desktop/src/main/capture/captureWindow.html new file mode 100644 index 0000000..b61bf42 --- /dev/null +++ b/apps/desktop/src/main/capture/captureWindow.html @@ -0,0 +1,21 @@ + + + + + + Lapse Capture + + + + + + + diff --git a/apps/desktop/src/main/capture/captureWindow.ts b/apps/desktop/src/main/capture/captureWindow.ts new file mode 100644 index 0000000..cc0cfc0 --- /dev/null +++ b/apps/desktop/src/main/capture/captureWindow.ts @@ -0,0 +1,49 @@ +/** + * Script that runs inside the hidden capture BrowserWindow. + * + * This is NOT the React renderer -- it is a tiny page whose sole purpose + * is to host a MediaRecorder and send chunks/snapshots back to the main + * process through the `captureApi` preload bridge. + */ + +import { MediaSession } from "./mediaSession"; + +let session: MediaSession | null = null; + +// Main process sends the sourceId once the page has loaded +window.captureApi.onInit(async (sourceId: string) => { + console.log("(captureWindow.ts) received sourceId:", sourceId); + + try { + session = new MediaSession(); + await session.start(sourceId); + console.log("(captureWindow.ts) capture started"); + } catch (err) { + console.error("(captureWindow.ts) failed to start capture:", err); + } +}); + +// Main process asks us to stop +window.captureApi.onStop(async () => { + console.log("(captureWindow.ts) stop requested"); + + if (session) { + await session.stop(); + session = null; + } else { + // Nothing to stop -- still notify main + window.captureApi.sendStopped(); + } +}); + +// Main process asks us to pause +window.captureApi.onPause(() => { + console.log("(captureWindow.ts) pause requested"); + session?.pause(); +}); + +// Main process asks us to resume +window.captureApi.onResume(() => { + console.log("(captureWindow.ts) resume requested"); + session?.resume(); +}); diff --git a/apps/desktop/src/main/capture/mediaSession.ts b/apps/desktop/src/main/capture/mediaSession.ts new file mode 100644 index 0000000..a4d6d29 --- /dev/null +++ b/apps/desktop/src/main/capture/mediaSession.ts @@ -0,0 +1,240 @@ +/** + * MediaSession -- capture logic that runs inside the hidden BrowserWindow. + * + * This is a *renderer-side* module (runs in a DOM context). It mirrors the + * web client's `TimelapseVideoSession` but communicates with the main process + * via the `captureApi` bridge exposed by `capturePreload.ts`. + * + * Flow: + * 1. Main process sends `capture:init` with a sourceId. + * 2. This module acquires a desktop media stream using `getUserMedia` with + * Electron's `chromeMediaSource: "desktop"` constraint. + * 3. A canvas-based frame capture loop runs at CAPTURE_INTERVAL_MS. + * 4. MediaRecorder encodes the canvas stream to webm. + * 5. Chunks and snapshot timestamps are sent back to main via IPC. + */ + +import type { CaptureApi } from "./capturePreload"; + +declare global { + interface Window { + captureApi: CaptureApi; + } +} + +// Re-export so captureWindow.ts does not need its own declare global +export type { CaptureApi }; + +// Timelapse timing constants (matching web client and shared/types.ts) +const TIMELAPSE_FACTOR = 60; +const TIMELAPSE_FPS = 24; +const CAPTURE_INTERVAL_MS = (1000 * TIMELAPSE_FACTOR) / TIMELAPSE_FPS; // ~2500ms +const BITS_PER_PIXEL = 48; + +class MediaSession { + private recorder: MediaRecorder | null = null; + private canvas: HTMLCanvasElement; + private canvasCtx: CanvasRenderingContext2D; + private video: HTMLVideoElement; + private intervalId: ReturnType | null = null; + private isPaused = false; + private mediaStream: MediaStream | null = null; + private canvasTrack: { synced: true; stream: CanvasCaptureMediaStreamTrack } | { synced: false; stream: MediaStreamTrack }; + + constructor() { + this.canvas = document.createElement("canvas"); + this.canvasCtx = this.canvas.getContext("2d")!; + this.video = document.createElement("video"); + this.video.autoplay = true; + this.video.muted = true; + + // Default -- will be overwritten once we have a stream + this.canvasTrack = { synced: false, stream: null as unknown as MediaStreamTrack }; + } + + /** + * Acquires the desktop stream for `sourceId` and starts the + * MediaRecorder + frame capture loop. + */ + async start(sourceId: string): Promise { + // Acquire screen stream using Electron's chromeMediaSource constraint + this.mediaStream = await navigator.mediaDevices.getUserMedia({ + audio: false, + video: { + // @ts-expect-error -- Electron-specific constraint + mandatory: { + chromeMediaSource: "desktop", + chromeMediaSourceId: sourceId + } + } + }); + + this.video.srcObject = this.mediaStream; + + // Wait for the video to be ready so we can read dimensions + await new Promise(resolve => { + this.video.onloadedmetadata = () => resolve(); + }); + + const w = this.video.videoWidth || 1920; + const h = this.video.videoHeight || 1080; + + // Set up the canvas capture stream + let canvasStream: MediaStream; + + if ("CanvasCaptureMediaStreamTrack" in window) { + canvasStream = this.canvas.captureStream(0); + this.canvasTrack = { + synced: true, + stream: canvasStream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack + }; + } else { + canvasStream = this.canvas.captureStream(TIMELAPSE_FPS / TIMELAPSE_FACTOR); + this.canvasTrack = { + synced: false, + stream: canvasStream.getVideoTracks()[0]! + }; + } + + // Create the MediaRecorder on the canvas stream + const bitrate = w * h * BITS_PER_PIXEL; + console.log(`(mediaSession.ts) bitrate=${bitrate} (${Math.floor(bitrate / 1000)}kbit/s)`); + + this.recorder = new MediaRecorder(canvasStream, { + videoBitsPerSecond: bitrate, + audioBitsPerSecond: 0 + }); + + this.recorder.ondataavailable = (ev: BlobEvent) => { + this.handleRecorderData(ev); + }; + + this.recorder.start(CAPTURE_INTERVAL_MS); + this.intervalId = this.setFrameInterval(); + } + + /** + * Stops recording and releases all resources. + * Returns a promise that resolves once the recorder has flushed. + */ + stop(): Promise { + return new Promise(resolve => { + if (this.intervalId != null) { + clearInterval(this.intervalId); + this.intervalId = null; + } + + if (!this.recorder || this.recorder.state === "inactive") { + this.cleanup(); + window.captureApi.sendStopped(); + resolve(); + return; + } + + this.recorder.onstop = () => { + this.cleanup(); + window.captureApi.sendStopped(); + resolve(); + }; + + this.recorder.stop(); + }); + } + + /** + * Pauses the frame capture loop and MediaRecorder. + */ + pause(): void { + this.isPaused = true; + + if (this.intervalId != null) { + clearInterval(this.intervalId); + this.intervalId = null; + } + + if (this.recorder && this.recorder.state === "recording") { + this.recorder.pause(); + } + } + + /** + * Resumes frame capture and MediaRecorder. + */ + resume(): void { + this.isPaused = false; + + if (this.recorder && this.recorder.state === "paused") { + this.recorder.resume(); + } + + this.intervalId = this.setFrameInterval(); + } + + // --------------------------------------------------------------------------- + // Internal + // --------------------------------------------------------------------------- + + private setFrameInterval(): ReturnType { + return setInterval(() => { + this.captureFrame(); + }, CAPTURE_INTERVAL_MS); + } + + private captureFrame(): void { + if (this.isPaused) return; + + const w = this.video.videoWidth; + const h = this.video.videoHeight; + if (w === 0 || h === 0) return; + + // Blit the live video onto the canvas + this.canvas.width = w; + this.canvas.height = h; + this.canvasCtx.drawImage(this.video, 0, 0); + + // If we have a synced canvas track, request a frame explicitly + if (this.canvasTrack.synced) { + this.canvasTrack.stream.requestFrame(); + } + + // Ensure the recorder is in a sane state + if (this.recorder) { + if (this.recorder.state === "inactive") { + console.warn("(mediaSession.ts) MediaRecorder became inactive -- restarting"); + this.recorder.start(CAPTURE_INTERVAL_MS); + } else if (this.recorder.state === "paused") { + console.warn("(mediaSession.ts) MediaRecorder became paused -- resuming"); + this.recorder.resume(); + } + + this.recorder.requestData(); + } + + // Notify main process about the snapshot + window.captureApi.sendSnapshot(Date.now()); + } + + private async handleRecorderData(ev: BlobEvent): Promise { + if (ev.data.size <= 0) { + console.warn("(mediaSession.ts) ignoring empty video chunk"); + return; + } + + const buffer = await ev.data.arrayBuffer(); + window.captureApi.sendChunk(buffer); + } + + private cleanup(): void { + if (this.mediaStream) { + for (const track of this.mediaStream.getTracks()) { + track.stop(); + } + this.mediaStream = null; + } + + this.video.srcObject = null; + } +} + +// Export for use by captureWindow.ts +export { MediaSession }; diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts new file mode 100644 index 0000000..0e95865 --- /dev/null +++ b/apps/desktop/src/main/index.ts @@ -0,0 +1,82 @@ +import { app, BrowserWindow, protocol } from "electron"; +import { join } from "node:path"; +import { createTray } from "./tray"; +import { createMainWindow } from "./windowManager"; +import { registerIpcHandlers } from "./ipc/index"; +import { initAutoUpdater } from "./updater"; +import { initStorageService } from "./storage/deviceStorage"; +import { authService } from "./auth/oauthFlow"; + +// Enforce single instance +const gotTheLock = app.requestSingleInstanceLock(); +if (!gotTheLock) { + app.quit(); +} + +let mainWindow: BrowserWindow | null = null; + +app.on("second-instance", (_event, argv) => { + // On Windows/Linux, the deep link URL is in argv + if (mainWindow) { + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.focus(); + } + + // Handle lapse:// protocol callback from argv + const url = argv.find(arg => arg.startsWith("lapse://")); + if (url) { + authService.handleCallback(url); + } +}); + +// Register lapse:// protocol for OAuth callbacks +if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient("lapse", process.execPath, [ + "--", process.argv[1]! + ]); + } +} else { + app.setAsDefaultProtocolClient("lapse"); +} + +// macOS: handle open-url event for deep links +app.on("open-url", (_event, url) => { + if (url.startsWith("lapse://")) { + authService.handleCallback(url); + } +}); + +app.whenReady().then(async () => { + // Initialize storage directory + await initStorageService(); + + // Load persisted auth token + await authService.initialize(); + + // Register IPC handlers before creating windows + registerIpcHandlers(); + + // Create main window + mainWindow = createMainWindow(); + + // Create system tray + createTray(mainWindow); + + // Initialize auto-updater + initAutoUpdater(mainWindow); + + app.on("activate", () => { + // macOS: re-create window when dock icon clicked and no windows open + if (BrowserWindow.getAllWindows().length === 0) { + mainWindow = createMainWindow(); + } + }); +}); + +app.on("window-all-closed", () => { + // On macOS, keep app running in tray when all windows closed + if (process.platform !== "darwin") { + // On Windows/Linux, keep running in tray too (don't quit) + } +}); diff --git a/apps/desktop/src/main/ipc/auth.ts b/apps/desktop/src/main/ipc/auth.ts new file mode 100644 index 0000000..03f440f --- /dev/null +++ b/apps/desktop/src/main/ipc/auth.ts @@ -0,0 +1,41 @@ +import { ipcMain } from "electron"; +import { authService } from "../auth/oauthFlow"; +import { apiClient } from "../auth/apiClient"; + +export function registerAuthHandlers() { + ipcMain.handle("auth:login", async () => { + try { + await authService.login(); + return { success: true }; + } catch { + return { success: false }; + } + }); + + ipcMain.handle("auth:logout", async () => { + return authService.logout(); + }); + + ipcMain.handle("auth:get-token", async () => { + return authService.getToken(); + }); + + ipcMain.handle("auth:set-token", async (_event, token: string) => { + try { + await authService.setToken(token); + return { success: true }; + } catch { + return { success: false }; + } + }); + + ipcMain.handle("auth:get-user", async () => { + const token = authService.getToken(); + if (!token) return null; + try { + return await apiClient.getUser(); + } catch { + return null; + } + }); +} diff --git a/apps/desktop/src/main/ipc/capture.ts b/apps/desktop/src/main/ipc/capture.ts new file mode 100644 index 0000000..ad85e59 --- /dev/null +++ b/apps/desktop/src/main/ipc/capture.ts @@ -0,0 +1,28 @@ +import { ipcMain } from "electron"; +import { captureManager } from "../capture/captureManager"; + +export function registerCaptureHandlers() { + ipcMain.handle("capture:get-sources", async () => { + return captureManager.getSources(); + }); + + ipcMain.handle("capture:start", async (_event, sourceId: string) => { + return captureManager.startCapture(sourceId); + }); + + ipcMain.handle("capture:stop", async () => { + return captureManager.stopCapture(); + }); + + ipcMain.handle("capture:pause", async () => { + return captureManager.pauseCapture(); + }); + + ipcMain.handle("capture:resume", async () => { + return captureManager.resumeCapture(); + }); + + ipcMain.handle("capture:status", async () => { + return captureManager.getStatus(); + }); +} diff --git a/apps/desktop/src/main/ipc/index.ts b/apps/desktop/src/main/ipc/index.ts new file mode 100644 index 0000000..0e27e1b --- /dev/null +++ b/apps/desktop/src/main/ipc/index.ts @@ -0,0 +1,13 @@ +import { registerCaptureHandlers } from "./capture"; +import { registerStorageHandlers } from "./storage"; +import { registerAuthHandlers } from "./auth"; +import { registerUploadHandlers } from "./upload"; +import { registerSystemHandlers } from "./system"; + +export function registerIpcHandlers() { + registerCaptureHandlers(); + registerStorageHandlers(); + registerAuthHandlers(); + registerUploadHandlers(); + registerSystemHandlers(); +} diff --git a/apps/desktop/src/main/ipc/storage.ts b/apps/desktop/src/main/ipc/storage.ts new file mode 100644 index 0000000..61d6cd5 --- /dev/null +++ b/apps/desktop/src/main/ipc/storage.ts @@ -0,0 +1,24 @@ +import { ipcMain } from "electron"; +import { storageService } from "../storage/deviceStorage"; + +export function registerStorageHandlers() { + ipcMain.handle("storage:get-timelapse", async () => { + return storageService.getTimelapse(); + }); + + ipcMain.handle("storage:get-devices", async () => { + return storageService.getAllDevices(); + }); + + ipcMain.handle("storage:save-device", async (_event, device) => { + return storageService.saveDevice(device); + }); + + ipcMain.handle("storage:get-video-size", async () => { + return storageService.getTimelapseVideoSize(); + }); + + ipcMain.handle("storage:delete-timelapse", async () => { + return storageService.deleteTimelapse(); + }); +} diff --git a/apps/desktop/src/main/ipc/system.ts b/apps/desktop/src/main/ipc/system.ts new file mode 100644 index 0000000..ca9debf --- /dev/null +++ b/apps/desktop/src/main/ipc/system.ts @@ -0,0 +1,33 @@ +import { ipcMain, app } from "electron"; +import { checkForUpdates } from "../updater"; +import { storageService } from "../storage/deviceStorage"; + +export function registerSystemHandlers() { + ipcMain.handle("app:get-version", () => { + return app.getVersion(); + }); + + ipcMain.handle("app:check-updates", async () => { + try { + const result = await checkForUpdates(); + return { + available: !!result?.updateInfo, + version: result?.updateInfo?.version + }; + } catch { + return { available: false }; + } + }); + + ipcMain.handle("app:open-dashboard", () => { + // Handled by renderer navigation — this is a no-op placeholder + }); + + ipcMain.handle("app:quit", () => { + app.quit(); + }); + + ipcMain.handle("app:get-storage-path", () => { + return storageService.getStoragePath(); + }); +} diff --git a/apps/desktop/src/main/ipc/upload.ts b/apps/desktop/src/main/ipc/upload.ts new file mode 100644 index 0000000..76b777b --- /dev/null +++ b/apps/desktop/src/main/ipc/upload.ts @@ -0,0 +1,15 @@ +import { ipcMain, BrowserWindow } from "electron"; +import { uploadPipeline } from "../storage/uploadPipeline"; + +export function registerUploadHandlers() { + ipcMain.handle("upload:start", async event => { + const win = BrowserWindow.fromWebContents(event.sender); + if (!win) throw new Error("No window found"); + + return uploadPipeline.startUpload(win); + }); + + ipcMain.handle("upload:cancel", async () => { + return uploadPipeline.cancelUpload(); + }); +} diff --git a/apps/desktop/src/main/storage/deviceStorage.ts b/apps/desktop/src/main/storage/deviceStorage.ts new file mode 100644 index 0000000..a6b495d --- /dev/null +++ b/apps/desktop/src/main/storage/deviceStorage.ts @@ -0,0 +1,431 @@ +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { app } from "electron"; +import { ascending } from "@hackclub/lapse-shared"; + +/** + * The metadata about a stored timelapse. This does *not* include the actual + * video -- invoke `storageService.getTimelapseVideoSessions` for that. + */ +export interface StoredTimelapse { + startedAt: number; + snapshots: number[]; + sessions: number[]; +} + +/** + * A device with an associated passkey. + */ +export interface LocalDevice { + id: string; + passkey: string; + legacyPasskey?: string; + thisDevice: boolean; +} + +/** + * Collection of locally stored devices. + */ +export type LocalDevices = LocalDevice[]; + +interface Store { + devices: LocalDevice[]; + timelapse: StoredTimelapse | null; +} + +class AsyncQueue { + private currentTask: Promise = Promise.resolve(); + + enqueue(task: () => Promise): Promise { + const promise = this.currentTask.then(() => task()); + this.currentTask = promise.then( + () => {}, + () => {} + ); + return promise; + } + + async synchronize(): Promise { + return this.enqueue(async () => {}); + } +} + +function getStorageDir(): string { + return path.join(app.getPath("userData"), "lapse"); +} + +function getStorePath(): string { + return path.join(getStorageDir(), "store.json"); +} + +function sessionFileName(sessionId: number): string { + return `session-${sessionId}.webm`; +} + +function sessionFilePath(sessionId: number): string { + return path.join(getStorageDir(), sessionFileName(sessionId)); +} + +async function fileExists(filePath: string): Promise { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +} + +/** + * Securely stores data on the local filesystem, mirroring the web client's + * OPFS-based DeviceStorage but using Node.js `fs.promises`. + */ +class DeviceStorage { + private initialized = false; + private serialQueue = new AsyncQueue(); + + private async ensureInit(): Promise { + if (this.initialized) return; + + const dir = getStorageDir(); + await fs.mkdir(dir, { recursive: true }); + + const storePath = getStorePath(); + if (!(await fileExists(storePath))) { + console.log("(deviceStorage.ts) no store found -- creating a new one"); + await this.writeStore({ devices: [], timelapse: null }); + } else { + console.log("(deviceStorage.ts) existing store found"); + } + + this.initialized = true; + } + + private async readStore(): Promise { + await this.ensureInit(); + + const storePath = getStorePath(); + const contents = await fs.readFile(storePath, "utf-8"); + + try { + const parsed = JSON.parse(contents) as Store; + return parsed; + } catch (err) { + console.error(`(deviceStorage.ts) JSON store corrupted -- rebuilding! ${contents}`, err); + + let restored: Record = {}; + try { + // Attempt to repair the JSON using jsonrepair if available + const { jsonrepair } = await import("jsonrepair"); + restored = JSON.parse(jsonrepair(contents)) as Record; + } catch (repairErr) { + console.warn("(deviceStorage.ts) could not restore JSON -- will rebuild completely", repairErr); + } + + function tryGet(keys: string[], defaultValue: T, coalesce: (x: unknown) => T): T { + let current: unknown = restored; + for (const key of keys) { + if ( + current == null || + (typeof current !== "object" && typeof current !== "function") || + !(key in (current as Record)) + ) { + return defaultValue; + } + current = (current as Record)[key]; + } + return coalesce(current); + } + + const devices = tryGet(["devices"], [] as LocalDevice[], x => { + if (!Array.isArray(x)) return []; + return x.filter((d: unknown) => { + if (d == null || typeof d !== "object") return false; + return "thisDevice" in d && (d as Record).thisDevice; + }) as LocalDevice[]; + }); + + const snapshots = tryGet(["timelapse", "snapshots"], [] as number[], x => { + if (Array.isArray(x)) return x.map(v => (typeof v === "number" ? v : Number(v))); + return []; + }); + + const startedAt = tryGet(["timelapse", "startedAt"], Date.now(), x => { + return typeof x === "number" ? x : Number(x); + }); + + // Rebuild sessions list from directory listing + const sessions: number[] = []; + const dir = getStorageDir(); + try { + const entries = await fs.readdir(dir); + for (const filename of entries) { + const match = /^session-([0-9]+)\.webm$/.exec(filename); + if (!match) { + console.log(`(deviceStorage.ts) ignoring file ${filename} for session restore`); + continue; + } + console.log(`(deviceStorage.ts) session found for restore: ${filename} (ID ${match[1]})`); + sessions.push(parseInt(match[1]!, 10)); + } + } catch (dirErr) { + console.warn("(deviceStorage.ts) could not list storage directory for session restore", dirErr); + } + + if (sessions.length === 0) { + console.warn("(deviceStorage.ts) no sessions found while restoring timelapse"); + const store: Store = { devices, timelapse: null }; + await this.writeStore(store); + return store; + } + + sessions.sort(ascending()); + + console.log("(deviceStorage.ts) existing timelapse successfully recovered from corrupted JSON!"); + console.log("(deviceStorage.ts) recovery payload:", devices, snapshots, startedAt, sessions); + + const data: Store = { + devices, + timelapse: { snapshots, startedAt, sessions } + }; + + await this.writeStore(data); + return data; + } + } + + private async writeStore(data: Store): Promise { + const storePath = getStorePath(); + await fs.writeFile(storePath, JSON.stringify(data), "utf-8"); + } + + private async operation(block: () => Promise): Promise { + return await this.serialQueue.enqueue(async () => { + return await block(); + }); + } + + /** + * Returns the storage directory path. + */ + getStoragePath(): string { + return getStorageDir(); + } + + /** + * Creates a new timelapse entry. + */ + async createTimelapse(): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + store.timelapse = { + startedAt: Date.now(), + snapshots: [], + sessions: [] + }; + + await this.writeStore(store); + console.log("(deviceStorage.ts) timelapse created:", store.timelapse); + return store.timelapse; + }); + } + + /** + * Gets the current timelapse metadata, or `null` if none exists. + */ + async getTimelapse(): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + return store.timelapse ?? null; + }); + } + + /** + * Deletes the current timelapse and all associated session files. + */ + async deleteTimelapse(): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + const sessions = store.timelapse?.sessions ?? []; + store.timelapse = null; + await this.writeStore(store); + + for (const sessionId of sessions) { + try { + await fs.unlink(sessionFilePath(sessionId)); + } catch (err) { + console.warn(`(deviceStorage.ts) could not delete session ${sessionId}`, err); + } + } + + console.log("(deviceStorage.ts) locally stored timelapse deleted"); + }); + } + + /** + * Appends a chunk of data to a session file. If the session is new, it is + * registered in the store's sessions array. + */ + async appendChunk(sessionId: number, data: Buffer): Promise { + console.debug(`(deviceStorage.ts) appendChunk(${sessionId}) -> ${data.length} bytes`); + + return await this.operation(async () => { + const store = await this.readStore(); + if (!store.timelapse) { + console.warn("(deviceStorage.ts) attempted to call appendChunk, but no timelapse is registered!"); + return; + } + + if (!store.timelapse.sessions.includes(sessionId)) { + store.timelapse.sessions.push(sessionId); + await this.writeStore(store); + } + + const filePath = sessionFilePath(sessionId); + await fs.appendFile(filePath, data); + }); + } + + /** + * Appends a snapshot timestamp to the current timelapse. + */ + async appendSnapshot(timestamp: number): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + if (!store.timelapse) return; + + store.timelapse.snapshots.push(timestamp); + await this.writeStore(store); + + console.debug("(deviceStorage.ts) appendSnapshot ->", timestamp); + }); + } + + /** + * Returns all session file paths and their contents as Buffers. + */ + async getTimelapseVideoSessions(): Promise<{ sessionId: number; filePath: string; data: Buffer }[]> { + return await this.operation(async () => { + const store = await this.readStore(); + if (!store.timelapse) return []; + + const results: { sessionId: number; filePath: string; data: Buffer }[] = []; + + for (const sessionId of store.timelapse.sessions) { + const fp = sessionFilePath(sessionId); + try { + const data = await fs.readFile(fp); + results.push({ sessionId, filePath: fp, data }); + } catch (err) { + console.warn(`(deviceStorage.ts) could not read session ${sessionId} -- skipping`, err); + } + } + + return results; + }); + } + + /** + * Returns the total size (in bytes) of all session files. + */ + async getTimelapseVideoSize(): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + if (!store.timelapse) return 0; + + let totalSize = 0; + + for (const sessionId of store.timelapse.sessions) { + try { + const stat = await fs.stat(sessionFilePath(sessionId)); + totalSize += stat.size; + } catch (err) { + console.warn(`(deviceStorage.ts) could not read session ${sessionId} size -- skipping`, err); + } + } + + return totalSize; + }); + } + + /** + * Saves or updates a device in the local store. + */ + async saveDevice(device: LocalDevice): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + const existingIndex = store.devices.findIndex(d => d.id === device.id); + + if (existingIndex >= 0) { + store.devices[existingIndex] = device; + } else { + store.devices.push(device); + } + + await this.writeStore(store); + console.log("(deviceStorage.ts) saveDevice ->", device); + return device.id; + }); + } + + /** + * Gets a device by ID, or `null` if not found. + */ + async getDevice(id: string): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + return store.devices.find(d => d.id === id) ?? null; + }); + } + + /** + * Gets all locally stored devices. + */ + async getAllDevices(): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + return store.devices; + }); + } + + /** + * Deletes a device by ID. + */ + async deleteDevice(id: string): Promise { + return await this.operation(async () => { + const store = await this.readStore(); + store.devices = store.devices.filter(d => d.id !== id); + await this.writeStore(store); + console.log(`(deviceStorage.ts) deleteDevice(${id})`); + }); + } + + /** + * Ensures all pending storage operations are complete. + */ + async sync(): Promise { + await this.serialQueue.synchronize(); + console.log("(deviceStorage.ts) device storage has been synchronized"); + } +} + +/** + * Singleton storage service instance. + */ +export const storageService = new DeviceStorage(); + +/** + * Initializes the storage service. Must be called once at app startup + * (before any storage operations). + */ +export async function initStorageService(): Promise { + const dir = getStorageDir(); + await fs.mkdir(dir, { recursive: true }); + + const storePath = getStorePath(); + if (!(await fileExists(storePath))) { + await fs.writeFile(storePath, JSON.stringify({ devices: [], timelapse: null }), "utf-8"); + console.log("(deviceStorage.ts) initialized new store at", storePath); + } else { + console.log("(deviceStorage.ts) store already exists at", storePath); + } +} diff --git a/apps/desktop/src/main/storage/encryption.ts b/apps/desktop/src/main/storage/encryption.ts new file mode 100644 index 0000000..fd7daf9 --- /dev/null +++ b/apps/desktop/src/main/storage/encryption.ts @@ -0,0 +1,61 @@ +import { + encryptData as sharedEncrypt, + decryptData as sharedDecrypt, + toHex, + fromHex +} from "@hackclub/lapse-shared"; + +export { toHex, fromHex }; + +/** + * Converts a Node.js `Buffer` (or `Uint8Array`) to an `ArrayBuffer`. + * If the input is already an `ArrayBuffer`, it is returned as-is. + */ +function toArrayBuffer(input: Buffer | ArrayBuffer | Uint8Array): ArrayBuffer { + if (input instanceof ArrayBuffer) { + return input; + } + + // Buffer and Uint8Array may share an underlying ArrayBuffer with an offset, + // so we need to slice to get a clean copy. + if (Buffer.isBuffer(input) || input instanceof Uint8Array) { + const buf = input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength); + return buf as ArrayBuffer; + } + + return input as ArrayBuffer; +} + +/** + * Encrypts `data` using AES-128-CBC via the given `key` and `iv`. + * Accepts `Buffer`, `Uint8Array`, or `ArrayBuffer` inputs (in addition to the + * `Blob | ArrayBuffer` accepted by the shared library). + */ +export async function encryptData( + key: ArrayBuffer | Buffer | Uint8Array, + iv: ArrayBuffer | Buffer | Uint8Array, + data: ArrayBuffer | Buffer | Uint8Array +): Promise { + return await sharedEncrypt( + toArrayBuffer(key), + toArrayBuffer(iv), + toArrayBuffer(data) + ); +} + +/** + * Decrypts `data` using AES-128-CBC via the given `key` and `iv`. + * Accepts `Buffer`, `Uint8Array`, or `ArrayBuffer` inputs (in addition to the + * `Blob | ArrayBuffer` accepted by the shared library). + */ +export async function decryptData( + key: ArrayBuffer | Buffer | Uint8Array, + iv: ArrayBuffer | Buffer | Uint8Array, + data: ArrayBuffer | Buffer | Uint8Array +): Promise { + return await sharedDecrypt( + toArrayBuffer(key), + toArrayBuffer(iv), + toArrayBuffer(data) + ); +} diff --git a/apps/desktop/src/main/storage/uploadPipeline.ts b/apps/desktop/src/main/storage/uploadPipeline.ts new file mode 100644 index 0000000..0991040 --- /dev/null +++ b/apps/desktop/src/main/storage/uploadPipeline.ts @@ -0,0 +1,242 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; +import { BrowserWindow } from "electron"; +import * as tus from "tus-js-client"; +import { MIN_SESSION_SIZE_BYTES, fromHex } from "@hackclub/lapse-shared"; + +import { storageService } from "./deviceStorage"; +import { encryptData } from "./encryption"; + +/** + * The retry delays used by tus-js-client, matching the web client. + */ +const TUS_RETRY_DELAYS = [0, 3000, 5000, 10000, 20000]; + +/** + * The base API URL for uploads. + */ +const API_URL = "https://api.lapse.hackclub.com"; + +interface ActiveUpload { + tusUpload: tus.Upload | null; + cancelled: boolean; +} + +/** + * Handles the encrypt-and-upload flow for publishing timelapses from the + * desktop app. Reads session files from local storage, encrypts each one, + * and uploads via tus-js-client to the server. + */ +class UploadPipeline { + private active: ActiveUpload | null = null; + + /** + * Starts the full upload pipeline: sync storage, read sessions, encrypt, + * upload, and send progress/completion/error events to the renderer. + */ + async startUpload(win: BrowserWindow): Promise<{ draftId: string }> { + if (this.active) { + throw new Error("An upload is already in progress"); + } + + this.active = { tusUpload: null, cancelled: false }; + + try { + // 1. Sync any pending writes + await storageService.sync(); + + // 2. Read all sessions and timelapse metadata + const sessions = (await storageService.getTimelapseVideoSessions()) + .filter(s => s.data.length > MIN_SESSION_SIZE_BYTES); + const timelapse = await storageService.getTimelapse(); + + if (!timelapse || sessions.length === 0) { + throw new Error("No local timelapse or no sessions have been captured"); + } + + // 3. Get the current device (the one marked as thisDevice) + const devices = await storageService.getAllDevices(); + const device = devices.find(d => d.thisDevice); + + if (!device) { + throw new Error("No device registered as thisDevice -- cannot encrypt"); + } + + // 4. Create a draft timelapse on the server + // We import the auth token getter dynamically to avoid circular deps + const token = await this.getAuthToken(); + if (!token) { + throw new Error("Not authenticated -- cannot upload"); + } + + const draftRes = await fetch(`${API_URL}/api/draft-timelapse/create`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + }, + body: JSON.stringify({ + snapshots: timelapse.snapshots, + thumbnailSize: 0, // desktop does not generate thumbnails yet + deviceId: device.id, + sessions: sessions.map(s => ({ fileSize: s.data.length + 8192 })) + }) + }); + + if (!draftRes.ok) { + const body = await draftRes.text(); + throw new Error(`Server returned ${draftRes.status}: ${body}`); + } + + const draft = (await draftRes.json()) as { + data: { + draftTimelapse: { id: string; iv: string }; + sessionUploadTokens: string[]; + thumbnailUploadToken: string; + }; + }; + + const draftId = draft.data.draftTimelapse.id; + const iv = fromHex(draft.data.draftTimelapse.iv); + const key = fromHex(device.passkey); + + // 5. Encrypt and upload each session + for (let i = 0; i < sessions.length; i++) { + if (this.active.cancelled) { + throw new Error("Upload cancelled"); + } + + const session = sessions[i]!; + const uploadToken = draft.data.sessionUploadTokens[i]!; + + // Encrypt + win.webContents.send("upload:progress", { + sessionId: String(session.sessionId), + bytesUploaded: 0, + bytesTotal: session.data.length + }); + + const encrypted = await encryptData( + key.buffer.slice(key.byteOffset, key.byteOffset + key.byteLength), + iv.buffer.slice(iv.byteOffset, iv.byteOffset + iv.byteLength), + session.data + ); + + const encryptedBuffer = Buffer.from(encrypted); + + // Upload via tus + if (this.active.cancelled) { + throw new Error("Upload cancelled"); + } + + await this.tusUpload( + win, + encryptedBuffer, + uploadToken, + String(session.sessionId) + ); + } + + // 6. Clean up local data + await storageService.deleteTimelapse(); + + // 7. Notify renderer of completion + win.webContents.send("upload:complete", { draftId }); + + this.active = null; + return { draftId }; + } catch (err) { + const message = err instanceof Error ? err.message : "An unknown error occurred during upload"; + + if (this.active && !this.active.cancelled) { + try { + win.webContents.send("upload:error", { message }); + } catch { + // Window may have been destroyed + } + } + + this.active = null; + throw err; + } + } + + /** + * Cancels any in-progress upload. + */ + cancelUpload(): void { + if (!this.active) return; + + this.active.cancelled = true; + if (this.active.tusUpload) { + this.active.tusUpload.abort(true); + } + } + + /** + * Performs a single tus upload of the given buffer. + */ + private tusUpload( + win: BrowserWindow, + data: Buffer, + token: string, + sessionId: string + ): Promise { + return new Promise((resolve, reject) => { + const upload = new tus.Upload( + new Blob([new Uint8Array(data)], { type: "video/webm" }), + { + endpoint: `${API_URL}/upload`, + retryDelays: TUS_RETRY_DELAYS, + headers: { + authorization: `Bearer ${token}` + }, + onProgress: (bytesUploaded: number, bytesTotal: number) => { + try { + win.webContents.send("upload:progress", { + sessionId, + bytesUploaded, + bytesTotal + }); + } catch { + // Window may have been destroyed + } + }, + onSuccess: () => { + resolve(); + }, + onError: (error: Error) => { + console.error(`(uploadPipeline.ts) tus upload failed for session ${sessionId}:`, error); + reject(error); + } + } + ); + + if (this.active) { + this.active.tusUpload = upload; + } + + upload.start(); + }); + } + + /** + * Retrieves the current auth token. This is a simple helper that uses + * ipcMain's handler indirectly -- we read it from the auth service. + */ + private async getAuthToken(): Promise { + try { + // Dynamic import to avoid circular dependency issues at module load time + const { authService } = await import("../auth/oauthFlow"); + return authService.getToken(); + } catch { + console.warn("(uploadPipeline.ts) could not import authService -- auth module may not exist yet"); + return null; + } + } +} + +/** + * Singleton upload pipeline instance. + */ +export const uploadPipeline = new UploadPipeline(); diff --git a/apps/desktop/src/main/tray.ts b/apps/desktop/src/main/tray.ts new file mode 100644 index 0000000..265a5c1 --- /dev/null +++ b/apps/desktop/src/main/tray.ts @@ -0,0 +1,115 @@ +import { Tray, Menu, nativeImage, BrowserWindow } from "electron"; +import { join } from "node:path"; +import type { CaptureStatus } from "@/shared/ipc-channels"; + +let tray: Tray | null = null; +let currentStatus: CaptureStatus = { state: "idle" }; + +export function createTray(mainWindow: BrowserWindow): Tray { + // Use a simple 16x16 icon — placeholder until real icons are provided + const iconPath = join(__dirname, "../../build/icon.ico"); + const icon = nativeImage.createEmpty(); + tray = new Tray(icon); + + tray.setToolTip("Lapse"); + updateTrayMenu(mainWindow); + + tray.on("click", () => { + if (mainWindow.isVisible()) { + mainWindow.focus(); + } else { + mainWindow.show(); + } + }); + + return tray; +} + +export function updateTrayStatus(status: CaptureStatus, mainWindow: BrowserWindow) { + currentStatus = status; + updateTrayMenu(mainWindow); + + if (tray) { + switch (status.state) { + case "recording": + tray.setToolTip(`Lapse — Recording (${status.sourceName})`); + break; + case "paused": + tray.setToolTip("Lapse — Paused"); + break; + default: + tray.setToolTip("Lapse"); + } + } +} + +function updateTrayMenu(mainWindow: BrowserWindow) { + const statusLabel = currentStatus.state === "recording" + ? `Recording: ${formatDuration(Date.now() - currentStatus.startedAt)}` + : currentStatus.state === "paused" + ? "Paused" + : "Idle"; + + const contextMenu = Menu.buildFromTemplate([ + { label: statusLabel, enabled: false }, + { type: "separator" }, + ...(currentStatus.state === "idle" ? [ + { + label: "Open Lapse", + click: () => { + mainWindow.show(); + mainWindow.focus(); + } + } + ] : []), + ...(currentStatus.state === "recording" ? [ + { + label: "Pause Recording", + click: () => mainWindow.webContents.send("tray:pause") + }, + { + label: "Stop Recording", + click: () => mainWindow.webContents.send("tray:stop") + } + ] : []), + ...(currentStatus.state === "paused" ? [ + { + label: "Resume Recording", + click: () => mainWindow.webContents.send("tray:resume") + }, + { + label: "Stop Recording", + click: () => mainWindow.webContents.send("tray:stop") + } + ] : []), + { type: "separator" as const }, + { + label: "Open Dashboard", + click: () => { + mainWindow.show(); + mainWindow.focus(); + } + }, + { type: "separator" as const }, + { + label: "Quit Lapse", + click: () => { + mainWindow.destroy(); + require("electron").app.quit(); + } + } + ]); + + tray?.setContextMenu(contextMenu); +} + +function formatDuration(ms: number): string { + const seconds = Math.floor(ms / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + if (hours > 0) { + return `${hours}h ${minutes % 60}m`; + } + return `${minutes}m ${seconds % 60}s`; +} diff --git a/apps/desktop/src/main/updater.ts b/apps/desktop/src/main/updater.ts new file mode 100644 index 0000000..7c4986a --- /dev/null +++ b/apps/desktop/src/main/updater.ts @@ -0,0 +1,52 @@ +import { autoUpdater } from "electron-updater"; +import { BrowserWindow, dialog, Notification } from "electron"; + +export function initAutoUpdater(mainWindow: BrowserWindow) { + autoUpdater.autoDownload = false; + autoUpdater.autoInstallOnAppQuit = true; + + autoUpdater.on("update-available", info => { + mainWindow.webContents.send("app:update-available", { + version: info.version + }); + + if (Notification.isSupported()) { + const notification = new Notification({ + title: "Lapse Update Available", + body: `Version ${info.version} is available. Click to download.` + }); + + notification.on("click", () => { + autoUpdater.downloadUpdate(); + }); + + notification.show(); + } + }); + + autoUpdater.on("update-downloaded", () => { + dialog.showMessageBox(mainWindow, { + type: "info", + title: "Update Ready", + message: "A new version of Lapse has been downloaded. Restart now to install?", + buttons: ["Restart", "Later"] + }).then(result => { + if (result.response === 0) { + autoUpdater.quitAndInstall(); + } + }); + }); + + autoUpdater.on("error", error => { + console.error("Auto-updater error:", error); + }); + + // Check for updates on startup (silently) + autoUpdater.checkForUpdates().catch(() => { + // Silently fail — user can manually check later + }); +} + +export function checkForUpdates() { + return autoUpdater.checkForUpdates(); +} diff --git a/apps/desktop/src/main/windowManager.ts b/apps/desktop/src/main/windowManager.ts new file mode 100644 index 0000000..dd0456e --- /dev/null +++ b/apps/desktop/src/main/windowManager.ts @@ -0,0 +1,39 @@ +import { BrowserWindow, shell } from "electron"; +import { join } from "node:path"; + +export function createMainWindow(): BrowserWindow { + const win = new BrowserWindow({ + width: 960, + height: 680, + minWidth: 640, + minHeight: 480, + title: "Lapse", + show: false, + webPreferences: { + preload: join(__dirname, "../preload/index.mjs"), + contextIsolation: true, + nodeIntegration: false, + sandbox: true + } + }); + + // Show window when ready to avoid flash + win.once("ready-to-show", () => { + win.show(); + }); + + // Open external links in system browser + win.webContents.setWindowOpenHandler(({ url }) => { + shell.openExternal(url); + return { action: "deny" }; + }); + + // Load the renderer + if (process.env.ELECTRON_RENDERER_URL) { + win.loadURL(process.env.ELECTRON_RENDERER_URL); + } else { + win.loadFile(join(__dirname, "../renderer/index.html")); + } + + return win; +} diff --git a/apps/desktop/src/preload/index.ts b/apps/desktop/src/preload/index.ts new file mode 100644 index 0000000..218baed --- /dev/null +++ b/apps/desktop/src/preload/index.ts @@ -0,0 +1,35 @@ +import { contextBridge, ipcRenderer } from "electron"; +import type { IpcChannelMap, IpcEventMap, IpcChannel, IpcEvent } from "@/shared/ipc-channels"; + +// Type-safe IPC bridge exposed to the renderer as window.lapse +const api = { + invoke: ( + channel: K, + ...args: IpcChannelMap[K]["args"] + ): Promise => { + return ipcRenderer.invoke(channel, ...args); + }, + + on: ( + channel: K, + callback: (data: IpcEventMap[K]) => void + ): (() => void) => { + const handler = (_event: Electron.IpcRendererEvent, data: IpcEventMap[K]) => callback(data); + ipcRenderer.on(channel, handler); + return () => { + ipcRenderer.removeListener(channel, handler); + }; + }, + + once: ( + channel: K, + callback: (data: IpcEventMap[K]) => void + ): void => { + ipcRenderer.once(channel, (_event, data) => callback(data)); + } +}; + +contextBridge.exposeInMainWorld("lapse", api); + +// Export the type for use in renderer +export type LapseApi = typeof api; diff --git a/apps/desktop/src/renderer/App.tsx b/apps/desktop/src/renderer/App.tsx new file mode 100644 index 0000000..01f1351 --- /dev/null +++ b/apps/desktop/src/renderer/App.tsx @@ -0,0 +1,48 @@ +import { HashRouter, Routes, Route, Navigate } from "react-router"; +import { AuthProvider, useAuth } from "./context/AuthContext"; +import { RecordingProvider } from "./context/RecordingContext"; +import { Login } from "./pages/Login"; +import { Dashboard } from "./pages/Dashboard"; +import { Recording } from "./pages/Recording"; +import { Upload } from "./pages/Upload"; +import { Settings } from "./pages/Settings"; + +function AppRoutes() { + const { user, isLoading } = useAuth(); + + if (isLoading) { + return ( +
+
Loading...
+
+ ); + } + + if (!user) { + return ; + } + + return ( + + } /> + } /> + } /> + } /> + } /> + + ); +} + +export function App() { + return ( + + + +
+ +
+
+
+
+ ); +} diff --git a/apps/desktop/src/renderer/components/RecordingControls.tsx b/apps/desktop/src/renderer/components/RecordingControls.tsx new file mode 100644 index 0000000..76fb4d7 --- /dev/null +++ b/apps/desktop/src/renderer/components/RecordingControls.tsx @@ -0,0 +1,77 @@ +import { useRecording } from "../context/RecordingContext"; +import { useRecordingState } from "../hooks/useRecordingState"; + +export function RecordingControls() { + const { stopRecording, pauseRecording, resumeRecording } = useRecording(); + const { isRecording, isPaused, formattedTime, snapshotCount, statusLabel, statusColor } = + useRecordingState(); + + const isActive = isRecording || isPaused; + + return ( +
+ {/* Status indicator */} +
+ {isRecording && ( + + + + + )} + {isPaused && } + {statusLabel} +
+ + {/* Timer */} + {isActive && ( +
+

{formattedTime}

+

+ {snapshotCount} snapshot{snapshotCount !== 1 ? "s" : ""} captured +

+
+ )} + + {/* Controls */} + {isActive && ( +
+ {isRecording ? ( + + ) : ( + + )} + + +
+ )} +
+ ); +} diff --git a/apps/desktop/src/renderer/components/SourcePicker.tsx b/apps/desktop/src/renderer/components/SourcePicker.tsx new file mode 100644 index 0000000..be1d63a --- /dev/null +++ b/apps/desktop/src/renderer/components/SourcePicker.tsx @@ -0,0 +1,114 @@ +import { useState, useEffect } from "react"; +import { lapse } from "../lib/desktop"; +import type { DesktopSource } from "@/shared/ipc-channels"; + +interface SourcePickerProps { + selectedId: string | null; + onSelect: (source: DesktopSource) => void; +} + +export function SourcePicker({ selectedId, onSelect }: SourcePickerProps) { + const [sources, setSources] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchSources = () => { + setIsLoading(true); + setError(null); + lapse + .invoke("capture:get-sources") + .then(result => { + setSources(result); + setIsLoading(false); + }) + .catch(err => { + setError(err instanceof Error ? err.message : String(err)); + setIsLoading(false); + }); + }; + + useEffect(() => { + fetchSources(); + }, []); + + if (isLoading) { + return ( +
+

Loading sources...

+
+ ); + } + + if (error) { + return ( +
+

Failed to load sources

+ +
+ ); + } + + if (sources.length === 0) { + return ( +
+

No capture sources found

+ +
+ ); + } + + return ( +
+
+

Select a source

+ +
+
+ {sources.map(source => { + const isSelected = source.id === selectedId; + return ( + + ); + })} +
+
+ ); +} diff --git a/apps/desktop/src/renderer/components/TimelapseCard.tsx b/apps/desktop/src/renderer/components/TimelapseCard.tsx new file mode 100644 index 0000000..625b7f1 --- /dev/null +++ b/apps/desktop/src/renderer/components/TimelapseCard.tsx @@ -0,0 +1,72 @@ +interface TimelapseCardProps { + title: string; + duration: number; + snapshotCount: number; + startedAt: number; + onClick?: () => void; +} + +function formatRelativeDate(timestamp: number): string { + const diff = Date.now() - timestamp; + const minutes = Math.floor(diff / 60000); + if (minutes < 1) return "Just now"; + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + if (days < 7) return `${days}d ago`; + return new Date(timestamp).toLocaleDateString(); +} + +function formatDurationShort(ms: number): string { + const totalSec = Math.floor(ms / 1000); + const h = Math.floor(totalSec / 3600); + const m = Math.floor((totalSec % 3600) / 60); + if (h > 0) return `${h}h ${m}m`; + return `${m}m`; +} + +export function TimelapseCard({ title, duration, snapshotCount, startedAt, onClick }: TimelapseCardProps) { + return ( + + ); +} diff --git a/apps/desktop/src/renderer/components/TitleBar.tsx b/apps/desktop/src/renderer/components/TitleBar.tsx new file mode 100644 index 0000000..c34def4 --- /dev/null +++ b/apps/desktop/src/renderer/components/TitleBar.tsx @@ -0,0 +1,67 @@ +import { useLocation, useNavigate } from "react-router"; +import { useAuth } from "../context/AuthContext"; + +const NAV_ITEMS = [ + { path: "/", label: "Dashboard" }, + { path: "/recording", label: "Recording" }, + { path: "/upload", label: "Upload" }, + { path: "/settings", label: "Settings" } +] as const; + +export function TitleBar() { + const location = useLocation(); + const navigate = useNavigate(); + const { user } = useAuth(); + + return ( +
+ + + {user && ( +
+ + {user.displayName ?? user.handle} + + {user.pictureUrl ? ( + {user.handle} + ) : ( +
+ {(user.displayName ?? user.handle).charAt(0).toUpperCase()} +
+ )} +
+ )} +
+ ); +} diff --git a/apps/desktop/src/renderer/components/UploadProgress.tsx b/apps/desktop/src/renderer/components/UploadProgress.tsx new file mode 100644 index 0000000..df83c73 --- /dev/null +++ b/apps/desktop/src/renderer/components/UploadProgress.tsx @@ -0,0 +1,80 @@ +import type { UploadProgress as UploadProgressData } from "@/shared/ipc-channels"; + +interface UploadProgressProps { + progress: Map; + totalProgress: number; + error: string | null; + onRetry: () => void; +} + +function formatBytes(bytes: number): string { + if (bytes === 0) return "0 B"; + const units = ["B", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return `${(bytes / 1024 ** i).toFixed(1)} ${units[i]}`; +} + +export function UploadProgress({ progress, totalProgress, error, onRetry }: UploadProgressProps) { + const sessions = Array.from(progress.entries()); + + return ( +
+ {/* Overall progress */} +
+
+ Overall progress + {totalProgress}% +
+
+
+
+
+ + {/* Per-session progress */} + {sessions.length > 0 && ( +
+ {sessions.map(([sessionId, data]) => { + const pct = data.bytesTotal > 0 + ? Math.round((data.bytesUploaded / data.bytesTotal) * 100) + : 0; + return ( +
+
+ + Session {sessionId.slice(0, 8)} + + + {formatBytes(data.bytesUploaded)} / {formatBytes(data.bytesTotal)} + +
+
+
+
+
+ ); + })} +
+ )} + + {/* Error state */} + {error && ( +
+

{error}

+ +
+ )} +
+ ); +} diff --git a/apps/desktop/src/renderer/context/AuthContext.tsx b/apps/desktop/src/renderer/context/AuthContext.tsx new file mode 100644 index 0000000..9230168 --- /dev/null +++ b/apps/desktop/src/renderer/context/AuthContext.tsx @@ -0,0 +1,59 @@ +import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react"; +import { lapse } from "../lib/desktop"; +import type { IpcChannelMap } from "@/shared/ipc-channels"; + +type User = NonNullable; + +interface AuthContextValue { + user: User | null; + isLoading: boolean; + login: () => Promise; + logout: () => Promise; +} + +const AuthContext = createContext(null); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + lapse + .invoke("auth:get-user") + .then(u => setUser(u)) + .catch(() => setUser(null)) + .finally(() => setIsLoading(false)); + }, []); + + const login = useCallback(async () => { + setIsLoading(true); + try { + const { success } = await lapse.invoke("auth:login"); + if (success) { + const u = await lapse.invoke("auth:get-user"); + setUser(u); + } + } finally { + setIsLoading(false); + } + }, []); + + const logout = useCallback(async () => { + await lapse.invoke("auth:logout"); + setUser(null); + }, []); + + return ( + + {children} + + ); +} + +export function useAuth(): AuthContextValue { + const ctx = useContext(AuthContext); + if (!ctx) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return ctx; +} diff --git a/apps/desktop/src/renderer/context/RecordingContext.tsx b/apps/desktop/src/renderer/context/RecordingContext.tsx new file mode 100644 index 0000000..25aec3c --- /dev/null +++ b/apps/desktop/src/renderer/context/RecordingContext.tsx @@ -0,0 +1,63 @@ +import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react"; +import { lapse } from "../lib/desktop"; +import type { CaptureStatus } from "@/shared/ipc-channels"; + +interface RecordingContextValue { + status: CaptureStatus; + startRecording: (sourceId: string) => Promise; + stopRecording: () => Promise; + pauseRecording: () => Promise; + resumeRecording: () => Promise; +} + +const RecordingContext = createContext(null); + +export function RecordingProvider({ children }: { children: ReactNode }) { + const [status, setStatus] = useState({ state: "idle" }); + + useEffect(() => { + lapse.invoke("capture:status").then(setStatus).catch(() => {}); + const unsub = lapse.on("capture:status-changed", setStatus); + return unsub; + }, []); + + const startRecording = useCallback(async (sourceId: string) => { + await lapse.invoke("capture:start", sourceId); + const s = await lapse.invoke("capture:status"); + setStatus(s); + }, []); + + const stopRecording = useCallback(async () => { + await lapse.invoke("capture:stop"); + const s = await lapse.invoke("capture:status"); + setStatus(s); + }, []); + + const pauseRecording = useCallback(async () => { + await lapse.invoke("capture:pause"); + const s = await lapse.invoke("capture:status"); + setStatus(s); + }, []); + + const resumeRecording = useCallback(async () => { + await lapse.invoke("capture:resume"); + const s = await lapse.invoke("capture:status"); + setStatus(s); + }, []); + + return ( + + {children} + + ); +} + +export function useRecording(): RecordingContextValue { + const ctx = useContext(RecordingContext); + if (!ctx) { + throw new Error("useRecording must be used within a RecordingProvider"); + } + return ctx; +} diff --git a/apps/desktop/src/renderer/hooks/useAuth.ts b/apps/desktop/src/renderer/hooks/useAuth.ts new file mode 100644 index 0000000..e686185 --- /dev/null +++ b/apps/desktop/src/renderer/hooks/useAuth.ts @@ -0,0 +1 @@ +export { useAuth } from "../context/AuthContext"; diff --git a/apps/desktop/src/renderer/hooks/useIpc.ts b/apps/desktop/src/renderer/hooks/useIpc.ts new file mode 100644 index 0000000..2a45cce --- /dev/null +++ b/apps/desktop/src/renderer/hooks/useIpc.ts @@ -0,0 +1,57 @@ +import { useState, useEffect, useCallback, useRef } from "react"; +import { lapse } from "../lib/desktop"; +import type { IpcChannelMap, IpcEventMap, IpcChannel, IpcEvent } from "@/shared/ipc-channels"; + +interface IpcQueryResult { + data: T | null; + isLoading: boolean; + error: Error | null; + refetch: () => void; +} + +export function useIpcQuery( + channel: K, + ...args: IpcChannelMap[K]["args"] +): IpcQueryResult { + type R = IpcChannelMap[K]["result"]; + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const argsRef = useRef(args); + argsRef.current = args; + + const fetch = useCallback(() => { + setIsLoading(true); + setError(null); + (lapse.invoke as (ch: string, ...a: unknown[]) => Promise)(channel, ...argsRef.current) + .then(result => { + setData(result); + setIsLoading(false); + }) + .catch((err: unknown) => { + setError(err instanceof Error ? err : new Error(String(err))); + setIsLoading(false); + }); + }, [channel]); + + useEffect(() => { + fetch(); + }, [fetch]); + + return { data, isLoading, error, refetch: fetch }; +} + +export function useIpcEvent( + event: K, + callback: (data: IpcEventMap[K]) => void +): void { + const callbackRef = useRef(callback); + callbackRef.current = callback; + + useEffect(() => { + const unsub = lapse.on(event, (data: IpcEventMap[K]) => { + callbackRef.current(data); + }); + return unsub; + }, [event]); +} diff --git a/apps/desktop/src/renderer/hooks/useRecordingState.ts b/apps/desktop/src/renderer/hooks/useRecordingState.ts new file mode 100644 index 0000000..7b94d10 --- /dev/null +++ b/apps/desktop/src/renderer/hooks/useRecordingState.ts @@ -0,0 +1,74 @@ +import { useState, useEffect } from "react"; +import { useRecording } from "../context/RecordingContext"; + +interface RecordingState { + isRecording: boolean; + isPaused: boolean; + isIdle: boolean; + elapsedSeconds: number; + formattedTime: string; + snapshotCount: number; + sourceName: string | null; + statusLabel: string; + statusColor: string; +} + +function formatDuration(seconds: number): string { + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + const s = seconds % 60; + const pad = (n: number) => String(n).padStart(2, "0"); + return h > 0 ? `${pad(h)}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`; +} + +export function useRecordingState(): RecordingState { + const { status } = useRecording(); + const [elapsedSeconds, setElapsedSeconds] = useState(0); + + const isRecording = status.state === "recording"; + const isPaused = status.state === "paused"; + const isIdle = status.state === "idle"; + const startedAt = status.state !== "idle" ? status.startedAt : 0; + + useEffect(() => { + if (!isRecording) { + if (isIdle) setElapsedSeconds(0); + return; + } + + const tick = () => { + setElapsedSeconds(Math.floor((Date.now() - startedAt) / 1000)); + }; + tick(); + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, [isRecording, isIdle, startedAt]); + + const snapshotCount = status.state !== "idle" ? status.snapshotCount : 0; + const sourceName = status.state !== "idle" ? status.sourceName : null; + + let statusLabel: string; + let statusColor: string; + if (isRecording) { + statusLabel = "Recording"; + statusColor = "text-red-500"; + } else if (isPaused) { + statusLabel = "Paused"; + statusColor = "text-amber-500"; + } else { + statusLabel = "Idle"; + statusColor = "text-neutral-400"; + } + + return { + isRecording, + isPaused, + isIdle, + elapsedSeconds, + formattedTime: formatDuration(elapsedSeconds), + snapshotCount, + sourceName, + statusLabel, + statusColor + }; +} diff --git a/apps/desktop/src/renderer/hooks/useUploadState.ts b/apps/desktop/src/renderer/hooks/useUploadState.ts new file mode 100644 index 0000000..34b0a58 --- /dev/null +++ b/apps/desktop/src/renderer/hooks/useUploadState.ts @@ -0,0 +1,83 @@ +import { useState, useCallback } from "react"; +import { lapse } from "../lib/desktop"; +import { useIpcEvent } from "./useIpc"; +import type { UploadProgress } from "@/shared/ipc-channels"; + +interface UploadState { + isUploading: boolean; + progress: Map; + totalProgress: number; + error: string | null; + draftId: string | null; + isComplete: boolean; + startUpload: () => Promise; + cancelUpload: () => Promise; +} + +export function useUploadState(): UploadState { + const [isUploading, setIsUploading] = useState(false); + const [progress, setProgress] = useState>(new Map()); + const [error, setError] = useState(null); + const [draftId, setDraftId] = useState(null); + const [isComplete, setIsComplete] = useState(false); + + useIpcEvent("upload:progress", data => { + setProgress(prev => { + const next = new Map(prev); + next.set(data.sessionId, data); + return next; + }); + }); + + useIpcEvent("upload:complete", data => { + setIsUploading(false); + setIsComplete(true); + setDraftId(data.draftId); + }); + + useIpcEvent("upload:error", data => { + setIsUploading(false); + setError(data.message); + }); + + const startUpload = useCallback(async () => { + setIsUploading(true); + setError(null); + setIsComplete(false); + setProgress(new Map()); + try { + const result = await lapse.invoke("upload:start"); + setDraftId(result.draftId); + } catch (err) { + setIsUploading(false); + setError(err instanceof Error ? err.message : String(err)); + } + }, []); + + const cancelUpload = useCallback(async () => { + await lapse.invoke("upload:cancel"); + setIsUploading(false); + }, []); + + let totalProgress = 0; + if (progress.size > 0) { + let totalBytes = 0; + let uploadedBytes = 0; + for (const p of progress.values()) { + totalBytes += p.bytesTotal; + uploadedBytes += p.bytesUploaded; + } + totalProgress = totalBytes > 0 ? Math.round((uploadedBytes / totalBytes) * 100) : 0; + } + + return { + isUploading, + progress, + totalProgress, + error, + draftId, + isComplete, + startUpload, + cancelUpload + }; +} diff --git a/apps/desktop/src/renderer/index.html b/apps/desktop/src/renderer/index.html new file mode 100644 index 0000000..40cb7cb --- /dev/null +++ b/apps/desktop/src/renderer/index.html @@ -0,0 +1,16 @@ + + + + + + + Lapse + + +
+ + + diff --git a/apps/desktop/src/renderer/lib/desktop.ts b/apps/desktop/src/renderer/lib/desktop.ts new file mode 100644 index 0000000..f4c3a4f --- /dev/null +++ b/apps/desktop/src/renderer/lib/desktop.ts @@ -0,0 +1,10 @@ +import type { LapseApi } from "@/preload/index"; + +// Typed access to the IPC bridge exposed by the preload script +declare global { + interface Window { + lapse: LapseApi; + } +} + +export const lapse = window.lapse; diff --git a/apps/desktop/src/renderer/main.tsx b/apps/desktop/src/renderer/main.tsx new file mode 100644 index 0000000..d03e884 --- /dev/null +++ b/apps/desktop/src/renderer/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App"; +import "./styles/globals.css"; + +createRoot(document.getElementById("root")!).render( + + + +); diff --git a/apps/desktop/src/renderer/pages/Dashboard.tsx b/apps/desktop/src/renderer/pages/Dashboard.tsx new file mode 100644 index 0000000..4a70f24 --- /dev/null +++ b/apps/desktop/src/renderer/pages/Dashboard.tsx @@ -0,0 +1,89 @@ +import { useNavigate } from "react-router"; +import { TitleBar } from "../components/TitleBar"; +import { TimelapseCard } from "../components/TimelapseCard"; +import { useAuth } from "../context/AuthContext"; +import { useRecordingState } from "../hooks/useRecordingState"; +import { useIpcQuery } from "../hooks/useIpc"; + +export function Dashboard() { + const navigate = useNavigate(); + const { user } = useAuth(); + const { isRecording, isPaused, formattedTime, statusLabel, statusColor } = useRecordingState(); + const { data: timelapse } = useIpcQuery("storage:get-timelapse"); + + const isActive = isRecording || isPaused; + + return ( + <> + +
+ {/* Welcome */} +
+

+ Welcome back{user?.displayName ? `, ${user.displayName}` : ""} +

+

+ {isActive ? ( + + {statusLabel} — {formattedTime} + + ) : ( + "Ready to start a new timelapse" + )} +

+
+ + {/* CTA */} +
+ {isActive ? ( + + ) : ( + + )} +
+ + {/* Recent timelapses */} +
+

Current Timelapse

+ {timelapse ? ( +
+ navigate("/upload")} + /> +
+ ) : ( +
+

No timelapses yet

+

+ Start recording to create your first timelapse +

+
+ )} +
+
+ + ); +} diff --git a/apps/desktop/src/renderer/pages/Login.tsx b/apps/desktop/src/renderer/pages/Login.tsx new file mode 100644 index 0000000..2227a6e --- /dev/null +++ b/apps/desktop/src/renderer/pages/Login.tsx @@ -0,0 +1,134 @@ +import { useState } from "react"; +import { useAuth } from "../context/AuthContext"; +import { lapse } from "../lib/desktop"; + +export function Login() { + const { login, isLoading } = useAuth(); + const [error, setError] = useState(null); + const [showTokenInput, setShowTokenInput] = useState(false); + const [tokenValue, setTokenValue] = useState(""); + + const handleLogin = async () => { + setError(null); + try { + await login(); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to sign in"); + } + }; + + const handlePasteToken = async () => { + if (!tokenValue.trim()) return; + setError(null); + try { + const { success } = await lapse.invoke("auth:set-token", tokenValue.trim()); + if (success) { + window.location.reload(); + } else { + setError("Failed to set token"); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to set token"); + } + }; + + return ( +
+ {/* Logo / Branding */} +
+
+
+ + + + +
+
+

Lapse

+

+ Screen recording timelapses for Hackatime +

+
+ + {/* Sign in card */} +
+ + + {error && ( +

{error}

+ )} +
+ + {/* Dev: paste token */} +
+ {!showTokenInput ? ( + + ) : ( +
+

+ Open lapse.hackclub.com, press F12, run:{" "} + + localStorage.getItem("lapse:token") + +

+
+ setTokenValue(e.target.value)} + placeholder="Paste token here..." + className="flex-1 rounded-lg border border-neutral-700 bg-neutral-800 px-3 py-2 text-xs text-white placeholder-neutral-500 outline-none focus:border-neutral-500" + /> + +
+
+ )} +
+
+ ); +} diff --git a/apps/desktop/src/renderer/pages/Recording.tsx b/apps/desktop/src/renderer/pages/Recording.tsx new file mode 100644 index 0000000..448a05a --- /dev/null +++ b/apps/desktop/src/renderer/pages/Recording.tsx @@ -0,0 +1,145 @@ +import { useState } from "react"; +import { useNavigate } from "react-router"; +import { TitleBar } from "../components/TitleBar"; +import { SourcePicker } from "../components/SourcePicker"; +import { RecordingControls } from "../components/RecordingControls"; +import { useRecording } from "../context/RecordingContext"; +import { useRecordingState } from "../hooks/useRecordingState"; +import { useIpcQuery } from "../hooks/useIpc"; +import type { DesktopSource } from "@/shared/ipc-channels"; + +export function Recording() { + const navigate = useNavigate(); + const { startRecording, stopRecording } = useRecording(); + const { isIdle, isRecording, isPaused, formattedTime, snapshotCount, sourceName } = + useRecordingState(); + const { data: videoSize } = useIpcQuery("storage:get-video-size"); + + const [selectedSource, setSelectedSource] = useState(null); + const [isStarting, setIsStarting] = useState(false); + + const isActive = isRecording || isPaused; + + const handleStart = async () => { + if (!selectedSource) return; + setIsStarting(true); + try { + await startRecording(selectedSource.id); + } finally { + setIsStarting(false); + } + }; + + const handleStop = async () => { + await stopRecording(); + navigate("/upload"); + }; + + function formatBytes(bytes: number): string { + if (bytes === 0) return "0 B"; + const units = ["B", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return `${(bytes / 1024 ** i).toFixed(1)} ${units[i]}`; + } + + return ( + <> + +
+ {isIdle ? ( + <> + {/* Source picker */} +
+

New Recording

+ +
+ + {/* Start button */} +
+ +
+ + ) : ( + <> + {/* Active recording */} +
+

+ Recording{sourceName ? ` — ${sourceName}` : ""} +

+
+ + + + {/* Live stats */} +
+
+

Duration

+

{formattedTime}

+
+
+

Snapshots

+

{snapshotCount}

+
+
+

File Size

+

+ {videoSize != null ? formatBytes(videoSize) : "--"} +

+
+
+ + {/* Stop and go to upload */} +
+ +
+ + )} +
+ + ); +} diff --git a/apps/desktop/src/renderer/pages/Settings.tsx b/apps/desktop/src/renderer/pages/Settings.tsx new file mode 100644 index 0000000..1b63a8b --- /dev/null +++ b/apps/desktop/src/renderer/pages/Settings.tsx @@ -0,0 +1,133 @@ +import { useState } from "react"; +import { TitleBar } from "../components/TitleBar"; +import { useAuth } from "../context/AuthContext"; +import { useIpcQuery } from "../hooks/useIpc"; +import { lapse } from "../lib/desktop"; + +export function Settings() { + const { user, logout } = useAuth(); + const { data: version } = useIpcQuery("app:get-version"); + const { data: storagePath } = useIpcQuery("app:get-storage-path"); + + const [isCheckingUpdates, setIsCheckingUpdates] = useState(false); + const [updateResult, setUpdateResult] = useState(null); + const [isLoggingOut, setIsLoggingOut] = useState(false); + + const handleCheckUpdates = async () => { + setIsCheckingUpdates(true); + setUpdateResult(null); + try { + const result = await lapse.invoke("app:check-updates"); + if (result.available) { + setUpdateResult(`Update available: v${result.version}`); + } else { + setUpdateResult("You're on the latest version"); + } + } catch { + setUpdateResult("Failed to check for updates"); + } finally { + setIsCheckingUpdates(false); + } + }; + + const handleLogout = async () => { + setIsLoggingOut(true); + try { + await logout(); + } finally { + setIsLoggingOut(false); + } + }; + + return ( + <> + +
+

Settings

+ +
+ {/* Account */} +
+

Account

+ {user && ( +
+ {user.pictureUrl ? ( + {user.handle} + ) : ( +
+ {(user.displayName ?? user.handle).charAt(0).toUpperCase()} +
+ )} +
+

+ {user.displayName ?? user.handle} +

+

@{user.handle}

+
+
+ )} +
+ + {/* Storage */} +
+

Storage

+
+
+

Storage Location

+

+ {storagePath ?? "..."} +

+
+
+
+ + {/* Updates */} +
+

Updates

+
+
+

App Version

+

{version ?? "..."}

+
+ +
+ {updateResult && ( +

{updateResult}

+ )} +
+ + {/* Sign out */} +
+
+
+

Sign Out

+

+ This will remove your session from this device +

+
+ +
+
+
+
+ + ); +} diff --git a/apps/desktop/src/renderer/pages/Upload.tsx b/apps/desktop/src/renderer/pages/Upload.tsx new file mode 100644 index 0000000..fdb0bcc --- /dev/null +++ b/apps/desktop/src/renderer/pages/Upload.tsx @@ -0,0 +1,149 @@ +import { useEffect } from "react"; +import { useNavigate } from "react-router"; +import { TitleBar } from "../components/TitleBar"; +import { UploadProgress } from "../components/UploadProgress"; +import { useUploadState } from "../hooks/useUploadState"; +import { useIpcQuery } from "../hooks/useIpc"; +import { lapse } from "../lib/desktop"; + +export function Upload() { + const navigate = useNavigate(); + const { + isUploading, + progress, + totalProgress, + error, + draftId, + isComplete, + startUpload, + cancelUpload + } = useUploadState(); + const { data: timelapse } = useIpcQuery("storage:get-timelapse"); + + // Auto-start upload if there is a timelapse and we are not already uploading + useEffect(() => { + if (timelapse && !isUploading && !isComplete && !error) { + startUpload(); + } + }, [timelapse?.startedAt]); + + const handleViewTimelapse = () => { + lapse.invoke("app:open-dashboard").catch(() => {}); + }; + + const handleDeleteLocal = async () => { + await lapse.invoke("storage:delete-timelapse"); + navigate("/"); + }; + + const handleCancel = async () => { + await cancelUpload(); + navigate("/"); + }; + + return ( + <> + +
+

Upload Timelapse

+ + {!timelapse && !isUploading && !isComplete ? ( +
+

No timelapse to upload

+ +
+ ) : isComplete ? ( + /* Success state */ +
+
+
+
+ + + +
+
+

Upload Complete

+

+ Your timelapse has been uploaded successfully +

+ {draftId && ( +

Draft ID: {draftId}

+ )} + +
+ + + +
+
+
+ ) : ( + /* Upload in progress */ +
+ + + {isUploading && ( +
+ +
+ )} + + {error && !isUploading && ( +
+ +
+ )} +
+ )} +
+ + ); +} diff --git a/apps/desktop/src/renderer/styles/globals.css b/apps/desktop/src/renderer/styles/globals.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/apps/desktop/src/renderer/styles/globals.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/apps/desktop/src/shared/ipc-channels.ts b/apps/desktop/src/shared/ipc-channels.ts new file mode 100644 index 0000000..57bb2b0 --- /dev/null +++ b/apps/desktop/src/shared/ipc-channels.ts @@ -0,0 +1,102 @@ +// Single source of truth for all IPC channel names and their type signatures. +// Imported by both main process and preload script to ensure type safety. + +export interface DesktopSource { + id: string; + name: string; + thumbnailDataUrl: string; +} + +export type CaptureStatus = { + state: "idle"; +} | { + state: "recording"; + sourceId: string; + sourceName: string; + startedAt: number; + sessionCount: number; + snapshotCount: number; +} | { + state: "paused"; + sourceId: string; + sourceName: string; + startedAt: number; + sessionCount: number; + snapshotCount: number; +}; + +export interface StoredTimelapse { + sessions: number[]; + snapshots: number[]; + startedAt: number; +} + +export interface LocalDevice { + id: string; + passkey: string; + label: string; +} + +export interface UploadProgress { + sessionId: string; + bytesUploaded: number; + bytesTotal: number; +} + +export interface UpdateCheckResult { + available: boolean; + version?: string; +} + +// Request-response channels (ipcMain.handle / ipcRenderer.invoke) +export type IpcChannelMap = { + // Auth + "auth:login": { args: []; result: { success: boolean } }; + "auth:logout": { args: []; result: void }; + "auth:get-token": { args: []; result: string | null }; + "auth:set-token": { args: [token: string]; result: { success: boolean } }; + "auth:get-user": { args: []; result: { id: string; handle: string; displayName: string | null; pictureUrl: string | null } | null }; + + // Capture + "capture:get-sources": { args: []; result: DesktopSource[] }; + "capture:start": { args: [sourceId: string]; result: void }; + "capture:stop": { args: []; result: void }; + "capture:pause": { args: []; result: void }; + "capture:resume": { args: []; result: void }; + "capture:status": { args: []; result: CaptureStatus }; + + // Storage + "storage:get-timelapse": { args: []; result: StoredTimelapse | null }; + "storage:get-devices": { args: []; result: LocalDevice[] }; + "storage:save-device": { args: [device: LocalDevice]; result: string }; + "storage:get-video-size": { args: []; result: number }; + "storage:delete-timelapse": { args: []; result: void }; + + // API proxy + "api:call": { args: [path: string, input: unknown]; result: unknown }; + + // Upload + "upload:start": { args: []; result: { draftId: string } }; + "upload:cancel": { args: []; result: void }; + + // App + "app:get-version": { args: []; result: string }; + "app:check-updates": { args: []; result: UpdateCheckResult }; + "app:open-dashboard": { args: []; result: void }; + "app:quit": { args: []; result: void }; + "app:get-storage-path": { args: []; result: string }; +}; + +// Push event channels (main -> renderer via webContents.send) +export type IpcEventMap = { + "capture:status-changed": CaptureStatus; + "capture:snapshot": { timestamp: number }; + "upload:progress": UploadProgress; + "upload:complete": { draftId: string }; + "upload:error": { message: string }; + "app:update-available": { version: string }; +}; + +// Helper types for extracting channel keys +export type IpcChannel = keyof IpcChannelMap; +export type IpcEvent = keyof IpcEventMap; diff --git a/apps/desktop/src/shared/types.ts b/apps/desktop/src/shared/types.ts new file mode 100644 index 0000000..2c38d23 --- /dev/null +++ b/apps/desktop/src/shared/types.ts @@ -0,0 +1,46 @@ +// Shared type definitions used across main and renderer processes. + +export interface TimelapseSession { + id: string; + filePath: string; + resolution: { width: number; height: number }; + startedAt: number; +} + +export interface StoreData { + currentTimelapse: { + id: string; + sessions: string[]; + snapshots: number[]; + startedAt: number; + } | null; + devices: Array<{ + id: string; + passkey: string; + label: string; + }>; +} + +export interface AppSettings { + serverUrl: string; + storagePath: string; + autoStartOnLogin: boolean; + notificationsEnabled: boolean; + captureQuality: "low" | "medium" | "high"; +} + +export const DEFAULT_SETTINGS: AppSettings = { + serverUrl: "https://lapse.hackclub.com", + storagePath: "", + autoStartOnLogin: false, + notificationsEnabled: true, + captureQuality: "medium" +}; + +// Timelapse timing constants (matching web client) +export const TIMELAPSE_FACTOR = 60; +export const TIMELAPSE_FPS = 24; +export const CAPTURE_INTERVAL_MS = (1000 * TIMELAPSE_FACTOR) / TIMELAPSE_FPS; // ~2500ms +export const BITS_PER_PIXEL = 48; +export const MIN_SESSION_SIZE_BYTES = 4096; +export const MAX_VIDEO_FRAME_COUNT = 86400; diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json new file mode 100644 index 0000000..b97bf97 --- /dev/null +++ b/apps/desktop/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "noEmit": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": ["node_modules", "dist", "out"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c6c016..09acc33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -177,6 +177,82 @@ importers: specifier: ^4.0.16 version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@2.0.1))(lightningcss@1.31.1)(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + apps/desktop: + dependencies: + '@hackclub/lapse-api': + specifier: workspace:* + version: link:../../packages/api + '@hackclub/lapse-shared': + specifier: workspace:* + version: link:../../packages/shared + '@orpc/client': + specifier: ^1.13.6 + version: 1.13.6(@opentelemetry/api@1.9.0) + '@orpc/contract': + specifier: ^1.13.5 + version: 1.13.5(@opentelemetry/api@1.9.0) + '@orpc/openapi-client': + specifier: ^1.13.5 + version: 1.13.5(@opentelemetry/api@1.9.0) + electron-updater: + specifier: ^6.6.2 + version: 6.8.3 + jsonrepair: + specifier: ^3.13.3 + version: 3.13.3 + react: + specifier: 19.2.3 + version: 19.2.3 + react-dom: + specifier: 19.2.3 + version: 19.2.3(react@19.2.3) + react-router: + specifier: ^7.6.3 + version: 7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + tus-js-client: + specifier: ^4.3.1 + version: 4.3.1 + zod: + specifier: ^4.2.1 + version: 4.2.1 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.18 + version: 4.2.0 + '@types/node': + specifier: ^25.0.3 + version: 25.0.3 + '@types/react': + specifier: ^19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@vitejs/plugin-react': + specifier: ^5.1.2 + version: 5.1.4(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + electron: + specifier: ^35.7.5 + version: 35.7.5 + electron-builder: + specifier: ^26.0.12 + version: 26.8.1(electron-builder-squirrel-windows@26.8.1) + electron-vite: + specifier: ^5.0.0 + version: 5.0.0(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + tailwindcss: + specifier: ^4.1.18 + version: 4.2.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^6.3.5 + version: 6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + apps/devscripts: dependencies: '@inquirer/prompts': @@ -421,6 +497,9 @@ importers: packages: + 7zip-bin@5.2.0: + resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==} + '@acemir/cssom@0.9.31': resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} @@ -1187,6 +1266,10 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -1243,13 +1326,22 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.0': - resolution: {integrity: sha512-H4tuz2nhWgNKLt1inYpoVCfbJbMwX/lQKp3g69rrrIMIYlFD9+zTykOKhNR8uGrAmbS/kT9n6hTFkmDkxLgeTA==} + '@csstools/css-syntax-patches-for-csstree@1.1.1': + resolution: {integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true '@csstools/css-tokenizer@4.0.0': resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@develar/schema-utils@2.6.5': + resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} + engines: {node: '>= 8.9.0'} + '@electric-sql/pglite-socket@0.0.6': resolution: {integrity: sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==} hasBin: true @@ -1264,6 +1356,46 @@ packages: '@electric-sql/pglite@0.3.2': resolution: {integrity: sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==} + '@electron/asar@3.4.1': + resolution: {integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==} + engines: {node: '>=10.12.0'} + hasBin: true + + '@electron/fuses@1.8.0': + resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==} + hasBin: true + + '@electron/get@2.0.3': + resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} + engines: {node: '>=12'} + + '@electron/get@3.1.0': + resolution: {integrity: sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==} + engines: {node: '>=14'} + + '@electron/notarize@2.5.0': + resolution: {integrity: sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==} + engines: {node: '>= 10.0.0'} + + '@electron/osx-sign@1.3.3': + resolution: {integrity: sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==} + engines: {node: '>=12.0.0'} + hasBin: true + + '@electron/rebuild@4.0.3': + resolution: {integrity: sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA==} + engines: {node: '>=22.12.0'} + hasBin: true + + '@electron/universal@2.0.3': + resolution: {integrity: sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==} + engines: {node: '>=16.4'} + + '@electron/windows-sign@1.2.2': + resolution: {integrity: sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==} + engines: {node: '>=14.14'} + hasBin: true + '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -1273,162 +1405,474 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.27.3': resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.27.3': resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.27.3': resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.27.3': resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.27.3': resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.27.3': resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.3': resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.27.3': resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.27.3': resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.27.3': resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.27.3': resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.27.3': resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.27.3': resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.27.3': resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.27.3': resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.27.3': resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.27.3': resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.3': resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.27.3': resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.3': resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.27.3': resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.27.3': resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.27.3': resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.27.3': resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.27.3': resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1840,6 +2284,14 @@ packages: '@ioredis/commands@1.5.0': resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1862,6 +2314,14 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@malept/cross-spawn-promise@2.0.0': + resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} + engines: {node: '>= 12.13.0'} + + '@malept/flatpak-bundler@0.4.0': + resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==} + engines: {node: '>= 10.0.0'} + '@mrleebo/prisma-ast@0.12.1': resolution: {integrity: sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==} engines: {node: '>=16'} @@ -1988,6 +2448,14 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -2342,6 +2810,10 @@ packages: '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -2472,126 +2944,251 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.58.0': resolution: {integrity: sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.58.0': resolution: {integrity: sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.58.0': resolution: {integrity: sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.58.0': resolution: {integrity: sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.58.0': resolution: {integrity: sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.58.0': resolution: {integrity: sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.58.0': resolution: {integrity: sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.58.0': resolution: {integrity: sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.58.0': resolution: {integrity: sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loong64-gnu@4.58.0': resolution: {integrity: sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-loong64-musl@4.58.0': resolution: {integrity: sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.58.0': resolution: {integrity: sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-ppc64-musl@4.58.0': resolution: {integrity: sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.58.0': resolution: {integrity: sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.58.0': resolution: {integrity: sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.58.0': resolution: {integrity: sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.58.0': resolution: {integrity: sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.58.0': resolution: {integrity: sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-openbsd-x64@4.58.0': resolution: {integrity: sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==} cpu: [x64] os: [openbsd] + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.58.0': resolution: {integrity: sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.58.0': resolution: {integrity: sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.58.0': resolution: {integrity: sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.58.0': resolution: {integrity: sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.58.0': resolution: {integrity: sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + cpu: [x64] + os: [win32] + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -2654,6 +3251,10 @@ packages: engines: {node: '>=18.12.0'} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} @@ -2958,6 +3559,10 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + '@tailwindcss/node@4.2.0': resolution: {integrity: sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==} @@ -3123,6 +3728,9 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -3132,6 +3740,9 @@ packages: '@types/cookiejar@2.1.5': resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -3150,6 +3761,12 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/http-cache-semantics@4.2.0': + resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} + '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -3162,6 +3779,9 @@ packages: '@types/jsonwebtoken@9.0.10': resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -3171,6 +3791,9 @@ packages: '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} + '@types/node@22.19.15': + resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} + '@types/node@25.0.3': resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} @@ -3186,6 +3809,9 @@ packages: '@types/platform@1.3.6': resolution: {integrity: sha512-ZmSaqHuvzv+jC232cFoz2QqPUkaj6EvMmCrWcx3WRr7xTPVFCMUOTcOq8m2d+Zw1iKRc1kDiaA+jtNrV0hkVew==} + '@types/plist@3.0.5': + resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -3194,6 +3820,9 @@ packages: '@types/react@19.2.7': resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} @@ -3209,6 +3838,12 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/verror@1.10.11': + resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@typescript-eslint/eslint-plugin@8.56.0': resolution: {integrity: sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3463,12 +4098,20 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@xmldom/xmldom@0.8.11': + resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} + engines: {node: '>=10.0.0'} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + abstract-logging@2.0.1: resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} @@ -3521,6 +4164,11 @@ packages: ajv: optional: true + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: @@ -3551,10 +4199,24 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-builder-bin@5.0.0-alpha.12: + resolution: {integrity: sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==} + + app-builder-lib@26.8.1: + resolution: {integrity: sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + dmg-builder: 26.8.1 + electron-builder-squirrel-windows: 26.8.1 + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3611,6 +4273,10 @@ packages: resolution: {integrity: sha512-brU24g7ryhRwGCI2y+1dGQmQXiZF7TtIj583S96y0jjdajIe6wn8BuXyELYhvD22dtIxDQVFk04YTJwwdwOYJw==} engines: {node: '>=0.8'} + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -3624,13 +4290,28 @@ packages: ast-v8-to-istanbul@0.3.11: resolution: {integrity: sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-exit-hook@2.0.1: + resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} + engines: {node: '>=0.12.0'} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} @@ -3708,9 +4389,16 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + boolean@3.2.0: + resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} @@ -3733,6 +4421,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -3742,6 +4433,13 @@ packages: buffer@5.6.0: resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + builder-util-runtime@9.5.1: + resolution: {integrity: sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==} + engines: {node: '>=12.0.0'} + + builder-util@26.8.1: + resolution: {integrity: sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==} + bullmq@5.69.3: resolution: {integrity: sha512-P9uLsR7fDvejH/1m6uur6j7U9mqY6nNt+XvhlhStOUe7jdwbZoP/c2oWNtE+8ljOlubw4pRUKymtRqkyvloc4A==} @@ -3753,6 +4451,22 @@ packages: magicast: optional: true + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -3805,24 +4519,51 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} + chromium-pickle-js@0.2.0: + resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} + + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + cli-spinners@3.4.0: resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} engines: {node: '>=18.20'} + cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -3834,6 +4575,9 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -3873,6 +4617,10 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -3884,6 +4632,10 @@ packages: commist@3.2.0: resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==} + compare-version@0.1.2: + resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} + engines: {node: '>=0.10.0'} + component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} @@ -3932,6 +4684,9 @@ packages: core-js@3.48.0: resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -3941,6 +4696,9 @@ packages: typescript: optional: true + crc@3.8.0: + resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -3948,6 +4706,9 @@ packages: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} + cross-dirname@0.1.0: + resolution: {integrity: sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -4033,6 +4794,10 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dedent@1.7.1: resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} peerDependencies: @@ -4055,6 +4820,10 @@ packages: defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -4089,6 +4858,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} @@ -4096,10 +4868,22 @@ packages: resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} + dir-compare@4.2.0: + resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dmg-builder@26.8.1: + resolution: {integrity: sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==} + + dmg-license@1.0.11: + resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==} + engines: {node: '>=8'} + os: [darwin] + hasBin: true + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -4130,6 +4914,10 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dotenv-expand@11.0.7: + resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} + engines: {node: '>=12'} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -4142,6 +4930,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + easy-table@1.1.0: resolution: {integrity: sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==} @@ -4151,9 +4942,48 @@ packages: effect@3.18.4: resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-builder-squirrel-windows@26.8.1: + resolution: {integrity: sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==} + + electron-builder@26.8.1: + resolution: {integrity: sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==} + engines: {node: '>=14.0.0'} + hasBin: true + + electron-publish@26.8.1: + resolution: {integrity: sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==} + electron-to-chromium@1.5.302: resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + electron-updater@6.8.3: + resolution: {integrity: sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==} + + electron-vite@5.0.0: + resolution: {integrity: sha512-OHp/vjdlubNlhNkPkL/+3JD34ii5ov7M0GpuXEVdQeqdQ3ulvVR7Dg/rNBLfS5XPIFwgoBLDf9sjjrL+CuDyRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@swc/core': ^1.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + '@swc/core': + optional: true + + electron-winstaller@5.4.0: + resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} + engines: {node: '>=8.0.0'} + + electron@35.7.5: + resolution: {integrity: sha512-dnL+JvLraKZl7iusXTVTGYs10TKfzUi30uEDTqsmTm0guN9V2tbOjTzyIZbh9n3ygUjgEYyo+igAwMRXIi3IPw==} + engines: {node: '>= 12.20.55'} + hasBin: true + elegant.def@3.0.0-alpha.2: resolution: {integrity: sha512-n8FEpGrT8yQc0ZoDjG16zZEvitPqVdcBuBctKCWIxcMbFptvjf6ZkHHf3wKSKoSy1yxx7iIGP9P4XpNY+h7RCg==} hasBin: true @@ -4168,6 +4998,9 @@ packages: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -4187,6 +5020,13 @@ packages: resolution: {integrity: sha512-IN9mE0yf+9DoUvGpNQfvJNJ9HyS9VGeo7llLkPdRBtMU2LHLXiQ/JcOI03u8lig0lefGCewv9x82vK8LI8rNPg==} engines: {node: '>=0.6.0'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -4228,11 +5068,24 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.27.3: resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -4393,9 +5246,21 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + extsprintf@1.4.1: + resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} + engines: {'0': node >=0.6.0} + fast-check@3.23.2: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} @@ -4457,6 +5322,9 @@ packages: fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -4483,6 +5351,9 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -4538,6 +5409,33 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} + engines: {node: '>=14.14'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4591,6 +5489,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + get-stream@9.0.1: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} @@ -4617,6 +5519,11 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -4625,6 +5532,14 @@ packages: resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + global-agent@3.0.0: + resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} + engines: {node: '>=10.0'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -4645,6 +5560,10 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -4698,6 +5617,10 @@ packages: resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} engines: {node: '>=16.9.0'} + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -4705,6 +5628,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -4712,6 +5638,10 @@ packages: http-status-codes@2.3.0: resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -4720,12 +5650,17 @@ packages: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} + iconv-corefoundation@1.1.7: + resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} + engines: {node: ^8.11.2 || >=10} + os: [darwin] + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} iconv-lite@0.7.2: @@ -4773,6 +5708,10 @@ packages: resolution: {integrity: sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ==} engines: {node: '>=12.22.0'} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + ipaddr.js@2.3.0: resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} @@ -4847,6 +5786,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -4912,6 +5855,10 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + is-unicode-supported@2.1.0: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} @@ -4935,9 +5882,17 @@ packages: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} + isbinaryfile@5.0.7: + resolution: {integrity: sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==} + engines: {node: '>= 18.0.0'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.5: + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -4958,6 +5913,14 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -5018,6 +5981,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -5027,6 +5993,12 @@ packages: engines: {node: '>=6'} hasBin: true + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonfy@0.1.0: resolution: {integrity: sha512-Xw4o10mEUftnLkpAvLMCZAv7sWtJIkqGzAYkuqEGx8l0KcQbbg6V6OYfUrZcXrRMTRt2xXcwNZC/1R1bInGs6w==} @@ -5058,6 +6030,9 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + lazy-val@1.0.5: + resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -5178,6 +6153,9 @@ packages: lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} @@ -5187,6 +6165,10 @@ packages: lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} @@ -5217,6 +6199,13 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + log-symbols@7.0.1: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} @@ -5231,13 +6220,28 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.6: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + lru.min@1.1.3: resolution: {integrity: sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} @@ -5263,9 +6267,17 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + matcher@3.0.0: + resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} + engines: {node: '>=10'} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -5334,10 +6346,22 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -5349,6 +6373,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -5356,10 +6384,42 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} @@ -5435,6 +6495,10 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -5462,9 +6526,19 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-abi@4.28.0: + resolution: {integrity: sha512-Qfp5XZL1cJDOabOT8H5gnqMTmM4NjvYzHp4I/Kt/Sl76OVkOBBHRFlPspGV0hYvMoqQsypFjT/Yp7Km0beXW9g==} + engines: {node: '>=22.12.0'} + node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + node-addon-api@1.7.2: + resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + + node-api-version@0.2.1: + resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==} + node-exports-info@1.6.0: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} @@ -5476,6 +6550,11 @@ packages: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true + node-gyp@11.5.0: + resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + node-mocks-http@1.17.2: resolution: {integrity: sha512-HVxSnjNzE9NzoWMx9T9z4MLqwMpLwVvA0oVZ+L+gXskYXEJ6tFn3Kx4LargoB6ie7ZlCLplv7QbWO6N+MysWGA==} engines: {node: '>=14'} @@ -5491,10 +6570,19 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -5552,6 +6640,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -5563,6 +6655,10 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + ora@9.3.0: resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} engines: {node: '>=20'} @@ -5574,6 +6670,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -5590,6 +6690,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-map@7.0.4: + resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + engines: {node: '>=18'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -5639,6 +6743,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -5653,6 +6761,13 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pe-library@0.4.1: + resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} + engines: {node: '>=12', npm: '>=6'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -5729,6 +6844,10 @@ packages: resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} engines: {node: '>=12'} + plist@3.1.0: + resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} + engines: {node: '>=10.4.0'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -5741,6 +6860,10 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -5777,6 +6900,11 @@ packages: rxjs: optional: true + postject@1.0.0-alpha.6: + resolution: {integrity: sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==} + engines: {node: '>=14.0.0'} + hasBin: true + preact@10.28.4: resolution: {integrity: sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==} @@ -5809,12 +6937,24 @@ packages: typescript: optional: true + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + process-warning@4.0.1: resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -5831,6 +6971,9 @@ packages: pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -5858,6 +7001,10 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + radash@12.1.1: resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==} engines: {node: '>=14.18.0'} @@ -5887,10 +7034,24 @@ packages: resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} + react-router@7.13.1: + resolution: {integrity: sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react@19.2.3: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} + read-binary-file-arch@1.0.6: + resolution: {integrity: sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==} + hasBin: true + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -5966,6 +7127,13 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resedit@1.7.2: + resolution: {integrity: sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==} + engines: {node: '>=12', npm: '>=6'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5987,6 +7155,13 @@ packages: engines: {node: '>= 0.4'} hasBin: true + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -6009,16 +7184,30 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rimraf@2.6.3: + resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@6.1.3: resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} engines: {node: 20 || >=22} hasBin: true + roarr@2.15.4: + resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} + engines: {node: '>=8.0'} + rollup@4.58.0: resolution: {integrity: sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rou3@0.7.12: resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} @@ -6053,6 +7242,13 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sanitize-filename@1.6.3: + resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -6067,6 +7263,13 @@ packages: secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -6084,6 +7287,10 @@ packages: seq-queue@0.0.5: resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + serialize-error@7.0.1: + resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} + engines: {node: '>=10'} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -6140,6 +7347,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} @@ -6148,9 +7359,25 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -6172,6 +7399,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sqlstring@2.3.3: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} @@ -6187,6 +7417,10 @@ packages: sscan@0.2.0: resolution: {integrity: sha512-zg2cRUUmEbRiRSJFMmVFDrRwyEDEYmK071TUkHWCOltA4ruyvpVSm1WmoUQ90nzLxEFbE4L9Km7qWGCWyY7mOQ==} + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -6196,6 +7430,10 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + stat-mode@1.0.0: + resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} + engines: {node: '>= 6'} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -6224,6 +7462,10 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string-width@8.2.0: resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} engines: {node: '>=20'} @@ -6298,6 +7540,10 @@ packages: babel-plugin-macros: optional: true + sumchecker@3.0.1: + resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} + engines: {node: '>= 8.0'} + superagent@10.3.0: resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} engines: {node: '>=14.18.0'} @@ -6340,6 +7586,17 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tar@7.5.11: + resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==} + engines: {node: '>=18'} + + temp-file@3.4.0: + resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} + + temp@0.9.4: + resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==} + engines: {node: '>=6.0.0'} + terser-webpack-plugin@5.3.16: resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} @@ -6365,6 +7622,12 @@ packages: resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} engines: {node: '>=20'} + tiny-async-pool@1.3.0: + resolution: {integrity: sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==} + + tiny-typed-emitter@2.1.0: + resolution: {integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -6380,13 +7643,20 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} - tldts-core@7.0.25: - resolution: {integrity: sha512-ZjCZK0rppSBu7rjHYDYsEaMOIbbT+nWF57hKkv4IUmZWBNrBWBOjIElc0mKRgLM8bm7x/BBlof6t2gi/Oq/Asw==} + tldts-core@7.0.26: + resolution: {integrity: sha512-5WJ2SqFsv4G2Dwi7ZFVRnz6b2H1od39QME1lc2y5Ew3eWiZMAeqOAfWpRP9jHvhUl881406QtZTODvjttJs+ew==} - tldts@7.0.25: - resolution: {integrity: sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w==} + tldts@7.0.26: + resolution: {integrity: sha512-WiGwQjr0qYdNNG8KpMKlSvpxz652lqa3Rd+/hSaDcY4Uo6SKWZq2LAF+hsAhUewTtYhXlorBKgNF3Kk8hnjGoQ==} hasBin: true + tmp-promise@3.0.3: + resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -6402,14 +7672,17 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - tough-cookie@6.0.0: - resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} tr46@6.0.0: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + truncate-utf8-bytes@1.0.2: + resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -6458,6 +7731,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -6466,6 +7743,10 @@ packages: resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} engines: {node: '>=20'} + type-fest@5.5.0: + resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} + engines: {node: '>=20'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -6509,6 +7790,9 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -6532,6 +7816,22 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -6550,6 +7850,9 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + utf8-byte-length@1.0.5: + resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -6568,9 +7871,53 @@ packages: typescript: optional: true + verror@1.10.1: + resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} + engines: {node: '>=0.6.0'} + vi-mock@1.0.0: resolution: {integrity: sha512-15H1/uoPGlMPcwnszlAMrz+eWCOI5PTplpPD7j1cPPqFzqH2VWzdJ1tHSwd6XevxWshE6FAvPIx6cuzB6ENXuQ==} + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6713,6 +8060,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -6733,6 +8085,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -6752,6 +8108,10 @@ packages: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -6769,6 +8129,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} @@ -6786,6 +8150,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -6823,6 +8190,8 @@ packages: snapshots: + 7zip-bin@5.2.0: {} + '@acemir/cssom@0.9.31': optional: true @@ -6846,7 +8215,7 @@ snapshots: '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.6 + lru-cache: 11.2.7 optional: true '@asamuzakjp/dom-selector@6.8.1': @@ -6855,7 +8224,7 @@ snapshots: bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.6 + lru-cache: 11.2.7 optional: true '@asamuzakjp/nwsapi@2.3.9': @@ -8107,6 +9476,8 @@ snapshots: '@babel/runtime@7.28.6': {} + '@babel/runtime@7.29.2': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 @@ -8168,26 +9539,133 @@ snapshots: '@csstools/css-tokenizer': 4.0.0 optional: true - '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + optional: true + + '@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + optional: true + + '@csstools/css-tokenizer@4.0.0': + optional: true + + '@develar/schema-utils@2.6.5': + dependencies: + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + '@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)': + dependencies: + '@electric-sql/pglite': 0.3.2 + + '@electric-sql/pglite-tools@0.2.7(@electric-sql/pglite@0.3.2)': + dependencies: + '@electric-sql/pglite': 0.3.2 + + '@electric-sql/pglite@0.3.2': {} + + '@electron/asar@3.4.1': + dependencies: + commander: 5.1.0 + glob: 7.2.3 + minimatch: 3.1.2 + + '@electron/fuses@1.8.0': + dependencies: + chalk: 4.1.2 + fs-extra: 9.1.0 + minimist: 1.2.8 + + '@electron/get@2.0.3': + dependencies: + debug: 4.4.3 + env-paths: 2.2.1 + fs-extra: 8.1.0 + got: 11.8.6 + progress: 2.0.3 + semver: 6.3.1 + sumchecker: 3.0.1 + optionalDependencies: + global-agent: 3.0.0 + transitivePeerDependencies: + - supports-color + + '@electron/get@3.1.0': + dependencies: + debug: 4.4.3 + env-paths: 2.2.1 + fs-extra: 8.1.0 + got: 11.8.6 + progress: 2.0.3 + semver: 6.3.1 + sumchecker: 3.0.1 + optionalDependencies: + global-agent: 3.0.0 + transitivePeerDependencies: + - supports-color + + '@electron/notarize@2.5.0': dependencies: - '@csstools/css-tokenizer': 4.0.0 - optional: true - - '@csstools/css-syntax-patches-for-csstree@1.1.0': - optional: true + debug: 4.4.3 + fs-extra: 9.1.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color - '@csstools/css-tokenizer@4.0.0': - optional: true + '@electron/osx-sign@1.3.3': + dependencies: + compare-version: 0.1.2 + debug: 4.4.3 + fs-extra: 10.1.0 + isbinaryfile: 4.0.10 + minimist: 1.2.8 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color - '@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)': + '@electron/rebuild@4.0.3': dependencies: - '@electric-sql/pglite': 0.3.2 + '@malept/cross-spawn-promise': 2.0.0 + debug: 4.4.3 + detect-libc: 2.1.2 + got: 11.8.6 + graceful-fs: 4.2.11 + node-abi: 4.28.0 + node-api-version: 0.2.1 + node-gyp: 11.5.0 + ora: 5.4.1 + read-binary-file-arch: 1.0.6 + semver: 7.7.4 + tar: 7.5.11 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color - '@electric-sql/pglite-tools@0.2.7(@electric-sql/pglite@0.3.2)': + '@electron/universal@2.0.3': dependencies: - '@electric-sql/pglite': 0.3.2 + '@electron/asar': 3.4.1 + '@malept/cross-spawn-promise': 2.0.0 + debug: 4.4.3 + dir-compare: 4.2.0 + fs-extra: 11.3.4 + minimatch: 9.0.5 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color - '@electric-sql/pglite@0.3.2': {} + '@electron/windows-sign@1.2.2': + dependencies: + cross-dirname: 0.1.0 + debug: 4.4.3 + fs-extra: 11.3.4 + minimist: 1.2.8 + postject: 1.0.0-alpha.6 + transitivePeerDependencies: + - supports-color + optional: true '@emnapi/core@1.8.1': dependencies: @@ -8205,84 +9683,240 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + '@esbuild/aix-ppc64@0.27.3': optional: true + '@esbuild/aix-ppc64@0.27.4': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + '@esbuild/android-arm64@0.27.3': optional: true + '@esbuild/android-arm64@0.27.4': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + '@esbuild/android-arm@0.27.3': optional: true + '@esbuild/android-arm@0.27.4': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + '@esbuild/android-x64@0.27.3': optional: true + '@esbuild/android-x64@0.27.4': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + '@esbuild/darwin-arm64@0.27.3': optional: true + '@esbuild/darwin-arm64@0.27.4': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + '@esbuild/darwin-x64@0.27.3': optional: true + '@esbuild/darwin-x64@0.27.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + '@esbuild/freebsd-arm64@0.27.3': optional: true + '@esbuild/freebsd-arm64@0.27.4': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + '@esbuild/freebsd-x64@0.27.3': optional: true + '@esbuild/freebsd-x64@0.27.4': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + '@esbuild/linux-arm64@0.27.3': optional: true + '@esbuild/linux-arm64@0.27.4': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + '@esbuild/linux-arm@0.27.3': optional: true + '@esbuild/linux-arm@0.27.4': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + '@esbuild/linux-ia32@0.27.3': optional: true + '@esbuild/linux-ia32@0.27.4': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + '@esbuild/linux-loong64@0.27.3': optional: true + '@esbuild/linux-loong64@0.27.4': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + '@esbuild/linux-mips64el@0.27.3': optional: true + '@esbuild/linux-mips64el@0.27.4': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + '@esbuild/linux-ppc64@0.27.3': optional: true + '@esbuild/linux-ppc64@0.27.4': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + '@esbuild/linux-riscv64@0.27.3': optional: true + '@esbuild/linux-riscv64@0.27.4': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + '@esbuild/linux-s390x@0.27.3': optional: true + '@esbuild/linux-s390x@0.27.4': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + '@esbuild/linux-x64@0.27.3': optional: true + '@esbuild/linux-x64@0.27.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.27.3': optional: true + '@esbuild/netbsd-arm64@0.27.4': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + '@esbuild/netbsd-x64@0.27.3': optional: true + '@esbuild/netbsd-x64@0.27.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.27.3': optional: true + '@esbuild/openbsd-arm64@0.27.4': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + '@esbuild/openbsd-x64@0.27.3': optional: true + '@esbuild/openbsd-x64@0.27.4': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + '@esbuild/openharmony-arm64@0.27.3': optional: true + '@esbuild/openharmony-arm64@0.27.4': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + '@esbuild/sunos-x64@0.27.3': optional: true + '@esbuild/sunos-x64@0.27.4': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + '@esbuild/win32-arm64@0.27.3': optional: true + '@esbuild/win32-arm64@0.27.4': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + '@esbuild/win32-ia32@0.27.3': optional: true + '@esbuild/win32-ia32@0.27.4': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@esbuild/win32-x64@0.27.3': optional: true + '@esbuild/win32-x64@0.27.4': + optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.6.1))': dependencies: eslint: 9.39.3(jiti@2.6.1) @@ -8636,6 +10270,19 @@ snapshots: '@ioredis/commands@1.5.0': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -8665,6 +10312,19 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@malept/cross-spawn-promise@2.0.0': + dependencies: + cross-spawn: 7.0.6 + + '@malept/flatpak-bundler@0.4.0': + dependencies: + debug: 4.4.3 + fs-extra: 9.1.0 + lodash: 4.17.21 + tmp-promise: 3.0.3 + transitivePeerDependencies: + - supports-color + '@mrleebo/prisma-ast@0.12.1': dependencies: chevrotain: 10.5.0 @@ -8761,6 +10421,20 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.4 + '@open-draft/deferred-promise@2.2.0': optional: true @@ -9281,6 +10955,9 @@ snapshots: '@pinojs/redact@0.4.0': {} + '@pkgjs/parseargs@0.11.0': + optional: true + '@polka/url@1.0.0-next.29': {} '@posthog/cli@0.5.30': @@ -9447,78 +11124,153 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.58.0': optional: true + '@rollup/rollup-android-arm-eabi@4.59.0': + optional: true + '@rollup/rollup-android-arm64@4.58.0': optional: true + '@rollup/rollup-android-arm64@4.59.0': + optional: true + '@rollup/rollup-darwin-arm64@4.58.0': optional: true + '@rollup/rollup-darwin-arm64@4.59.0': + optional: true + '@rollup/rollup-darwin-x64@4.58.0': optional: true + '@rollup/rollup-darwin-x64@4.59.0': + optional: true + '@rollup/rollup-freebsd-arm64@4.58.0': optional: true + '@rollup/rollup-freebsd-arm64@4.59.0': + optional: true + '@rollup/rollup-freebsd-x64@4.58.0': optional: true + '@rollup/rollup-freebsd-x64@4.59.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.58.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.58.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.58.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.58.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-loong64-gnu@4.58.0': optional: true + '@rollup/rollup-linux-loong64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-loong64-musl@4.58.0': optional: true + '@rollup/rollup-linux-loong64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-ppc64-gnu@4.58.0': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-ppc64-musl@4.58.0': optional: true + '@rollup/rollup-linux-ppc64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.58.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.58.0': optional: true + '@rollup/rollup-linux-riscv64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.58.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.58.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.58.0': optional: true + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + '@rollup/rollup-openbsd-x64@4.58.0': optional: true + '@rollup/rollup-openbsd-x64@4.59.0': + optional: true + '@rollup/rollup-openharmony-arm64@4.58.0': optional: true + '@rollup/rollup-openharmony-arm64@4.59.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.58.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.58.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-x64-gnu@4.58.0': optional: true + '@rollup/rollup-win32-x64-gnu@4.59.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.58.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.59.0': + optional: true + '@rtsao/scc@1.1.0': {} '@scure/base@2.0.0': {} @@ -9600,6 +11352,8 @@ snapshots: '@shopify/semaphore@3.1.0': {} + '@sindresorhus/is@4.6.0': {} + '@sindresorhus/merge-streams@4.0.0': {} '@smithy/abort-controller@4.2.8': @@ -10039,6 +11793,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + '@tailwindcss/node@4.2.0': dependencies: '@jridgewell/remapping': 2.3.5 @@ -10111,7 +11869,7 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -10208,6 +11966,13 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.2.0 + '@types/keyv': 3.1.4 + '@types/node': 25.0.3 + '@types/responselike': 1.0.3 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -10219,6 +11984,10 @@ snapshots: '@types/cookiejar@2.1.5': {} + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} '@types/dom-mediacapture-transform@0.1.11': @@ -10239,6 +12008,12 @@ snapshots: '@types/estree@1.0.8': {} + '@types/fs-extra@9.0.13': + dependencies: + '@types/node': 25.0.3 + + '@types/http-cache-semantics@4.2.0': {} + '@types/js-yaml@4.0.9': {} '@types/json-schema@7.0.15': {} @@ -10250,6 +12025,10 @@ snapshots: '@types/ms': 2.1.0 '@types/node': 25.0.3 + '@types/keyv@3.1.4': + dependencies: + '@types/node': 25.0.3 + '@types/methods@1.1.4': {} '@types/ms@2.1.0': {} @@ -10258,6 +12037,10 @@ snapshots: dependencies: '@types/node': 25.0.3 + '@types/node@22.19.15': + dependencies: + undici-types: 6.21.0 + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 @@ -10280,6 +12063,12 @@ snapshots: '@types/platform@1.3.6': {} + '@types/plist@3.0.5': + dependencies: + '@types/node': 25.0.3 + xmlbuilder: 15.1.1 + optional: true + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: '@types/react': 19.2.7 @@ -10288,6 +12077,10 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/responselike@1.0.3': + dependencies: + '@types/node': 25.0.3 + '@types/statuses@2.0.6': optional: true @@ -10310,6 +12103,14 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/verror@1.10.11': + optional: true + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 25.0.3 + optional: true + '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -10460,6 +12261,18 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@vitejs/plugin-react@5.1.4(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-rc.3 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.29.0 @@ -10498,14 +12311,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.10(@types/node@25.0.3)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.16': dependencies: @@ -10625,10 +12438,14 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 + '@xmldom/xmldom@0.8.11': {} + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} + abbrev@3.0.1: {} + abstract-logging@2.0.1: {} accepts@1.3.8: @@ -10654,8 +12471,7 @@ snapshots: acorn@8.15.0: {} - agent-base@7.1.4: - optional: true + agent-base@7.1.4: {} ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: @@ -10665,6 +12481,10 @@ snapshots: optionalDependencies: ajv: 8.18.0 + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + ajv-keywords@5.1.0(ajv@8.17.1): dependencies: ajv: 8.17.1 @@ -10701,11 +12521,58 @@ snapshots: ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + app-builder-bin@5.0.0-alpha.12: {} + + app-builder-lib@26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1): + dependencies: + '@develar/schema-utils': 2.6.5 + '@electron/asar': 3.4.1 + '@electron/fuses': 1.8.0 + '@electron/get': 3.1.0 + '@electron/notarize': 2.5.0 + '@electron/osx-sign': 1.3.3 + '@electron/rebuild': 4.0.3 + '@electron/universal': 2.0.3 + '@malept/flatpak-bundler': 0.4.0 + '@types/fs-extra': 9.0.13 + async-exit-hook: 2.0.1 + builder-util: 26.8.1 + builder-util-runtime: 9.5.1 + chromium-pickle-js: 0.2.0 + ci-info: 4.3.1 + debug: 4.4.3 + dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.8.1) + dotenv: 16.6.1 + dotenv-expand: 11.0.7 + ejs: 3.1.10 + electron-builder-squirrel-windows: 26.8.1(dmg-builder@26.8.1) + electron-publish: 26.8.1 + fs-extra: 10.1.0 + hosted-git-info: 4.1.0 + isbinaryfile: 5.0.7 + jiti: 2.6.1 + js-yaml: 4.1.1 + json5: 2.2.3 + lazy-val: 1.0.5 + minimatch: 10.2.4 + plist: 3.1.0 + proper-lockfile: 4.1.2 + resedit: 1.7.2 + semver: 7.7.4 + tar: 7.5.11 + temp-file: 3.4.0 + tiny-async-pool: 1.3.0 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + arg@4.1.3: {} argparse@2.0.1: {} @@ -10789,6 +12656,9 @@ snapshots: assert-plus@0.1.5: {} + assert-plus@1.0.0: + optional: true + assertion-error@2.0.1: {} ast-traverse@0.1.1: {} @@ -10801,10 +12671,19 @@ snapshots: estree-walker: 3.0.3 js-tokens: 10.0.0 + astral-regex@2.0.0: + optional: true + + async-exit-hook@2.0.1: {} + async-function@1.0.0: {} + async@3.2.6: {} + asynckit@0.4.0: {} + at-least-node@1.0.0: {} + atomic-sleep@1.0.0: {} available-typed-arrays@1.0.7: @@ -10883,8 +12762,17 @@ snapshots: binary-extensions@2.3.0: {} + bl@4.1.0: + dependencies: + buffer: 5.6.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + boolbase@1.0.0: {} + boolean@3.2.0: + optional: true + bowser@2.14.1: {} brace-expansion@1.1.12: @@ -10912,6 +12800,8 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) + buffer-crc32@0.2.13: {} + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} @@ -10921,6 +12811,34 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + builder-util-runtime@9.5.1: + dependencies: + debug: 4.4.3 + sax: 1.6.0 + transitivePeerDependencies: + - supports-color + + builder-util@26.8.1: + dependencies: + 7zip-bin: 5.2.0 + '@types/debug': 4.1.12 + app-builder-bin: 5.0.0-alpha.12 + builder-util-runtime: 9.5.1 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + fs-extra: 10.1.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + js-yaml: 4.1.1 + sanitize-filename: 1.6.3 + source-map-support: 0.5.21 + stat-mode: 1.0.0 + temp-file: 3.4.0 + tiny-async-pool: 1.3.0 + transitivePeerDependencies: + - supports-color + bullmq@5.69.3: dependencies: cron-parser: 4.9.0 @@ -10948,6 +12866,35 @@ snapshots: pkg-types: 2.3.0 rc9: 2.1.2 + cac@6.7.14: {} + + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.5.0 + lru-cache: 10.4.3 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.4 + ssri: 12.0.0 + tar: 7.5.11 + unique-filename: 4.0.0 + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -11009,20 +12956,40 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@3.0.0: {} + chrome-trace-event@1.0.4: {} + chromium-pickle-js@0.2.0: {} + + ci-info@4.3.1: {} + + ci-info@4.4.0: {} + citty@0.1.6: dependencies: consola: 3.4.2 cjs-module-lexer@2.2.0: {} + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 + cli-spinners@2.9.2: {} + cli-spinners@3.4.0: {} + cli-truncate@2.1.0: + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + optional: true + cli-width@4.1.0: {} client-only@0.0.1: {} @@ -11032,10 +12999,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - optional: true - clone@1.0.4: - optional: true + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + clone@1.0.4: {} close-with-grace@2.5.0: {} @@ -11064,12 +13033,16 @@ snapshots: commander@2.20.3: {} + commander@5.1.0: {} + commander@7.2.0: {} commander@9.5.0: {} commist@3.2.0: {} + compare-version@0.1.2: {} + component-emitter@1.3.1: {} concat-map@0.0.1: {} @@ -11106,6 +13079,9 @@ snapshots: core-js@3.48.0: {} + core-util-is@1.0.2: + optional: true + cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.1 @@ -11115,12 +13091,20 @@ snapshots: optionalDependencies: typescript: 5.9.3 + crc@3.8.0: + dependencies: + buffer: 5.6.0 + optional: true + create-require@1.1.1: {} cron-parser@4.9.0: dependencies: luxon: 3.7.2 + cross-dirname@0.1.0: + optional: true + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -11167,9 +13151,9 @@ snapshots: cssstyle@5.3.7: dependencies: '@asamuzakjp/css-color': 4.1.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.0 + '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) css-tree: 3.2.1 - lru-cache: 11.2.6 + lru-cache: 11.2.7 optional: true csstype@3.2.3: {} @@ -11215,6 +13199,10 @@ snapshots: decimal.js@10.6.0: optional: true + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + dedent@1.7.1: {} deep-is@0.1.4: {} @@ -11226,7 +13214,8 @@ snapshots: defaults@1.0.4: dependencies: clone: 1.0.4 - optional: true + + defer-to-connect@2.0.1: {} define-data-property@1.1.4: dependencies: @@ -11254,6 +13243,9 @@ snapshots: detect-libc@2.1.2: {} + detect-node@2.1.0: + optional: true + dezalgo@1.0.4: dependencies: asap: 2.0.6 @@ -11261,10 +13253,40 @@ snapshots: diff@4.0.4: {} + dir-compare@4.2.0: + dependencies: + minimatch: 3.1.2 + p-limit: 3.1.0 + dir-glob@3.0.1: dependencies: path-type: 4.0.0 + dmg-builder@26.8.1(electron-builder-squirrel-windows@26.8.1): + dependencies: + app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) + builder-util: 26.8.1 + fs-extra: 10.1.0 + iconv-lite: 0.6.3 + js-yaml: 4.1.1 + optionalDependencies: + dmg-license: 1.0.11 + transitivePeerDependencies: + - electron-builder-squirrel-windows + - supports-color + + dmg-license@1.0.11: + dependencies: + '@types/plist': 3.0.5 + '@types/verror': 1.10.11 + ajv: 6.12.6 + crc: 3.8.0 + iconv-corefoundation: 1.1.7 + plist: 3.1.0 + smart-buffer: 4.2.0 + verror: 1.10.1 + optional: true + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -11300,6 +13322,10 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 + dotenv-expand@11.0.7: + dependencies: + dotenv: 16.6.1 + dotenv@16.6.1: {} dotenv@17.2.3: {} @@ -11310,6 +13336,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + easy-table@1.1.0: optionalDependencies: wcwidth: 1.0.1 @@ -11323,8 +13351,95 @@ snapshots: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-builder-squirrel-windows@26.8.1(dmg-builder@26.8.1): + dependencies: + app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) + builder-util: 26.8.1 + electron-winstaller: 5.4.0 + transitivePeerDependencies: + - dmg-builder + - supports-color + + electron-builder@26.8.1(electron-builder-squirrel-windows@26.8.1): + dependencies: + app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) + builder-util: 26.8.1 + builder-util-runtime: 9.5.1 + chalk: 4.1.2 + ci-info: 4.4.0 + dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.8.1) + fs-extra: 10.1.0 + lazy-val: 1.0.5 + simple-update-notifier: 2.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - electron-builder-squirrel-windows + - supports-color + + electron-publish@26.8.1: + dependencies: + '@types/fs-extra': 9.0.13 + builder-util: 26.8.1 + builder-util-runtime: 9.5.1 + chalk: 4.1.2 + form-data: 4.0.5 + fs-extra: 10.1.0 + lazy-val: 1.0.5 + mime: 2.6.0 + transitivePeerDependencies: + - supports-color + electron-to-chromium@1.5.302: {} + electron-updater@6.8.3: + dependencies: + builder-util-runtime: 9.5.1 + fs-extra: 10.1.0 + js-yaml: 4.1.1 + lazy-val: 1.0.5 + lodash.escaperegexp: 4.1.2 + lodash.isequal: 4.5.0 + semver: 7.7.4 + tiny-typed-emitter: 2.1.0 + transitivePeerDependencies: + - supports-color + + electron-vite@5.0.0(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + cac: 6.7.14 + esbuild: 0.25.12 + magic-string: 0.30.21 + picocolors: 1.1.1 + vite: 6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + + electron-winstaller@5.4.0: + dependencies: + '@electron/asar': 3.4.1 + debug: 4.4.3 + fs-extra: 7.0.1 + lodash: 4.17.23 + temp: 0.9.4 + optionalDependencies: + '@electron/windows-sign': 1.2.2 + transitivePeerDependencies: + - supports-color + + electron@35.7.5: + dependencies: + '@electron/get': 2.0.3 + '@types/node': 22.19.15 + extract-zip: 2.0.1 + transitivePeerDependencies: + - supports-color + elegant.def@3.0.0-alpha.2: dependencies: ast-traverse: 0.1.1 @@ -11334,13 +13449,17 @@ snapshots: jsonfy: 0.1.0 sscan: 0.1.3 - emoji-regex@8.0.0: - optional: true + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} empathic@2.0.0: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -11359,6 +13478,10 @@ snapshots: dependencies: is-buffer: 1.1.6 + env-paths@2.2.1: {} + + err-code@2.0.3: {} + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -11468,6 +13591,38 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es6-error@4.1.1: + optional: true + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.3: optionalDependencies: '@esbuild/aix-ppc64': 0.27.3 @@ -11497,6 +13652,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + esbuild@0.27.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -11740,8 +13924,23 @@ snapshots: expect-type@1.3.0: {} + exponential-backoff@3.1.3: {} + exsolve@1.0.8: {} + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + extsprintf@1.4.1: + optional: true + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 @@ -11838,6 +14037,10 @@ snapshots: dependencies: reusify: 1.1.0 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -11856,6 +14059,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -11911,6 +14118,43 @@ snapshots: fresh@0.5.2: {} + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@11.3.4: + 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 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -11945,8 +14189,7 @@ snapshots: gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: - optional: true + get-caller-file@2.0.5: {} get-east-asian-width@1.5.0: {} @@ -11970,6 +14213,10 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + get-stream@9.0.1: dependencies: '@sec-ant/readable-stream': 0.4.1 @@ -12004,6 +14251,15 @@ snapshots: glob-to-regexp@0.4.1: {} + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@13.0.6: dependencies: minimatch: 10.2.4 @@ -12018,6 +14274,25 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-agent@3.0.0: + dependencies: + boolean: 3.2.0 + es6-error: 4.1.1 + matcher: 3.0.0 + roarr: 2.15.4 + semver: 7.7.4 + serialize-error: 7.0.1 + optional: true + globals@14.0.0: {} globals@16.4.0: {} @@ -12038,6 +14313,20 @@ snapshots: gopd@1.2.0: {} + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + graceful-fs@4.2.11: {} grammex@3.1.12: {} @@ -12080,6 +14369,10 @@ snapshots: hono@4.10.6: {} + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) @@ -12089,31 +14382,42 @@ snapshots: html-escaper@2.0.2: {} + http-cache-semantics@4.2.0: {} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 debug: 4.4.3 transitivePeerDependencies: - supports-color - optional: true http-status-codes@2.3.0: {} + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 debug: 4.4.3 transitivePeerDependencies: - supports-color - optional: true human-signals@8.0.1: {} + iconv-corefoundation@1.1.7: + dependencies: + cli-truncate: 2.1.0 + node-addon-api: 1.7.2 + optional: true + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -12170,6 +14474,8 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.1.0: {} + ipaddr.js@2.3.0: {} is-array-buffer@3.0.5: @@ -12232,8 +14538,7 @@ snapshots: dependencies: call-bound: 1.0.4 - is-fullwidth-code-point@3.0.0: - optional: true + is-fullwidth-code-point@3.0.0: {} is-generator-function@1.1.2: dependencies: @@ -12247,6 +14552,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-interactive@1.0.0: {} + is-interactive@2.0.0: {} is-map@2.0.3: {} @@ -12302,6 +14609,8 @@ snapshots: dependencies: which-typed-array: 1.1.20 + is-unicode-supported@0.1.0: {} + is-unicode-supported@2.1.0: {} is-weakmap@2.0.2: {} @@ -12319,8 +14628,12 @@ snapshots: isbinaryfile@4.0.10: {} + isbinaryfile@5.0.7: {} + isexe@2.0.0: {} + isexe@3.1.5: {} + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -12351,6 +14664,18 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 + jest-worker@27.5.1: dependencies: '@types/node': 25.0.3 @@ -12386,7 +14711,7 @@ snapshots: parse5: 8.0.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.0 + tough-cookie: 6.0.1 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 4.0.0 @@ -12418,11 +14743,24 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-safe@5.0.1: + optional: true + json5@1.0.2: dependencies: minimist: 1.2.8 - json5@2.2.3: {} + json5@2.2.3: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 jsonfy@0.1.0: {} @@ -12469,6 +14807,8 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + lazy-val@1.0.5: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -12567,12 +14907,16 @@ snapshots: lodash.defaults@4.2.0: {} + lodash.escaperegexp@4.1.2: {} + lodash.includes@4.3.0: {} lodash.isarguments@3.1.0: {} lodash.isboolean@3.0.3: {} + lodash.isequal@4.5.0: {} + lodash.isinteger@4.0.4: {} lodash.isnumber@3.0.3: {} @@ -12596,6 +14940,13 @@ snapshots: lodash@4.17.21: {} + lodash@4.17.23: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + log-symbols@7.0.1: dependencies: is-unicode-supported: 2.1.0 @@ -12611,12 +14962,23 @@ snapshots: dependencies: tslib: 2.8.1 + lowercase-keys@2.0.0: {} + + lru-cache@10.4.3: {} + lru-cache@11.2.6: {} + lru-cache@11.2.7: + optional: true + lru-cache@5.1.1: dependencies: yallist: 3.1.1 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + lru.min@1.1.3: {} luxon@3.7.2: {} @@ -12639,10 +15001,31 @@ snapshots: make-error@1.3.6: {} + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.3 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 + transitivePeerDependencies: + - supports-color + makeerror@1.0.12: dependencies: tmpl: 1.0.5 + matcher@3.0.0: + dependencies: + escape-string-regexp: 4.0.0 + optional: true + math-intrinsics@1.1.0: {} mdn-data@2.0.28: {} @@ -12690,8 +15073,14 @@ snapshots: mime@2.6.0: {} + mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} + mimic-response@1.0.1: {} + + mimic-response@3.1.0: {} + min-indent@1.0.1: {} minimatch@10.2.4: @@ -12702,14 +15091,54 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 minimist@1.2.8: {} + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.3 + minipass-sized: 1.0.3 + minizlib: 3.1.0 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + minipass@7.1.3: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + module-details-from-path@1.0.4: {} moment@2.10.2: {} @@ -12750,8 +15179,8 @@ snapshots: rettime: 0.10.1 statuses: 2.0.2 strict-event-emitter: 0.5.1 - tough-cookie: 6.0.0 - type-fest: 5.4.4 + tough-cookie: 6.0.1 + type-fest: 5.5.0 until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: @@ -12777,7 +15206,7 @@ snapshots: aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 long: 5.3.2 lru.min: 1.1.3 named-placeholders: 1.1.6 @@ -12798,6 +15227,8 @@ snapshots: negotiator@0.6.3: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): @@ -12830,8 +15261,19 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 + node-abi@4.28.0: + dependencies: + semver: 7.7.4 + node-abort-controller@3.1.1: {} + node-addon-api@1.7.2: + optional: true + + node-api-version@0.2.1: + dependencies: + semver: 7.7.4 + node-exports-info@1.6.0: dependencies: array.prototype.flatmap: 1.3.3 @@ -12846,6 +15288,21 @@ snapshots: detect-libc: 2.1.2 optional: true + node-gyp@11.5.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.4 + tar: 7.5.11 + tinyglobby: 0.2.15 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + node-mocks-http@1.17.2(@types/node@25.0.3): dependencies: accepts: 1.3.8 @@ -12863,8 +15320,14 @@ snapshots: node-releases@2.0.27: {} + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + normalize-path@3.0.0: {} + normalize-url@6.1.0: {} + npm-run-path@6.0.0: dependencies: path-key: 4.0.0 @@ -12934,6 +15397,10 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -12949,6 +15416,18 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + ora@9.3.0: dependencies: chalk: 5.6.2 @@ -12969,6 +15448,8 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-cancelable@2.1.1: {} + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -12985,6 +15466,8 @@ snapshots: dependencies: p-limit: 3.1.0 + p-map@7.0.4: {} + p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -13021,6 +15504,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + path-scurry@2.0.2: dependencies: lru-cache: 11.2.6 @@ -13033,6 +15521,10 @@ snapshots: pathe@2.0.3: {} + pe-library@0.4.1: {} + + pend@1.2.0: {} + perfect-debounce@1.0.0: {} pg-cloudflare@1.2.7: @@ -13128,6 +15620,12 @@ snapshots: dependencies: queue-lit: 1.5.2 + plist@3.1.0: + dependencies: + '@xmldom/xmldom': 0.8.11 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + possible-typed-array-names@1.1.0: {} postcss@8.4.31: @@ -13142,6 +15640,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgres-array@2.0.0: {} postgres-array@3.0.4: {} @@ -13176,6 +15680,11 @@ snapshots: dependencies: '@posthog/core': 1.23.2 + postject@1.0.0-alpha.6: + dependencies: + commander: 9.5.0 + optional: true + preact@10.28.4: {} prelude-ls@1.2.1: {} @@ -13208,10 +15717,19 @@ snapshots: - react - react-dom + proc-log@5.0.0: {} + process-warning@4.0.1: {} process-warning@5.0.0: {} + progress@2.0.3: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -13246,6 +15764,11 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -13264,6 +15787,8 @@ snapshots: quick-format-unescaped@4.0.4: {} + quick-lru@5.1.1: {} + radash@12.1.1: {} randombytes@2.1.0: @@ -13288,8 +15813,22 @@ snapshots: react-refresh@0.18.0: {} + react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + cookie: 1.1.1 + react: 19.2.3 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.3(react@19.2.3) + react@19.2.3: {} + read-binary-file-arch@1.0.6: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -13362,8 +15901,7 @@ snapshots: dependencies: type-fest: 4.41.0 - require-directory@2.1.1: - optional: true + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -13376,6 +15914,12 @@ snapshots: requires-port@1.0.0: {} + resedit@1.7.2: + dependencies: + pe-library: 0.4.1 + + resolve-alpn@1.2.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -13397,6 +15941,15 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -13413,11 +15966,25 @@ snapshots: rfdc@1.4.1: {} + rimraf@2.6.3: + dependencies: + glob: 7.2.3 + rimraf@6.1.3: dependencies: glob: 13.0.6 package-json-from-dist: 1.0.1 + roarr@2.15.4: + dependencies: + boolean: 3.2.0 + detect-node: 2.1.0 + globalthis: 1.0.4 + json-stringify-safe: 5.0.1 + semver-compare: 1.0.0 + sprintf-js: 1.1.3 + optional: true + rollup@4.58.0: dependencies: '@types/estree': 1.0.8 @@ -13449,6 +16016,37 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.58.0 fsevents: 2.3.3 + rollup@4.59.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 + fsevents: 2.3.3 + rou3@0.7.12: {} run-parallel@1.2.0: @@ -13486,6 +16084,12 @@ snapshots: safer-buffer@2.1.2: {} + sanitize-filename@1.6.3: + dependencies: + truncate-utf8-bytes: 1.0.2 + + sax@1.6.0: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -13502,6 +16106,11 @@ snapshots: secure-json-parse@4.1.0: {} + semver-compare@1.0.0: + optional: true + + semver@5.7.2: {} + semver@6.3.1: {} semver@7.7.3: {} @@ -13510,6 +16119,11 @@ snapshots: seq-queue@0.0.5: {} + serialize-error@7.0.1: + dependencies: + type-fest: 0.13.1 + optional: true + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -13610,6 +16224,10 @@ snapshots: signal-exit@4.1.0: {} + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.4 + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 @@ -13618,11 +16236,33 @@ snapshots: slash@3.0.0: {} + slice-ansi@3.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + optional: true + + smart-buffer@4.2.0: {} + snake-case@3.0.4: dependencies: dot-case: 3.0.4 tslib: 2.8.1 + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.1.0 + smart-buffer: 4.2.0 + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -13642,6 +16282,9 @@ snapshots: split2@4.2.0: {} + sprintf-js@1.1.3: + optional: true + sqlstring@2.3.3: {} srvx@0.8.16: {} @@ -13650,12 +16293,18 @@ snapshots: sscan@0.2.0: {} + ssri@12.0.0: + dependencies: + minipass: 7.1.3 + stable-hash@0.0.5: {} stackback@0.0.2: {} standard-as-callback@2.1.0: {} + stat-mode@1.0.0: {} + statuses@2.0.2: optional: true @@ -13683,7 +16332,12 @@ snapshots: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - optional: true + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 string-width@8.2.0: dependencies: @@ -13747,7 +16401,6 @@ snapshots: strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - optional: true strip-ansi@7.1.2: dependencies: @@ -13774,6 +16427,12 @@ snapshots: optionalDependencies: '@babel/core': 7.29.0 + sumchecker@3.0.1: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + superagent@10.3.0: dependencies: component-emitter: 1.3.1 @@ -13827,6 +16486,24 @@ snapshots: tapable@2.3.0: {} + tar@7.5.11: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + temp-file@3.4.0: + dependencies: + async-exit-hook: 2.0.1 + fs-extra: 10.1.0 + + temp@0.9.4: + dependencies: + mkdirp: 0.5.6 + rimraf: 2.6.3 + terser-webpack-plugin@5.3.16(webpack@5.105.2): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -13847,6 +16524,12 @@ snapshots: dependencies: real-require: 0.2.0 + tiny-async-pool@1.3.0: + dependencies: + semver: 5.7.2 + + tiny-typed-emitter@2.1.0: {} + tinybench@2.9.0: {} tinyexec@1.0.2: {} @@ -13858,14 +16541,20 @@ snapshots: tinyrainbow@3.0.3: {} - tldts-core@7.0.25: + tldts-core@7.0.26: optional: true - tldts@7.0.25: + tldts@7.0.26: dependencies: - tldts-core: 7.0.25 + tldts-core: 7.0.26 optional: true + tmp-promise@3.0.3: + dependencies: + tmp: 0.2.5 + + tmp@0.2.5: {} + tmpl@1.0.5: {} to-regex-range@5.0.1: @@ -13876,9 +16565,9 @@ snapshots: totalist@3.0.1: {} - tough-cookie@6.0.0: + tough-cookie@6.0.1: dependencies: - tldts: 7.0.25 + tldts: 7.0.26 optional: true tr46@6.0.0: @@ -13886,6 +16575,10 @@ snapshots: punycode: 2.3.1 optional: true + truncate-utf8-bytes@1.0.2: + dependencies: + utf8-byte-length: 1.0.5 + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -13950,12 +16643,20 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@0.13.1: + optional: true + type-fest@4.41.0: {} type-fest@5.4.4: dependencies: tagged-tag: 1.0.0 + type-fest@5.5.0: + dependencies: + tagged-tag: 1.0.0 + optional: true + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -14023,6 +16724,8 @@ snapshots: uncrypto@0.1.3: optional: true + undici-types@6.21.0: {} + undici-types@7.16.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -14038,6 +16741,18 @@ snapshots: unicorn-magic@0.3.0: {} + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + + universalify@0.1.2: {} + + universalify@2.0.1: {} + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -14080,6 +16795,8 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + utf8-byte-length@1.0.5: {} + util-deprecate@1.0.2: {} uuid@11.1.0: {} @@ -14090,15 +16807,22 @@ snapshots: optionalDependencies: typescript: 5.9.3 + verror@1.10.1: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.1 + optional: true + vi-mock@1.0.0: dependencies: assert-plus: 0.1.5 enum: 2.5.0 yod-mock: 0.3.1 - vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: - esbuild: 0.27.3 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 @@ -14113,10 +16837,27 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 + vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.4 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.0.3 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.31.1 + terser: 5.46.0 + tsx: 4.21.0 + yaml: 2.8.2 + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(@vitest/ui@4.0.18)(jiti@2.6.1)(jsdom@27.4.0(@noble/hashes@2.0.1))(lightningcss@1.31.1)(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -14133,7 +16874,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -14170,7 +16911,6 @@ snapshots: wcwidth@1.0.1: dependencies: defaults: 1.0.4 - optional: true web-vitals@5.1.0: {} @@ -14268,6 +17008,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@5.0.0: + dependencies: + isexe: 3.1.5 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -14289,7 +17033,12 @@ snapshots: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - optional: true + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 wrappy@1.0.2: {} @@ -14299,24 +17048,25 @@ snapshots: xml-name-validator@5.0.0: optional: true + xmlbuilder@15.1.1: {} + xmlchars@2.2.0: optional: true xtend@4.0.2: {} - y18n@5.0.8: - optional: true + y18n@5.0.8: {} yallist@3.1.1: {} - yallist@4.0.0: - optional: true + yallist@4.0.0: {} + + yallist@5.0.0: {} yaml@2.8.2: optional: true - yargs-parser@21.1.1: - optional: true + yargs-parser@21.1.1: {} yargs-parser@22.0.0: {} @@ -14329,7 +17079,11 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - optional: true + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 yn@3.1.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 54fcb0b..85a5ccd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,6 +7,8 @@ onlyBuiltDependencies: - '@prisma/engines' - '@sentry/cli' - esbuild + - electron + - electron-winstaller - prisma - sharp - unrs-resolver