From 100e3441644b5048789277285bba6578b06ea5c3 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Wed, 26 Mar 2025 12:38:30 -0700 Subject: [PATCH] feat: sqlite support --- package.json | 2 + packages/actor-core/package.json | 14 +- packages/actor-core/src/actor/action.ts | 6 +- packages/actor-core/src/actor/config.ts | 12 +- packages/actor-core/src/actor/context.ts | 5 + packages/actor-core/src/actor/driver.ts | 5 +- packages/actor-core/src/actor/errors.ts | 18 + packages/actor-core/src/actor/instance.ts | 26 + packages/actor-core/src/actor/sql/mod.ts | 8 + packages/actor-core/src/test/driver/actor.ts | 14 +- packages/actor-core/src/test/driver/sql.ts | 15 + .../tests/schemas/chat-room/drizzle.config.ts | 7 + .../drizzle/0000_military_elektra.sql | 5 + .../chat-room/drizzle/0001_greedy_raza.sql | 11 + .../drizzle/0002_left_christian_walker.sql | 1 + .../chat-room/drizzle/0003_massive_rogue.sql | 1 + .../chat-room/drizzle/meta/0000_snapshot.json | 49 ++ .../chat-room/drizzle/meta/0001_snapshot.json | 49 ++ .../chat-room/drizzle/meta/0002_snapshot.json | 57 ++ .../chat-room/drizzle/meta/0003_snapshot.json | 65 ++ .../chat-room/drizzle/meta/_journal.json | 34 + .../tests/schemas/chat-room/schema.ts | 17 + packages/actor-core/tests/sqlite.test.ts | 162 +++++ packages/drivers/memory/package.json | 1 + packages/drivers/memory/src/actor.ts | 7 + packages/drivers/memory/src/sql.ts | 15 + yarn.lock | 631 +++++++++++++++++- 27 files changed, 1218 insertions(+), 19 deletions(-) create mode 100644 packages/actor-core/src/actor/sql/mod.ts create mode 100644 packages/actor-core/src/test/driver/sql.ts create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle.config.ts create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/0000_military_elektra.sql create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/0001_greedy_raza.sql create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/0002_left_christian_walker.sql create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/0003_massive_rogue.sql create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/meta/0000_snapshot.json create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/meta/0001_snapshot.json create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/meta/0002_snapshot.json create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/meta/0003_snapshot.json create mode 100644 packages/actor-core/tests/schemas/chat-room/drizzle/meta/_journal.json create mode 100644 packages/actor-core/tests/schemas/chat-room/schema.ts create mode 100644 packages/actor-core/tests/sqlite.test.ts create mode 100644 packages/drivers/memory/src/sql.ts diff --git a/package.json b/package.json index 27085bffc..586cee176 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "dev": "npx turbo watch dev", "build": "npx turbo build", "test": "npx turbo test", + "test:watch": "npx turbo watch test", "check-types": "npx turbo check-types", "fmt": "yarn biome check --write .", "dev-docs": "cd docs && yarn dlx mintlify@latest dev", @@ -28,6 +29,7 @@ "devDependencies": { "@biomejs/biome": "^1.9.4", "@types/ws": "^8.5.14", + "better-sqlite3": "^11.9.1", "dedent": "^1.5.3", "lefthook": "^1.6.12", "turbo": "^2.0.1", diff --git a/packages/actor-core/package.json b/packages/actor-core/package.json index 64705a692..674c4cebc 100644 --- a/packages/actor-core/package.json +++ b/packages/actor-core/package.json @@ -52,6 +52,16 @@ "default": "./dist/actor/errors.cjs" } }, + "./sql": { + "import": { + "types": "./dist/actor/sql/mod.d.ts", + "default": "./dist/actor/sql/mod.js" + }, + "require": { + "types": "./dist/actor/sql/mod.d.cts", + "default": "./dist/actor/sql/mod.cjs" + } + }, "./utils": { "import": { "types": "./dist/utils.d.ts", @@ -135,7 +145,7 @@ }, "sideEffects": false, "scripts": { - "build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/cli/mod.ts src/actor/protocol/inspector/mod.ts src/actor/protocol/http/rpc.ts src/test/mod.ts", + "build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/actor/errors.ts src/actor/sql/mod.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/cli/mod.ts src/actor/protocol/inspector/mod.ts src/actor/protocol/http/rpc.ts src/test/mod.ts", "check-types": "tsc --noEmit", "boop": "tsc --outDir dist/test -d", "test": "vitest run", @@ -153,6 +163,8 @@ "@types/invariant": "^2", "@types/node": "^22.13.1", "@types/ws": "^8", + "drizzle-kit": "^0.30.5", + "drizzle-orm": "^0.41.0", "eventsource": "^3.0.5", "tsup": "^8.4.0", "typescript": "^5.7.3", diff --git a/packages/actor-core/src/actor/action.ts b/packages/actor-core/src/actor/action.ts index 846dc1870..6cc15156e 100644 --- a/packages/actor-core/src/actor/action.ts +++ b/packages/actor-core/src/actor/action.ts @@ -5,8 +5,8 @@ import type { ActorTags } from "@/common/utils"; import type { Schedule } from "./schedule"; import type { ConnId } from "./connection"; import type { SaveStateOptions } from "./instance"; -import { Actions } from "./config"; import { ActorContext } from "./context"; +import { SqlConnection } from "@/actor/sql/mod"; /** * Context for a remote procedure call. @@ -36,6 +36,10 @@ export class ActionContext { return this.#actorContext.state; } + get sql(): SqlConnection { + return this.#actorContext.sql + } + /** * Get the actor variables */ diff --git a/packages/actor-core/src/actor/config.ts b/packages/actor-core/src/actor/config.ts index e8fd8b468..3866c839a 100644 --- a/packages/actor-core/src/actor/config.ts +++ b/packages/actor-core/src/actor/config.ts @@ -19,6 +19,7 @@ export const ActorConfigSchema = z onBeforeActionResponse: z.function().optional(), actions: z.record(z.function()), state: z.any().optional(), + sql: z.boolean().default(false), createState: z.function().optional(), connState: z.any().optional(), createConnState: z.function().optional(), @@ -81,7 +82,11 @@ export interface OnConnectOptions { // This must have only one or the other or else S will not be able to be inferred type CreateState = | { state: S } - | { createState: (c: ActorContext) => S | Promise } + | { + createState: ( + c: ActorContext, + ) => S | Promise; + } | Record; // Creates connection state config @@ -114,7 +119,10 @@ type CreateVars = /** * @experimental */ - createVars: (c: ActorContext, driverCtx: unknown) => V | Promise; + createVars: ( + c: ActorContext, + driverCtx: unknown, + ) => V | Promise; } | Record; diff --git a/packages/actor-core/src/actor/context.ts b/packages/actor-core/src/actor/context.ts index 8a32798c6..c977d9915 100644 --- a/packages/actor-core/src/actor/context.ts +++ b/packages/actor-core/src/actor/context.ts @@ -4,6 +4,7 @@ import { ActorInstance, SaveStateOptions } from "./instance"; import { Conn, ConnId } from "./connection"; import { ActorTags } from "@/common/utils"; import { Schedule } from "./schedule"; +import { SqlConnection } from "@/actor/sql/mod"; /** @@ -23,6 +24,10 @@ export class ActorContext { return this.#actor.state; } + get sql(): SqlConnection { + return this.#actor.sql; + } + /** * Get the actor variables */ diff --git a/packages/actor-core/src/actor/driver.ts b/packages/actor-core/src/actor/driver.ts index 97f17da6b..caa4b4cfc 100644 --- a/packages/actor-core/src/actor/driver.ts +++ b/packages/actor-core/src/actor/driver.ts @@ -2,17 +2,20 @@ import type * as messageToClient from "@/actor/protocol/message/to-client"; import type { CachedSerializer } from "@/actor/protocol/serde"; import type { AnyActorInstance } from "./instance"; import { AnyConn } from "./connection"; +import type { SqlConnection } from "./sql/mod"; export type ConnDrivers = Record; export type KvKey = unknown[]; export type KvValue = unknown; - export interface ActorDriver { //load(): Promise; get context(): unknown; + // SQL connection + createSqlConnection?(): SqlConnection; + // HACK: Clean these up kvGet(actorId: string, key: KvKey): Promise; kvGetBatch(actorId: string, key: KvKey[]): Promise<(KvValue | undefined)[]>; diff --git a/packages/actor-core/src/actor/errors.ts b/packages/actor-core/src/actor/errors.ts index 1246703da..c6dcc75c9 100644 --- a/packages/actor-core/src/actor/errors.ts +++ b/packages/actor-core/src/actor/errors.ts @@ -48,6 +48,15 @@ export class StateNotEnabled extends ActorError { } } +export class SqlNotEnabled extends ActorError { + constructor() { + super( + "sql_not_enabled", + "SQL not enabled.", + ); + } +} + export class ConnStateNotEnabled extends ActorError { constructor() { super( @@ -164,6 +173,15 @@ export class Unsupported extends ActorError { } } +export class DriverDoesNotSupportSql extends ActorError { + constructor() { + super( + "driver_does_not_support_sql", + "Driver does not support SQL. Check https://actorcore.org for supported platforms.", + ); + } +} + /** * Options for the UserError class. */ diff --git a/packages/actor-core/src/actor/instance.ts b/packages/actor-core/src/actor/instance.ts index 01c3154af..c6fb26ca4 100644 --- a/packages/actor-core/src/actor/instance.ts +++ b/packages/actor-core/src/actor/instance.ts @@ -18,6 +18,7 @@ import { CachedSerializer } from "./protocol/serde"; import { Inspector } from "@/actor/inspect"; import { ActorContext } from "./context"; import invariant from "invariant"; +import { SqlConnection } from "./sql/mod"; /** * Options for the `_saveState` method. @@ -97,6 +98,8 @@ export class ActorInstance { /** Raw state without the proxy wrapper */ #persistRaw!: PersistedActor; + #sqlConn?: SqlConnection; + #writePersistLock = new Lock(void 0); #lastSaveTime = 0; @@ -158,6 +161,13 @@ export class ActorInstance { this.#schedule = new Schedule(this, actorDriver); this.inspector = new Inspector(this); + // Create SQL connection + if (this.#config.sql) { + if (!this.#actorDriver.createSqlConnection) throw new errors.DriverDoesNotSupportSql(); + this.#sqlConn = this.#actorDriver.createSqlConnection(); + } + + // Initialize server // // Store the promise so network requests can await initialization @@ -214,6 +224,16 @@ export class ActorInstance { } } + get sqlEnabled() { + return this.#config.sql; + } + + #validateSqlEnabled() { + if (!this.sqlEnabled) { + throw new errors.SqlNotEnabled(); + } + } + get #connStateEnabled() { return "createConnState" in this.#config || "connState" in this.#config; } @@ -846,6 +866,12 @@ export class ActorInstance { return this.#persist.s; } + get sql(): SqlConnection { + this.#validateSqlEnabled(); + invariant(this.#sqlConn !== undefined, "#sqlConn is undefined"); + return this.#sqlConn; + } + /** * Sets the current state. * diff --git a/packages/actor-core/src/actor/sql/mod.ts b/packages/actor-core/src/actor/sql/mod.ts new file mode 100644 index 000000000..70b54f8f5 --- /dev/null +++ b/packages/actor-core/src/actor/sql/mod.ts @@ -0,0 +1,8 @@ +export interface SqlConnection { + // TODO: Remove this + HACK_raw: unknown; + + // TODO: Find methods that are required for Drizzle-compat + // TODO: Support both sync and async +} + diff --git a/packages/actor-core/src/test/driver/actor.ts b/packages/actor-core/src/test/driver/actor.ts index 261548419..138044afa 100644 --- a/packages/actor-core/src/test/driver/actor.ts +++ b/packages/actor-core/src/test/driver/actor.ts @@ -1,5 +1,13 @@ -import type { ActorDriver, KvKey, KvValue, AnyActorInstance } from "@/driver-helpers/mod"; +import type { + ActorDriver, + KvKey, + KvValue, + AnyActorInstance, +} from "@/driver-helpers/mod"; import type { TestGlobalState } from "./global_state"; +import { SqlConnection } from "@/actor/sql/mod"; +import Database from "better-sqlite3"; +import { TestSqlConnection } from "./sql"; export interface ActorDriverContext { state: TestGlobalState; @@ -16,6 +24,10 @@ export class TestActorDriver implements ActorDriver { return { state: this.#state }; } + createSqlConnection(): SqlConnection { + return new TestSqlConnection(new Database(":memory:")); + } + async kvGet(actorId: string, key: KvKey): Promise { const serializedKey = this.#serializeKey(key); const value = this.#state.getKv(actorId, serializedKey); diff --git a/packages/actor-core/src/test/driver/sql.ts b/packages/actor-core/src/test/driver/sql.ts new file mode 100644 index 000000000..8b6d8479c --- /dev/null +++ b/packages/actor-core/src/test/driver/sql.ts @@ -0,0 +1,15 @@ +import { SqlConnection } from "@/actor/sql/mod"; +import { Database } from "better-sqlite3"; + +export class TestSqlConnection implements SqlConnection { + #raw: Database; + + // TODO: This is a temporary hack for Drizzle + get HACK_raw(): unknown { + return this.#raw; + } + + constructor(db: Database) { + this.#raw = db; + } +} diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle.config.ts b/packages/actor-core/tests/schemas/chat-room/drizzle.config.ts new file mode 100644 index 000000000..0b2422e27 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + out: "./tests/schemas/chat-room/drizzle", + dialect: "sqlite", + schema: "./tests/schemas/chat-room/schema.ts", +}); diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/0000_military_elektra.sql b/packages/actor-core/tests/schemas/chat-room/drizzle/0000_military_elektra.sql new file mode 100644 index 000000000..4bdead107 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/0000_military_elektra.sql @@ -0,0 +1,5 @@ +CREATE TABLE `messages` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `username` text, + `message` text +); diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/0001_greedy_raza.sql b/packages/actor-core/tests/schemas/chat-room/drizzle/0001_greedy_raza.sql new file mode 100644 index 000000000..d6c5a7f62 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/0001_greedy_raza.sql @@ -0,0 +1,11 @@ +PRAGMA foreign_keys=OFF;--> statement-breakpoint +CREATE TABLE `__new_messages` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `username` text NOT NULL, + `message` text NOT NULL +); +--> statement-breakpoint +INSERT INTO `__new_messages`("id", "username", "message") SELECT "id", "username", "message" FROM `messages`;--> statement-breakpoint +DROP TABLE `messages`;--> statement-breakpoint +ALTER TABLE `__new_messages` RENAME TO `messages`;--> statement-breakpoint +PRAGMA foreign_keys=ON; \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/0002_left_christian_walker.sql b/packages/actor-core/tests/schemas/chat-room/drizzle/0002_left_christian_walker.sql new file mode 100644 index 000000000..b7f2b1329 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/0002_left_christian_walker.sql @@ -0,0 +1 @@ +ALTER TABLE `messages` ADD `createdAt` integer DEFAULT (unixepoch() * 1000) NOT NULL; \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/0003_massive_rogue.sql b/packages/actor-core/tests/schemas/chat-room/drizzle/0003_massive_rogue.sql new file mode 100644 index 000000000..0bed13918 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/0003_massive_rogue.sql @@ -0,0 +1 @@ +CREATE INDEX `created_at_idx` ON `messages` (`createdAt`); \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0000_snapshot.json b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0000_snapshot.json new file mode 100644 index 000000000..2f45d6411 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0000_snapshot.json @@ -0,0 +1,49 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "78d399d7-9b51-4e04-ad31-1c3dc909e24a", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "messages": { + "name": "messages", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0001_snapshot.json b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0001_snapshot.json new file mode 100644 index 000000000..b2394d5bf --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0001_snapshot.json @@ -0,0 +1,49 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "1ba2793b-2e39-4e99-926c-f8673d595baa", + "prevId": "78d399d7-9b51-4e04-ad31-1c3dc909e24a", + "tables": { + "messages": { + "name": "messages", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0002_snapshot.json b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0002_snapshot.json new file mode 100644 index 000000000..0876d3f49 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0002_snapshot.json @@ -0,0 +1,57 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "ccb715a2-65e4-455c-a165-0a67ed055a33", + "prevId": "1ba2793b-2e39-4e99-926c-f8673d595baa", + "tables": { + "messages": { + "name": "messages", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0003_snapshot.json b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0003_snapshot.json new file mode 100644 index 000000000..d809f0b82 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/0003_snapshot.json @@ -0,0 +1,65 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "fe3a63b6-5143-4456-9b12-ff1b9028230a", + "prevId": "ccb715a2-65e4-455c-a165-0a67ed055a33", + "tables": { + "messages": { + "name": "messages", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "createdAt" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/drizzle/meta/_journal.json b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/_journal.json new file mode 100644 index 000000000..28c29dbfb --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/drizzle/meta/_journal.json @@ -0,0 +1,34 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1743016581641, + "tag": "0000_military_elektra", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1743034703736, + "tag": "0001_greedy_raza", + "breakpoints": true + }, + { + "idx": 2, + "version": "6", + "when": 1743034919351, + "tag": "0002_left_christian_walker", + "breakpoints": true + }, + { + "idx": 3, + "version": "6", + "when": 1743036218845, + "tag": "0003_massive_rogue", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/packages/actor-core/tests/schemas/chat-room/schema.ts b/packages/actor-core/tests/schemas/chat-room/schema.ts new file mode 100644 index 000000000..44d807fd3 --- /dev/null +++ b/packages/actor-core/tests/schemas/chat-room/schema.ts @@ -0,0 +1,17 @@ +import { sql } from "drizzle-orm"; +import { sqliteTable as table, index } from "drizzle-orm/sqlite-core"; +import * as t from "drizzle-orm/sqlite-core"; + +export const messages = table( + "messages", + { + id: t.int().primaryKey({ autoIncrement: true }), + createdAt: t + .int({ mode: "timestamp" }) + .notNull() + .default(sql`(unixepoch() * 1000)`), + username: t.text().notNull(), + message: t.text().notNull(), + }, + (t) => [index("created_at_idx").on(t.createdAt)], +); diff --git a/packages/actor-core/tests/sqlite.test.ts b/packages/actor-core/tests/sqlite.test.ts new file mode 100644 index 000000000..fe18a6009 --- /dev/null +++ b/packages/actor-core/tests/sqlite.test.ts @@ -0,0 +1,162 @@ +import { actor, setup } from "@/mod"; +import { describe, test, expect } from "vitest"; +import { setupTest } from "@/test/mod"; +import { sql } from "drizzle-orm"; +import { drizzle } from "drizzle-orm/better-sqlite3"; +import { migrate } from "drizzle-orm/better-sqlite3/migrator"; +import path from "path"; +import { messages } from "./schemas/chat-room/schema"; + +describe("Actor SQLite Chat Room", () => { + test("chat room with SQLite storage should work correctly", async () => { + interface Message { + username: string; + message: string; + } + + // Define actor with SQLite integration + const chatRoom = actor({ + sql: true, + createVars: (c) => ({ + drizzle: drizzle(c.sql.HACK_raw), + }), + onStart: (c) => { + // Run migrations to create database schema + migrate(c.vars.drizzle, { + migrationsFolder: path.join( + __dirname, + "./schemas/chat-room/drizzle/", + ), + }); + }, + actions: { + // Send a message to the chat room + sendMessage: async (c, username: string, message: string) => { + // Insert message into database + await c.vars.drizzle.insert(messages).values({ + username, + message, + }); + + // Broadcast message to all connected clients + c.broadcast("newMessage", username, message); + }, + + // Get a specific number of recent messages + getMessages: async (c, count: number) => { + // Query the most recent messages + const result = await c.vars.drizzle + .select() + .from(messages) + .orderBy(sql`${messages.createdAt} desc`) + .limit(count); + return result as Message[]; + }, + + // Search for messages containing specific text + searchMessages: async (c, searchTerm: string) => { + // Query messages that contain the search term (simple implementation) + const allMessages = await c.vars.drizzle.select().from(messages).orderBy(messages.createdAt); + return allMessages.filter( + (msg) => + msg.message.toLowerCase().includes(searchTerm.toLowerCase()) || + msg.username.toLowerCase().includes(searchTerm.toLowerCase()), + ) as Message[]; + }, + + // Clear all messages (for testing purposes) + clearAllMessages: async (c) => { + await c.vars.drizzle.delete(messages); + return true; + }, + }, + }); + + // Set up the application with our chat room actor + const app = setup({ + actors: { chatRoom }, + }); + + // Initialize test environment + const { client } = await setupTest(app); + + // Get instance of chat room + const roomInstance = await client.chatRoom.get(); + + // Initially, there should be no messages + const initialMessages = await roomInstance.getMessages(10); + expect(initialMessages).toEqual([]); + + // Set up event listener for newMessage events + const receivedEvents: { username: string; message: string }[] = []; + roomInstance.on("newMessage", (username: string, message: string) => { + receivedEvents.push({ username, message }); + }); + + // Send a single message + const user1 = "alice"; + const message1 = "Hello, world!"; + await roomInstance.sendMessage(user1, message1); + + // Check event was emitted + expect(receivedEvents).toEqual([{ username: user1, message: message1 }]); + + // Check message was stored in database + const messagesAfterOne = await roomInstance.getMessages(10); + expect(messagesAfterOne.length).toBe(1); + expect(messagesAfterOne[0].username).toBe(user1); + expect(messagesAfterOne[0].message).toBe(message1); + + // Send multiple messages + const testUsers = ["bob", "charlie", "diana"]; + const testMessages = [ + "What a beautiful day!", + "How are you doing?", + "I'm learning about actor models", + ]; + + // Send all test messages + for (let i = 0; i < testUsers.length; i++) { + await roomInstance.sendMessage(testUsers[i], testMessages[i]); + } + + // Check all messages were stored correctly + const allMessages = await roomInstance.getMessages(10); + expect(allMessages.length).toBe(4); // initial + 3 new ones + + // Check first message is still there + expect(allMessages[3].username).toBe(user1); + expect(allMessages[3].message).toBe(message1); + + // Check new messages are there in order + for (let i = 0; i < testUsers.length; i++) { + expect(allMessages[2 - i].username).toBe(testUsers[i]); + expect(allMessages[2 - i].message).toBe(testMessages[i]); + } + + // Test getRecentMessages + const recentMessages = await roomInstance.getMessages(2); + expect(recentMessages.length).toBe(2); + expect(recentMessages[0].username).toBe("diana"); + expect(recentMessages[1].username).toBe("charlie"); + + // Test searchMessages + const searchResults = await roomInstance.searchMessages("actor"); + expect(searchResults.length).toBe(1); + expect(searchResults[0].username).toBe("diana"); + + // Test search by username + const userSearch = await roomInstance.searchMessages("bob"); + expect(userSearch.length).toBe(1); + expect(userSearch[0].message).toBe("What a beautiful day!"); + + // Test clearing all messages + await roomInstance.clearAllMessages(); + const emptyMessages = await roomInstance.getMessages(10); + expect(emptyMessages).toEqual([]); + + // Events should still contain the previous messages + expect(receivedEvents.length).toBe(4); + }); +}); + diff --git a/packages/drivers/memory/package.json b/packages/drivers/memory/package.json index 874e262a0..87cf41f1f 100644 --- a/packages/drivers/memory/package.json +++ b/packages/drivers/memory/package.json @@ -27,6 +27,7 @@ }, "devDependencies": { "actor-core": "workspace:*", + "better-sqlite3": "^11.9.1", "tsup": "^8.4.0", "typescript": "^5.5.2" }, diff --git a/packages/drivers/memory/src/actor.ts b/packages/drivers/memory/src/actor.ts index ccaba4df9..6d62cd262 100644 --- a/packages/drivers/memory/src/actor.ts +++ b/packages/drivers/memory/src/actor.ts @@ -1,5 +1,8 @@ import type { ActorDriver, KvKey, KvValue, AnyActorInstance } from "actor-core/driver-helpers"; +import type { SqlConnection } from "actor-core/sql"; import type { MemoryGlobalState } from "./global_state"; +import { MemorySqlConnection } from "./sql"; +import Database from "better-sqlite3"; export interface ActorDriverContext { state: MemoryGlobalState; @@ -16,6 +19,10 @@ export class MemoryActorDriver implements ActorDriver { return { state: this.#state }; } + createSqlConnection(): SqlConnection { + return new MemorySqlConnection(new Database(":memory:")); + } + async kvGet(actorId: string, key: KvKey): Promise { const serializedKey = this.#serializeKey(key); const value = this.#state.getKv(actorId, serializedKey); diff --git a/packages/drivers/memory/src/sql.ts b/packages/drivers/memory/src/sql.ts new file mode 100644 index 000000000..77cb15e33 --- /dev/null +++ b/packages/drivers/memory/src/sql.ts @@ -0,0 +1,15 @@ +import type { SqlConnection } from "actor-core/sql"; +import type { Database } from "better-sqlite3"; + +export class MemorySqlConnection implements SqlConnection { + #raw: Database; + + // TODO: This is a temporary hack for Drizzle + get HACK_raw(): unknown { + return this.#raw; + } + + constructor(db: Database) { + this.#raw = db; + } +} diff --git a/yarn.lock b/yarn.lock index d8cefb68a..978a5cc5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -100,6 +100,7 @@ __metadata: dependencies: "@types/node": "npm:^22.13.1" actor-core: "workspace:*" + better-sqlite3: "npm:^11.9.1" hono: "npm:^4.7.0" tsup: "npm:^8.4.0" typescript: "npm:^5.5.2" @@ -180,6 +181,7 @@ __metadata: dependencies: "@biomejs/biome": "npm:^1.9.4" "@types/ws": "npm:^8.5.14" + better-sqlite3: "npm:^11.9.1" dedent: "npm:^1.5.3" esbuild: "npm:^0.25.1" lefthook: "npm:^1.6.12" @@ -632,6 +634,13 @@ __metadata: languageName: node linkType: hard +"@drizzle-team/brocli@npm:^0.10.2": + version: 0.10.2 + resolution: "@drizzle-team/brocli@npm:0.10.2" + checksum: 10c0/3d8b99d680f0b14fea32b45c59b938b6665e0840cc67f04801b1aa3c6747da3c7d01c00e321645034fa100abdba7e0c20ce07cf46fc2ca769ee4cafd97562484 + languageName: node + linkType: hard + "@emnapi/runtime@npm:^1.2.0": version: 1.3.1 resolution: "@emnapi/runtime@npm:1.3.1" @@ -641,6 +650,26 @@ __metadata: languageName: node linkType: hard +"@esbuild-kit/core-utils@npm:^3.3.2": + version: 3.3.2 + resolution: "@esbuild-kit/core-utils@npm:3.3.2" + dependencies: + esbuild: "npm:~0.18.20" + source-map-support: "npm:^0.5.21" + checksum: 10c0/d856f5bd720814593f911d781ed7558a3f8ec1a39802f3831d0eea0d1306e0e2dc11b7b2443af621c413ec6557f1f3034a9a4f1472a4cb40e52cd6e3b356aa05 + languageName: node + linkType: hard + +"@esbuild-kit/esm-loader@npm:^2.5.5": + version: 2.6.5 + resolution: "@esbuild-kit/esm-loader@npm:2.6.5" + dependencies: + "@esbuild-kit/core-utils": "npm:^3.3.2" + get-tsconfig: "npm:^4.7.0" + checksum: 10c0/6894b29176eda62bdce0d458d57f32daed5cb8fcff14cb3ddfbc995cfe3e2fa8599f3b0b1af66db446903b30167f57069f27e9cf79a69cf9b41f557115811cde + languageName: node + linkType: hard + "@esbuild-plugins/node-globals-polyfill@npm:0.2.3": version: 0.2.3 resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" @@ -662,6 +691,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/aix-ppc64@npm:0.19.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/aix-ppc64@npm:0.21.5" @@ -704,6 +740,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm64@npm:0.19.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm64@npm:0.21.5" @@ -746,6 +789,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-arm@npm:0.19.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm@npm:0.21.5" @@ -788,6 +838,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/android-x64@npm:0.19.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-x64@npm:0.21.5" @@ -830,6 +887,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-arm64@npm:0.19.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-arm64@npm:0.21.5" @@ -872,6 +936,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/darwin-x64@npm:0.19.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-x64@npm:0.21.5" @@ -914,6 +985,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-arm64@npm:0.19.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-arm64@npm:0.21.5" @@ -956,6 +1034,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/freebsd-x64@npm:0.19.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-x64@npm:0.21.5" @@ -998,6 +1083,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm64@npm:0.19.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm64@npm:0.21.5" @@ -1040,6 +1132,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-arm@npm:0.19.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm@npm:0.21.5" @@ -1082,6 +1181,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ia32@npm:0.19.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ia32@npm:0.21.5" @@ -1124,6 +1230,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-loong64@npm:0.19.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-loong64@npm:0.21.5" @@ -1166,6 +1279,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-mips64el@npm:0.19.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-mips64el@npm:0.21.5" @@ -1208,6 +1328,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-ppc64@npm:0.19.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ppc64@npm:0.21.5" @@ -1250,6 +1377,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-riscv64@npm:0.19.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-riscv64@npm:0.21.5" @@ -1292,6 +1426,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-s390x@npm:0.19.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-s390x@npm:0.21.5" @@ -1334,6 +1475,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/linux-x64@npm:0.19.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-x64@npm:0.21.5" @@ -1397,6 +1545,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/netbsd-x64@npm:0.19.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/netbsd-x64@npm:0.21.5" @@ -1460,6 +1615,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/openbsd-x64@npm:0.19.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/openbsd-x64@npm:0.21.5" @@ -1502,6 +1664,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/sunos-x64@npm:0.19.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/sunos-x64@npm:0.21.5" @@ -1544,6 +1713,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-arm64@npm:0.19.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-arm64@npm:0.21.5" @@ -1586,6 +1762,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-ia32@npm:0.19.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-ia32@npm:0.21.5" @@ -1628,6 +1811,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.19.12": + version: 0.19.12 + resolution: "@esbuild/win32-x64@npm:0.19.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-x64@npm:0.21.5" @@ -2378,6 +2568,13 @@ __metadata: languageName: node linkType: hard +"@petamoriken/float16@npm:^3.8.7": + version: 3.9.2 + resolution: "@petamoriken/float16@npm:3.9.2" + checksum: 10c0/af229a0fc429083e76c459cc3bf7e682f578518c34c21e2d1c1b1c2744e69a5208b94245c41757f500ef722dd79fb6c55e8858d29d7fb57d4fe2b3e5734f1149 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -3650,6 +3847,8 @@ __metadata: "@types/node": "npm:^22.13.1" "@types/ws": "npm:^8" cbor-x: "npm:^1.6.0" + drizzle-kit: "npm:^0.30.5" + drizzle-orm: "npm:^0.41.0" eventsource: "npm:^3.0.5" hono: "npm:^4.7.0" invariant: "npm:^2.2.4" @@ -3928,6 +4127,17 @@ __metadata: languageName: node linkType: hard +"better-sqlite3@npm:^11.9.1": + version: 11.9.1 + resolution: "better-sqlite3@npm:11.9.1" + dependencies: + bindings: "npm:^1.5.0" + node-gyp: "npm:latest" + prebuild-install: "npm:^7.1.1" + checksum: 10c0/87079d95f76898ec57922cf19d375d22dcdf65a4e1f52a71022231bbee9c80f90b9eb9fa80ac7dc00bca25673fdeba89b27f5f36bb82479dbe220be4dc20071d + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -3935,6 +4145,15 @@ __metadata: languageName: node linkType: hard +"bindings@npm:^1.5.0": + version: 1.5.0 + resolution: "bindings@npm:1.5.0" + dependencies: + file-uri-to-path: "npm:1.0.0" + checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba + languageName: node + linkType: hard + "birpc@npm:0.2.14": version: 0.2.14 resolution: "birpc@npm:0.2.14" @@ -4542,6 +4761,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^6.0.0": + version: 6.0.0 + resolution: "decompress-response@npm:6.0.0" + dependencies: + mimic-response: "npm:^3.1.0" + checksum: 10c0/bd89d23141b96d80577e70c54fb226b2f40e74a6817652b80a116d7befb8758261ad073a8895648a29cc0a5947021ab66705cb542fa9c143c82022b27c5b175e + languageName: node + linkType: hard + "dedent@npm:^1.5.3": version: 1.5.3 resolution: "dedent@npm:1.5.3" @@ -4561,6 +4789,13 @@ __metadata: languageName: node linkType: hard +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 + languageName: node + linkType: hard + "deepmerge@npm:^4.3.1": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" @@ -4589,7 +4824,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.1, detect-libc@npm:^2.0.3": +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1, detect-libc@npm:^2.0.3": version: 2.0.3 resolution: "detect-libc@npm:2.0.3" checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 @@ -4653,6 +4888,113 @@ __metadata: languageName: node linkType: hard +"drizzle-kit@npm:^0.30.5": + version: 0.30.5 + resolution: "drizzle-kit@npm:0.30.5" + dependencies: + "@drizzle-team/brocli": "npm:^0.10.2" + "@esbuild-kit/esm-loader": "npm:^2.5.5" + esbuild: "npm:^0.19.7" + esbuild-register: "npm:^3.5.0" + gel: "npm:^2.0.0" + bin: + drizzle-kit: bin.cjs + checksum: 10c0/9bb64214d795e12e6ef941c5bc46fe0fbf2de0f8e56dbb16eb69ee2d02655907f291aa0d17ae19c0adf399f6fcc833630fe4f969e093936d8baac0069df4a542 + languageName: node + linkType: hard + +"drizzle-orm@npm:^0.41.0": + version: 0.41.0 + resolution: "drizzle-orm@npm:0.41.0" + 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" + "@prisma/client": "*" + "@tidbcloud/serverless": "*" + "@types/better-sqlite3": "*" + "@types/pg": "*" + "@types/sql.js": "*" + "@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" + 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 + "@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 + checksum: 10c0/4abc7ab2958a7862a9bdd149f3ec892ec05892e7cd2042f996b6e58c5504e51d50b408833ff5bdd6d44a4a71af4b4c280d281b01a0ce59acfc26a0117b6b6875 + languageName: node + linkType: hard + "dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1" @@ -4724,6 +5066,13 @@ __metadata: languageName: node linkType: hard +"env-paths@npm:^3.0.0": + version: 3.0.0 + resolution: "env-paths@npm:3.0.0" + checksum: 10c0/76dec878cee47f841103bacd7fae03283af16f0702dad65102ef0a556f310b98a377885e0f32943831eb08b5ab37842a323d02529f3dfd5d0a40ca71b01b435f + languageName: node + linkType: hard + "environment@npm:^1.0.0": version: 1.1.0 resolution: "environment@npm:1.1.0" @@ -4792,6 +5141,17 @@ __metadata: languageName: node linkType: hard +"esbuild-register@npm:^3.5.0": + version: 3.6.0 + resolution: "esbuild-register@npm:3.6.0" + dependencies: + debug: "npm:^4.3.4" + peerDependencies: + esbuild: ">=0.12 <1" + checksum: 10c0/77193b7ca32ba9f81b35ddf3d3d0138efb0b1429d71b39480cfee932e1189dd2e492bd32bf04a4d0bc3adfbc7ec7381ceb5ffd06efe35f3e70904f1f686566d5 + languageName: node + linkType: hard + "esbuild@npm:0.17.19": version: 0.17.19 resolution: "esbuild@npm:0.17.19" @@ -4869,6 +5229,86 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.19.7": + version: 0.19.12 + resolution: "esbuild@npm:0.19.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.19.12" + "@esbuild/android-arm": "npm:0.19.12" + "@esbuild/android-arm64": "npm:0.19.12" + "@esbuild/android-x64": "npm:0.19.12" + "@esbuild/darwin-arm64": "npm:0.19.12" + "@esbuild/darwin-x64": "npm:0.19.12" + "@esbuild/freebsd-arm64": "npm:0.19.12" + "@esbuild/freebsd-x64": "npm:0.19.12" + "@esbuild/linux-arm": "npm:0.19.12" + "@esbuild/linux-arm64": "npm:0.19.12" + "@esbuild/linux-ia32": "npm:0.19.12" + "@esbuild/linux-loong64": "npm:0.19.12" + "@esbuild/linux-mips64el": "npm:0.19.12" + "@esbuild/linux-ppc64": "npm:0.19.12" + "@esbuild/linux-riscv64": "npm:0.19.12" + "@esbuild/linux-s390x": "npm:0.19.12" + "@esbuild/linux-x64": "npm:0.19.12" + "@esbuild/netbsd-x64": "npm:0.19.12" + "@esbuild/openbsd-x64": "npm:0.19.12" + "@esbuild/sunos-x64": "npm:0.19.12" + "@esbuild/win32-arm64": "npm:0.19.12" + "@esbuild/win32-ia32": "npm:0.19.12" + "@esbuild/win32-x64": "npm:0.19.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/0f2d21ffe24ebead64843f87c3aebe2e703a5ed9feb086a0728b24907fac2eb9923e4a79857d3df9059c915739bd7a870dd667972eae325c67f478b592b8582d + languageName: node + linkType: hard + "esbuild@npm:^0.21.3": version: 0.21.5 resolution: "esbuild@npm:0.21.5" @@ -5378,6 +5818,13 @@ __metadata: languageName: node linkType: hard +"expand-template@npm:^2.0.3": + version: 2.0.3 + resolution: "expand-template@npm:2.0.3" + checksum: 10c0/1c9e7afe9acadf9d373301d27f6a47b34e89b3391b1ef38b7471d381812537ef2457e620ae7f819d2642ce9c43b189b3583813ec395e2938319abe356a9b2f51 + languageName: node + linkType: hard + "expect-type@npm:^1.1.0": version: 1.1.0 resolution: "expect-type@npm:1.1.0" @@ -5420,6 +5867,13 @@ __metadata: languageName: node linkType: hard +"file-uri-to-path@npm:1.0.0": + version: 1.0.0 + resolution: "file-uri-to-path@npm:1.0.0" + checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519 + languageName: node + linkType: hard + "fill-range@npm:^7.1.1": version: 7.1.1 resolution: "fill-range@npm:7.1.1" @@ -5517,6 +5971,22 @@ __metadata: languageName: node linkType: hard +"gel@npm:^2.0.0": + version: 2.0.1 + resolution: "gel@npm:2.0.1" + dependencies: + "@petamoriken/float16": "npm:^3.8.7" + debug: "npm:^4.3.4" + env-paths: "npm:^3.0.0" + semver: "npm:^7.6.2" + shell-quote: "npm:^1.8.1" + which: "npm:^4.0.0" + bin: + gel: dist/cli.mjs + checksum: 10c0/9b57281cb8895b0cfb101ebb1159abd696c3cb13627476cc65bfcef15b15d6ea589aee7973f7edf362f0cc21e667903f542f4f9849d926b74e141d65e6216897 + languageName: node + linkType: hard + "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -5586,7 +6056,7 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.7.2": +"get-tsconfig@npm:^4.7.0, get-tsconfig@npm:^4.7.2": version: 4.10.0 resolution: "get-tsconfig@npm:4.10.0" dependencies: @@ -5595,6 +6065,13 @@ __metadata: languageName: node linkType: hard +"github-from-package@npm:0.0.0": + version: 0.0.0 + resolution: "github-from-package@npm:0.0.0" + checksum: 10c0/737ee3f52d0a27e26332cde85b533c21fcdc0b09fb716c3f8e522cfaa9c600d4a631dec9fcde179ec9d47cca89017b7848ed4d6ae6b6b78f936c06825b1fcc12 + languageName: node + linkType: hard + "glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -5814,6 +6291,13 @@ __metadata: languageName: node linkType: hard +"ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + "ink-gradient@npm:^3.0.0": version: 3.0.0 resolution: "ink-gradient@npm:3.0.0" @@ -6447,6 +6931,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 10c0/0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362 + languageName: node + linkType: hard + "miniflare@npm:3.20250129.0": version: 3.20250129.0 resolution: "miniflare@npm:3.20250129.0" @@ -6516,6 +7007,13 @@ __metadata: languageName: node linkType: hard +"minimist@npm:^1.2.0, minimist@npm:^1.2.3": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + "minipass-collect@npm:^2.0.1": version: 2.0.1 resolution: "minipass-collect@npm:2.0.1" @@ -6600,7 +7098,7 @@ __metadata: languageName: node linkType: hard -"mkdirp-classic@npm:^0.5.2": +"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": version: 0.5.3 resolution: "mkdirp-classic@npm:0.5.3" checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168 @@ -6689,6 +7187,13 @@ __metadata: languageName: node linkType: hard +"napi-build-utils@npm:^2.0.0": + version: 2.0.0 + resolution: "napi-build-utils@npm:2.0.0" + checksum: 10c0/5833aaeb5cc5c173da47a102efa4680a95842c13e0d9cc70428bd3ee8d96bb2172f8860d2811799b5daa5cbeda779933601492a2028a6a5351c6d0fcf6de83db + languageName: node + linkType: hard + "negotiator@npm:^1.0.0": version: 1.0.0 resolution: "negotiator@npm:1.0.0" @@ -6696,7 +7201,7 @@ __metadata: languageName: node linkType: hard -"node-abi@npm:^3.73.0": +"node-abi@npm:^3.3.0, node-abi@npm:^3.73.0": version: 3.74.0 resolution: "node-abi@npm:3.74.0" dependencies: @@ -7180,6 +7685,28 @@ __metadata: languageName: node linkType: hard +"prebuild-install@npm:^7.1.1": + version: 7.1.3 + resolution: "prebuild-install@npm:7.1.3" + dependencies: + detect-libc: "npm:^2.0.0" + expand-template: "npm:^2.0.3" + github-from-package: "npm:0.0.0" + minimist: "npm:^1.2.3" + mkdirp-classic: "npm:^0.5.3" + napi-build-utils: "npm:^2.0.0" + node-abi: "npm:^3.3.0" + pump: "npm:^3.0.0" + rc: "npm:^1.2.7" + simple-get: "npm:^4.0.0" + tar-fs: "npm:^2.0.0" + tunnel-agent: "npm:^0.6.0" + bin: + prebuild-install: bin.js + checksum: 10c0/25919a42b52734606a4036ab492d37cfe8b601273d8dfb1fa3c84e141a0a475e7bad3ab848c741d2f810cef892fcf6059b8c7fe5b29f98d30e0c29ad009bedff + languageName: node + linkType: hard + "pretty-ms@npm:^9.0.0": version: 9.2.0 resolution: "pretty-ms@npm:9.2.0" @@ -7308,6 +7835,20 @@ __metadata: languageName: node linkType: hard +"rc@npm:^1.2.7": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: "npm:^0.6.0" + ini: "npm:~1.3.0" + minimist: "npm:^1.2.0" + strip-json-comments: "npm:~2.0.1" + bin: + rc: ./cli.js + checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 + languageName: node + linkType: hard + "react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -7747,6 +8288,13 @@ __metadata: languageName: node linkType: hard +"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + "safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" @@ -7754,13 +8302,6 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:~5.2.0": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 - languageName: node - linkType: hard - "safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -7795,7 +8336,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.5.1, semver@npm:^7.5.2, semver@npm:^7.6.3, semver@npm:^7.7.1": +"semver@npm:^7.5.1, semver@npm:^7.5.2, semver@npm:^7.6.2, semver@npm:^7.6.3, semver@npm:^7.7.1": version: 7.7.1 resolution: "semver@npm:7.7.1" bin: @@ -7889,6 +8430,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.8.1": + version: 1.8.2 + resolution: "shell-quote@npm:1.8.2" + checksum: 10c0/85fdd44f2ad76e723d34eb72c753f04d847ab64e9f1f10677e3f518d0e5b0752a176fd805297b30bb8c3a1556ebe6e77d2288dbd7b7b0110c7e941e9e9c20ce1 + languageName: node + linkType: hard + "shimmer@npm:^1.2.1": version: 1.2.1 resolution: "shimmer@npm:1.2.1" @@ -7965,6 +8513,24 @@ __metadata: languageName: node linkType: hard +"simple-concat@npm:^1.0.0": + version: 1.0.1 + resolution: "simple-concat@npm:1.0.1" + checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 + languageName: node + linkType: hard + +"simple-get@npm:^4.0.0": + version: 4.0.1 + resolution: "simple-get@npm:4.0.1" + dependencies: + decompress-response: "npm:^6.0.0" + once: "npm:^1.3.1" + simple-concat: "npm:^1.0.0" + checksum: 10c0/b0649a581dbca741babb960423248899203165769747142033479a7dc5e77d7b0fced0253c731cd57cf21e31e4d77c9157c3069f4448d558ebc96cf9e1eebcf0 + languageName: node + linkType: hard + "simple-swizzle@npm:^0.2.2": version: 0.2.2 resolution: "simple-swizzle@npm:0.2.2" @@ -8256,6 +8822,13 @@ __metadata: languageName: node linkType: hard +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 + languageName: node + linkType: hard + "sucrase@npm:^3.35.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -8300,6 +8873,18 @@ __metadata: languageName: node linkType: hard +"tar-fs@npm:^2.0.0": + version: 2.1.2 + resolution: "tar-fs@npm:2.1.2" + dependencies: + chownr: "npm:^1.1.1" + mkdirp-classic: "npm:^0.5.2" + pump: "npm:^3.0.0" + tar-stream: "npm:^2.1.4" + checksum: 10c0/9c704bd4a53be7565caf34ed001d1428532457fe3546d8fc1233f0f0882c3d2403f8602e8046e0b0adeb31fe95336572a69fb28851a391523126b697537670fc + languageName: node + linkType: hard + "tar-fs@npm:^3.0.6": version: 3.0.8 resolution: "tar-fs@npm:3.0.8" @@ -8329,7 +8914,7 @@ __metadata: languageName: node linkType: hard -"tar-stream@npm:^2.0.0": +"tar-stream@npm:^2.0.0, tar-stream@npm:^2.1.4": version: 2.2.0 resolution: "tar-stream@npm:2.2.0" dependencies: @@ -8609,6 +9194,15 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a + languageName: node + linkType: hard + "turbo-darwin-64@npm:2.4.0": version: 2.4.0 resolution: "turbo-darwin-64@npm:2.4.0" @@ -9270,6 +9864,17 @@ __metadata: languageName: node linkType: hard +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a + languageName: node + linkType: hard + "which@npm:^5.0.0": version: 5.0.0 resolution: "which@npm:5.0.0"