From 198d6e968b2a08b777882102ee3ff8022e56076d Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Mon, 11 Aug 2025 02:56:25 +0530 Subject: [PATCH] chore(cli): add smoke tests (#489) --- apps/cli/.gitignore | 3 +- apps/cli/package.json | 6 +- .../project-generation/post-installation.ts | 2 +- .../project-generation/template-manager.ts | 71 +- apps/cli/src/utils/errors.ts | 2 +- apps/cli/test/cli.smoke.test.ts | 2612 +++++++++++++++++ apps/cli/tsconfig.json | 2 +- apps/cli/vitest.config.ts | 15 + bun.lock | 169 +- 9 files changed, 2831 insertions(+), 51 deletions(-) create mode 100644 apps/cli/test/cli.smoke.test.ts create mode 100644 apps/cli/vitest.config.ts diff --git a/apps/cli/.gitignore b/apps/cli/.gitignore index b2d59d1..9837191 100644 --- a/apps/cli/.gitignore +++ b/apps/cli/.gitignore @@ -1,2 +1,3 @@ /node_modules -/dist \ No newline at end of file +/dist +.smoke \ No newline at end of file diff --git a/apps/cli/package.json b/apps/cli/package.json index cc6e161..34264ae 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -49,7 +49,8 @@ "dev": "tsdown --watch", "check-types": "tsc --noEmit", "check": "biome check --write .", - "test": "vitest run", + "test": "bun run build && vitest run", + "test:with-build": "bun run build && WITH_BUILD=1 vitest run", "prepublishOnly": "npm run build" }, "dependencies": { @@ -70,6 +71,7 @@ "@types/fs-extra": "^11.0.4", "@types/node": "^24.2.0", "tsdown": "^0.13.3", - "typescript": "^5.9.2" + "typescript": "^5.9.2", + "vitest": "^3.2.4" } } diff --git a/apps/cli/src/helpers/project-generation/post-installation.ts b/apps/cli/src/helpers/project-generation/post-installation.ts index 45b8652..e89504d 100644 --- a/apps/cli/src/helpers/project-generation/post-installation.ts +++ b/apps/cli/src/helpers/project-generation/post-installation.ts @@ -236,7 +236,7 @@ async function getDatabaseInstructions( ); instructions.push( `${pc.cyan("4.")} Generate migrations: ${pc.white( - "cd apps/server && bun db:generate", + `cd apps/server && ${packageManager} db:generate`, )}`, ); instructions.push( diff --git a/apps/cli/src/helpers/project-generation/template-manager.ts b/apps/cli/src/helpers/project-generation/template-manager.ts index b94b284..c3141da 100644 --- a/apps/cli/src/helpers/project-generation/template-manager.ts +++ b/apps/cli/src/helpers/project-generation/template-manager.ts @@ -11,12 +11,14 @@ async function processAndCopyFiles( destDir: string, context: ProjectConfig, overwrite = true, + ignorePatterns?: string[], ) { const sourceFiles = await globby(sourcePattern, { cwd: baseSourceDir, dot: true, onlyFiles: true, absolute: false, + ignore: ignorePatterns, }); for (const relativeSrcPath of sourceFiles) { @@ -26,28 +28,30 @@ async function processAndCopyFiles( if (relativeSrcPath.endsWith(".hbs")) { relativeDestPath = relativeSrcPath.slice(0, -4); } - const basename = path.basename(relativeSrcPath); + + const basename = path.basename(relativeDestPath); if (basename === "_gitignore") { - relativeDestPath = path.join(path.dirname(relativeSrcPath), ".gitignore"); + relativeDestPath = path.join( + path.dirname(relativeDestPath), + ".gitignore", + ); } else if (basename === "_npmrc") { - relativeDestPath = path.join(path.dirname(relativeSrcPath), ".npmrc"); + relativeDestPath = path.join(path.dirname(relativeDestPath), ".npmrc"); } const destPath = path.join(destDir, relativeDestPath); - try { - await fs.ensureDir(path.dirname(destPath)); + await fs.ensureDir(path.dirname(destPath)); - if (!overwrite && (await fs.pathExists(destPath))) { - continue; - } + if (!overwrite && (await fs.pathExists(destPath))) { + continue; + } - if (srcPath.endsWith(".hbs")) { - await processTemplate(srcPath, destPath, context); - } else { - await fs.copy(srcPath, destPath, { overwrite: true }); - } - } catch (_error) {} + if (srcPath.endsWith(".hbs")) { + await processTemplate(srcPath, destPath, context); + } else { + await fs.copy(srcPath, destPath, { overwrite: true }); + } } } @@ -655,26 +659,14 @@ export async function setupExamplesTemplate( ignorePatterns.push("next/**"); } - const generalServerFiles = await globby(["**/*.ts", "**/*.hbs"], { - cwd: exampleServerSrc, - onlyFiles: true, - deep: 1, - ignore: ignorePatterns, - }); - - for (const file of generalServerFiles) { - const srcPath = path.join(exampleServerSrc, file); - const destPath = path.join(serverAppDir, file.replace(".hbs", "")); - try { - if (srcPath.endsWith(".hbs")) { - await processTemplate(srcPath, destPath, context); - } else { - if (!(await fs.pathExists(destPath))) { - await fs.copy(srcPath, destPath, { overwrite: false }); - } - } - } catch (_error) {} - } + await processAndCopyFiles( + ["**/*.ts", "**/*.hbs"], + exampleServerSrc, + serverAppDir, + context, + false, + ignorePatterns, + ); } if (webAppDirExists) { @@ -791,9 +783,13 @@ export async function handleExtras(projectDir: string, context: ProjectConfig) { if (context.packageManager === "bun") { const bunfigSrc = path.join(extrasDir, "bunfig.toml.hbs"); - const bunfigDest = path.join(projectDir, "bunfig.toml"); if (await fs.pathExists(bunfigSrc)) { - await processTemplate(bunfigSrc, bunfigDest, context); + await processAndCopyFiles( + "bunfig.toml.hbs", + extrasDir, + projectDir, + context, + ); } } @@ -802,9 +798,8 @@ export async function handleExtras(projectDir: string, context: ProjectConfig) { (hasNative || context.frontend.includes("nuxt")) ) { const npmrcTemplateSrc = path.join(extrasDir, "_npmrc.hbs"); - const npmrcDest = path.join(projectDir, ".npmrc"); if (await fs.pathExists(npmrcTemplateSrc)) { - await processTemplate(npmrcTemplateSrc, npmrcDest, context); + await processAndCopyFiles("_npmrc.hbs", extrasDir, projectDir, context); } } diff --git a/apps/cli/src/utils/errors.ts b/apps/cli/src/utils/errors.ts index f5601f0..6afad34 100644 --- a/apps/cli/src/utils/errors.ts +++ b/apps/cli/src/utils/errors.ts @@ -1,5 +1,5 @@ import { cancel } from "@clack/prompts"; -import { consola } from "consola"; +import consola from "consola"; import pc from "picocolors"; export function exitWithError(message: string): never { diff --git a/apps/cli/test/cli.smoke.test.ts b/apps/cli/test/cli.smoke.test.ts new file mode 100644 index 0000000..5565b33 --- /dev/null +++ b/apps/cli/test/cli.smoke.test.ts @@ -0,0 +1,2612 @@ +import { join } from "node:path"; +import consola from "consola"; +import { execa } from "execa"; +import { + ensureDirSync, + existsSync, + readFileSync, + readJsonSync, + removeSync, +} from "fs-extra"; +import * as JSONC from "jsonc-parser"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +const CLI_BIN = join(__dirname, "..", "dist", "index.js"); + +function createTmpDir(_prefix: string) { + const dir = join(__dirname, "..", ".smoke"); + if (existsSync(dir)) { + removeSync(dir); + } + ensureDirSync(dir); + return dir; +} + +async function runCli(args: string[], cwd: string, env?: NodeJS.ProcessEnv) { + const subprocess = execa("node", [CLI_BIN, ...args], { + cwd, + env: { + ...process.env, + BTS_TELEMETRY_DISABLED: "1", + ...env, + }, + }); + subprocess.stdout?.pipe(process.stdout); + subprocess.stderr?.pipe(process.stderr); + const { exitCode } = await subprocess; + expect(exitCode).toBe(0); +} + +async function runCliExpectingError( + args: string[], + cwd: string, + env?: NodeJS.ProcessEnv, +) { + const subprocess = execa("node", [CLI_BIN, ...args], { + cwd, + env: { + ...process.env, + BTS_TELEMETRY_DISABLED: "1", + ...env, + }, + reject: false, + }); + subprocess.stdout?.pipe(process.stdout); + subprocess.stderr?.pipe(process.stderr); + const { exitCode } = await subprocess; + expect(exitCode).not.toBe(0); +} + +function assertScaffoldedProject(dir: string) { + const pkgJsonPath = join(dir, "package.json"); + expect(existsSync(pkgJsonPath)).toBe(true); + const pkg = readJsonSync(pkgJsonPath); + expect(typeof pkg.name).toBe("string"); + expect(Array.isArray(pkg.workspaces)).toBe(true); +} + +function assertProjectStructure( + dir: string, + options: { + hasWeb?: boolean; + hasNative?: boolean; + hasServer?: boolean; + hasConvexBackend?: boolean; + hasTurborepo?: boolean; + hasBiome?: boolean; + hasAuth?: boolean; + hasDatabase?: boolean; + }, +) { + const { + hasWeb = false, + hasNative = false, + hasServer = false, + hasConvexBackend = false, + hasTurborepo = false, + hasBiome = false, + hasAuth = false, + hasDatabase = false, + } = options; + + expect(existsSync(join(dir, "package.json"))).toBe(true); + expect(existsSync(join(dir, ".gitignore"))).toBe(true); + + if (hasWeb) { + expect(existsSync(join(dir, "apps", "web", "package.json"))).toBe(true); + const webDir = join(dir, "apps", "web"); + const hasViteConfig = existsSync(join(webDir, "vite.config.ts")); + const hasNextConfig = + existsSync(join(webDir, "next.config.mjs")) || + existsSync(join(webDir, "next.config.js")); + const hasNuxtConfig = existsSync(join(webDir, "nuxt.config.ts")); + const hasSvelteConfig = existsSync(join(webDir, "svelte.config.js")); + const hasTsConfig = existsSync(join(webDir, "tsconfig.json")); + + const hasSrcDir = existsSync(join(webDir, "src")); + const hasAppDir = existsSync(join(webDir, "app")); + const hasPublicDir = existsSync(join(webDir, "public")); + + expect( + hasViteConfig || + hasNextConfig || + hasNuxtConfig || + hasSvelteConfig || + hasTsConfig || + hasSrcDir || + hasAppDir || + hasPublicDir, + ).toBe(true); + } + + if (hasNative) { + const nativeDir = join(dir, "apps", "native"); + expect(existsSync(join(nativeDir, "package.json"))).toBe(true); + const hasAppConfig = existsSync(join(nativeDir, "app.json")); + const hasExpoConfig = existsSync(join(nativeDir, "expo")); + const hasSrcDir = existsSync(join(nativeDir, "src")); + const hasMainFile = + existsSync(join(nativeDir, "App.tsx")) || + existsSync(join(nativeDir, "index.tsx")) || + existsSync(join(nativeDir, "index.js")); + expect(hasAppConfig || hasExpoConfig || hasSrcDir || hasMainFile).toBe( + true, + ); + } + + if (hasServer) { + expect(existsSync(join(dir, "apps", "server", "package.json"))).toBe(true); + expect(existsSync(join(dir, "apps", "server", "src", "index.ts"))).toBe( + true, + ); + } + + if (hasConvexBackend) { + const hasPackagesDir = existsSync(join(dir, "packages")); + const hasConvexRelated = + existsSync(join(dir, "packages", "backend")) || + existsSync(join(dir, "convex")) || + existsSync(join(dir, "convex.config.ts")); + expect(hasPackagesDir || hasConvexRelated).toBe(true); + } + + if (hasTurborepo) { + expect(existsSync(join(dir, "turbo.json"))).toBe(true); + } + + if (hasBiome) { + expect(existsSync(join(dir, "biome.json"))).toBe(true); + } + + if (hasAuth && hasServer) { + expect( + existsSync(join(dir, "apps", "server", "src", "lib", "auth.ts")), + ).toBe(true); + } + + if (hasDatabase && hasServer) { + const serverDir = join(dir, "apps", "server"); + if (existsSync(serverDir)) { + const hasDrizzleConfig = existsSync(join(serverDir, "drizzle.config.ts")); + const hasPrismaSchema = existsSync( + join(serverDir, "prisma", "schema.prisma"), + ); + const hasDbFolder = existsSync(join(serverDir, "src", "db")); + const hasSchemaFile = existsSync(join(serverDir, "src", "schema.ts")); + const hasLibFolder = existsSync(join(serverDir, "src", "lib")); + + const hasRootPrismaDir = existsSync(join(dir, "prisma")); + const hasRootPrismaSchema = existsSync( + join(dir, "prisma", "schema.prisma"), + ); + + expect( + hasDrizzleConfig || + hasPrismaSchema || + hasDbFolder || + hasSchemaFile || + hasLibFolder || + hasRootPrismaDir || + hasRootPrismaSchema, + ).toBe(true); + } + } + + expect(existsSync(join(dir, "bts.jsonc"))).toBe(true); + const btsConfig = readFileSync(join(dir, "bts.jsonc"), "utf8"); + expect(btsConfig).toContain("Better-T-Stack configuration"); +} + +function assertBtsConfig( + dir: string, + expectedConfig: Partial<{ + frontend: string[]; + backend: string; + database: string; + orm: string; + auth: boolean; + addons: string[]; + examples: string[]; + api: string; + runtime: string; + packageManager: string; + }>, +) { + const btsConfigPath = join(dir, "bts.jsonc"); + expect(existsSync(btsConfigPath)).toBe(true); + const content = readFileSync(btsConfigPath, "utf8"); + + type BtsConfig = { + frontend?: string[]; + backend?: string; + database?: string; + orm?: string; + auth?: boolean; + addons?: string[]; + examples?: string[]; + api?: string; + runtime?: string; + packageManager?: string; + }; + + const errors: JSONC.ParseError[] = []; + const parsed = JSONC.parse(content, errors, { + allowTrailingComma: true, + disallowComments: false, + }) as BtsConfig | null; + + if (errors.length > 0 || !parsed) { + consola.error("Failed to parse bts.jsonc", errors); + throw new Error("Failed to parse bts.jsonc"); + } + const config = parsed; + + if (expectedConfig.frontend) { + expect(config.frontend).toEqual(expectedConfig.frontend); + } + if (expectedConfig.backend) { + expect(config.backend).toBe(expectedConfig.backend); + } + if (expectedConfig.database) { + expect(config.database).toBe(expectedConfig.database); + } + if (expectedConfig.orm) { + expect(config.orm).toBe(expectedConfig.orm); + } + if (expectedConfig.auth !== undefined) { + expect(config.auth).toBe(expectedConfig.auth); + } + if (expectedConfig.addons) { + expect(config.addons).toEqual(expectedConfig.addons); + } + if (expectedConfig.examples) { + expect(config.examples).toEqual(expectedConfig.examples); + } + if (expectedConfig.api) { + expect(config.api).toBe(expectedConfig.api); + } + if (expectedConfig.runtime) { + expect(config.runtime).toBe(expectedConfig.runtime); + } + if (expectedConfig.packageManager) { + expect(config.packageManager).toBe(expectedConfig.packageManager); + } +} + +function readBtsConfig(dir: string) { + const btsConfigPath = join(dir, "bts.jsonc"); + if (!existsSync(btsConfigPath)) return {} as Record; + + const content = readFileSync(btsConfigPath, "utf8"); + const errors: JSONC.ParseError[] = []; + const parsed = JSONC.parse(content, errors, { + allowTrailingComma: true, + disallowComments: false, + }) as Record | null; + + if (errors.length > 0 || !parsed) { + return {} as Record; + } + return parsed; +} + +describe("create-better-t-stack smoke", () => { + let workdir: string; + + beforeAll(async () => { + workdir = createTmpDir("cli"); + consola.start("Building CLI..."); + const buildProc = execa("bun", ["run", "build"], { + cwd: join(__dirname, ".."), + env: { + ...process.env, + CI: "true", + NODE_ENV: "production", + }, + }); + buildProc.stdout?.pipe(process.stdout); + buildProc.stderr?.pipe(process.stderr); + const { exitCode } = await buildProc; + expect(exitCode).toBe(0); + consola.success("CLI build completed"); + + const cliBinExists = existsSync(CLI_BIN); + expect(cliBinExists).toBe(true); + consola.info(`CLI binary: ${CLI_BIN}`); + }); + + // Exhaustive matrix: all frontends x standard backends (no db, no orm, no api, no auth) + describe("frontend x backend matrix (no db, no api)", () => { + const FRONTENDS = [ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + "solid", + "native-nativewind", + "native-unistyles", + ] as const; + const BACKENDS = ["hono", "express", "fastify", "elysia"] as const; + + const WEB_FRONTENDS = new Set([ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + "solid", + ]); + + for (const backend of BACKENDS) { + describe(`backend=${backend}`, () => { + for (const frontend of FRONTENDS) { + it(`scaffolds ${frontend} + ${backend}`, async () => { + const projectName = `app-${backend}-${frontend.replace(/[^a-z-]/g, "").slice(0, 30)}`; + await runCli( + [ + projectName, + "--yes", + "--frontend", + frontend, + "--backend", + backend, + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: WEB_FRONTENDS.has(frontend), + hasNative: + frontend === "native-nativewind" || + frontend === "native-unistyles", + hasServer: true, + }); + assertBtsConfig(projectDir, { + frontend: [frontend], + backend, + database: "none", + orm: "none", + auth: false, + }); + }); + } + }); + } + }); + + describe("convex backend with all compatible frontends", () => { + const FRONTENDS = [ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + "native-nativewind", + "native-unistyles", + ] as const; + const WEB_FRONTENDS = new Set([ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + ]); + + for (const frontend of FRONTENDS) { + it(`scaffolds ${frontend} + convex`, async () => { + const projectName = `app-convex-${frontend.replace(/[^a-z-]/g, "").slice(0, 30)}`; + await runCli( + [ + projectName, + "--yes", + "--frontend", + frontend, + "--backend", + "convex", + "--db-setup", + "none", + "--addons", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: WEB_FRONTENDS.has(frontend), + hasNative: + frontend === "native-nativewind" || frontend === "native-unistyles", + hasConvexBackend: true, + hasServer: false, + }); + assertBtsConfig(projectDir, { + frontend: [frontend], + backend: "convex", + database: "none", + orm: "none", + auth: false, + }); + }); + } + }); + afterAll(() => { + try { + removeSync(workdir); + } catch {} + }); + + it("scaffolds minimal default project with yes flag", async () => { + const projectName = "app-default"; + await runCli( + [ + projectName, + "--yes", + "--db-setup", + "none", + "--addons", + "none", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasAuth: true, + hasDatabase: true, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "hono", + database: "sqlite", + orm: "drizzle", + auth: true, + addons: [], + }); + }); + + it("scaffolds with explicit minimal flags (no db, no api, no auth, no addons)", async () => { + const projectName = "app-min"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasAuth: false, + hasDatabase: false, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "hono", + database: "none", + orm: "none", + auth: false, + addons: [], + }); + }); + + it("scaffolds with turborepo addon", async () => { + const projectName = "app-turbo"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "turborepo", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasTurborepo: true, + hasAuth: false, + hasDatabase: false, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "hono", + addons: ["turborepo"], + }); + }); + + it("scaffolds convex preset", async () => { + const projectName = "app-convex"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "convex", + "--db-setup", + "none", + "--addons", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasConvexBackend: true, + hasServer: false, + hasAuth: false, + hasDatabase: false, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "convex", + database: "none", + orm: "none", + auth: false, + examples: ["todo"], + }); + }); + + describe("frontend combinations", () => { + it("scaffolds with Next.js frontend", async () => { + const projectName = "app-next"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "next", + "--backend", + "none", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: false, + }); + assertBtsConfig(projectDir, { + frontend: ["next"], + backend: "none", + }); + }); + + it("scaffolds with Nuxt frontend", async () => { + const projectName = "app-nuxt"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "nuxt", + "--backend", + "none", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: false, + }); + assertBtsConfig(projectDir, { + frontend: ["nuxt"], + backend: "none", + }); + }); + + it("scaffolds with Svelte frontend", async () => { + const projectName = "app-svelte"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "svelte", + "--backend", + "none", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: false, + }); + assertBtsConfig(projectDir, { + frontend: ["svelte"], + backend: "none", + }); + }); + + it("scaffolds with Solid frontend", async () => { + const projectName = "app-solid"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "solid", + "--backend", + "none", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: false, + }); + assertBtsConfig(projectDir, { + frontend: ["solid"], + backend: "none", + }); + }); + + it("scaffolds with React Native (NativeWind)", async () => { + const projectName = "app-native"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "native-nativewind", + "--backend", + "none", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasNative: true, + hasServer: false, + }); + assertBtsConfig(projectDir, { + frontend: ["native-nativewind"], + backend: "none", + }); + }); + }); + + describe("backend combinations", () => { + it("scaffolds with Express backend", async () => { + const projectName = "app-express"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "express", + "--runtime", + "node", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "express", + }); + }); + + it("scaffolds with Fastify backend", async () => { + const projectName = "app-fastify"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "fastify", + "--runtime", + "node", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "fastify", + }); + }); + + it("scaffolds with Elysia backend", async () => { + const projectName = "app-elysia"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "elysia", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + }); + assertBtsConfig(projectDir, { + frontend: ["tanstack-router"], + backend: "elysia", + }); + }); + }); + + describe("database and ORM combinations", () => { + it("scaffolds with SQLite + Drizzle", async () => { + const projectName = "app-sqlite-drizzle"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasDatabase: true, + }); + assertBtsConfig(projectDir, { + database: "sqlite", + orm: "drizzle", + }); + }); + + it("scaffolds with PostgreSQL + Prisma", async () => { + const projectName = "app-postgres-prisma"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "postgres", + "--orm", + "prisma", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasDatabase: true, + }); + assertBtsConfig(projectDir, { + database: "postgres", + orm: "prisma", + }); + }); + + it("scaffolds with MongoDB + Mongoose", async () => { + const projectName = "app-mongo-mongoose"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "mongodb", + "--orm", + "mongoose", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasDatabase: true, + }); + assertBtsConfig(projectDir, { + database: "mongodb", + orm: "mongoose", + }); + }); + }); + + describe("addon combinations", () => { + it("scaffolds with Biome addon", async () => { + const projectName = "app-biome"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "biome", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasBiome: true, + }); + assertBtsConfig(projectDir, { + addons: ["biome"], + }); + }); + + it("scaffolds with multiple addons", async () => { + const projectName = "app-multi-addons"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "turborepo", + "biome", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasTurborepo: true, + hasBiome: true, + }); + assertBtsConfig(projectDir, { + addons: ["turborepo", "biome"], + }); + }); + }); + + describe("API types", () => { + it("scaffolds with tRPC API", async () => { + const projectName = "app-trpc"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + api: "trpc", + }); + }); + + it("scaffolds with oRPC API", async () => { + const projectName = "app-orpc"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "orpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + api: "orpc", + }); + }); + }); + + describe("validation and error cases", () => { + it("rejects invalid project names", async () => { + await runCliExpectingError( + [ + "", + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + }); + + it("rejects incompatible database and ORM combinations", async () => { + await runCliExpectingError( + [ + "invalid-combo", + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "mongodb", + "--orm", + "drizzle", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + }); + + it("rejects incompatible frontend and API combinations", async () => { + await runCliExpectingError( + [ + "invalid-combo", + "--yes", + "--frontend", + "nuxt", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + }); + + it("rejects multiple web frontends", async () => { + await runCliExpectingError( + [ + "invalid-combo", + "--yes", + "--frontend", + "tanstack-router", + "next", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + }); + }); + + describe("runtime compatibility", () => { + it("scaffolds with Cloudflare Workers runtime", async () => { + const projectName = "app-workers"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "workers", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + backend: "hono", + runtime: "workers", + orm: "drizzle", + }); + + expect( + existsSync(join(projectDir, "apps", "server", "wrangler.jsonc")), + ).toBe(true); + }); + + it("rejects incompatible runtime and backend combinations", async () => { + await runCliExpectingError( + [ + "invalid-combo", + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "express", + "--runtime", + "workers", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + }); + + it("rejects incompatible runtime and ORM combinations", async () => { + await runCliExpectingError( + [ + "invalid-combo", + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "workers", + "--database", + "postgres", + "--orm", + "prisma", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + }); + }); + + describe("package managers", () => { + it("scaffolds with npm package manager", async () => { + const projectName = "app-npm"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "npm", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + packageManager: "npm", + }); + }); + + it("scaffolds with pnpm package manager", async () => { + const projectName = "app-pnpm"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "pnpm", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + packageManager: "pnpm", + }); + + expect(existsSync(join(projectDir, "pnpm-workspace.yaml"))).toBe(true); + }); + }); + + describe("comprehensive missing combinations", () => { + it("scaffolds Nuxt + AI example", async () => { + const projectName = "app-nuxt-ai"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "nuxt", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "orpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "ai", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + frontend: ["nuxt"], + backend: "hono", + examples: ["ai"], + api: "orpc", + }); + }); + it("scaffolds with todo example", async () => { + const projectName = "app-example-todo"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "todo", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + examples: ["todo"], + }); + }); + + it("scaffolds with ai example", async () => { + const projectName = "app-example-ai"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "ai", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + examples: ["ai"], + }); + }); + + it("scaffolds convex with todo example (default)", async () => { + const projectName = "app-convex-todo"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "convex", + "--db-setup", + "none", + "--addons", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + backend: "convex", + examples: ["todo"], + }); + }); + + // Git and install flag variations + it("scaffolds with git enabled", async () => { + const projectName = "app-with-git"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--git", + "--no-install", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + expect(existsSync(join(projectDir, ".git"))).toBe(true); + }); + + it("scaffolds with install enabled", async () => { + const projectName = "app-with-install"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + expect(existsSync(join(projectDir, "node_modules"))).toBe(true); + }); + + // Additional addons beyond turborepo and biome + it("scaffolds with PWA addon", async () => { + const projectName = "app-addon-pwa"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "pwa", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + addons: ["pwa"], + }); + }); + + it("scaffolds with Tauri addon", async () => { + const projectName = "app-addon-tauri"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "tauri", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + addons: ["tauri"], + }); + }); + + it("scaffolds with Husky addon", async () => { + const projectName = "app-addon-husky"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "husky", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + addons: ["husky"], + }); + }); + + // Authentication combinations + it("scaffolds with authentication enabled", async () => { + const projectName = "app-with-auth"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "trpc", + "--auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertProjectStructure(projectDir, { + hasWeb: true, + hasServer: true, + hasAuth: true, + hasDatabase: true, + }); + assertBtsConfig(projectDir, { + auth: true, + }); + }); + + // MySQL database + it("scaffolds with MySQL + Prisma", async () => { + const projectName = "app-mysql-prisma"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "mysql", + "--orm", + "prisma", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + database: "mysql", + orm: "prisma", + }); + }); + + it("scaffolds with MySQL + Drizzle", async () => { + const projectName = "app-mysql-drizzle"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "mysql", + "--orm", + "drizzle", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + database: "mysql", + orm: "drizzle", + }); + }); + + // oRPC API with more frontends + it("scaffolds oRPC with Next.js", async () => { + const projectName = "app-orpc-next"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "next", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "orpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + frontend: ["next"], + api: "orpc", + }); + }); + + it("scaffolds oRPC with Nuxt (compatible)", async () => { + const projectName = "app-orpc-nuxt"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "nuxt", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "orpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + frontend: ["nuxt"], + api: "orpc", + }); + }); + + it("scaffolds oRPC with Svelte", async () => { + const projectName = "app-orpc-svelte"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "svelte", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "orpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + frontend: ["svelte"], + api: "orpc", + }); + }); + + it("scaffolds oRPC with Solid", async () => { + const projectName = "app-orpc-solid"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "solid", + "--backend", + "hono", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "orpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + frontend: ["solid"], + api: "orpc", + }); + }); + + // Backend next combinations + it("scaffolds with Next.js backend", async () => { + const projectName = "app-backend-next"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "next", + "--backend", + "next", + "--runtime", + "bun", + "--database", + "sqlite", + "--orm", + "drizzle", + "--api", + "trpc", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + frontend: ["next"], + backend: "next", + }); + }); + + // Node runtime combinations + it("scaffolds with Node runtime", async () => { + const projectName = "app-node-runtime"; + await runCli( + [ + projectName, + "--yes", + "--frontend", + "tanstack-router", + "--backend", + "express", + "--runtime", + "node", + "--database", + "none", + "--orm", + "none", + "--api", + "none", + "--no-auth", + "--addons", + "none", + "--db-setup", + "none", + "--examples", + "none", + "--package-manager", + "bun", + "--no-install", + "--no-git", + ], + workdir, + ); + + const projectDir = join(workdir, projectName); + assertScaffoldedProject(projectDir); + assertBtsConfig(projectDir, { + runtime: "node", + }); + }); + }); + + describe.runIf(process.env.WITH_BUILD === "1")( + "build each scaffolded project", + () => { + const sanitize = (s: string) => s.replace(/[^a-z-]/g, "").slice(0, 30); + const FRONTENDS_ALL = [ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + "solid", + "native-nativewind", + "native-unistyles", + ] as const; + const BACKENDS_STANDARD = [ + "hono", + "express", + "fastify", + "elysia", + ] as const; + const CONVEX_COMPATIBLE_FRONTENDS = FRONTENDS_ALL.filter( + (f) => f !== "solid", + ); + + const projectNames = new Set(); + for (const backend of BACKENDS_STANDARD) { + for (const frontend of FRONTENDS_ALL) { + projectNames.add(`app-${backend}-${sanitize(frontend)}`); + } + } + for (const frontend of CONVEX_COMPATIBLE_FRONTENDS) { + projectNames.add(`app-convex-${sanitize(frontend)}`); + } + [ + "app-default", + "app-min", + "app-turbo", + "app-convex", + "app-next", + "app-nuxt", + "app-svelte", + "app-solid", + "app-native", + "app-express", + "app-fastify", + "app-elysia", + "app-sqlite-drizzle", + "app-postgres-prisma", + "app-mongo-mongoose", + "app-biome", + "app-multi-addons", + "app-trpc", + "app-orpc", + "app-nuxt-ai", + "app-example-todo", + "app-example-ai", + "app-convex-todo", + "app-with-git", + "app-with-install", + "app-addon-pwa", + "app-addon-tauri", + "app-addon-husky", + "app-with-auth", + "app-mysql-prisma", + "app-mysql-drizzle", + "app-orpc-next", + "app-orpc-nuxt", + "app-orpc-svelte", + "app-orpc-solid", + "app-backend-next", + "app-node-runtime", + ].forEach((n) => projectNames.add(n)); + + const detectPackageManager = ( + projectDir: string, + ): "bun" | "pnpm" | "npm" => { + const bts = readBtsConfig(projectDir) as { packageManager?: string }; + const pkgJsonPath = join(projectDir, "package.json"); + const pkg = existsSync(pkgJsonPath) ? readJsonSync(pkgJsonPath) : {}; + const pkgMgrField = + (pkg.packageManager as string | undefined) || bts.packageManager; + + if (typeof pkgMgrField === "string") { + if (pkgMgrField.includes("pnpm")) return "pnpm"; + if (pkgMgrField.includes("npm")) return "npm"; + if (pkgMgrField.includes("bun")) return "bun"; + } + if (existsSync(join(projectDir, "pnpm-workspace.yaml"))) return "pnpm"; + return "bun"; + }; + + const runInstall = async (pm: "bun" | "pnpm" | "npm", cwd: string) => { + if (pm === "bun") + return execa("bun", ["install"], { cwd, stdio: "inherit" }); + if (pm === "pnpm") + return execa("pnpm", ["install", "--no-frozen-lockfile"], { + cwd, + stdio: "inherit", + }); + return execa("npm", ["install", "--no-audit", "--no-fund"], { + cwd, + stdio: "inherit", + }); + }; + + const runScript = async ( + pm: "bun" | "pnpm" | "npm", + cwd: string, + script: string, + extraArgs: string[] = [], + timeout?: number, + ) => { + const base = pm === "bun" ? ["run", script] : ["run", script]; + const cmd = pm === "bun" ? "bun" : pm; + return execa(cmd, [...base, ...extraArgs], { + cwd, + timeout, + env: { ...process.env, NODE_ENV: "production", CI: "true" }, + stdio: "inherit", + }); + }; + + const runCodegen = async (pm: "bun" | "pnpm" | "npm", cwd: string) => { + if (pm === "bun") + return execa("bunx", ["convex", "codegen"], { + cwd, + stdio: "inherit", + }); + if (pm === "pnpm") + return execa("pnpm", ["dlx", "convex", "codegen"], { + cwd, + stdio: "inherit", + }); + return execa("npx", ["convex", "codegen"], { cwd, stdio: "inherit" }); + }; + + for (const dirName of projectNames) { + it(`builds ${dirName}`, async () => { + const projectDir = join(workdir, dirName); + if (!existsSync(projectDir)) { + consola.info(`${dirName} not found, skipping`); + return; + } + const pm = detectPackageManager(projectDir); + + consola.info(`Processing ${dirName} (pm=${pm})`); + try { + consola.start(`Installing dependencies for ${dirName}...`); + try { + const res = await runInstall(pm, projectDir); + expect(res.exitCode).toBe(0); + } catch (installErr) { + if (pm !== "bun") { + consola.warn( + `Primary install with ${pm} failed. Retrying with bun...`, + ); + const fallback = await runInstall("bun", projectDir); + expect(fallback.exitCode).toBe(0); + } else { + throw installErr; + } + } + + const pkgJsonPath = join(projectDir, "package.json"); + const pkg = readJsonSync(pkgJsonPath); + const scripts = pkg.scripts || {}; + consola.info(`Scripts: ${Object.keys(scripts).join(", ")}`); + + const bts = readBtsConfig(projectDir) as { + backend?: string; + frontend?: string[]; + }; + if (bts.backend === "convex") { + const frontends = Array.isArray(bts.frontend) ? bts.frontend : []; + const WEB_FRONTENDS = new Set([ + "tanstack-router", + "react-router", + "tanstack-start", + "next", + "nuxt", + "svelte", + "solid", + ]); + const hasWebFrontend = frontends.some((f) => + WEB_FRONTENDS.has(f), + ); + if (!hasWebFrontend) { + consola.info( + "Skipping Convex native-only project (no web app)", + ); + return; + } + consola.start("Running Convex codegen in packages/backend ..."); + const backendDir = join(projectDir, "packages", "backend"); + const codegenRes = await runCodegen(pm, backendDir); + expect(codegenRes.exitCode).toBe(0); + } + + if (scripts.build) { + consola.start(`Building ${dirName}...`); + const isTurbo = existsSync(join(projectDir, "turbo.json")); + const extraArgs = isTurbo ? ["--force"] : []; + const buildRes = await runScript( + pm, + projectDir, + "build", + extraArgs, + 300_000, + ); + expect(buildRes.exitCode).toBe(0); + consola.success(`${dirName} built successfully`); + } else if (scripts["check-types"]) { + consola.start(`Type checking ${dirName}...`); + const typeRes = await runScript( + pm, + projectDir, + "check-types", + [], + 120_000, + ); + expect(typeRes.exitCode).toBe(0); + consola.success(`${dirName} type check passed`); + } else { + consola.info( + `No build or check-types script for ${dirName}, skipping`, + ); + } + } catch (error) { + consola.error(`${dirName} failed`, error); + throw error; + } + }); + } + }, + ); +}); diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json index 746fefe..2e5e008 100644 --- a/apps/cli/tsconfig.json +++ b/apps/cli/tsconfig.json @@ -11,5 +11,5 @@ "types": ["node"] }, "include": ["src/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts"] } diff --git a/apps/cli/vitest.config.ts b/apps/cli/vitest.config.ts new file mode 100644 index 0000000..4afeb2c --- /dev/null +++ b/apps/cli/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + watch: false, + testTimeout: 180_000, + hookTimeout: 120_000, + reporters: "default", + poolOptions: { + threads: { + singleThread: true, + }, + }, + }, +}); diff --git a/bun.lock b/bun.lock index 9e0cd4e..73ff139 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ }, "apps/cli": { "name": "create-better-t-stack", - "version": "2.30.0", + "version": "2.31.0", "bin": { "create-better-t-stack": "dist/index.js", }, @@ -37,6 +37,7 @@ "@types/node": "^24.2.0", "tsdown": "^0.13.3", "typescript": "^5.9.2", + "vitest": "^3.2.4", }, }, "apps/web": { @@ -747,6 +748,46 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.31", "", {}, "sha512-IaDZ9NhjOIOkYtm+hH0GX33h3iVZ2OeSUnFF0+7Z4+1GuKs4Kj5wK3+I2zNV9IPLfqV4XlwWif8SXrZNutxciQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.2", "", { "os": "android", "cpu": "arm" }, "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.46.2", "", { "os": "android", "cpu": "arm64" }, "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.46.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.46.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.46.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.46.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.46.2", "", { "os": "linux", "cpu": "arm" }, "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.46.2", "", { "os": "linux", "cpu": "arm" }, "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.46.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.46.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.46.2", "", { "os": "linux", "cpu": "none" }, "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.46.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.46.2", "", { "os": "linux", "cpu": "none" }, "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.46.2", "", { "os": "linux", "cpu": "none" }, "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.46.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.46.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.46.2", "", { "os": "linux", "cpu": "x64" }, "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.46.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.46.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.46.2", "", { "os": "win32", "cpu": "x64" }, "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg=="], + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], "@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.12.0", "", {}, "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw=="], @@ -923,6 +964,8 @@ "@types/aws-lambda": ["@types/aws-lambda@8.10.152", "", {}, "sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw=="], + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], + "@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="], "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], @@ -943,6 +986,8 @@ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], @@ -1041,6 +1086,20 @@ "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], @@ -1091,6 +1150,8 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "ast-kit": ["ast-kit@2.1.1", "", { "dependencies": { "@babel/parser": "^7.27.7", "pathe": "^2.0.3" } }, "sha512-mfh6a7gKXE8pDlxTvqIc/syH/P3RkzbOF6LeHdcKztLEzYe6IMsRCL7N8vI7hqTGWNxpkCuuRTpT21xNWqhRtQ=="], "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], @@ -1157,6 +1218,8 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "chai": ["chai@5.2.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A=="], + "chalk": ["chalk@5.5.0", "", {}, "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], @@ -1169,6 +1232,8 @@ "chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="], + "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], + "cheerio": ["cheerio@1.1.2", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.0.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.12.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg=="], "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], @@ -1287,6 +1352,8 @@ "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], @@ -1367,6 +1434,8 @@ "es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="], + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], @@ -1449,6 +1518,8 @@ "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], + "express": ["express@5.0.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.0.1", "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "^1.2.1", "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "^2.0.0", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", "mime-types": "^3.0.0", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.1.0", "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "^2.0.0", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ=="], "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], @@ -1727,7 +1798,7 @@ "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], @@ -1807,6 +1878,8 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "loupe": ["loupe@3.2.0", "", {}, "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw=="], + "lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], "lucide-react": ["lucide-react@0.536.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2PgvNa9v+qz4Jt/ni8vPLt4jwoFybXHuubQT8fv4iCW5TjDxkbZjNZZHa485ad73NSEn/jdsEtU57eE1g+ma8A=="], @@ -2099,9 +2172,11 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], @@ -2223,6 +2298,8 @@ "rolldown-plugin-dts": ["rolldown-plugin-dts@0.15.4", "", { "dependencies": { "@babel/generator": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/types": "^7.28.2", "ast-kit": "^2.1.1", "birpc": "^2.5.0", "debug": "^4.4.1", "dts-resolver": "^2.1.1", "get-tsconfig": "^4.10.1" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "~3.0.3" }, "optionalPeers": ["@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-6R+WLRJNfTNv60u7wLFS9vzINRs0jUMomiiRFSp8rgFgrudfQC9q3TB6oDv2jAgcsSyokZHCbHQIbSKI0Je/bA=="], + "rollup": ["rollup@4.46.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.46.2", "@rollup/rollup-android-arm64": "4.46.2", "@rollup/rollup-darwin-arm64": "4.46.2", "@rollup/rollup-darwin-x64": "4.46.2", "@rollup/rollup-freebsd-arm64": "4.46.2", "@rollup/rollup-freebsd-x64": "4.46.2", "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", "@rollup/rollup-linux-arm-musleabihf": "4.46.2", "@rollup/rollup-linux-arm64-gnu": "4.46.2", "@rollup/rollup-linux-arm64-musl": "4.46.2", "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", "@rollup/rollup-linux-ppc64-gnu": "4.46.2", "@rollup/rollup-linux-riscv64-gnu": "4.46.2", "@rollup/rollup-linux-riscv64-musl": "4.46.2", "@rollup/rollup-linux-s390x-gnu": "4.46.2", "@rollup/rollup-linux-x64-gnu": "4.46.2", "@rollup/rollup-linux-x64-musl": "4.46.2", "@rollup/rollup-win32-arm64-msvc": "4.46.2", "@rollup/rollup-win32-ia32-msvc": "4.46.2", "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -2275,6 +2352,8 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], @@ -2301,8 +2380,12 @@ "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="], + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], @@ -2339,6 +2422,8 @@ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], + "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], "style-to-js": ["style-to-js@1.1.17", "", { "dependencies": { "style-to-object": "1.0.9" } }, "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA=="], @@ -2369,6 +2454,8 @@ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], "tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], @@ -2377,6 +2464,12 @@ "tinygradient": ["tinygradient@1.1.5", "", { "dependencies": { "@types/tinycolor2": "^1.4.0", "tinycolor2": "^1.0.0" } }, "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw=="], + "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], + "tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="], "to-object-path": ["to-object-path@0.3.0", "", { "dependencies": { "kind-of": "^3.0.2" } }, "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg=="], @@ -2505,6 +2598,12 @@ "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="], + "vite": ["vite@7.1.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ=="], + + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + + "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "web": ["web@workspace:apps/web"], "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], @@ -2529,6 +2628,8 @@ "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], @@ -3093,8 +3194,6 @@ "@dotenvx/dotenvx/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "@dotenvx/dotenvx/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "@dotenvx/dotenvx/which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], "@erquhart/convex-oss-stats/p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], @@ -3273,8 +3372,12 @@ "log-update/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], "miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], @@ -3317,12 +3420,14 @@ "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], "trpc-cli/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "vite/esbuild": ["esbuild@0.25.8", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.8", "@esbuild/android-arm": "0.25.8", "@esbuild/android-arm64": "0.25.8", "@esbuild/android-x64": "0.25.8", "@esbuild/darwin-arm64": "0.25.8", "@esbuild/darwin-x64": "0.25.8", "@esbuild/freebsd-arm64": "0.25.8", "@esbuild/freebsd-x64": "0.25.8", "@esbuild/linux-arm": "0.25.8", "@esbuild/linux-arm64": "0.25.8", "@esbuild/linux-ia32": "0.25.8", "@esbuild/linux-loong64": "0.25.8", "@esbuild/linux-mips64el": "0.25.8", "@esbuild/linux-ppc64": "0.25.8", "@esbuild/linux-riscv64": "0.25.8", "@esbuild/linux-s390x": "0.25.8", "@esbuild/linux-x64": "0.25.8", "@esbuild/netbsd-arm64": "0.25.8", "@esbuild/netbsd-x64": "0.25.8", "@esbuild/openbsd-arm64": "0.25.8", "@esbuild/openbsd-x64": "0.25.8", "@esbuild/openharmony-arm64": "0.25.8", "@esbuild/sunos-x64": "0.25.8", "@esbuild/win32-arm64": "0.25.8", "@esbuild/win32-ia32": "0.25.8", "@esbuild/win32-x64": "0.25.8" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q=="], + + "vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "web/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], "web/typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], @@ -3941,6 +4046,56 @@ "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.8", "", { "os": "aix", "cpu": "ppc64" }, "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA=="], + + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.8", "", { "os": "android", "cpu": "arm" }, "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw=="], + + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.8", "", { "os": "android", "cpu": "arm64" }, "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w=="], + + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.8", "", { "os": "android", "cpu": "x64" }, "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA=="], + + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw=="], + + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg=="], + + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.8", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA=="], + + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw=="], + + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.8", "", { "os": "linux", "cpu": "arm" }, "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg=="], + + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w=="], + + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.8", "", { "os": "linux", "cpu": "ia32" }, "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg=="], + + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ=="], + + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw=="], + + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.8", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ=="], + + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.8", "", { "os": "linux", "cpu": "none" }, "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg=="], + + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.8", "", { "os": "linux", "cpu": "s390x" }, "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg=="], + + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.8", "", { "os": "linux", "cpu": "x64" }, "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ=="], + + "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.8", "", { "os": "none", "cpu": "arm64" }, "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw=="], + + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.8", "", { "os": "none", "cpu": "x64" }, "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg=="], + + "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.8", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ=="], + + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.8", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ=="], + + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.8", "", { "os": "sunos", "cpu": "x64" }, "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w=="], + + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ=="], + + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg=="], + + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], + "web/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],