From f8d2a9090ee3250e0de9c012f8d7a40affbcc5a7 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 13 Jun 2025 14:38:30 +0200 Subject: [PATCH] fixing tests, move node tests to vitest --- app/__test__/adapter/adapter.test.ts | 25 +++- app/__test__/app/App.spec.ts | 2 +- app/__test__/app/repro.spec.ts | 3 +- .../integration/config.integration.test.ts | 2 +- app/__test__/media/MediaController.spec.ts | 3 +- app/__test__/modules/AppAuth.spec.ts | 2 +- app/__test__/modules/AppMedia.spec.ts | 3 +- app/package.json | 7 +- app/src/adapter/bun/bun.adapter.ts | 16 +-- .../cloudflare-workers.adapter.spec.ts | 40 ++++--- app/src/adapter/cloudflare/config.ts | 87 ++++++++------ .../storage/StorageR2Adapter.native-spec.ts | 32 ----- .../storage/StorageR2Adapter.vitest.ts | 32 +++++ app/src/adapter/cloudflare/vitest.config.ts | 14 +++ app/src/adapter/index.ts | 18 ++- app/src/adapter/node/node.adapter.spec.ts | 6 +- app/src/adapter/node/node.adapter.ts | 18 +-- ...-spec.ts => StorageLocalAdapter.vitest.ts} | 6 +- app/src/adapter/sqlite/bun.ts | 2 - app/src/adapter/sqlite/edge.ts | 2 - app/src/adapter/sqlite/node.ts | 2 - app/src/core/test/utils.ts | 12 ++ .../sqlite/GenericSqliteConnection.ts | 12 ++ .../sqlite/LibsqlConnection.spec.ts | 3 +- app/src/modules/ModuleManager.ts | 25 ++-- app/vitest.config.ts | 28 +++-- bun.lock | 113 +++++++++++++++++- 27 files changed, 341 insertions(+), 174 deletions(-) delete mode 100644 app/src/adapter/cloudflare/storage/StorageR2Adapter.native-spec.ts create mode 100644 app/src/adapter/cloudflare/storage/StorageR2Adapter.vitest.ts create mode 100644 app/src/adapter/cloudflare/vitest.config.ts rename app/src/adapter/node/storage/{StorageLocalAdapter.native-spec.ts => StorageLocalAdapter.vitest.ts} (79%) create mode 100644 app/src/core/test/utils.ts diff --git a/app/__test__/adapter/adapter.test.ts b/app/__test__/adapter/adapter.test.ts index 734fb08..f7d623c 100644 --- a/app/__test__/adapter/adapter.test.ts +++ b/app/__test__/adapter/adapter.test.ts @@ -3,21 +3,36 @@ import * as adapter from "adapter"; import { disableConsoleLog, enableConsoleLog } from "core/utils"; import { adapterTestSuite } from "adapter/adapter-test-suite"; import { bunTestRunner } from "adapter/bun/test"; +import { omitKeys } from "core/utils"; beforeAll(disableConsoleLog); afterAll(enableConsoleLog); describe("adapter", () => { it("makes config", () => { - expect(adapter.makeConfig({})).toEqual({}); - expect(adapter.makeConfig({}, { env: { TEST: "test" } })).toEqual({}); + expect(omitKeys(adapter.makeConfig({}), ["connection"])).toEqual({}); + expect(omitKeys(adapter.makeConfig({}, { env: { TEST: "test" } }), ["connection"])).toEqual( + {}, + ); // merges everything returned from `app` with the config - expect(adapter.makeConfig({ app: (a) => a as any }, { env: { TEST: "test" } })).toEqual({ - env: { TEST: "test" }, - } as any); + expect( + omitKeys( + adapter.makeConfig( + { app: (a) => ({ initialConfig: { server: { cors: { origin: a.env.TEST } } } }) }, + { env: { TEST: "test" } }, + ), + ["connection"], + ), + ).toEqual({ + initialConfig: { server: { cors: { origin: "test" } } }, + }); }); + /* it.only("...", async () => { + const app = await adapter.createAdapterApp(); + }); */ + it("reuses apps correctly", async () => { const id = crypto.randomUUID(); diff --git a/app/__test__/app/App.spec.ts b/app/__test__/app/App.spec.ts index 860258a..a6eb3de 100644 --- a/app/__test__/app/App.spec.ts +++ b/app/__test__/app/App.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, mock, test } from "bun:test"; import type { ModuleBuildContext } from "../../src"; -import { App, createApp } from "../../src/App"; +import { App, createApp } from "core/test/utils"; import * as proto from "../../src/data/prototype"; describe("App", () => { diff --git a/app/__test__/app/repro.spec.ts b/app/__test__/app/repro.spec.ts index b27aa51..be2da38 100644 --- a/app/__test__/app/repro.spec.ts +++ b/app/__test__/app/repro.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "bun:test"; -import { createApp, registries } from "../../src"; +import { registries } from "../../src"; +import { createApp } from "core/test/utils"; import * as proto from "../../src/data/prototype"; import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter"; diff --git a/app/__test__/integration/config.integration.test.ts b/app/__test__/integration/config.integration.test.ts index f370e69..7fde411 100644 --- a/app/__test__/integration/config.integration.test.ts +++ b/app/__test__/integration/config.integration.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { createApp } from "../../src"; +import { createApp } from "core/test/utils"; import { Api } from "../../src/Api"; describe("integration config", () => { diff --git a/app/__test__/media/MediaController.spec.ts b/app/__test__/media/MediaController.spec.ts index d0e56c9..6478072 100644 --- a/app/__test__/media/MediaController.spec.ts +++ b/app/__test__/media/MediaController.spec.ts @@ -1,7 +1,8 @@ /// import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { createApp, registries } from "../../src"; +import { registries } from "../../src"; +import { createApp } from "core/test/utils"; import { mergeObject, randomString } from "../../src/core/utils"; import type { TAppMediaConfig } from "../../src/media/media-schema"; import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter"; diff --git a/app/__test__/modules/AppAuth.spec.ts b/app/__test__/modules/AppAuth.spec.ts index 50c38f2..ddbaf3b 100644 --- a/app/__test__/modules/AppAuth.spec.ts +++ b/app/__test__/modules/AppAuth.spec.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, beforeEach, describe, expect, spyOn, test } from "bun:test"; -import { createApp } from "../../src"; +import { createApp } from "core/test/utils"; import { AuthController } from "../../src/auth/api/AuthController"; import { em, entity, make, text } from "../../src/data"; import { AppAuth, type ModuleBuildContext } from "../../src/modules"; diff --git a/app/__test__/modules/AppMedia.spec.ts b/app/__test__/modules/AppMedia.spec.ts index 8e6b5b2..bbcc0c2 100644 --- a/app/__test__/modules/AppMedia.spec.ts +++ b/app/__test__/modules/AppMedia.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "bun:test"; -import { createApp, registries } from "../../src"; +import { registries } from "../../src"; +import { createApp } from "core/test/utils"; import { em, entity, text } from "../../src/data"; import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter"; import { AppMedia } from "../../src/modules"; diff --git a/app/package.json b/app/package.json index 516d1d9..0e93a59 100644 --- a/app/package.json +++ b/app/package.json @@ -31,11 +31,9 @@ "test": "ALL_TESTS=1 bun test --bail", "test:all": "bun run test && bun run test:node", "test:bun": "ALL_TESTS=1 bun test --bail", - "test:node": "tsx --test $(find . -type f -name '*.native-spec.ts')", + "test:node": "vitest run", "test:adapters": "bun test src/adapter/**/*.adapter.spec.ts --bail", "test:coverage": "ALL_TESTS=1 bun test --bail --coverage", - "test:vitest": "vitest run", - "test:vitest:watch": "vitest", "test:vitest:coverage": "vitest run --coverage", "test:e2e": "playwright test", "test:e2e:adapters": "bun run e2e/adapters.ts", @@ -73,14 +71,15 @@ "uuid": "^11.1.0" }, "devDependencies": { - "@libsql/client": "^0.15.9", "@aws-sdk/client-s3": "^3.758.0", "@bluwy/giget-core": "^0.1.2", + "@cloudflare/vitest-pool-workers": "^0.8.38", "@cloudflare/workers-types": "^4.20250606.0", "@dagrejs/dagre": "^1.1.4", "@hono/typebox-validator": "^0.3.3", "@hono/vite-dev-server": "^0.19.1", "@hookform/resolvers": "^4.1.3", + "@libsql/client": "^0.15.9", "@libsql/kysely-libsql": "^0.4.1", "@mantine/modals": "^7.17.1", "@mantine/notifications": "^7.17.1", diff --git a/app/src/adapter/bun/bun.adapter.ts b/app/src/adapter/bun/bun.adapter.ts index 07b778a..5d7c148 100644 --- a/app/src/adapter/bun/bun.adapter.ts +++ b/app/src/adapter/bun/bun.adapter.ts @@ -1,17 +1,11 @@ /// import path from "node:path"; -import { - type RuntimeBkndConfig, - createRuntimeApp, - type RuntimeOptions, - Connection, -} from "bknd/adapter"; +import { type RuntimeBkndConfig, createRuntimeApp, type RuntimeOptions } from "bknd/adapter"; import { registerLocalMediaAdapter } from "."; import { config } from "bknd/core"; import type { ServeOptions } from "bun"; import { serveStatic } from "hono/bun"; -import { sqlite } from "bknd/adapter/sqlite"; import type { App } from "App"; type BunEnv = Bun.Env; @@ -25,17 +19,9 @@ export async function createApp( const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static"); registerLocalMediaAdapter(); - let connection: Connection | undefined; - if (Connection.isConnection(config.connection)) { - connection = config.connection; - } else { - connection = sqlite(config.connection ?? { url: ":memory:" }); - } - return await createRuntimeApp( { ...config, - connection, serveStatic: serveStatic({ root }), }, args ?? (process.env as Env), diff --git a/app/src/adapter/cloudflare/cloudflare-workers.adapter.spec.ts b/app/src/adapter/cloudflare/cloudflare-workers.adapter.spec.ts index 0c51acb..5cdde1a 100644 --- a/app/src/adapter/cloudflare/cloudflare-workers.adapter.spec.ts +++ b/app/src/adapter/cloudflare/cloudflare-workers.adapter.spec.ts @@ -13,30 +13,32 @@ describe("cf adapter", () => { const DB_URL = ":memory:"; const $ctx = (env?: any, request?: Request, ctx?: ExecutionContext) => ({ request: request ?? (null as any), - env: env ?? { DB_URL }, + env: env ?? { url: DB_URL }, ctx: ctx ?? (null as any), }); it("makes config", async () => { - expect( - makeConfig( - { - connection: { url: DB_URL }, - }, - $ctx({ DB_URL }), - ), - ).toEqual({ connection: { url: DB_URL } }); + const staticConfig = makeConfig( + { + connection: { url: DB_URL }, + initialConfig: { data: { basepath: DB_URL } }, + }, + $ctx({ DB_URL }), + ); + expect(staticConfig.initialConfig).toEqual({ data: { basepath: DB_URL } }); + expect(staticConfig.connection).toBeDefined(); - expect( - makeConfig( - { - app: (env) => ({ - connection: { url: env.DB_URL }, - }), - }, - $ctx({ DB_URL }), - ), - ).toEqual({ connection: { url: DB_URL } }); + const dynamicConfig = makeConfig( + { + app: (env) => ({ + initialConfig: { data: { basepath: env.DB_URL } }, + connection: { url: env.DB_URL }, + }), + }, + $ctx({ DB_URL }), + ); + expect(dynamicConfig.initialConfig).toEqual({ data: { basepath: DB_URL } }); + expect(dynamicConfig.connection).toBeDefined(); }); adapterTestSuite>(bunTestRunner, { diff --git a/app/src/adapter/cloudflare/config.ts b/app/src/adapter/cloudflare/config.ts index d6e75b1..6ae89d7 100644 --- a/app/src/adapter/cloudflare/config.ts +++ b/app/src/adapter/cloudflare/config.ts @@ -9,6 +9,7 @@ import { makeConfig as makeAdapterConfig } from "bknd/adapter"; import type { Context, ExecutionContext } from "hono"; import { $console } from "core"; import { setCookie } from "hono/cookie"; +import { sqlite } from "bknd/adapter/sqlite"; export const constants = { exec_async_event_id: "cf_register_waituntil", @@ -98,54 +99,70 @@ export function makeConfig( const appConfig = makeAdapterConfig(config, args?.env); - if (args?.env) { - const bindings = config.bindings?.(args?.env); + // if connection instance is given, don't do anything + // other than checking if D1 session is defined + if (D1Connection.isConnection(appConfig.connection)) { + if (config.d1?.session) { + // we cannot guarantee that db was opened with session + throw new Error( + "D1 session don't work when D1 is directly given as connection. Define it in bindings instead.", + ); + } + // if connection is given, try to open with unified sqlite adapter + } else if (appConfig.connection) { + appConfig.connection = sqlite(appConfig.connection); + // if connection is not given, but env is set + // try to make D1 from bindings + } else if (args?.env) { + const bindings = config.bindings?.(args?.env); const sessionHelper = d1SessionHelper(config); const sessionId = sessionHelper.get(args.request); let session: D1DatabaseSession | undefined; + let db: D1Database | undefined; - if (!appConfig.connection) { - let db: D1Database | undefined; - if (bindings?.db) { - $console.log("Using database from bindings"); - db = bindings.db; - } else if (Object.keys(args).length > 0) { - const binding = getBinding(args.env, "D1Database"); - if (binding) { - $console.log(`Using database from env "${binding.key}"`); - db = binding.value; - } - } + // if db is given in bindings, use it + if (bindings?.db) { + $console.log("Using database from bindings"); + db = bindings.db; - if (db) { - if (config.d1?.session) { - session = db.withSession(sessionId ?? config.d1?.first); - appConfig.connection = new D1Connection({ binding: session }); - } else { - appConfig.connection = new D1Connection({ binding: db }); - } - } else { - throw new Error("No database connection given"); + // scan for D1Database in args + } else { + const binding = getBinding(args.env, "D1Database"); + if (binding) { + $console.log(`Using database from env "${binding.key}"`); + db = binding.value; } } - if (config.d1?.session) { - appConfig.options = { - ...appConfig.options, - manager: { - ...appConfig.options?.manager, - onServerInit: (server) => { - server.use(async (c, next) => { - sessionHelper.set(c, session); - await next(); - }); + // if db is found, check if session is requested + if (db) { + if (config.d1?.session) { + session = db.withSession(sessionId ?? config.d1?.first); + + appConfig.connection = new D1Connection({ binding: session }); + appConfig.options = { + ...appConfig.options, + manager: { + ...appConfig.options?.manager, + onServerInit: (server) => { + server.use(async (c, next) => { + sessionHelper.set(c, session); + await next(); + }); + }, }, - }, - }; + }; + } else { + appConfig.connection = new D1Connection({ binding: db }); + } } } + if (!D1Connection.isConnection(appConfig.connection)) { + throw new Error("Couldn't find database connection"); + } + return appConfig; } diff --git a/app/src/adapter/cloudflare/storage/StorageR2Adapter.native-spec.ts b/app/src/adapter/cloudflare/storage/StorageR2Adapter.native-spec.ts deleted file mode 100644 index ffa98af..0000000 --- a/app/src/adapter/cloudflare/storage/StorageR2Adapter.native-spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createWriteStream, readFileSync } from "node:fs"; -import { test } from "node:test"; -import { Miniflare } from "miniflare"; -import { StorageR2Adapter } from "./StorageR2Adapter"; -import { adapterTestSuite } from "media"; -import { nodeTestRunner } from "adapter/node/test"; -import path from "node:path"; - -// https://github.com/nodejs/node/issues/44372#issuecomment-1736530480 -console.log = async (message: any) => { - const tty = createWriteStream("/dev/tty"); - const msg = typeof message === "string" ? message : JSON.stringify(message, null, 2); - return tty.write(`${msg}\n`); -}; - -test("StorageR2Adapter", async () => { - const mf = new Miniflare({ - modules: true, - script: "export default { async fetch() { return new Response(null); } }", - r2Buckets: ["BUCKET"], - }); - - const bucket = (await mf.getR2Bucket("BUCKET")) as unknown as R2Bucket; - const adapter = new StorageR2Adapter(bucket); - - const basePath = path.resolve(import.meta.dirname, "../../../../__test__/_assets"); - const buffer = readFileSync(path.join(basePath, "image.png")); - const file = new File([buffer], "image.png", { type: "image/png" }); - - await adapterTestSuite(nodeTestRunner, adapter, file); - await mf.dispose(); -}); diff --git a/app/src/adapter/cloudflare/storage/StorageR2Adapter.vitest.ts b/app/src/adapter/cloudflare/storage/StorageR2Adapter.vitest.ts new file mode 100644 index 0000000..437ec9b --- /dev/null +++ b/app/src/adapter/cloudflare/storage/StorageR2Adapter.vitest.ts @@ -0,0 +1,32 @@ +import { readFileSync } from "node:fs"; +import { Miniflare } from "miniflare"; +import { StorageR2Adapter } from "./StorageR2Adapter"; +import { adapterTestSuite } from "media/storage/adapters/adapter-test-suite"; +import path from "node:path"; +import { describe, afterAll, test, expect } from "vitest"; +import { viTestRunner } from "adapter/node/vitest"; + +let mf: Miniflare | undefined; +describe("StorageR2Adapter", async () => { + mf = new Miniflare({ + modules: true, + script: "export default { async fetch() { return new Response(null); } }", + r2Buckets: ["BUCKET"], + }); + const bucket = (await mf?.getR2Bucket("BUCKET")) as unknown as R2Bucket; + + test("test", () => { + expect(bucket).toBeDefined(); + }); + const adapter = new StorageR2Adapter(bucket); + + const basePath = path.resolve(import.meta.dirname, "../../../../__test__/_assets"); + const buffer = readFileSync(path.join(basePath, "image.png")); + const file = new File([buffer], "image.png", { type: "image/png" }); + + await adapterTestSuite(viTestRunner, adapter, file); +}); + +afterAll(async () => { + await mf?.dispose(); +}); diff --git a/app/src/adapter/cloudflare/vitest.config.ts b/app/src/adapter/cloudflare/vitest.config.ts new file mode 100644 index 0000000..590ee4f --- /dev/null +++ b/app/src/adapter/cloudflare/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"; + +export default defineWorkersConfig({ + test: { + poolOptions: { + workers: { + miniflare: { + compatibilityDate: "2025-06-04", + }, + }, + }, + include: ["**/*.vi-test.ts", "**/*.vitest.ts"], + }, +}); diff --git a/app/src/adapter/index.ts b/app/src/adapter/index.ts index 569afd6..abd2f8e 100644 --- a/app/src/adapter/index.ts +++ b/app/src/adapter/index.ts @@ -1,7 +1,8 @@ import { App, type CreateAppConfig } from "bknd"; -import { config as $config } from "bknd/core"; +import { config as $config, $console } from "bknd/core"; import type { MiddlewareHandler } from "hono"; import type { AdminControllerOptions } from "modules/server/AdminController"; +import { Connection } from "bknd/data"; export { Connection } from "bknd/data"; @@ -61,7 +62,20 @@ export async function createAdapterApp { adapterTestSuite(bunTestRunner, { - makeApp: node.createApp, - makeHandler: node.createHandler, + makeApp: createApp, + makeHandler: createHandler, }); }); diff --git a/app/src/adapter/node/node.adapter.ts b/app/src/adapter/node/node.adapter.ts index 02aa829..9d9d5d5 100644 --- a/app/src/adapter/node/node.adapter.ts +++ b/app/src/adapter/node/node.adapter.ts @@ -1,16 +1,10 @@ import path from "node:path"; import { serve as honoServe } from "@hono/node-server"; import { serveStatic } from "@hono/node-server/serve-static"; -import { registerLocalMediaAdapter } from "adapter/node/index"; -import { - type RuntimeBkndConfig, - createRuntimeApp, - type RuntimeOptions, - Connection, -} from "bknd/adapter"; +import { registerLocalMediaAdapter } from "adapter/node/storage"; +import { type RuntimeBkndConfig, createRuntimeApp, type RuntimeOptions } from "bknd/adapter"; import { config as $config } from "bknd/core"; import { $console } from "core"; -import { sqlite } from "bknd/adapter/sqlite"; import type { App } from "App"; type NodeEnv = NodeJS.ProcessEnv; @@ -35,18 +29,10 @@ export async function createApp( console.warn("relativeDistPath is deprecated, please use distPath instead"); } - let connection: Connection | undefined; - if (Connection.isConnection(config.connection)) { - connection = config.connection; - } else { - connection = sqlite(config.connection ?? { url: ":memory:" }); - } - registerLocalMediaAdapter(); return await createRuntimeApp( { ...config, - connection, serveStatic: serveStatic({ root }), }, // @ts-ignore diff --git a/app/src/adapter/node/storage/StorageLocalAdapter.native-spec.ts b/app/src/adapter/node/storage/StorageLocalAdapter.vitest.ts similarity index 79% rename from app/src/adapter/node/storage/StorageLocalAdapter.native-spec.ts rename to app/src/adapter/node/storage/StorageLocalAdapter.vitest.ts index d237a53..3d13bd5 100644 --- a/app/src/adapter/node/storage/StorageLocalAdapter.native-spec.ts +++ b/app/src/adapter/node/storage/StorageLocalAdapter.vitest.ts @@ -1,5 +1,5 @@ -import { describe } from "node:test"; -import { nodeTestRunner } from "adapter/node/test"; +import { describe } from "vitest"; +import { viTestRunner } from "adapter/node/vitest"; import { StorageLocalAdapter } from "adapter/node"; import { adapterTestSuite } from "media/storage/adapters/adapter-test-suite"; import { readFileSync } from "node:fs"; @@ -14,5 +14,5 @@ describe("StorageLocalAdapter (node)", async () => { path: path.join(basePath, "tmp"), }); - await adapterTestSuite(nodeTestRunner, adapter, file); + await adapterTestSuite(viTestRunner, adapter, file); }); diff --git a/app/src/adapter/sqlite/bun.ts b/app/src/adapter/sqlite/bun.ts index 328186c..eaf02c4 100644 --- a/app/src/adapter/sqlite/bun.ts +++ b/app/src/adapter/sqlite/bun.ts @@ -1,8 +1,6 @@ import type { Connection } from "bknd/data"; -import { $console } from "bknd/core"; import { bunSqlite } from "../bun/connection/BunSqliteConnection"; export function sqlite(config: { url: string }): Connection { - $console.info("Using bun-sqlite", config); return bunSqlite(config); } diff --git a/app/src/adapter/sqlite/edge.ts b/app/src/adapter/sqlite/edge.ts index 996d7f5..97b09ec 100644 --- a/app/src/adapter/sqlite/edge.ts +++ b/app/src/adapter/sqlite/edge.ts @@ -1,8 +1,6 @@ -import { $console } from "bknd/core"; import type { Connection } from "bknd/data"; import { libsql } from "../../data/connection/sqlite/LibsqlConnection"; export function sqlite(config: { url: string }): Connection { - $console.info("Using libsql", config); return libsql(config); } diff --git a/app/src/adapter/sqlite/node.ts b/app/src/adapter/sqlite/node.ts index 1643cce..7bc6ebd 100644 --- a/app/src/adapter/sqlite/node.ts +++ b/app/src/adapter/sqlite/node.ts @@ -1,8 +1,6 @@ -import { $console } from "bknd/core"; import type { Connection } from "bknd/data"; import { nodeSqlite } from "../node/connection/NodeSqliteConnection"; export function sqlite(config: { url: string }): Connection { - $console.info("Using node-sqlite", config); return nodeSqlite(config); } diff --git a/app/src/core/test/utils.ts b/app/src/core/test/utils.ts new file mode 100644 index 0000000..d4cefa9 --- /dev/null +++ b/app/src/core/test/utils.ts @@ -0,0 +1,12 @@ +import { createApp as createAppInternal, type CreateAppConfig } from "App"; +import { bunSqlite } from "adapter/bun/connection/BunSqliteConnection"; +import { Connection } from "data/connection/Connection"; + +export { App } from "App"; + +export function createApp({ connection, ...config }: CreateAppConfig = {}) { + return createAppInternal({ + ...config, + connection: Connection.isConnection(connection) ? connection : bunSqlite(connection as any), + }); +} diff --git a/app/src/data/connection/sqlite/GenericSqliteConnection.ts b/app/src/data/connection/sqlite/GenericSqliteConnection.ts index 3707181..d9abc5f 100644 --- a/app/src/data/connection/sqlite/GenericSqliteConnection.ts +++ b/app/src/data/connection/sqlite/GenericSqliteConnection.ts @@ -8,12 +8,14 @@ import { GenericSqliteDialect, } from "kysely-generic-sqlite"; import { SqliteConnection } from "./SqliteConnection"; +import type { Features } from "../Connection"; export type GenericSqliteConnectionConfig = { name: string; additionalPlugins?: KyselyPlugin[]; excludeTables?: string[]; onCreateConnection?: OnCreateConnection; + supports?: Partial; }; export { parseBigInt, buildQueryFn, GenericSqliteDialect, type IGenericSqlite }; @@ -33,5 +35,15 @@ export class GenericSqliteConnection extends SqliteConnection excludeTables: config?.excludeTables, }); this.client = db; + if (config?.name) { + this.name = config.name; + } + if (config?.supports) { + for (const [key, value] of Object.entries(config.supports)) { + if (value) { + this.supported[key] = value; + } + } + } } } diff --git a/app/src/data/connection/sqlite/LibsqlConnection.spec.ts b/app/src/data/connection/sqlite/LibsqlConnection.spec.ts index dde5a84..d6f14d1 100644 --- a/app/src/data/connection/sqlite/LibsqlConnection.spec.ts +++ b/app/src/data/connection/sqlite/LibsqlConnection.spec.ts @@ -2,10 +2,11 @@ import { connectionTestSuite } from "../connection-test-suite"; import { LibsqlConnection } from "./LibsqlConnection"; import { bunTestRunner } from "adapter/bun/test"; import { describe } from "bun:test"; +import { createClient } from "@libsql/client"; describe("LibsqlConnection", () => { connectionTestSuite(bunTestRunner, { - makeConnection: () => new LibsqlConnection({ url: ":memory:" }), + makeConnection: () => new LibsqlConnection(createClient({ url: ":memory:" })), rawDialectDetails: ["rowsAffected", "lastInsertRowid"], }); }); diff --git a/app/src/modules/ModuleManager.ts b/app/src/modules/ModuleManager.ts index d933ceb..3380934 100644 --- a/app/src/modules/ModuleManager.ts +++ b/app/src/modules/ModuleManager.ts @@ -11,16 +11,9 @@ import { stripMark, transformObject, } from "core/utils"; -import { - type Connection, - EntityManager, - type Schema, - datetime, - entity, - enumm, - jsonSchema, - number, -} from "data"; +import type { Connection, Schema } from "data"; +import { EntityManager } from "data/entities/EntityManager"; +import * as proto from "data/prototype"; import { TransformPersistFailedException } from "data/errors"; import { Hono } from "hono"; import type { Kysely } from "kysely"; @@ -116,12 +109,12 @@ const configJsonSchema = Type.Union([ }), ), ]); -export const __bknd = entity(TABLE_NAME, { - version: number().required(), - type: enumm({ enum: ["config", "diff", "backup"] }).required(), - json: jsonSchema({ schema: configJsonSchema }).required(), - created_at: datetime(), - updated_at: datetime(), +export const __bknd = proto.entity(TABLE_NAME, { + version: proto.number().required(), + type: proto.enumm({ enum: ["config", "diff", "backup"] }).required(), + json: proto.jsonSchema({ schema: configJsonSchema }).required(), + created_at: proto.datetime(), + updated_at: proto.datetime(), }); type ConfigTable2 = Schema; interface T_INTERNAL_EM { diff --git a/app/vitest.config.ts b/app/vitest.config.ts index f86248a..adc92b7 100644 --- a/app/vitest.config.ts +++ b/app/vitest.config.ts @@ -1,18 +1,26 @@ import { defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react"; import tsconfigPaths from "vite-tsconfig-paths"; - +import path from "node:path"; export default defineConfig({ - plugins: [react(), tsconfigPaths()], + plugins: [tsconfigPaths()], test: { - globals: true, - environment: "jsdom", - setupFiles: ["./__test__/vitest/setup.ts"], + projects: ["**/*.vitest.config.ts"], include: ["**/*.vi-test.ts", "**/*.vitest.ts"], - coverage: { - provider: "v8", - reporter: ["text", "json", "html"], - exclude: ["node_modules/", "**/*.d.ts", "**/*.test.ts", "**/*.config.ts"], - }, }, }); + +// export defineConfig({ +// plugins: [tsconfigPaths()], +// test: { +// globals: true, +// environment: "jsdom", +// setupFiles: ["./__test__/vitest/setup.ts"], +// include: ["**/*.vi-test.ts", "**/*.vitest.ts"], +// coverage: { +// provider: "v8", +// reporter: ["text", "json", "html"], +// exclude: ["node_modules/", "**/*.d.ts", "**/*.test.ts", "**/*.config.ts"], +// }, +// }, +// }); diff --git a/bun.lock b/bun.lock index 56fe68e..f442cdd 100644 --- a/bun.lock +++ b/bun.lock @@ -34,7 +34,6 @@ "@codemirror/lang-json": "^6.0.1", "@hello-pangea/dnd": "^18.0.1", "@hono/swagger-ui": "^0.5.1", - "@libsql/client": "^0.15.9", "@mantine/core": "^7.17.1", "@mantine/hooks": "^7.17.1", "@sinclair/typebox": "0.34.30", @@ -60,11 +59,13 @@ "devDependencies": { "@aws-sdk/client-s3": "^3.758.0", "@bluwy/giget-core": "^0.1.2", + "@cloudflare/vitest-pool-workers": "^0.8.38", "@cloudflare/workers-types": "^4.20250606.0", "@dagrejs/dagre": "^1.1.4", "@hono/typebox-validator": "^0.3.3", "@hono/vite-dev-server": "^0.19.1", "@hookform/resolvers": "^4.1.3", + "@libsql/client": "^0.15.9", "@libsql/kysely-libsql": "^0.4.1", "@mantine/modals": "^7.17.1", "@mantine/notifications": "^7.17.1", @@ -520,6 +521,10 @@ "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.3.4", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q=="], + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="], + + "@cloudflare/vitest-pool-workers": ["@cloudflare/vitest-pool-workers@0.8.38", "", { "dependencies": { "birpc": "0.2.14", "cjs-module-lexer": "^1.2.3", "devalue": "^4.3.0", "miniflare": "4.20250604.1", "semver": "^7.7.1", "wrangler": "4.20.0", "zod": "^3.22.3" }, "peerDependencies": { "@vitest/runner": "2.0.x - 3.2.x", "@vitest/snapshot": "2.0.x - 3.2.x", "vitest": "2.0.x - 3.2.x" } }, "sha512-/eeHW5RbU/eWy5UHOZlYh+7J4DTC5GmMW33Iz7GfKAPBgsNHUqx0/AQotjb4qqB3jW/QvsIQM+jZofmMfdPSZw=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250224.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-sBbaAF2vgQ9+T50ik1ihekdepStBp0w4fvNghBfXIw1iWqfNWnypcjDMmi/7JhXJt2uBxBrSlXCvE5H7Gz+kbw=="], "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250224.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-naetGefgjAaDbEacpwaVruJXNwxmRRL7v3ppStgEiqAlPmTpQ/Edjn2SQ284QwOw3MvaVPHrWcaTBupUpkqCyg=="], @@ -1592,6 +1597,8 @@ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + "birpc": ["birpc@0.2.14", "", {}, "sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA=="], + "bknd": ["bknd@workspace:app"], "bknd-cli": ["bknd-cli@workspace:packages/cli"], @@ -1682,6 +1689,8 @@ "ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], + "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], + "class-utils": ["class-utils@0.3.6", "", { "dependencies": { "arr-union": "^3.1.0", "define-property": "^0.2.5", "isobject": "^3.0.0", "static-extend": "^0.1.1" } }, "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg=="], "classcat": ["classcat@5.0.5", "", {}, "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="], @@ -1886,6 +1895,8 @@ "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + "devalue": ["devalue@4.3.3", "", {}, "sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg=="], + "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], @@ -2052,6 +2063,8 @@ "express-rate-limit": ["express-rate-limit@5.5.1", "", {}, "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg=="], + "exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "extend-shallow": ["extend-shallow@3.0.2", "", { "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" } }, "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q=="], @@ -3882,6 +3895,12 @@ "@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="], + "@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], + + "@cloudflare/vitest-pool-workers/miniflare": ["miniflare@4.20250604.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250604.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-HJQ9YhH0F0fI73Vsdy3PNVau+PfHZYK7trI5WJEcbfl5HzqhMU0DRNtA/G5EXQgiumkjrmbW4Zh1DVTtsqICPg=="], + + "@cloudflare/vitest-pool-workers/wrangler": ["wrangler@4.20.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.2", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250604.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250604.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250604.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-gxMLaSnYp3VLdGPZu4fc/9UlB7PnSVwni25v32NM9szG2yTt+gx5RunWzmoLplplIfEMkBuV3wA47vccNu7zcA=="], + "@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], "@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -4744,6 +4763,22 @@ "@bundled-es-modules/tough-cookie/tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="], + "@cloudflare/unenv-preset/unenv/ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + + "@cloudflare/unenv-preset/unenv/ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + + "@cloudflare/vitest-pool-workers/miniflare/workerd": ["workerd@1.20250604.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250604.0", "@cloudflare/workerd-darwin-arm64": "1.20250604.0", "@cloudflare/workerd-linux-64": "1.20250604.0", "@cloudflare/workerd-linux-arm64": "1.20250604.0", "@cloudflare/workerd-windows-64": "1.20250604.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-sHz9R1sxPpnyq3ptrI/5I96sYTMA2+Ljm75oJDbmEcZQwNyezpu9Emerzt3kzzjCJQqtdscGOidWv4RKGZXzAA=="], + + "@cloudflare/vitest-pool-workers/miniflare/youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], + + "@cloudflare/vitest-pool-workers/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], + + "@cloudflare/vitest-pool-workers/wrangler/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], + + "@cloudflare/vitest-pool-workers/wrangler/workerd": ["workerd@1.20250604.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250604.0", "@cloudflare/workerd-darwin-arm64": "1.20250604.0", "@cloudflare/workerd-linux-64": "1.20250604.0", "@cloudflare/workerd-linux-arm64": "1.20250604.0", "@cloudflare/workerd-windows-64": "1.20250604.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-sHz9R1sxPpnyq3ptrI/5I96sYTMA2+Ljm75oJDbmEcZQwNyezpu9Emerzt3kzzjCJQqtdscGOidWv4RKGZXzAA=="], + "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], @@ -5290,6 +5325,82 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250604.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-PI6AWAzhHg75KVhYkSWFBf3HKCHstpaKg4nrx6LYZaEvz0TaTz+JQpYU2fNAgGFmVsK5xEzwFTGh3DAVAKONPw=="], + + "@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250604.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hOiZZSop7QRQgGERtTIy9eU5GvPpIsgE2/BDsUdHMl7OBZ7QLniqvgDzLNDzj0aTkCldm9Yl/Z+C7aUgRdOccw=="], + + "@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250604.0", "", { "os": "linux", "cpu": "x64" }, "sha512-S0R9r7U4nv9qejYygQj01hArC4KUbQQ4u29rvegR0MGoXZY8AHIEuJxon0kE7r7aWFJxvl4W3tOH+5hwW51LYw=="], + + "@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250604.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BTFU/rXpNy03wpeueI2P7q1vVjbg2V6mCyyFGqDqMn2gSVYXH1G0zFNolV13PQXa0HgaqM6oYnqtAxluqbA+kQ=="], + + "@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250604.0", "", { "os": "win32", "cpu": "x64" }, "sha512-tW/U9/qDmDZBeoEVcK5skb2uouVAMXMzt7o/uGvaIFLeZsQkOp4NBmvoQQd+nbOc7nVCJIwFoSMokd89AhzCkA=="], + + "@cloudflare/vitest-pool-workers/miniflare/youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="], + + "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/unenv/ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/unenv/ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + + "@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250604.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-PI6AWAzhHg75KVhYkSWFBf3HKCHstpaKg4nrx6LYZaEvz0TaTz+JQpYU2fNAgGFmVsK5xEzwFTGh3DAVAKONPw=="], + + "@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250604.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hOiZZSop7QRQgGERtTIy9eU5GvPpIsgE2/BDsUdHMl7OBZ7QLniqvgDzLNDzj0aTkCldm9Yl/Z+C7aUgRdOccw=="], + + "@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250604.0", "", { "os": "linux", "cpu": "x64" }, "sha512-S0R9r7U4nv9qejYygQj01hArC4KUbQQ4u29rvegR0MGoXZY8AHIEuJxon0kE7r7aWFJxvl4W3tOH+5hwW51LYw=="], + + "@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250604.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BTFU/rXpNy03wpeueI2P7q1vVjbg2V6mCyyFGqDqMn2gSVYXH1G0zFNolV13PQXa0HgaqM6oYnqtAxluqbA+kQ=="], + + "@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250604.0", "", { "os": "win32", "cpu": "x64" }, "sha512-tW/U9/qDmDZBeoEVcK5skb2uouVAMXMzt7o/uGvaIFLeZsQkOp4NBmvoQQd+nbOc7nVCJIwFoSMokd89AhzCkA=="], + "@jest/core/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "@jest/reporters/glob/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],