From 5e205cf1011c534521576f56060d9ef18e920a87 Mon Sep 17 00:00:00 2001 From: Kacper Wojciechowski <39823706+jog1t@users.noreply.github.com> Date: Thu, 19 Jun 2025 00:31:15 +0200 Subject: [PATCH] feat: sqlite --- examples/counter/scripts/connect.ts | 8 +- examples/drizzle/.env.sample | 1 + examples/drizzle/README.md | 33 + examples/drizzle/drizzle.config.ts | 6 + .../drizzle/0000_wonderful_iron_patriot.sql | 8 + .../drizzle/drizzle/meta/0000_snapshot.json | 64 ++ examples/drizzle/drizzle/meta/_journal.json | 20 + examples/drizzle/drizzle/migrations.js | 10 + examples/drizzle/hooks.js | 10 + examples/drizzle/package.json | 27 + examples/drizzle/register.js | 15 + examples/drizzle/src/db/schema.ts | 9 + examples/drizzle/src/registry.ts | 27 + examples/drizzle/src/server.ts | 7 + examples/drizzle/tsconfig.json | 44 ++ .../driver-test-suite/action-timeout.ts | 68 ++ packages/core/src/client/worker-common.ts | 13 +- packages/core/src/drivers/memory/worker.ts | 4 + .../core/src/drivers/rivet/worker-driver.ts | 5 + packages/core/src/registry/config.ts | 3 +- packages/core/src/test/driver/worker.ts | 4 + packages/core/src/worker/action.ts | 21 +- packages/core/src/worker/config.ts | 122 +++- packages/core/src/worker/connection.ts | 12 +- packages/core/src/worker/context.ts | 28 +- packages/core/src/worker/definition.ts | 22 +- packages/core/src/worker/driver.ts | 7 + packages/core/src/worker/errors.ts | 9 + packages/core/src/worker/instance.ts | 86 ++- packages/core/src/worker/mod.ts | 25 +- .../core/src/worker/protocol/message/mod.ts | 34 +- packages/core/tests/worker-types.test.ts | 12 +- packages/db/package.json | 63 ++ packages/db/src/config.ts | 12 + packages/db/src/drizzle/mod.ts | 76 ++ packages/db/src/mod.ts | 46 ++ packages/db/tsconfig.json | 9 + packages/db/tsup.config.ts | 7 + packages/db/turbo.json | 4 + packages/drivers/file-system/src/worker.ts | 76 +- packages/drivers/redis/src/worker.ts | 5 + .../cloudflare-workers/src/worker-driver.ts | 6 +- pnpm-lock.yaml | 650 +++++++++++++++++- 43 files changed, 1545 insertions(+), 173 deletions(-) create mode 100644 examples/drizzle/.env.sample create mode 100644 examples/drizzle/README.md create mode 100644 examples/drizzle/drizzle.config.ts create mode 100644 examples/drizzle/drizzle/0000_wonderful_iron_patriot.sql create mode 100644 examples/drizzle/drizzle/meta/0000_snapshot.json create mode 100644 examples/drizzle/drizzle/meta/_journal.json create mode 100644 examples/drizzle/drizzle/migrations.js create mode 100644 examples/drizzle/hooks.js create mode 100644 examples/drizzle/package.json create mode 100644 examples/drizzle/register.js create mode 100644 examples/drizzle/src/db/schema.ts create mode 100644 examples/drizzle/src/registry.ts create mode 100644 examples/drizzle/src/server.ts create mode 100644 examples/drizzle/tsconfig.json create mode 100644 packages/core/fixtures/driver-test-suite/action-timeout.ts create mode 100644 packages/db/package.json create mode 100644 packages/db/src/config.ts create mode 100644 packages/db/src/drizzle/mod.ts create mode 100644 packages/db/src/mod.ts create mode 100644 packages/db/tsconfig.json create mode 100644 packages/db/tsup.config.ts create mode 100644 packages/db/turbo.json diff --git a/examples/counter/scripts/connect.ts b/examples/counter/scripts/connect.ts index 04091852d..b8abc6cd7 100644 --- a/examples/counter/scripts/connect.ts +++ b/examples/counter/scripts/connect.ts @@ -1,11 +1,13 @@ /// import { createClient } from "@rivetkit/worker/client"; -import type { Registry } from "../workers/registry"; +import type { Registry } from "../src/workers/registry"; async function main() { - const client = createClient(process.env.ENDPOINT ?? "http://localhost:6420"); + const client = createClient( + process.env.ENDPOINT ?? "http://127.0.0.1:8080", + ); - const counter = client.counter.connect() + const counter = (await client.counter.getOrCreate()).connect(); counter.on("newCount", (count: number) => console.log("Event:", count)); diff --git a/examples/drizzle/.env.sample b/examples/drizzle/.env.sample new file mode 100644 index 000000000..842bef05d --- /dev/null +++ b/examples/drizzle/.env.sample @@ -0,0 +1 @@ +DB_FILE_NAME=file:local.db \ No newline at end of file diff --git a/examples/drizzle/README.md b/examples/drizzle/README.md new file mode 100644 index 000000000..bb41842a6 --- /dev/null +++ b/examples/drizzle/README.md @@ -0,0 +1,33 @@ +# Hono Integration for RivetKit + +Example project demonstrating Hono web framework integration with [RivetKit](https://rivetkit.org). + +[Learn More →](https://github.com/rivet-gg/rivetkit) + +[Discord](https://rivet.gg/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-gg/rivetkit/issues) + +## Getting Started + +### Prerequisites + +- Node.js + +### Installation + +```sh +git clone https://github.com/rivet-gg/rivetkit +cd rivetkit/examples/hono +npm install +``` + +### Development + +```sh +npm run dev +``` + +Open your browser to http://localhost:3000 to see the Hono server with RivetKit integration. + +## License + +Apache 2.0 \ No newline at end of file diff --git a/examples/drizzle/drizzle.config.ts b/examples/drizzle/drizzle.config.ts new file mode 100644 index 000000000..cbb8b521e --- /dev/null +++ b/examples/drizzle/drizzle.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "@rivetkit/db/drizzle"; + +export default defineConfig({ + out: "./drizzle", + schema: "./src/db/schema.ts", +}); diff --git a/examples/drizzle/drizzle/0000_wonderful_iron_patriot.sql b/examples/drizzle/drizzle/0000_wonderful_iron_patriot.sql new file mode 100644 index 000000000..2382ea5a2 --- /dev/null +++ b/examples/drizzle/drizzle/0000_wonderful_iron_patriot.sql @@ -0,0 +1,8 @@ +CREATE TABLE `users_table` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `name` text NOT NULL, + `age` integer NOT NULL, + `email` text NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_table_email_unique` ON `users_table` (`email`); \ No newline at end of file diff --git a/examples/drizzle/drizzle/meta/0000_snapshot.json b/examples/drizzle/drizzle/meta/0000_snapshot.json new file mode 100644 index 000000000..c6434f1d6 --- /dev/null +++ b/examples/drizzle/drizzle/meta/0000_snapshot.json @@ -0,0 +1,64 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "22f3d49c-97d5-46ca-b0f1-99950c3efec7", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "users_table": { + "name": "users_table", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_table_email_unique": { + "name": "users_table_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/examples/drizzle/drizzle/meta/_journal.json b/examples/drizzle/drizzle/meta/_journal.json new file mode 100644 index 000000000..5e0783723 --- /dev/null +++ b/examples/drizzle/drizzle/meta/_journal.json @@ -0,0 +1,20 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1750711614205, + "tag": "0000_wonderful_iron_patriot", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1750716663518, + "tag": "0001_rich_susan_delgado", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/examples/drizzle/drizzle/migrations.js b/examples/drizzle/drizzle/migrations.js new file mode 100644 index 000000000..33f0f927c --- /dev/null +++ b/examples/drizzle/drizzle/migrations.js @@ -0,0 +1,10 @@ +import journal from './meta/_journal.json'; +import m0000 from './0000_wonderful_iron_patriot.sql'; + + export default { + journal, + migrations: { + m0000, + } + } + \ No newline at end of file diff --git a/examples/drizzle/hooks.js b/examples/drizzle/hooks.js new file mode 100644 index 000000000..cc7bce84f --- /dev/null +++ b/examples/drizzle/hooks.js @@ -0,0 +1,10 @@ +export async function load(url, context, nextLoad) { + if(url.endsWith('.sql')) { + return { + shortCircuit: true, + format: 'module', + source: `export default 'SQL file loaded from ${url}';` + } + } + return nextLoad(url, context) +} \ No newline at end of file diff --git a/examples/drizzle/package.json b/examples/drizzle/package.json new file mode 100644 index 000000000..f2b525c14 --- /dev/null +++ b/examples/drizzle/package.json @@ -0,0 +1,27 @@ +{ + "name": "example-sqlite", + "version": "0.9.0-rc.1", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx --watch src/server.ts", + "check-types": "tsc --noEmit" + }, + "devDependencies": { + "@types/node": "^22.13.9", + "rivetkit": "workspace:*", + "tsx": "^3.12.7", + "typescript": "^5.5.2" + }, + "dependencies": { + "@rivetkit/db": "workspace:0.9.0-rc.1", + "drizzle-kit": "^0.31.2", + "drizzle-orm": "^0.44.2" + }, + "example": { + "platforms": [ + "*" + ] + }, + "stableVersion": "0.8.0" +} diff --git a/examples/drizzle/register.js b/examples/drizzle/register.js new file mode 100644 index 000000000..31ef20696 --- /dev/null +++ b/examples/drizzle/register.js @@ -0,0 +1,15 @@ +import {register} from "node:module"; +import { pathToFileURL } from 'node:url'; + + +register("./hooks.js", pathToFileURL(__filename)) + + +// registerHooks({ +// resolve(specifier, context, nextResolve) { +// console.log({specifier, context}); +// }, +// load(url, context, nextLoad) { +// console.log({url, context}); +// }, +// }); \ No newline at end of file diff --git a/examples/drizzle/src/db/schema.ts b/examples/drizzle/src/db/schema.ts new file mode 100644 index 000000000..f24058ac8 --- /dev/null +++ b/examples/drizzle/src/db/schema.ts @@ -0,0 +1,9 @@ +// import { int, sqliteTable, text } from "@rivetkit/db/drizzle"; + +// export const usersTable = sqliteTable("users_table", { +// id: int().primaryKey({ autoIncrement: true }), +// name: text().notNull(), +// age: int().notNull(), +// email: text().notNull().unique(), +// email2: text().notNull().unique(), +// }); diff --git a/examples/drizzle/src/registry.ts b/examples/drizzle/src/registry.ts new file mode 100644 index 000000000..b7896048f --- /dev/null +++ b/examples/drizzle/src/registry.ts @@ -0,0 +1,27 @@ +// import { worker, setup } from "rivetkit"; +// import { db } from "@rivetkit/db/drizzle"; +// import * as schema from "./db/schema"; +// import migrations from "../drizzle/migrations"; + +// export const counter = worker({ +// db: db({ schema, migrations }), +// state: { +// count: 0, +// }, +// onAuth: () => { +// // Configure auth here +// }, +// actions: { +// increment: (c, x: number) => { +// // createState or state fix fix fix +// c.db.c.state.count += x; +// return c.state.count; +// }, +// }, +// }); + +// export const registry = setup({ +// workers: { counter }, +// }); + +// export type Registry = typeof registry; diff --git a/examples/drizzle/src/server.ts b/examples/drizzle/src/server.ts new file mode 100644 index 000000000..5be165d64 --- /dev/null +++ b/examples/drizzle/src/server.ts @@ -0,0 +1,7 @@ +// import { registry } from "./registry"; +// import { createMemoryDriver } from "@rivetkit/memory"; +// import { serve } from "@rivetkit/nodejs"; + +// serve(registry, { +// driver: createMemoryDriver(), +// }); diff --git a/examples/drizzle/tsconfig.json b/examples/drizzle/tsconfig.json new file mode 100644 index 000000000..4f308bb30 --- /dev/null +++ b/examples/drizzle/tsconfig.json @@ -0,0 +1,44 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "esnext", + /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "lib": ["esnext"], + /* Specify what JSX code is generated. */ + "jsx": "react-jsx", + "allowArbitraryExtensions": true, + + /* Specify what module code is generated. */ + "module": "esnext", + /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "bundler", + /* Specify type package names to be included without being referenced in a source file. */ + "types": ["node"], + /* Enable importing .json files */ + "resolveJsonModule": true, + + /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + "allowJs": true, + /* Enable error reporting in type-checked JavaScript files. */ + "checkJs": false, + + /* Disable emitting files from a compilation. */ + "noEmit": true, + + /* Ensure that each file can be safely transpiled without relying on other imports. */ + "isolatedModules": true, + /* Allow 'import x from y' when a module doesn't have a default export. */ + "allowSyntheticDefaultImports": true, + /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true, + + /* Enable all strict type-checking options. */ + "strict": true, + + /* Skip type checking all .d.ts files. */ + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/core/fixtures/driver-test-suite/action-timeout.ts b/packages/core/fixtures/driver-test-suite/action-timeout.ts new file mode 100644 index 000000000..598d47e8e --- /dev/null +++ b/packages/core/fixtures/driver-test-suite/action-timeout.ts @@ -0,0 +1,68 @@ +import { worker } from "rivetkit"; + +// Short timeout worker +export const shortTimeoutWorker = worker({ + onAuth: () => {}, + state: { value: 0 }, + options: { + action: { + timeout: 50, // 50ms timeout + }, + }, + actions: { + quickAction: async (c) => { + return "quick response"; + }, + slowAction: async (c) => { + // This action should timeout + await new Promise((resolve) => setTimeout(resolve, 100)); + return "slow response"; + }, + }, +}); + +// Long timeout worker +export const longTimeoutWorker = worker({ + onAuth: () => {}, + state: { value: 0 }, + options: { + action: { + timeout: 200, // 200ms timeout + }, + }, + actions: { + delayedAction: async (c) => { + // This action should complete within timeout + await new Promise((resolve) => setTimeout(resolve, 100)); + return "delayed response"; + }, + }, +}); + +// Default timeout worker +export const defaultTimeoutWorker = worker({ + onAuth: () => {}, + state: { value: 0 }, + actions: { + normalAction: async (c) => { + await new Promise((resolve) => setTimeout(resolve, 50)); + return "normal response"; + }, + }, +}); + +// Sync worker (timeout shouldn't apply) +export const syncTimeoutWorker = worker({ + onAuth: () => {}, + state: { value: 0 }, + options: { + action: { + timeout: 50, // 50ms timeout + }, + }, + actions: { + syncAction: (c) => { + return "sync response"; + }, + }, +}); diff --git a/packages/core/src/client/worker-common.ts b/packages/core/src/client/worker-common.ts index cabd449f5..4d17862fb 100644 --- a/packages/core/src/client/worker-common.ts +++ b/packages/core/src/client/worker-common.ts @@ -2,16 +2,6 @@ import type { AnyWorkerDefinition, WorkerDefinition, } from "@/worker/definition"; -import type * as protoHttpResolve from "@/worker/protocol/http/resolve"; -import type { Encoding } from "@/worker/protocol/serde"; -import type { WorkerQuery } from "@/manager/protocol/query"; -import { logger } from "./log"; -import * as errors from "./errors"; -import { sendHttpRequest } from "./utils"; -import { - HEADER_WORKER_QUERY, - HEADER_ENCODING, -} from "@/worker/router-endpoints"; /** * Action function returned by Worker connections and handles. @@ -33,7 +23,8 @@ export type WorkerActionFunction< * Maps action methods from worker definition to typed function signatures. */ export type WorkerDefinitionActions = - AD extends WorkerDefinition + // biome-ignore lint/suspicious/noExplicitAny: safe to use any here + AD extends WorkerDefinition ? { [K in keyof R]: R[K] extends (...args: infer Args) => infer Return ? WorkerActionFunction diff --git a/packages/core/src/drivers/memory/worker.ts b/packages/core/src/drivers/memory/worker.ts index d22cb4d6a..c4e404edc 100644 --- a/packages/core/src/drivers/memory/worker.ts +++ b/packages/core/src/drivers/memory/worker.ts @@ -32,4 +32,8 @@ export class MemoryWorkerDriver implements WorkerDriver { worker.onAlarm(); }, delay); } + + getDatabase(workerId: string): Promise { + return Promise.resolve(undefined); + } } diff --git a/packages/core/src/drivers/rivet/worker-driver.ts b/packages/core/src/drivers/rivet/worker-driver.ts index 9533f8a01..f7d8a6a2d 100644 --- a/packages/core/src/drivers/rivet/worker-driver.ts +++ b/packages/core/src/drivers/rivet/worker-driver.ts @@ -52,4 +52,9 @@ export class RivetWorkerDriver implements WorkerDriver { worker.onAlarm(); }, timeout); } + + getDatabase(_workerId: string): Promise { + // TODO: Implement database access + return Promise.resolve(undefined); + } } diff --git a/packages/core/src/registry/config.ts b/packages/core/src/registry/config.ts index 6bfed9437..833b3c1e2 100644 --- a/packages/core/src/registry/config.ts +++ b/packages/core/src/registry/config.ts @@ -5,7 +5,7 @@ import { z } from "zod"; export const WorkersSchema = z.record( z.string(), - z.custom>(), + z.custom>(), ); export type RegistryWorkers = z.infer; @@ -21,6 +21,7 @@ export const RegistryConfigSchema = z.object({ * Test configuration. * * DO NOT MANUALLY ENABLE. THIS IS USED INTERNALLY. + * @internal **/ test: TestConfigSchema.optional().default({ enabled: false }), }); diff --git a/packages/core/src/test/driver/worker.ts b/packages/core/src/test/driver/worker.ts index c6820a2be..1140896b4 100644 --- a/packages/core/src/test/driver/worker.ts +++ b/packages/core/src/test/driver/worker.ts @@ -37,4 +37,8 @@ export class TestWorkerDriver implements WorkerDriver { worker.onAlarm(); }, delay); } + + getDatabase(workerId: string): Promise { + return Promise.resolve(undefined); + } } diff --git a/packages/core/src/worker/action.ts b/packages/core/src/worker/action.ts index bc03dd4da..ed9f49119 100644 --- a/packages/core/src/worker/action.ts +++ b/packages/core/src/worker/action.ts @@ -1,20 +1,18 @@ -import type { AnyWorkerInstance } from "./instance"; import type { Conn } from "./connection"; import type { Logger } from "@/common/log"; import type { WorkerKey } from "@/common/utils"; import type { Schedule } from "./schedule"; import type { ConnId } from "./connection"; import type { SaveStateOptions } from "./instance"; -import { Actions } from "./config"; -import { WorkerContext } from "./context"; +import type { WorkerContext } from "./context"; /** * Context for a remote procedure call. * * @typeParam A Worker this action belongs to */ -export class ActionContext { - #workerContext: WorkerContext; +export class ActionContext { + #workerContext: WorkerContext; /** * Should not be called directly. @@ -23,8 +21,8 @@ export class ActionContext { * @param conn - The connection associated with the action */ constructor( - workerContext: WorkerContext, - public readonly conn: Conn, + workerContext: WorkerContext, + public readonly conn: Conn, ) { this.#workerContext = workerContext; } @@ -95,10 +93,17 @@ export class ActionContext { /** * Gets the map of connections. */ - get conns(): Map> { + get conns(): Map> { return this.#workerContext.conns; } + /** + * @experimental + */ + get db(): DB { + return this.#workerContext.db; + } + /** * Forces the state to get saved. */ diff --git a/packages/core/src/worker/config.ts b/packages/core/src/worker/config.ts index a660f131c..8a6aeb917 100644 --- a/packages/core/src/worker/config.ts +++ b/packages/core/src/worker/config.ts @@ -24,6 +24,7 @@ export const WorkerConfigSchema = z connState: z.any().optional(), createConnState: z.function().optional(), vars: z.any().optional(), + db: z.any().optional(), createVars: z.function().optional(), options: z .object({ @@ -98,11 +99,19 @@ export interface OnConnectOptions { // This must have only one or the other or else S will not be able to be inferred // // Data returned from this handler will be available on `c.state`. -type CreateState = +type CreateState = | { state: S } | { createState: ( - c: WorkerContext, + c: WorkerContext< + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined + >, opts: CreateStateOptions, ) => S | Promise; } @@ -113,11 +122,19 @@ type CreateState = // This must have only one or the other or else S will not be able to be inferred // // Data returned from this handler will be available on `c.conn.state`. -type CreateConnState = +type CreateConnState = | { connState: CS } | { createConnState: ( - c: WorkerContext, + c: WorkerContext< + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined + >, opts: OnConnectOptions, ) => CS | Promise; } @@ -129,7 +146,7 @@ type CreateConnState = /** * @experimental */ -type CreateVars = +type CreateVars = | { /** * @experimental @@ -141,15 +158,23 @@ type CreateVars = * @experimental */ createVars: ( - c: WorkerContext, + c: WorkerContext< + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined + >, driverCtx: unknown, ) => V | Promise; } | Record; -export interface Actions { +export interface Actions { [Action: string]: ( - c: ActionContext, + c: ActionContext, ...args: any[] ) => any; } @@ -180,7 +205,8 @@ interface BaseWorkerConfig< V, I, AD, - R extends Actions, + DB, + R extends Actions, > { /** * Called on the HTTP server before clients can interact with the worker. @@ -216,7 +242,7 @@ interface BaseWorkerConfig< * This is called before any other lifecycle hooks. */ onCreate?: ( - c: WorkerContext, + c: WorkerContext, opts: OnCreateOptions, ) => void | Promise; @@ -228,7 +254,7 @@ interface BaseWorkerConfig< * * @returns Void or a Promise that resolves when startup is complete */ - onStart?: (c: WorkerContext) => void | Promise; + onStart?: (c: WorkerContext) => void | Promise; /** * Called when the worker's state changes. @@ -238,7 +264,10 @@ interface BaseWorkerConfig< * * @param newState The updated state */ - onStateChange?: (c: WorkerContext, newState: S) => void; + onStateChange?: ( + c: WorkerContext, + newState: S, + ) => void; /** * Called before a client connects to the worker. @@ -261,7 +290,7 @@ interface BaseWorkerConfig< * @throws Throw an error to reject the connection */ onBeforeConnect?: ( - c: WorkerContext, + c: WorkerContext, opts: OnConnectOptions, ) => void | Promise; @@ -275,8 +304,8 @@ interface BaseWorkerConfig< * @returns Void or a Promise that resolves when connection handling is complete */ onConnect?: ( - c: WorkerContext, - conn: Conn, + c: WorkerContext, + conn: Conn, ) => void | Promise; /** @@ -289,8 +318,8 @@ interface BaseWorkerConfig< * @returns Void or a Promise that resolves when disconnect handling is complete */ onDisconnect?: ( - c: WorkerContext, - conn: Conn, + c: WorkerContext, + conn: Conn, ) => void | Promise; /** @@ -306,7 +335,7 @@ interface BaseWorkerConfig< * @returns The modified output to send to the client */ onBeforeActionResponse?: ( - c: WorkerContext, + c: WorkerContext, name: string, args: unknown[], output: Out, @@ -315,10 +344,32 @@ interface BaseWorkerConfig< actions: R; } +export type DatabaseFactory = (ctx: { + createDatabase: () => Promise; +}) => Promise<{ + /** + * @experimental + */ + db?: DB; + /** + * @experimental + */ + onMigrate?: () => void | Promise; +}>; + +type WorkerDatabaseConfig = + | { + /** + * @experimental + */ + db: DatabaseFactory; + } + | Record; + // 1. Infer schema // 2. Omit keys that we'll manually define (because of generics) // 3. Define our own types that have generic constraints -export type WorkerConfig = Omit< +export type WorkerConfig = Omit< z.infer, | "actions" | "onAuth" @@ -335,11 +386,13 @@ export type WorkerConfig = Omit< | "createConnState" | "vars" | "createVars" + | "db" > & - BaseWorkerConfig> & - CreateState & - CreateConnState & - CreateVars; + BaseWorkerConfig> & + CreateState & + CreateConnState & + CreateVars & + WorkerDatabaseConfig; // See description on `WorkerConfig` export type WorkerConfigInput< @@ -349,7 +402,8 @@ export type WorkerConfigInput< V, I, AD, - R extends Actions, + DB, + R extends Actions, > = Omit< z.input, | "actions" @@ -367,11 +421,13 @@ export type WorkerConfigInput< | "createConnState" | "vars" | "createVars" + | "db" > & - BaseWorkerConfig & - CreateState & - CreateConnState & - CreateVars; + BaseWorkerConfig & + CreateState & + CreateConnState & + CreateVars & + WorkerDatabaseConfig; // For testing type definitions: export function test< @@ -381,17 +437,19 @@ export function test< V, I, AD, - R extends Actions, + DB, + R extends Actions, >( - input: WorkerConfigInput, -): WorkerConfig { + input: WorkerConfigInput, +): WorkerConfig { const config = WorkerConfigSchema.parse(input) as WorkerConfig< S, CP, CS, V, I, - AD + AD, + DB >; return config; } diff --git a/packages/core/src/worker/connection.ts b/packages/core/src/worker/connection.ts index 8f0645f35..897f35c1a 100644 --- a/packages/core/src/worker/connection.ts +++ b/packages/core/src/worker/connection.ts @@ -3,9 +3,9 @@ import * as errors from "./errors"; import { generateSecureToken } from "./utils"; import { CachedSerializer } from "./protocol/serde"; import type { ConnDriver } from "./driver"; -import * as messageToClient from "@/worker/protocol/message/to-client"; +import type * as messageToClient from "@/worker/protocol/message/to-client"; import type { PersistedConn } from "./persisted"; -import * as wsToClient from "@/worker/protocol/message/to-client"; +import type * as wsToClient from "@/worker/protocol/message/to-client"; export function generateConnId(): string { return crypto.randomUUID(); @@ -17,7 +17,7 @@ export function generateConnToken(): string { export type ConnId = string; -export type AnyConn = Conn; +export type AnyConn = Conn; /** * Represents a client connection to a worker. @@ -26,13 +26,13 @@ export type AnyConn = Conn; * * @see {@link https://rivet.gg/docs/connections|Connection Documentation} */ -export class Conn { +export class Conn { subscriptions: Set = new Set(); #stateEnabled: boolean; // TODO: Remove this cyclical reference - #worker: WorkerInstance; + #worker: WorkerInstance; /** * The proxied state that notifies of changes automatically. @@ -103,7 +103,7 @@ export class Conn { * @protected */ public constructor( - worker: WorkerInstance, + worker: WorkerInstance, persist: PersistedConn, driver: ConnDriver, stateEnabled: boolean, diff --git a/packages/core/src/worker/context.ts b/packages/core/src/worker/context.ts index f7fd07064..6363e2a4b 100644 --- a/packages/core/src/worker/context.ts +++ b/packages/core/src/worker/context.ts @@ -1,17 +1,16 @@ -import { Logger } from "@/common/log"; -import { Actions } from "./config"; -import { WorkerInstance, SaveStateOptions } from "./instance"; -import { Conn, ConnId } from "./connection"; -import { WorkerKey } from "@/common/utils"; -import { Schedule } from "./schedule"; +import type { Logger } from "@/common/log"; +import type { WorkerInstance, SaveStateOptions } from "./instance"; +import type { Conn, ConnId } from "./connection"; +import type { WorkerKey } from "@/common/utils"; +import type { Schedule } from "./schedule"; /** * WorkerContext class that provides access to worker methods and state */ -export class WorkerContext { - #worker: WorkerInstance; +export class WorkerContext { + #worker: WorkerInstance; - constructor(worker: WorkerInstance) { + constructor(worker: WorkerInstance) { this.#worker = worker; } @@ -84,10 +83,19 @@ export class WorkerContext { /** * Gets the map of connections. */ - get conns(): Map> { + get conns(): Map> { return this.#worker.conns; } + /** + * Gets the database. + * @experimental + * @throws {DatabaseNotEnabled} If the database is not enabled. + */ + get db(): DB { + return this.#worker.db; + } + /** * Forces the state to get saved. * diff --git a/packages/core/src/worker/definition.ts b/packages/core/src/worker/definition.ts index 617040a38..31bcaf0c0 100644 --- a/packages/core/src/worker/definition.ts +++ b/packages/core/src/worker/definition.ts @@ -1,6 +1,6 @@ -import { type WorkerConfig, type Actions } from "./config"; +import type { WorkerConfig, Actions } from "./config"; import { WorkerInstance } from "./instance"; -import { WorkerContext } from "./context"; +import type { WorkerContext } from "./context"; import type { ActionContext } from "./action"; export type AnyWorkerDefinition = WorkerDefinition< @@ -10,6 +10,7 @@ export type AnyWorkerDefinition = WorkerDefinition< any, any, any, + any, any >; @@ -24,9 +25,10 @@ export type WorkerContextOf = infer V, infer I, infer AD, + infer DB, any > - ? WorkerContext + ? WorkerContext : never; /** @@ -40,9 +42,10 @@ export type ActionContextOf = infer V, infer I, infer AD, + infer DB, any > - ? ActionContext + ? ActionContext : never; export class WorkerDefinition< @@ -52,19 +55,20 @@ export class WorkerDefinition< V, I, AD, - R extends Actions, + DB, + R extends Actions, > { - #config: WorkerConfig; + #config: WorkerConfig; - constructor(config: WorkerConfig) { + constructor(config: WorkerConfig) { this.#config = config; } - get config(): WorkerConfig { + get config(): WorkerConfig { return this.#config; } - instantiate(): WorkerInstance { + instantiate(): WorkerInstance { return new WorkerInstance(this.#config); } } diff --git a/packages/core/src/worker/driver.ts b/packages/core/src/worker/driver.ts index 7eb2a184a..ae51063fc 100644 --- a/packages/core/src/worker/driver.ts +++ b/packages/core/src/worker/driver.ts @@ -17,6 +17,13 @@ export interface WorkerDriver { // Schedule setAlarm(worker: AnyWorkerInstance, timestamp: number): Promise; + // Database + /** + * @experimental + * This is an experimental API that may change in the future. + */ + getDatabase(workerId: string): Promise; + // TODO: //destroy(): Promise; //readState(): void; diff --git a/packages/core/src/worker/errors.ts b/packages/core/src/worker/errors.ts index fbbe87077..f97b600cd 100644 --- a/packages/core/src/worker/errors.ts +++ b/packages/core/src/worker/errors.ts @@ -295,3 +295,12 @@ export class Forbidden extends WorkerError { this.statusCode = 403; } } + +export class DatabaseNotEnabled extends WorkerError { + constructor() { + super( + "database_not_enabled", + "Database not enabled. Must implement `database` to use database.", + ); + } +} diff --git a/packages/core/src/worker/instance.ts b/packages/core/src/worker/instance.ts index 200356c29..ab078b371 100644 --- a/packages/core/src/worker/instance.ts +++ b/packages/core/src/worker/instance.ts @@ -15,7 +15,7 @@ import { instanceLogger, logger } from "./log"; import type { ActionContext } from "./action"; import { DeadlineError, Lock, deadline } from "./utils"; import { Schedule } from "./schedule"; -import * as wsToClient from "@/worker/protocol/message/to-client"; +import type * as wsToClient from "@/worker/protocol/message/to-client"; import type * as wsToServer from "@/worker/protocol/message/to-server"; import { CachedSerializer } from "./protocol/serde"; import { WorkerContext } from "./context"; @@ -37,8 +37,22 @@ export interface SaveStateOptions { } /** Worker type alias with all `any` types. Used for `extends` in classes referencing this worker. */ -// biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` -export type AnyWorkerInstance = WorkerInstance; +export type AnyWorkerInstance = WorkerInstance< + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends` + any +>; export type ExtractWorkerState = A extends WorkerInstance< @@ -52,6 +66,8 @@ export type ExtractWorkerState = // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` any, // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` any > ? State @@ -69,6 +85,8 @@ export type ExtractWorkerConnParams = // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` any, // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` any > ? ConnParams @@ -86,14 +104,16 @@ export type ExtractWorkerConnState = // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` any, // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` + any, + // biome-ignore lint/suspicious/noExplicitAny: Must be used for `extends` any > ? ConnState : never; -export class WorkerInstance { +export class WorkerInstance { // Shared worker context for this instance - workerContext: WorkerContext; + workerContext: WorkerContext; isStopping = false; #persistChanged = false; @@ -116,7 +136,7 @@ export class WorkerInstance { #vars?: V; #backgroundPromises: Promise[] = []; - #config: WorkerConfig; + #config: WorkerConfig; #connectionDrivers!: ConnDrivers; #workerDriver!: WorkerDriver; #workerId!: string; @@ -125,12 +145,13 @@ export class WorkerInstance { #region!: string; #ready = false; - #connections = new Map>(); - #subscriptionIndex = new Map>>(); + #connections = new Map>(); + #subscriptionIndex = new Map>>(); #schedule!: Schedule; // inspector!: WorkerInspector; + #db!: DB; get id() { return this.#workerId; @@ -143,7 +164,7 @@ export class WorkerInstance { * * @private */ - constructor(config: WorkerConfig) { + constructor(config: WorkerConfig) { this.#config = config; this.workerContext = new WorkerContext(this); } @@ -181,6 +202,7 @@ export class WorkerInstance { undefined, undefined, undefined, + undefined, undefined >, this.#workerDriver.getContext(this.#workerId), @@ -210,6 +232,17 @@ export class WorkerInstance { } } + // Setup Database + if ("db" in this.#config) { + const db = await this.#config.db({ + createDatabase: () => workerDriver.getDatabase(this.#workerId), + }); + + logger().info("database migration starting"); + await db.onMigrate?.(); + logger().info("database migration complete"); + } + // Set alarm for next scheduled event if any exist after finishing initiation sequence if (this.#persist.e.length > 0) { await this.#workerDriver.setAlarm(this, this.#persist.e[0].t); @@ -491,7 +524,7 @@ export class WorkerInstance { for (const connPersist of this.#persist.c) { // Create connections const driver = this.__getConnDriver(connPersist.d); - const conn = new Conn( + const conn = new Conn( this, connPersist, driver, @@ -525,6 +558,7 @@ export class WorkerInstance { undefined, undefined, undefined, + undefined, undefined >, { input }, @@ -557,14 +591,14 @@ export class WorkerInstance { } } - __getConnForId(id: string): Conn | undefined { + __getConnForId(id: string): Conn | undefined { return this.#connections.get(id); } /** * Removes a connection and cleans up its resources. */ - __removeConn(conn: Conn | undefined) { + __removeConn(conn: Conn | undefined) { if (!conn) { logger().warn("`conn` does not exist"); return; @@ -638,6 +672,7 @@ export class WorkerInstance { undefined, undefined, undefined, + undefined, undefined >, onBeforeConnectOpts, @@ -680,7 +715,7 @@ export class WorkerInstance { driverId: string, driverState: unknown, authData: unknown, - ): Promise> { + ): Promise> { if (this.#connections.has(connectionId)) { throw new Error(`Connection already exists: ${connectionId}`); } @@ -697,7 +732,7 @@ export class WorkerInstance { a: authData, su: [], }; - const conn = new Conn( + const conn = new Conn( this, persist, driver, @@ -753,7 +788,7 @@ export class WorkerInstance { // MARK: Messages async processMessage( message: wsToServer.ToServer, - conn: Conn, + conn: Conn, ) { await processMessage(message, this, conn, { onExecuteAction: async (ctx, name, args) => { @@ -771,7 +806,7 @@ export class WorkerInstance { // MARK: Events #addSubscription( eventName: string, - connection: Conn, + connection: Conn, fromPersist: boolean, ) { if (connection.subscriptions.has(eventName)) { @@ -801,7 +836,7 @@ export class WorkerInstance { #removeSubscription( eventName: string, - connection: Conn, + connection: Conn, fromRemoveConn: boolean, ) { if (!connection.subscriptions.has(eventName)) { @@ -860,7 +895,7 @@ export class WorkerInstance { * @internal */ async executeAction( - ctx: ActionContext, + ctx: ActionContext, actionName: string, args: unknown[], ): Promise { @@ -873,7 +908,6 @@ export class WorkerInstance { } // Check if the method exists on this object - // biome-ignore lint/suspicious/noExplicitAny: action name is dynamic from client const actionFunction = this.#config.actions[actionName]; if (typeof actionFunction !== "function") { logger().warn("action not found", { actionName: actionName }); @@ -1004,7 +1038,7 @@ export class WorkerInstance { /** * Gets the map of connections. */ - get conns(): Map> { + get conns(): Map> { return this.#connections; } @@ -1018,6 +1052,18 @@ export class WorkerInstance { return this.#persist.s; } + /** + * Gets the database. + * @experimental + * @throws {DatabaseNotEnabled} If the database is not enabled. + */ + get db(): DB { + if (!this.#db) { + throw new errors.DatabaseNotEnabled(); + } + return this.#db; + } + /** * Sets the current state. * diff --git a/packages/core/src/worker/mod.ts b/packages/core/src/worker/mod.ts index 08c5af305..3dede08b2 100644 --- a/packages/core/src/worker/mod.ts +++ b/packages/core/src/worker/mod.ts @@ -20,9 +20,26 @@ export type { ActionContextOf, } from "./definition"; -export function worker>( - input: WorkerConfigInput, -): WorkerDefinition { - const config = WorkerConfigSchema.parse(input) as WorkerConfig; +export function worker< + S, + CP, + CS, + V, + I, + AD, + DB, + R extends Actions, +>( + input: WorkerConfigInput, +): WorkerDefinition { + const config = WorkerConfigSchema.parse(input) as WorkerConfig< + S, + CP, + CS, + V, + I, + AD, + DB + >; return new WorkerDefinition(config); } diff --git a/packages/core/src/worker/protocol/message/mod.ts b/packages/core/src/worker/protocol/message/mod.ts index 9dd20e36c..0d9619e19 100644 --- a/packages/core/src/worker/protocol/message/mod.ts +++ b/packages/core/src/worker/protocol/message/mod.ts @@ -1,6 +1,6 @@ -import * as wsToClient from "@/worker/protocol/message/to-client"; +import type * as wsToClient from "@/worker/protocol/message/to-client"; import * as wsToServer from "@/worker/protocol/message/to-server"; -import type { WorkerInstance, AnyWorkerInstance } from "../../instance"; +import type { WorkerInstance } from "../../instance"; import type { Conn } from "../../connection"; import * as errors from "../../errors"; import { logger } from "../../log"; @@ -9,13 +9,11 @@ import { assertUnreachable } from "../../utils"; import { z } from "zod"; import { deserialize, - Encoding, - InputData, + type Encoding, + type InputData, CachedSerializer, } from "@/worker/protocol/serde"; import { deconstructError } from "@/common/utils"; -import { Actions } from "@/worker/config"; -import invariant from "invariant"; export const TransportSchema = z.enum(["websocket", "sse"]); @@ -69,24 +67,27 @@ export async function parseMessage( return message; } -export interface ProcessMessageHandler { +export interface ProcessMessageHandler { onExecuteAction?: ( - ctx: ActionContext, + ctx: ActionContext, name: string, args: unknown[], ) => Promise; - onSubscribe?: (eventName: string, conn: Conn) => Promise; + onSubscribe?: ( + eventName: string, + conn: Conn, + ) => Promise; onUnsubscribe?: ( eventName: string, - conn: Conn, + conn: Conn, ) => Promise; } -export async function processMessage( +export async function processMessage( message: wsToServer.ToServer, - worker: WorkerInstance, - conn: Conn, - handler: ProcessMessageHandler, + worker: WorkerInstance, + conn: Conn, + handler: ProcessMessageHandler, ) { let actionId: number | undefined; let actionName: string | undefined; @@ -110,7 +111,10 @@ export async function processMessage( argsCount: args.length, }); - const ctx = new ActionContext(worker.workerContext, conn); + const ctx = new ActionContext( + worker.workerContext, + conn, + ); // Process the action request and wait for the result // This will wait for async actions to complete diff --git a/packages/core/tests/worker-types.test.ts b/packages/core/tests/worker-types.test.ts index 3fc1a5223..268f9cf87 100644 --- a/packages/core/tests/worker-types.test.ts +++ b/packages/core/tests/worker-types.test.ts @@ -30,6 +30,11 @@ describe("WorkerDefinition", () => { baz: string; } + interface TestDatabase { + onMigrate: () => void; + client: object; + } + // For testing type utilities, we don't need a real worker instance // We just need a properly typed WorkerDefinition to check against type TestActions = Record; @@ -40,6 +45,7 @@ describe("WorkerDefinition", () => { TestVars, TestInput, TestAuthData, + TestDatabase, TestActions >; @@ -51,7 +57,8 @@ describe("WorkerDefinition", () => { TestConnState, TestVars, TestInput, - TestAuthData + TestAuthData, + TestDatabase > >(); @@ -67,7 +74,8 @@ describe("WorkerDefinition", () => { TestConnState, TestVars, TestInput, - TestAuthData + TestAuthData, + TestDatabase > >(); }); diff --git a/packages/db/package.json b/packages/db/package.json new file mode 100644 index 000000000..0077d44e0 --- /dev/null +++ b/packages/db/package.json @@ -0,0 +1,63 @@ +{ + "name": "@rivetkit/db", + "version": "0.9.0-rc.1", + "license": "Apache-2.0", + "sideEffects": false, + "type": "module", + "files": [ + "dist", + "package.json" + ], + "exports": { + ".": { + "import": { + "types": "./dist/mod.d.ts", + "default": "./dist/mod.js" + }, + "require": { + "types": "./dist/mod.d.cts", + "default": "./dist/mod.cjs" + } + }, + "./drizzle": { + "import": { + "types": "./dist/drizzle/mod.d.ts", + "default": "./dist/drizzle/mod.js" + }, + "require": { + "types": "./dist/drizzle/mod.d.cts", + "default": "./dist/drizzle/mod.cjs" + } + } + }, + "scripts": { + "build": "tsup src/mod.ts src/drizzle/mod.ts", + "check-types": "tsc --noEmit" + }, + "peerDependencies": { + "drizzle-kit": "^0.31.2", + "drizzle-orm": "^0.44.2", + "rivetkit": "*" + }, + "peerDependenciesMeta": { + "drizzle-orm": { + "optional": true + }, + "drizzle-kit": { + "optional": true + } + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.13", + "@types/node": "^24.0.4", + "drizzle-orm": "^0.44.2", + "rivetkit": "workspace:*", + "tsup": "^8.3.6", + "typescript": "^5.5.2", + "vitest": "^3.1.1" + }, + "stableVersion": "0.8.0", + "dependencies": { + "better-sqlite3": "^11.10.0" + } +} diff --git a/packages/db/src/config.ts b/packages/db/src/config.ts new file mode 100644 index 000000000..988e82142 --- /dev/null +++ b/packages/db/src/config.ts @@ -0,0 +1,12 @@ +export interface DatabaseConfig { + client: DB; + onMigrate: () => void; +} + +export interface DatabaseFactoryContext { + createDatabase: () => Promise; +} + +export type DatabaseFactory = ( + ctx: DatabaseFactoryContext, +) => Promise>; diff --git a/packages/db/src/drizzle/mod.ts b/packages/db/src/drizzle/mod.ts new file mode 100644 index 000000000..d7fe198d3 --- /dev/null +++ b/packages/db/src/drizzle/mod.ts @@ -0,0 +1,76 @@ +import { + type BetterSQLite3Database, + drizzle as sqliteDrizzle, +} from "drizzle-orm/better-sqlite3"; + +import { migrate as sqliteMigrate } from "drizzle-orm/durable-sqlite/migrator"; +import { drizzle as durableDrizzle } from "drizzle-orm/durable-sqlite"; +import { migrate as durableMigrate } from "drizzle-orm/durable-sqlite/migrator"; +import * as Database from "better-sqlite3"; +import type { DatabaseFactory } from "@/config"; + +export * from "drizzle-orm/sqlite-core"; + +import { defineConfig as originalDefineConfig, type Config } from "drizzle-kit"; + +export function defineConfig( + config: Partial, +): Config { + // This is a workaround to avoid the "drizzle-kit" import issue in the examples. + // It allows us to use the same defineConfig function in both the main package and the examples. + return originalDefineConfig({ + dialect: "sqlite", + driver: "durable-sqlite", + ...config, + }); +} + +interface DatabaseFactoryConfig< + TSchema extends Record = Record, +> { + /** + * The database schema. + */ + schema?: TSchema; + migrations?: any; +} + +export function db< + TSchema extends Record = Record, +>( + config?: DatabaseFactoryConfig, +): DatabaseFactory> { + return async (ctx) => { + const conn = await ctx.createDatabase(); + + if (!conn) { + throw new Error( + "Cannot create database connection, or database feature is not enabled.", + ); + } + + if (typeof conn === "object" && conn && "exec" in conn) { + // If the connection is already an object with exec method, return it + // i.e. in serverless environments (Cloudflare Workers) + const client = durableDrizzle(conn, config); + return { + client, + onMigrate: async () => { + await durableMigrate(client, config?.migrations); + }, + }; + } + + const client = sqliteDrizzle({ + client: new Database(conn as string), + ...config, + }); + + return { + client, + onMigrate: async () => { + await sqliteMigrate(client, config?.migrations); + }, + }; + }; +} diff --git a/packages/db/src/mod.ts b/packages/db/src/mod.ts new file mode 100644 index 000000000..47e0172d5 --- /dev/null +++ b/packages/db/src/mod.ts @@ -0,0 +1,46 @@ +import * as SQLite from "better-sqlite3"; +import type { DatabaseFactory } from "./config"; + +/** + * On serverless environments, we use a shim, as not all methods are available. + * This is a minimal shim that only includes the `exec` method, which is used for + * running raw SQL commands. + */ +type SQLiteShim = Pick; + +interface DatabaseFactoryConfig { + onMigrate?: (db: SQLiteShim) => void; +} + +export function db({ + onMigrate, +}: DatabaseFactoryConfig = {}): DatabaseFactory { + return async (ctx) => { + const conn = await ctx.createDatabase(); + + if (!conn) { + throw new Error( + "Cannot create database connection, or database feature is not enabled.", + ); + } + + if (typeof conn === "object" && conn && "exec" in conn) { + // if the connection is already an object with exec method, return it + // i.e. in serverless environments (cloudflare) + return { + client: conn as SQLiteShim, + onMigrate: () => { + onMigrate?.(client); + }, + }; + } + + const client = new SQLite(conn as string); + return { + client, + onMigrate: () => { + onMigrate?.(client); + }, + }; + }; +} diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json new file mode 100644 index 000000000..b98b96a49 --- /dev/null +++ b/packages/db/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/**/*"] +} diff --git a/packages/db/tsup.config.ts b/packages/db/tsup.config.ts new file mode 100644 index 000000000..e1becbf0a --- /dev/null +++ b/packages/db/tsup.config.ts @@ -0,0 +1,7 @@ +import defaultConfig from "../../tsup.base.ts"; +import { defineConfig } from "tsup"; + +export default defineConfig({ + ...defaultConfig, + external: ["better-sqlite3"], +}); diff --git a/packages/db/turbo.json b/packages/db/turbo.json new file mode 100644 index 000000000..95960709b --- /dev/null +++ b/packages/db/turbo.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"] +} diff --git a/packages/drivers/file-system/src/worker.ts b/packages/drivers/file-system/src/worker.ts index 96c5fc160..9b7412da6 100644 --- a/packages/drivers/file-system/src/worker.ts +++ b/packages/drivers/file-system/src/worker.ts @@ -7,42 +7,46 @@ export type WorkerDriverContext = Record; * File System implementation of the Worker Driver */ export class FileSystemWorkerDriver implements WorkerDriver { - #state: FileSystemGlobalState; - - constructor(state: FileSystemGlobalState) { - this.#state = state; - } - - /** - * Get the current storage directory path - */ - get storagePath(): string { - return this.#state.storagePath; - } - - getContext(_workerId: string): WorkerDriverContext { - return {}; - } + #state: FileSystemGlobalState; + + constructor(state: FileSystemGlobalState) { + this.#state = state; + } + + /** + * Get the current storage directory path + */ + get storagePath(): string { + return this.#state.storagePath; + } + + getContext(_workerId: string): WorkerDriverContext { + return {}; + } async readInput(workerId: string): Promise { - return this.#state.readInput(workerId); - } - - async readPersistedData(workerId: string): Promise { - return this.#state.readPersistedData(workerId); - } - - async writePersistedData(workerId: string, data: unknown): Promise { - this.#state.writePersistedData(workerId, data); - - // Save state to disk - await this.#state.saveWorkerState(workerId); - } - - async setAlarm(worker: AnyWorkerInstance, timestamp: number): Promise { - const delay = Math.max(0, timestamp - Date.now()); - setTimeout(() => { - worker.onAlarm(); - }, delay); - } + return this.#state.readInput(workerId); + } + + async readPersistedData(workerId: string): Promise { + return this.#state.readPersistedData(workerId); + } + + async writePersistedData(workerId: string, data: unknown): Promise { + this.#state.writePersistedData(workerId, data); + + // Save state to disk + await this.#state.saveWorkerState(workerId); + } + + async setAlarm(worker: AnyWorkerInstance, timestamp: number): Promise { + const delay = Math.max(0, timestamp - Date.now()); + setTimeout(() => { + worker.onAlarm(); + }, delay); + } + + getDatabase(workerId: string): Promise { + return Promise.resolve(undefined); + } } diff --git a/packages/drivers/redis/src/worker.ts b/packages/drivers/redis/src/worker.ts index b68ce49ed..1c32c80ce 100644 --- a/packages/drivers/redis/src/worker.ts +++ b/packages/drivers/redis/src/worker.ts @@ -43,4 +43,9 @@ export class RedisWorkerDriver implements WorkerDriver { worker.onAlarm(); }, delay); } + + getDatabase(workerId: string): Promise { + // Redis does not have a database concept like other drivers, so we return undefined + return Promise.resolve(undefined); + } } diff --git a/packages/platforms/cloudflare-workers/src/worker-driver.ts b/packages/platforms/cloudflare-workers/src/worker-driver.ts index d47c8724f..df36fc738 100644 --- a/packages/platforms/cloudflare-workers/src/worker-driver.ts +++ b/packages/platforms/cloudflare-workers/src/worker-driver.ts @@ -1,4 +1,4 @@ -// import { WorkerDriver, AnyWorkerInstance } from "@rivetkit/core/driver-helpers"; +// import type { WorkerDriver, AnyWorkerInstance } from "rivetkit/driver-helpers"; // import invariant from "invariant"; // import { KEYS } from "./worker-handler-do"; // @@ -63,4 +63,8 @@ // async setAlarm(worker: AnyWorkerInstance, timestamp: number): Promise { // await this.#getDOCtx(worker.id).storage.setAlarm(timestamp); // } +// +// async getDatabase(workerId: string): Promise { +// return this.#getDOCtx(workerId).storage.sql; +// } // } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1c53383e..d2d151562 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,7 +44,7 @@ importers: version: 2.5.4 vitest: specifier: ^3.1.1 - version: 3.2.4(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + version: 3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) zx: specifier: ^8.3.2 version: 8.5.5 @@ -168,6 +168,31 @@ importers: specifier: ^3.1.1 version: 3.2.4(@types/node@22.15.32)(tsx@3.14.0)(yaml@2.8.0) + examples/drizzle: + dependencies: + '@rivetkit/db': + specifier: workspace:0.9.0-rc.1 + version: link:../../packages/db + drizzle-kit: + specifier: ^0.31.2 + version: 0.31.2 + drizzle-orm: + specifier: ^0.44.2 + version: 0.44.2(@cloudflare/workers-types@4.20250619.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.2) + devDependencies: + '@types/node': + specifier: ^22.13.9 + version: 22.15.32 + rivetkit: + specifier: workspace:* + version: link:../../packages/rivetkit + tsx: + specifier: ^3.12.7 + version: 3.14.0 + typescript: + specifier: ^5.5.2 + version: 5.8.3 + examples/elysia: dependencies: '@rivetkit/react': @@ -446,6 +471,37 @@ importers: specifier: ^8.18.1 version: 8.18.2 + packages/db: + dependencies: + better-sqlite3: + specifier: ^11.10.0 + version: 11.10.0 + drizzle-kit: + specifier: ^0.31.2 + version: 0.31.2 + devDependencies: + '@types/better-sqlite3': + specifier: ^7.6.13 + version: 7.6.13 + '@types/node': + specifier: ^24.0.4 + version: 24.0.4 + drizzle-orm: + specifier: ^0.44.2 + version: 0.44.2(@cloudflare/workers-types@4.20250619.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.2) + rivetkit: + specifier: workspace:* + version: link:../rivetkit + tsup: + specifier: ^8.3.6 + version: 8.5.0(@microsoft/api-extractor@7.52.8(@types/node@24.0.4))(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) + typescript: + specifier: ^5.5.2 + version: 5.8.3 + vitest: + specifier: ^3.1.1 + version: 3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) + packages/drivers/file-system: dependencies: env-paths: @@ -525,13 +581,13 @@ importers: version: 5.8.3 vite: specifier: ^6.3.5 - version: 6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + version: 6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@24.0.3)(rollup@4.44.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0)) + version: 4.5.4(@types/node@24.0.4)(rollup@4.44.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.1.1 - version: 3.2.4(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + version: 3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) packages/frameworks/react: dependencies: @@ -559,7 +615,7 @@ importers: version: 19.1.6(@types/react@19.1.8) tsup: specifier: ^8.4.0 - version: 8.5.0(@microsoft/api-extractor@7.52.8(@types/node@24.0.3))(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) + version: 8.5.0(@microsoft/api-extractor@7.52.8(@types/node@24.0.4))(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) typescript: specifier: ^5.5.2 version: 5.8.3 @@ -575,13 +631,13 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: ^0.6.4 - version: 0.6.16(@cloudflare/workers-types@4.20250619.0)(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0)) + version: 0.6.16(@cloudflare/workers-types@4.20250619.0)(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0)) typescript: specifier: ^5.5.2 version: 5.8.3 vitest: specifier: ^3.1.1 - version: 3.2.4(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + version: 3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) wrangler: specifier: ^3.101.0 version: 3.114.9(@cloudflare/workers-types@4.20250619.0) @@ -911,9 +967,20 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + '@emnapi/runtime@1.4.3': resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + '@esbuild-plugins/node-globals-polyfill@0.2.3': resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} peerDependencies: @@ -1911,6 +1978,9 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -1959,6 +2029,9 @@ packages: '@types/node@24.0.3': resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} + '@types/node@24.0.4': + resolution: {integrity: sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==} + '@types/prompts@2.4.9': resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} @@ -2159,15 +2232,27 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + better-auth@1.2.10: resolution: {integrity: sha512-nEj1RG4DdLUuJiV5CR93ORyPCptGRBwksaPPCkUtGo9ka+UIlTpaiKoTaTqVLLYlqwX4bOj9tJ32oBNdf2G3Kg==} better-call@1.0.9: resolution: {integrity: sha512-Qfm0gjk0XQz0oI7qvTK1hbqTsBY4xV2hsHAxF8LZfUYl3RaECCIifXuVqtPpZJWvlCCMlQSvkvhhyuApGUba6g==} + better-sqlite3@11.10.0: + resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + birpc@0.2.14: resolution: {integrity: sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} @@ -2193,6 +2278,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2245,6 +2333,9 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} @@ -2360,6 +2451,10 @@ packages: supports-color: optional: true + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dedent@1.6.0: resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} peerDependencies: @@ -2372,6 +2467,10 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -2394,6 +2493,102 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + drizzle-kit@0.31.2: + resolution: {integrity: sha512-Z2Uqxvu4HNFzlDkG3NQ2BYpII8SlOMkpjsC5XFh9TsYP2nYhfVamVjQ8spiMFXH3vGOyUt1cQ5FZ1JSgl6+8QQ==} + hasBin: true + + drizzle-orm@0.44.2: + resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -2427,6 +2622,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2450,6 +2648,11 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -2517,6 +2720,10 @@ packages: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} @@ -2560,6 +2767,9 @@ packages: resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} engines: {node: '>=20'} + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2593,6 +2803,9 @@ packages: from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -2631,6 +2844,9 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2699,6 +2915,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -2929,6 +3148,10 @@ packages: engines: {node: '>=10.0.0'} hasBin: true + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + miniflare@3.20250204.1: resolution: {integrity: sha512-B4PQi/Ai4d0ZTWahQwsFe5WAfr1j8ISMYxJZTc56g2/btgbX+Go099LmojAZY/fMRLhIYsglcStW8SeW3f/afA==} engines: {node: '>=16.13'} @@ -2953,6 +3176,9 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -2982,10 +3208,17 @@ packages: resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} engines: {node: ^18.0.0 || >=20.0.0} + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + node-abi@3.75.0: + resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + engines: {node: '>=10'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -3125,6 +3358,11 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} @@ -3141,6 +3379,9 @@ packages: engines: {node: '>= 0.10'} hasBin: true + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3170,6 +3411,10 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-dom@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -3192,6 +3437,10 @@ packages: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3339,6 +3588,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -3420,6 +3675,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3428,6 +3686,10 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3456,6 +3718,13 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tar-fs@2.1.3: + resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -3545,6 +3814,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + turbo-darwin-64@2.5.4: resolution: {integrity: sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ==} cpu: [x64] @@ -3644,6 +3916,9 @@ packages: peerDependencies: react: ^19.0.0 + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -4097,7 +4372,7 @@ snapshots: optionalDependencies: workerd: 1.20250408.0 - '@cloudflare/vitest-pool-workers@0.6.16(@cloudflare/workers-types@4.20250619.0)(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0))': + '@cloudflare/vitest-pool-workers@0.6.16(@cloudflare/workers-types@4.20250619.0)(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -4107,7 +4382,7 @@ snapshots: esbuild: 0.17.19 miniflare: 3.20250204.1 semver: 7.7.2 - vitest: 3.2.4(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + vitest: 3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) wrangler: 3.109.1(@cloudflare/workers-types@4.20250619.0) zod: 3.25.67 transitivePeerDependencies: @@ -4151,11 +4426,23 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@drizzle-team/brocli@0.10.2': {} + '@emnapi/runtime@1.4.3': dependencies: tslib: 2.8.1 optional: true + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.10.1 + '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19)': dependencies: esbuild: 0.17.19 @@ -4597,6 +4884,15 @@ snapshots: '@rushstack/node-core-library': 5.13.1(@types/node@24.0.3) transitivePeerDependencies: - '@types/node' + optional: true + + '@microsoft/api-extractor-model@7.30.6(@types/node@24.0.4)': + dependencies: + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.13.1(@types/node@24.0.4) + transitivePeerDependencies: + - '@types/node' '@microsoft/api-extractor@7.52.8(@types/node@22.15.32)': dependencies: @@ -4634,6 +4930,25 @@ snapshots: typescript: 5.8.2 transitivePeerDependencies: - '@types/node' + optional: true + + '@microsoft/api-extractor@7.52.8(@types/node@24.0.4)': + dependencies: + '@microsoft/api-extractor-model': 7.30.6(@types/node@24.0.4) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.13.1(@types/node@24.0.4) + '@rushstack/rig-package': 0.5.3 + '@rushstack/terminal': 0.15.3(@types/node@24.0.4) + '@rushstack/ts-command-line': 5.0.1(@types/node@24.0.4) + lodash: 4.17.21 + minimatch: 3.0.8 + resolve: 1.22.10 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' '@microsoft/tsdoc-config@0.17.1': dependencies: @@ -4822,6 +5137,20 @@ snapshots: semver: 7.5.4 optionalDependencies: '@types/node': 24.0.3 + optional: true + + '@rushstack/node-core-library@5.13.1(@types/node@24.0.4)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 11.3.0 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.10 + semver: 7.5.4 + optionalDependencies: + '@types/node': 24.0.4 '@rushstack/rig-package@0.5.3': dependencies: @@ -4842,6 +5171,14 @@ snapshots: supports-color: 8.1.1 optionalDependencies: '@types/node': 24.0.3 + optional: true + + '@rushstack/terminal@0.15.3(@types/node@24.0.4)': + dependencies: + '@rushstack/node-core-library': 5.13.1(@types/node@24.0.4) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 24.0.4 '@rushstack/ts-command-line@5.0.1(@types/node@22.15.32)': dependencies: @@ -4861,6 +5198,16 @@ snapshots: string-argv: 0.3.2 transitivePeerDependencies: - '@types/node' + optional: true + + '@rushstack/ts-command-line@5.0.1(@types/node@24.0.4)': + dependencies: + '@rushstack/terminal': 0.15.3(@types/node@24.0.4) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' '@simplewebauthn/browser@13.1.0': {} @@ -4928,6 +5275,10 @@ snapshots: dependencies: '@babel/types': 7.27.6 + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 22.15.32 + '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -4988,6 +5339,10 @@ snapshots: dependencies: undici-types: 7.8.0 + '@types/node@24.0.4': + dependencies: + undici-types: 7.8.0 + '@types/prompts@2.4.9': dependencies: '@types/node': 22.15.32 @@ -5065,6 +5420,14 @@ snapshots: optionalDependencies: vite: 6.3.5(@types/node@22.15.32)(tsx@3.14.0)(yaml@2.8.0) + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.15.32)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@22.15.32)(tsx@4.20.3)(yaml@2.8.0) + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 @@ -5073,6 +5436,14 @@ snapshots: optionalDependencies: vite: 6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) + '@vitest/pretty-format@3.1.1': dependencies: tinyrainbow: 2.0.0 @@ -5230,6 +5601,8 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + better-auth@1.2.10: dependencies: '@better-auth/utils': 0.2.5 @@ -5252,8 +5625,23 @@ snapshots: set-cookie-parser: 2.7.1 uncrypto: 0.1.3 + better-sqlite3@11.10.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + birpc@0.2.14: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + blake3-wasm@2.1.5: {} body-parser@2.2.0: @@ -5292,6 +5680,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-require@5.1.0(esbuild@0.25.5): dependencies: esbuild: 0.25.5 @@ -5350,6 +5743,8 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@1.1.4: {} + cjs-module-lexer@1.4.3: {} cliui@8.0.1: @@ -5444,18 +5839,23 @@ snapshots: dependencies: ms: 2.1.3 + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + dedent@1.6.0: {} deep-eql@5.0.2: {} + deep-extend@0.6.0: {} + defu@6.1.4: {} denque@2.1.0: {} depd@2.0.0: {} - detect-libc@2.0.4: - optional: true + detect-libc@2.0.4: {} devalue@4.3.3: {} @@ -5463,6 +5863,22 @@ snapshots: dependencies: path-type: 4.0.0 + drizzle-kit@0.31.2: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.5 + esbuild-register: 3.6.0(esbuild@0.25.5) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.44.2(@cloudflare/workers-types@4.20250619.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.2): + optionalDependencies: + '@cloudflare/workers-types': 4.20250619.0 + '@types/better-sqlite3': 7.6.13 + better-sqlite3: 11.10.0 + kysely: 0.28.2 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5494,6 +5910,10 @@ snapshots: encodeurl@2.0.0: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + entities@4.5.0: {} env-paths@3.0.0: {} @@ -5508,6 +5928,13 @@ snapshots: dependencies: es-errors: 1.3.0 + esbuild-register@3.6.0(esbuild@0.25.5): + dependencies: + debug: 4.4.1 + esbuild: 0.25.5 + transitivePeerDependencies: + - supports-color + esbuild@0.17.19: optionalDependencies: '@esbuild/android-arm': 0.17.19 @@ -5650,6 +6077,8 @@ snapshots: exit-hook@2.2.1: {} + expand-template@2.0.3: {} + expect-type@1.2.1: {} express@5.1.0: @@ -5722,6 +6151,8 @@ snapshots: transitivePeerDependencies: - supports-color + file-uri-to-path@1.0.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -5760,6 +6191,8 @@ snapshots: from@0.1.7: {} + fs-constants@1.0.0: {} + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -5804,6 +6237,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + github-from-package@0.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -5865,6 +6300,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + invariant@2.2.4: dependencies: loose-envify: 1.4.0 @@ -6052,6 +6489,8 @@ snapshots: mime@3.0.0: {} + mimic-response@3.1.0: {} + miniflare@3.20250204.1: dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -6098,6 +6537,8 @@ snapshots: minipass@7.1.2: {} + mkdirp-classic@0.5.3: {} + mlly@1.7.4: dependencies: acorn: 8.15.0 @@ -6123,8 +6564,14 @@ snapshots: nanostores@0.11.4: {} + napi-build-utils@2.0.0: {} + negotiator@1.0.0: {} + node-abi@3.75.0: + dependencies: + semver: 7.7.2 + node-domexception@1.0.0: {} node-fetch@3.3.1: @@ -6236,6 +6683,21 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.4 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.75.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.3 + tunnel-agent: 0.6.0 + printable-characters@1.0.42: {} prompts@2.4.2: @@ -6252,6 +6714,11 @@ snapshots: dependencies: event-stream: 3.3.4 + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + punycode@2.3.1: {} pvtsutils@1.3.6: @@ -6277,6 +6744,13 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-dom@18.2.0(react@18.3.1): dependencies: loose-envify: 1.4.0 @@ -6296,6 +6770,12 @@ snapshots: react@19.1.0: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readdirp@4.1.2: {} redis-errors@1.2.0: {} @@ -6496,6 +6976,14 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 @@ -6569,6 +7057,10 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -6577,6 +7069,8 @@ snapshots: dependencies: ansi-regex: 6.1.0 + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} strip-literal@3.0.0: @@ -6607,6 +7101,21 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -6713,6 +7222,35 @@ snapshots: - tsx - yaml + tsup@8.5.0(@microsoft/api-extractor@7.52.8(@types/node@24.0.4))(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0): + dependencies: + bundle-require: 5.1.0(esbuild@0.25.5) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.1 + esbuild: 0.25.5 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@4.20.3)(yaml@2.8.0) + resolve-from: 5.0.0 + rollup: 4.44.0 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tree-kill: 1.2.2 + optionalDependencies: + '@microsoft/api-extractor': 7.52.8(@types/node@24.0.4) + postcss: 8.5.6 + typescript: 5.8.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + tsx@3.14.0: dependencies: esbuild: 0.18.20 @@ -6728,6 +7266,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + turbo-darwin-64@2.5.4: optional: true @@ -6815,6 +7357,8 @@ snapshots: dependencies: react: 19.1.0 + util-deprecate@1.0.2: {} + vary@1.1.2: {} vite-node@3.2.4(@types/node@22.15.32)(tsx@3.14.0)(yaml@2.8.0): @@ -6880,9 +7424,30 @@ snapshots: - tsx - yaml - vite-plugin-dts@4.5.4(@types/node@24.0.3)(rollup@4.44.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0)): + vite-node@3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0): dependencies: - '@microsoft/api-extractor': 7.52.8(@types/node@24.0.3) + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-plugin-dts@4.5.4(@types/node@24.0.4)(rollup@4.44.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0)): + dependencies: + '@microsoft/api-extractor': 7.52.8(@types/node@24.0.4) '@rollup/pluginutils': 5.2.0(rollup@4.44.0) '@volar/typescript': 2.4.14 '@vue/language-core': 2.2.0(typescript@5.8.3) @@ -6893,7 +7458,7 @@ snapshots: magic-string: 0.30.17 typescript: 5.8.3 optionalDependencies: - vite: 6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0) + vite: 6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - rollup @@ -6950,11 +7515,25 @@ snapshots: tsx: 4.20.3 yaml: 2.8.0 + vite@6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + esbuild: 0.25.5 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.44.0 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.0.4 + fsevents: 2.3.3 + tsx: 4.20.3 + yaml: 2.8.0 + vitest@3.2.4(@types/node@22.15.32)(@vitest/ui@3.1.1)(tsx@4.20.3)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.0.3)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.15.32)(tsx@4.20.3)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -7074,6 +7653,47 @@ snapshots: - tsx - yaml + vitest@3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0)) + '@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: 6.3.5(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@24.0.4)(tsx@4.20.3)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.0.4 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vscode-uri@3.1.0: {} web-streams-polyfill@3.3.3: {}