@sqlite-sync/core 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/memory-db/sqlite-reactive-db.ts","../src/bound-map.ts","../src/sqlite-crdt/crdt-schema.ts","../src/sqlite-crdt/crdt-storage-mutator.ts","../src/sqlite-crdt/make-crdt-table.ts","../src/db-id.ts","../src/memory-db/memory-db.ts","../src/worker-db/db-worker-client.ts","../src/sync-db.ts"],"sourcesContent":["import sqlite3InitModule, { type Sqlite3Static } from \"@sqlite.org/sqlite-wasm\";\nimport { BoundMap } from \"../bound-map\";\nimport { type Logger, startPerformanceLogger } from \"../logger\";\nimport { type PreparedStatement, SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { createTypedEventTarget, type TypedEvent } from \"../utils\";\n\nlet sqliteModule: Sqlite3Static | null = null;\n\ntype TableName<Database> = keyof Database extends string ? keyof Database : never;\n\ntype SQLiteReactiveDbOptions = {\n snapshot: Uint8Array<ArrayBufferLike>;\n logger: Logger;\n};\n\ntype EventsMap = {\n \"transaction-committed\": undefined;\n \"transaction-rolled-back\": undefined;\n \"any-table-changed\": undefined;\n} & Record<`table:${string}`, void>;\n\nexport function createSQLiteReactiveDb<Database>(opts: SQLiteReactiveDbOptions) {\n return SQLiteReactiveDb.create<Database>(opts);\n}\n\nexport class SQLiteReactiveDb<Database> {\n readonly db: SQLiteDbWrapper<Database>;\n private sqlite3: Sqlite3Static;\n\n private readonly logger: Logger;\n\n private tablesUsedStatement: PreparedStatement<[string], { name: string; isWrite: boolean }> | null = null;\n\n private eventTarget = createTypedEventTarget<EventsMap>();\n\n private constructor(sqlite3: Sqlite3Static, logger: Logger) {\n this.sqlite3 = sqlite3;\n this.logger = logger;\n\n this.db = new SQLiteDbWrapper({\n db: () => new sqlite3.oo1.DB({ filename: \":memory:\" }),\n logger: this.logger,\n loggerPrefix: \"memory\",\n sqlite3,\n });\n }\n\n static async create<Database>(opts: SQLiteReactiveDbOptions) {\n const logger = opts.logger;\n const perf = startPerformanceLogger(logger);\n if (!sqliteModule) {\n sqliteModule = await sqlite3InitModule();\n }\n\n const db = new SQLiteReactiveDb<Database>(sqliteModule, logger);\n\n if (opts.snapshot) {\n db.useSnapshot(opts.snapshot);\n }\n db.registerDbHooks();\n\n perf.logEnd(\"createSQLiteMemoryDb\", \"success\", \"system\");\n\n return db;\n }\n\n private liveQueryStatements = new BoundMap<string, PreparedStatement<any[], unknown>>({\n maxSize: 100,\n onRemove(_, value) {\n value.finalize();\n },\n });\n\n createLiveQuery<TResult>(query: { sql: string; parameters: readonly unknown[] }) {\n const fetchRows = (parameters: readonly unknown[]) => {\n let statement = this.liveQueryStatements.get(query.sql);\n if (!statement) {\n statement = this.db.prepare<any[], any>(query.sql);\n this.liveQueryStatements.set(query.sql, statement);\n }\n return statement.execute(parameters as any) as TResult[];\n };\n\n let rows: TResult[] | null = null;\n\n const getRows = () => {\n if (!rows) {\n rows = fetchRows(query.parameters);\n }\n return rows;\n };\n\n let subscriber: (() => void) | null = null;\n\n let lastParameters: readonly unknown[] = query.parameters;\n const refresh = (parameters?: readonly unknown[]) => {\n if (parameters) {\n lastParameters = parameters;\n }\n rows = fetchRows(lastParameters);\n subscriber?.();\n };\n\n const subscribe = (onchange: () => void) => {\n if (subscriber) {\n throw new Error(\"Subscriber already exists\");\n }\n\n subscriber = onchange;\n const subscription = this.subscribeToQueryChanges({\n sql: query.sql,\n onDataChange: refresh,\n });\n\n return () => {\n subscription.unsubscribe();\n subscriber = null;\n };\n };\n\n return { getRows, refresh, subscribe };\n }\n\n subscribeToQueryChanges(params: { sql: string; onDataChange: () => void }) {\n const { sql, onDataChange } = params;\n\n const tables = this.getTablesUsed(sql);\n const readTables = new Set<string>();\n for (const table of tables) {\n if (!readTables.has(table.name)) {\n readTables.add(table.name);\n } else if (table.isWrite) {\n throw new Error(\"This query writes and reads from the same table. This may cause infinite loops.\");\n }\n }\n\n const notifyDataChange = createDebouncedCallback(() => {\n onDataChange();\n }, 30);\n\n for (const table of readTables) {\n this.eventTarget.addEventListener(`table:${table}`, notifyDataChange);\n }\n this.eventTarget.addEventListener(\"any-table-changed\", notifyDataChange);\n\n return {\n unsubscribe: () => {\n for (const table of readTables) {\n this.eventTarget.removeEventListener(`table:${table}`, notifyDataChange);\n }\n this.eventTarget.removeEventListener(\"any-table-changed\", notifyDataChange);\n },\n };\n }\n\n subscribeToTableChanges(table: string, onChanges: () => void) {\n this.eventTarget.addEventListener(`table:${table}`, onChanges);\n this.eventTarget.addEventListener(\"any-table-changed\", onChanges);\n return {\n unsubscribe: () => {\n this.eventTarget.removeEventListener(`table:${table}`, onChanges);\n this.eventTarget.removeEventListener(\"any-table-changed\", onChanges);\n },\n };\n }\n\n getTablesUsed(query: string) {\n if (!this.tablesUsedStatement) {\n this.tablesUsedStatement = this.db.prepare<[string], { name: string; isWrite: boolean }>(\n \"select t.tbl_name as name, u.wr as isWrite from tables_used(?) as u inner join sqlite_master as t on t.name = u.name where u.schema = 'main'\",\n { loggerLevel: \"system\" },\n );\n }\n\n const tables = this.tablesUsedStatement.execute([query]);\n\n if (tables.length === 0 && query.toLowerCase().includes(\"delete\")) {\n // tables_used function does not work with delete queries that clear entire tables\n tables.push(...this.getClearedTables(query));\n }\n\n return tables;\n }\n\n private getClearedTables(query: string) {\n const operations = this.db.execute<{\n opcode: string;\n p1: number;\n p2: number;\n }>(`EXPLAIN ${query.split(\";\")[0]}`, { loggerLevel: \"system\" }).rows;\n\n const clearedTablesRootPages = new Set<number>();\n for (const operation of operations) {\n if (operation.opcode === \"Clear\" && operation.p2 === 0) {\n clearedTablesRootPages.add(operation.p1);\n }\n }\n\n if (clearedTablesRootPages.size === 0) {\n return [];\n }\n\n const tableNames = this.db.execute<{ name: string; isWrite: boolean }>(\n `select t.tbl_name as name, true as isWrite from sqlite_master as t where t.rootpage in (${Array.from(\n clearedTablesRootPages,\n ).join(\",\")})`,\n { loggerLevel: \"system\" },\n ).rows;\n\n return tableNames;\n }\n\n addEventListener<K extends keyof EventsMap>(type: K, listener: (event: TypedEvent<EventsMap[K]>) => void) {\n this.eventTarget.addEventListener(type, listener);\n }\n\n removeEventListener<K extends keyof EventsMap>(type: K, listener: (event: TypedEvent<EventsMap[K]>) => void) {\n this.eventTarget.removeEventListener(type, listener);\n }\n\n notifyTableSubscribers(tables: (TableName<Database> | (string & {}))[] = []) {\n if (tables.length === 0) {\n this.eventTarget.dispatchEvent(\"any-table-changed\", undefined);\n return;\n }\n\n for (const table of tables) {\n this.eventTarget.dispatchEvent(`table:${table}`, undefined);\n }\n }\n\n private registerDbHooks() {\n const updateQueue = new Set<string>();\n\n this.sqlite3.capi.sqlite3_update_hook(\n this.db.ensureDb,\n (_ctx, _opId, _db, table) => {\n updateQueue.add(table);\n },\n 0,\n );\n\n this.sqlite3.capi.sqlite3_rollback_hook(\n this.db.ensureDb,\n () => {\n if (updateQueue.size === 0) {\n return 0;\n }\n\n updateQueue.clear();\n this.eventTarget.dispatchEvent(\"transaction-rolled-back\", undefined);\n\n return 0;\n },\n 0,\n );\n\n this.sqlite3.capi.sqlite3_commit_hook(\n this.db.ensureDb,\n () => {\n if (updateQueue.size === 0) {\n return 0;\n }\n\n const tables = Array.from(updateQueue);\n updateQueue.clear();\n this.eventTarget.dispatchEvent(\"transaction-committed\", undefined);\n\n queueMicrotask(() => {\n this.notifyTableSubscribers(tables);\n });\n return 0;\n },\n 0,\n );\n }\n\n createSnapshot() {\n const perf = startPerformanceLogger(this.logger);\n const snapshot = this.sqlite3.capi.sqlite3_js_db_export(this.db.ensureDb);\n perf.logEnd(\"createSnapshot\", `snapshot size: ${snapshot.byteLength}`, \"info\");\n\n return snapshot;\n }\n\n useSnapshot(snapshot: Uint8Array<ArrayBufferLike>) {\n this.db.useSnapshot(snapshot);\n this.notifyTableSubscribers();\n }\n\n dispose() {\n this.liveQueryStatements.clear();\n if (this.tablesUsedStatement) {\n this.tablesUsedStatement.finalize();\n this.tablesUsedStatement = null;\n }\n this.db.close();\n }\n}\n\nfunction createDebouncedCallback<TArgs extends unknown[]>(callback: (...args: TArgs) => void, delay: number) {\n let timeout: unknown | null = null;\n let shouldCallWithoutDelay = true;\n\n return (...args: TArgs) => {\n if (shouldCallWithoutDelay) {\n callback(...args);\n shouldCallWithoutDelay = false;\n return;\n }\n\n const effect = () => {\n timeout = null;\n shouldCallWithoutDelay = true;\n return callback(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout as any);\n }\n\n timeout = setTimeout(effect, delay);\n };\n}\n","interface BoundMapOptions<K, V> {\n maxSize: number;\n onRemove: ((key: K, value: V) => void) | undefined;\n}\n\nexport class BoundMap<K, V> {\n private map = new Map<K, V>();\n private maxSize: number;\n private onRemove: ((key: K, value: V) => void) | undefined;\n\n constructor(opts: BoundMapOptions<K, V>) {\n this.maxSize = opts.maxSize;\n this.onRemove = opts.onRemove;\n }\n\n set = (key: K, value: V) => {\n if (this.onRemove && this.map.has(key)) {\n const old = this.map.get(key) as V;\n this.map.set(key, value);\n this.onRemove(key, old);\n } else {\n this.map.set(key, value);\n }\n if (this.map.size > this.maxSize) {\n const firstKey = this.map.keys().next().value as K;\n this.delete(firstKey);\n }\n };\n\n get = (key: K): V | undefined => {\n return this.map.get(key);\n };\n\n delete = (key: K) => {\n if (this.onRemove && this.map.has(key)) {\n const value = this.map.get(key) as V;\n this.map.delete(key);\n this.onRemove(key, value);\n } else {\n this.map.delete(key);\n }\n };\n\n clear = () => {\n const onRemove = this.onRemove;\n if (onRemove) {\n this.map.forEach((value, key) => {\n onRemove(key, value);\n });\n }\n this.map.clear();\n };\n}\n","import type { ColumnType } from \"kysely\";\nimport type { Migrations } from \"../migrations/migrator\";\n\nexport type CrdtTableConfig = {\n baseTableName: string;\n crdtTableName: string;\n};\n\nexport function createSyncDbSchema({ migrations }: { migrations: Migrations }) {\n return new CrdtSchemaBuilder({ tables: [], migrations });\n}\n\nexport interface CreateCrdtSchemaOptions {\n tables: CrdtTableConfig[];\n migrations: Migrations;\n}\n\n// biome-ignore lint/complexity/noBannedTypes: required generic\nclass CrdtSchemaBuilder<ClientDB = {}, ServerDB = {}, MutationsDB = {}>\n implements SyncDbSchema<ClientDB, ServerDB, MutationsDB>\n{\n constructor(private config: CreateCrdtSchemaOptions) {}\n\n get tablesConfig() {\n return this.config.tables;\n }\n\n get migrations() {\n return this.config.migrations;\n }\n\n get \"~clientSchema\"() {\n console.warn(\"~clientSchema should not be accessed on the client\");\n return null as any;\n }\n\n get \"~serverSchema\"() {\n console.warn(\"~serverSchema should not be accessed on the server\");\n return null as any;\n }\n\n get \"~mutationsSchema\"() {\n console.warn(\"~mutationsSchema should not be accessed on the client\");\n return null as any;\n }\n\n addTable<Table extends Record<string, unknown>>() {\n const withConfig = <const CrdtTable extends string, const BaseTable extends string>({\n baseTableName,\n crdtTableName,\n }: {\n baseTableName: BaseTable;\n crdtTableName: CrdtTable;\n }) => {\n this.config.tables.push({ baseTableName, crdtTableName });\n return new CrdtSchemaBuilder<\n ClientDB & { [K in CrdtTable]: Table } & { [K in BaseTable]: ReadonlyTable<Table> },\n ServerDB & { [K in BaseTable]: ReadonlyTable<Table> },\n MutationsDB & { [K in BaseTable]: Table }\n >(this.config);\n };\n\n return { withConfig };\n }\n\n build() {\n return this as SyncDbSchema<ClientDB, ServerDB, MutationsDB>;\n }\n}\n\n// biome-ignore lint/complexity/noBannedTypes: required generic\nexport interface SyncDbSchema<ClientDB = {}, ServerDB = {}, MutationsDB = {}> {\n get tablesConfig(): CrdtTableConfig[];\n get migrations(): Migrations;\n \"~clientSchema\": ClientDB;\n \"~serverSchema\": ServerDB;\n \"~mutationsSchema\": MutationsDB;\n}\n\ntype ReadonlyTable<Table extends Record<string, unknown>> = {\n [K in keyof Table]: ColumnType<Table[K], never, never>;\n};\n","import type { CrdtStorage, OwnCrdtEvent } from \"./crdt-storage\";\n\nexport type CrdtStorageMutator<Database> = ReturnType<typeof createCrdtStorageMutator<Database>>;\n\ntype CommitEventOptions<Database, Table extends keyof Database & string> =\n | {\n type: \"item-created\";\n dataset: Table;\n item_id: string;\n payload: CreateEventPayload<Database, Table>;\n }\n | {\n type: \"item-updated\";\n dataset: Table;\n item_id: string;\n payload: UpdateEventPayload<Database, Table>;\n }\n | {\n type: \"item-deleted\";\n dataset: Table;\n item_id: string;\n };\n\ntype CreateEventPayload<Database, Table extends keyof Database> = Omit<Database[Table], \"tombstone\">;\ntype UpdateEventPayload<Database, Table extends keyof Database> = Omit<Partial<Database[Table]>, \"id\" | \"tombstone\">;\n\nexport function createCrdtStorageMutator<Database>({ storage }: { storage: CrdtStorage }) {\n const mapToStorageEvent = (event: CommitEventOptions<Database, keyof Database & string>): OwnCrdtEvent => {\n switch (event.type) {\n case \"item-created\":\n return {\n type: \"item-created\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-updated\":\n return {\n type: \"item-updated\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-deleted\":\n return {\n type: \"item-updated\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify({ tombstone: 1 }),\n };\n }\n };\n\n const enqueueEvents = (events: CommitEventOptions<Database, keyof Database & string>[]) => {\n storage.enqueueOwnEvents(events.map(mapToStorageEvent));\n };\n\n const createEvent = <Table extends keyof Database & string>(event: CommitEventOptions<Database, Table>) => {\n return event;\n };\n\n const enqueueEvent = (event: CommitEventOptions<Database, keyof Database & string>) => {\n storage.enqueueOwnEvents([mapToStorageEvent(event)]);\n };\n\n return {\n enqueueEvents,\n createEvent,\n enqueueEvent,\n };\n}\n","import type { SQLiteReactiveDb } from \"../memory-db/sqlite-reactive-db\";\nimport type { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { quoteId } from \"../utils\";\nimport type { CrdtStorage } from \"./crdt-storage\";\n\nexport function makeCrdtTable({\n db,\n baseTableName,\n crdtTableName,\n}: {\n db: SQLiteDbWrapper<any>;\n baseTableName: string;\n crdtTableName: string;\n}) {\n const tableSchema = db.dbSchema[baseTableName];\n\n if (!tableSchema) {\n throw new Error(`Table ${baseTableName} not found`);\n }\n\n const columns = new Map(tableSchema.columns.map((c) => [c.name, c]));\n\n const idColumn = columns.get(\"id\");\n if (!idColumn) {\n throw new Error(\n `Table \"${baseTableName}\" is missing a required \"id\" column. CRDT tables must have an \"id\" column to identify items.`,\n );\n }\n if (idColumn.dataType.toUpperCase() !== \"TEXT\") {\n throw new Error(\n `Table \"${baseTableName}\": \"id\" column must be of type TEXT, got \"${idColumn.dataType}\". CRDT item IDs are stored as strings.`,\n );\n }\n\n const tombstoneColumn = columns.get(\"tombstone\");\n if (!tombstoneColumn) {\n throw new Error(\n `Table \"${baseTableName}\" is missing a required \"tombstone\" column. CRDT tables must have a \"tombstone\" INTEGER column for soft deletes.`,\n );\n }\n const tombstoneType = tombstoneColumn.dataType.toUpperCase();\n if (tombstoneType !== \"INTEGER\" && tombstoneType !== \"BOOLEAN\") {\n throw new Error(\n `Table \"${baseTableName}\": \"tombstone\" column must be of type INTEGER or BOOLEAN, got \"${tombstoneColumn.dataType}\". It is compared as 0/1 for soft deletes.`,\n );\n }\n\n db.execute(\n `\ncreate view ${quoteId(crdtTableName)} as\nselect * from ${quoteId(baseTableName)}\nwhere tombstone = 0;`,\n { loggerLevel: \"system\" },\n );\n\n const allColumnNames = tableSchema.columns.map((column) => column.name);\n\n const jsonPayload = (from: \"new\" | \"old\") =>\n `'{'||${allColumnNames.map((col) => `'\"${col}\":'||json_quote(${from}.${quoteId(col)})`).join(\"||','||\")}||'}'`;\n\n db.execute(\n `\ncreate trigger ${quoteId(`${crdtTableName}_created`)}\ninstead of insert on ${quoteId(crdtTableName)}\nfor each row\nbegin\nselect handle_item_created('${baseTableName}', ${jsonPayload(\"new\")});\nend;\n`,\n { loggerLevel: \"system\" },\n );\n\n db.execute(\n `\ncreate trigger ${quoteId(`${crdtTableName}_updated`)}\ninstead of update on ${quoteId(crdtTableName)}\nfor each row\nbegin\nselect handle_item_updated(\n '${baseTableName}',\n ${jsonPayload(\"old\")},\n ${jsonPayload(\"new\")}\n);\nend;\n`,\n { loggerLevel: \"system\" },\n );\n\n db.execute(\n `\ncreate trigger ${quoteId(`${crdtTableName}_deleted`)}\ninstead of delete on ${quoteId(crdtTableName)}\nfor each row\nwhen old.tombstone = 0\nbegin\nselect handle_item_deleted('${baseTableName}', old.id);\nend;\n`,\n { loggerLevel: \"system\" },\n );\n}\n\nexport function registerCrdtFunctions({\n reactiveDb,\n storage,\n}: {\n reactiveDb: SQLiteReactiveDb<any>;\n storage: CrdtStorage;\n}) {\n let eventApplied = false;\n\n reactiveDb.db.createScalarFunction({\n name: \"handle_item_created\",\n deterministic: false,\n directOnly: false,\n innocuous: false,\n callback: (dataset: string, payloadRaw: string) => {\n const payload = JSON.parse(payloadRaw) as { id: string };\n\n storage.applyOwnEvent(\n {\n type: \"item-created\",\n dataset,\n item_id: payload.id,\n payload: payloadRaw,\n },\n {\n wrapInTransaction: false,\n },\n );\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.db.createScalarFunction({\n name: \"handle_item_updated\",\n deterministic: false,\n directOnly: false,\n innocuous: false,\n callback: (dataset: string, oldPayloadRaw: string, newPayloadRaw: string) => {\n const tableSchema = reactiveDb.db.dbSchema[dataset];\n const oldPayload = JSON.parse(oldPayloadRaw);\n const newPayload = JSON.parse(newPayloadRaw);\n\n let hasDiff = false;\n const updatePayload = {} as Record<string, unknown>;\n\n for (const column of tableSchema.columns) {\n const oldValue = oldPayload[column.name];\n const newValue = newPayload[column.name];\n if (oldValue === newValue) {\n continue;\n }\n hasDiff = true;\n updatePayload[column.name] = newValue;\n }\n\n if (!hasDiff) {\n return;\n }\n\n storage.applyOwnEvent(\n {\n type: \"item-updated\",\n dataset,\n item_id: oldPayload.id,\n payload: JSON.stringify(updatePayload),\n },\n {\n wrapInTransaction: false,\n },\n );\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.db.createScalarFunction({\n name: \"handle_item_deleted\",\n deterministic: false,\n directOnly: false,\n innocuous: false,\n callback: (dataset: string, itemId: string) => {\n storage.applyOwnEvent(\n {\n type: \"item-updated\",\n dataset,\n item_id: itemId,\n payload: JSON.stringify({ tombstone: 1 }),\n },\n {\n wrapInTransaction: false,\n },\n );\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.addEventListener(\"transaction-committed\", () => {\n if (eventApplied) {\n eventApplied = false;\n storage.dispatchEventsApplied();\n }\n });\n\n reactiveDb.addEventListener(\"transaction-rolled-back\", () => {\n eventApplied = false;\n });\n}\n","const dbIdRegex = /^[a-zA-Z][a-zA-Z\\-0-9]{2,63}$/;\n\nexport function validateDbId(dbId: string) {\n if (!dbIdRegex.test(dbId)) {\n throw new Error(\"Invalid dbId. Must be between 3 and 64 characters long and start with a letter.\");\n }\n}\n","import type { HLCCounter } from \"../hlc\";\nimport type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { applyMemoryDbSchema, type MemoryDbSchema } from \"../migrations/system-schema\";\nimport { createSQLiteCrdtApplyFunction } from \"../sqlite-crdt/apply-crdt-event\";\nimport type { CrdtTableConfig } from \"../sqlite-crdt/crdt-schema\";\nimport { createCrdtStorage, type EventUpdate, type GetEventsOptions } from \"../sqlite-crdt/crdt-storage\";\nimport type { PersistedCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport { applyKyselyEventsBatchFilters } from \"../sqlite-crdt/events-batch-filters\";\nimport { makeCrdtTable, registerCrdtFunctions } from \"../sqlite-crdt/make-crdt-table\";\nimport { createStoredValue } from \"../sqlite-crdt/stored-value\";\nimport type { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport type { SQLiteReactiveDb } from \"./sqlite-reactive-db\";\n\ntype MemoryDbOptions<Database> = {\n nodeId: string;\n migrator: SyncDbMigrator;\n reactiveDb: SQLiteReactiveDb<Database>;\n hlcCounter: HLCCounter;\n crdtTables: CrdtTableConfig[];\n};\n\nexport async function createMemoryDb<Database>({\n nodeId,\n migrator,\n reactiveDb: _reactiveDb,\n hlcCounter,\n crdtTables,\n}: MemoryDbOptions<Database>) {\n const reactiveDb = _reactiveDb as unknown as SQLiteReactiveDb<MemoryDbSchema>;\n const db = reactiveDb.db;\n\n applyMemoryDbSchema(db);\n for (const table of crdtTables) {\n makeCrdtTable({\n db,\n baseTableName: table.baseTableName,\n crdtTableName: table.crdtTableName,\n });\n }\n\n const localSyncId = createStoredValue({\n initialValue: 0,\n });\n\n const crdtStorage = createCrdtStorage({\n nodeId,\n syncId: localSyncId,\n hlc: hlcCounter,\n persistEvent: (event) => persistEvent(db, event),\n getEventsBatch: (opts) => getEventsBatch(db, opts),\n migrator,\n handleCrdtEventApply: createSQLiteCrdtApplyFunction({\n db,\n updateLogTableName: \"crdt_update_log\",\n }),\n updateEvent: (syncId, update) => updateEvent(db, syncId, update),\n transaction: (callback) => db.executeTransaction(callback),\n });\n\n registerCrdtFunctions({\n reactiveDb,\n storage: crdtStorage,\n });\n\n return {\n crdtStorage,\n };\n}\n\nfunction persistEvent(db: SQLiteDbWrapper<MemoryDbSchema>, event: PersistedCrdtEvent) {\n db.executePrepared(\n \"persist-crdt-event\",\n event,\n (db, params) =>\n db.insertInto(\"persisted_crdt_events\").values({\n type: params(\"type\"),\n dataset: params(\"dataset\"),\n item_id: params(\"item_id\"),\n payload: params(\"payload\"),\n schema_version: params(\"schema_version\"),\n sync_id: params(\"sync_id\"),\n status: params(\"status\"),\n timestamp: params(\"timestamp\"),\n origin: params(\"origin\"),\n source_node_id: params(\"source_node_id\"),\n }),\n { loggerLevel: \"system\" },\n );\n}\n\nfunction getEventsBatch(db: SQLiteDbWrapper<MemoryDbSchema>, opts: GetEventsOptions) {\n return db.executeKysely(\n (db) =>\n applyKyselyEventsBatchFilters(db.selectFrom(\"persisted_crdt_events\").selectAll(), {\n limit: 50,\n ...opts,\n }),\n { loggerLevel: \"system\" },\n ).rows;\n}\n\nfunction updateEvent(db: SQLiteDbWrapper<MemoryDbSchema>, syncId: number, update: EventUpdate) {\n db.executePrepared(\n \"update-crdt-event\",\n { syncId, ...update },\n (db, params) =>\n db\n .updateTable(\"persisted_crdt_events\")\n .set({\n status: params(\"status\"),\n schema_version: params(\"schema_version\"),\n type: params(\"type\"),\n dataset: params(\"dataset\"),\n item_id: params(\"item_id\"),\n payload: params(\"payload\"),\n })\n .where(\"sync_id\", \"=\", params(\"syncId\")),\n { loggerLevel: \"system\" },\n );\n}\n","import {\n createDeferredPromise,\n createTypedEventTarget,\n type DeferredPromise,\n type TypedEvent,\n type TypedEventTarget,\n} from \"../utils\";\nimport type {\n AsyncRpc,\n WorkerBroadcastChannels,\n WorkerConfig,\n WorkerErrorResponseMessage,\n WorkerInitMessage,\n WorkerNotificationMessage,\n WorkerRequestMessage,\n WorkerRequestMethod,\n WorkerResponseMessage,\n WorkerRpc,\n WorkerState,\n} from \"./worker-common\";\nimport { isWorkerErrorResponseMessage, isWorkerNotificationMessage, isWorkerResponseMessage } from \"./worker-common\";\n\ntype NotificationEvents = {\n [K in WorkerNotificationMessage[\"notificationType\"]]: Extract<WorkerNotificationMessage, { notificationType: K }>;\n};\n\nexport const createWorkerDbClient = async ({\n broadcastChannels,\n worker,\n config,\n}: {\n broadcastChannels: WorkerBroadcastChannels;\n worker: Worker;\n config: WorkerConfig;\n}) => {\n const eventTarget = createTypedEventTarget<NotificationEvents>();\n const workerRequestsMap = new Map<string, DeferredPromise<unknown>>();\n let isDisposed = false;\n\n const queryWorker = <TMethod extends WorkerRequestMethod>(\n method: TMethod,\n args: Parameters<WorkerRpc[TMethod]>,\n ): Promise<Awaited<ReturnType<WorkerRpc[TMethod]>>> => {\n if (isDisposed) {\n return Promise.reject(new Error(\"Worker client disposed\"));\n }\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<unknown>({\n timeout: 30_000,\n onTimeout: () => workerRequestsMap.delete(requestId),\n });\n workerRequestsMap.set(requestId, promise);\n\n const request: WorkerRequestMessage<TMethod> = {\n type: \"request\",\n requestId,\n method,\n args,\n };\n\n broadcastChannels.requests.postMessage(request);\n\n return promise.promise as Promise<Awaited<ReturnType<WorkerRpc[TMethod]>>>;\n };\n\n const handleWorkerResponse = (message: WorkerResponseMessage) => {\n const promise = workerRequestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n\n promise.resolve(message.data);\n workerRequestsMap.delete(message.requestId);\n };\n\n const handleWorkerError = (message: WorkerErrorResponseMessage) => {\n const promise = workerRequestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n\n promise.reject(new Error(message.error));\n workerRequestsMap.delete(message.requestId);\n };\n\n broadcastChannels.responses.onmessage = (event) => {\n const message = event.data;\n\n if (isWorkerResponseMessage(message)) {\n handleWorkerResponse(message);\n } else if (isWorkerErrorResponseMessage(message)) {\n handleWorkerError(message);\n } else if (isWorkerNotificationMessage(message)) {\n eventTarget.dispatchEvent(message.notificationType, message);\n }\n };\n\n const rpc: AsyncRpc<WorkerRpc> = {\n execute: (query) => queryWorker(\"execute\", [query]),\n getSnapshot: () => queryWorker(\"getSnapshot\", []),\n pushTabEvents: (request) => queryWorker(\"pushTabEvents\", [request]),\n pullEvents: (params) => queryWorker(\"pullEvents\", [params]),\n postState: () => queryWorker(\"postState\", []),\n goOnline: () => queryWorker(\"goOnline\", []),\n goOffline: () => queryWorker(\"goOffline\", []),\n };\n\n const statePromise = awaitWorkerState(eventTarget);\n postWorkerConfig(worker, config);\n // On first tab, the worker may not have its BroadcastChannel listener ready yet,\n // so this request can be silently dropped and timeout. The worker will independently\n // post state at the end of init. For subsequent tabs, this triggers the already-running\n // worker to send its current state.\n rpc.postState().catch(() => {});\n\n let workerState = await statePromise;\n\n eventTarget.addEventListener(\"state-changed\", (event) => {\n workerState = event.payload.state;\n });\n\n const dispose = () => {\n isDisposed = true;\n broadcastChannels.responses.onmessage = null;\n for (const [id, deferred] of workerRequestsMap) {\n deferred.reject(new Error(\"Worker client disposed\"));\n workerRequestsMap.delete(id);\n }\n };\n\n return {\n ...rpc,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n getState: () => workerState,\n dispose,\n };\n};\n\nfunction awaitWorkerState(eventTarget: TypedEventTarget<NotificationEvents>) {\n const promise = createDeferredPromise<WorkerState>({ timeout: 15_000 });\n\n const onStateChanged = (\n event: TypedEvent<Extract<WorkerNotificationMessage, { notificationType: \"state-changed\" }>>,\n ) => {\n promise.resolve(event.payload.state);\n eventTarget.removeEventListener(\"state-changed\", onStateChanged);\n };\n\n eventTarget.addEventListener(\"state-changed\", onStateChanged);\n\n return promise.promise;\n}\n\nfunction postWorkerConfig(worker: Worker, config: WorkerConfig) {\n const configMessage: WorkerInitMessage = {\n type: \"init\",\n config,\n };\n worker.postMessage(configMessage);\n}\n","import { validateDbId } from \"./db-id\";\nimport { HLCCounter } from \"./hlc\";\nimport { type Logger, startPerformanceLogger } from \"./logger\";\nimport { createMemoryDb } from \"./memory-db/memory-db\";\nimport { createSQLiteReactiveDb } from \"./memory-db/sqlite-reactive-db\";\nimport type { SyncDbMigrator } from \"./migrations/migrator\";\nimport type { SyncDbSchema } from \"./sqlite-crdt/crdt-schema\";\nimport { createCrdtSyncRemoteSource } from \"./sqlite-crdt/crdt-sync-remote-source\";\nimport { createStoredValue } from \"./sqlite-crdt/stored-value\";\nimport { createDeferredPromise, generateId, type TypedEvent } from \"./utils\";\nimport { createWorkerDbClient } from \"./worker-db/db-worker-client\";\nimport {\n createBroadcastChannels,\n syncDbClientLockName,\n type WorkerNotificationMessage,\n} from \"./worker-db/worker-common\";\n\ntype SyncedDbOptions<Database, Props = undefined> = {\n dbId: string;\n clearOnInit?: boolean;\n worker: Worker;\n workerProps: Props;\n syncDbSchema: SyncDbSchema<Database>;\n};\n\nconst defaultLogger: Logger = (type, message, level = \"info\") => {\n const logMessage = `[${type}] ${message}`;\n switch (level) {\n case \"info\":\n console.log(logMessage);\n break;\n case \"warning\":\n console.warn(logMessage);\n break;\n case \"error\":\n console.error(logMessage);\n break;\n case \"trace\":\n console.trace(logMessage);\n break;\n }\n};\n\nexport async function createSyncedDb<Database, Props = undefined>(options: SyncedDbOptions<Database, Props>) {\n validateDbId(options.dbId);\n\n const perf = startPerformanceLogger(defaultLogger);\n\n const tabId = generateId();\n\n const broadcastChannels = createBroadcastChannels(options.dbId);\n\n const clientLockAcquired = createDeferredPromise<void>();\n const clientLockRelease = createDeferredPromise<void>();\n navigator.locks.request(`${syncDbClientLockName}-${options.dbId}`, { mode: \"shared\" }, () => {\n clientLockAcquired.resolve();\n return clientLockRelease.promise;\n });\n await clientLockAcquired.promise;\n\n const workerClient = await createWorkerDbClient({\n worker: options.worker,\n config: {\n clientId: generateId(),\n dbId: options.dbId,\n clearOnInit: options.clearOnInit,\n props: options.workerProps as never,\n },\n broadcastChannels,\n });\n\n const hlcCounter = new HLCCounter(tabId, () => Date.now());\n\n const workerClientSnapshot = await workerClient.getSnapshot();\n const reactiveDb = await createSQLiteReactiveDb<Database>({\n snapshot: workerClientSnapshot.file,\n logger: defaultLogger,\n });\n\n const memoryDbMigrator: SyncDbMigrator = {\n currentSchemaVersion: workerClientSnapshot.schemaVersion,\n latestSchemaVersion: workerClientSnapshot.schemaVersion,\n migrateDbToLatest: () => {\n throw new Error(\"Memory DB migrations are not implemented\");\n },\n migrateEvent: (event, targetVersion) => {\n if (event.schema_version === targetVersion) {\n return event;\n }\n throw new Error(\"Memory DB migrations are not implemented\");\n },\n migrateEvents: (events) => events,\n };\n const { crdtStorage } = await createMemoryDb({\n nodeId: tabId,\n migrator: memoryDbMigrator,\n reactiveDb: reactiveDb,\n hlcCounter,\n crdtTables: options.syncDbSchema.tablesConfig,\n });\n\n const pullSyncId = createStoredValue({\n initialValue: workerClientSnapshot.syncId,\n });\n const pushSyncId = createStoredValue({\n initialValue: 0,\n });\n const tabRemoteSource = createCrdtSyncRemoteSource({\n bufferSize: 500,\n pullSyncId,\n pushSyncId,\n storage: crdtStorage,\n nodeId: tabId,\n migrator: memoryDbMigrator,\n remoteFactory: ({ onEventsAvailable }) => {\n const onNewEventChunkApplied = (\n event: TypedEvent<Extract<WorkerNotificationMessage, { notificationType: \"new-event-chunk-applied\" }>>,\n ) => {\n onEventsAvailable(event.payload.newSyncId);\n };\n workerClient.addEventListener(\"new-event-chunk-applied\", onNewEventChunkApplied);\n\n return {\n pullEvents: (request) => workerClient.pullEvents(request),\n pushEvents: (request) => workerClient.pushTabEvents(request),\n disconnect: () => {\n workerClient.removeEventListener(\"new-event-chunk-applied\", onNewEventChunkApplied);\n },\n };\n },\n });\n tabRemoteSource.goOnline();\n\n perf.logEnd(\"createSyncedDb\", \"initialized\", \"info\");\n\n let isDisposed = false;\n const dispose = async () => {\n if (isDisposed) return;\n isDisposed = true;\n\n clientLockRelease.resolve();\n await tabRemoteSource.dispose();\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n workerClient.dispose();\n reactiveDb.dispose();\n };\n\n return {\n db: {\n execute: reactiveDb.db.execute.bind(reactiveDb.db),\n executeKysely: reactiveDb.db.executeKysely.bind(reactiveDb.db),\n executeTransaction: reactiveDb.db.executeTransaction.bind(reactiveDb.db),\n createLiveQuery: reactiveDb.createLiveQuery.bind(reactiveDb),\n },\n state: {\n getState: workerClient.getState.bind(workerClient),\n subscribe: (onChange: () => void) => {\n workerClient.addEventListener(\"state-changed\", onChange);\n return () => {\n workerClient.removeEventListener(\"state-changed\", onChange);\n };\n },\n goOnline: workerClient.goOnline.bind(workerClient),\n goOffline: workerClient.goOffline.bind(workerClient),\n },\n dispose,\n _internal: {\n executeAsync: workerClient.execute.bind(workerClient),\n },\n };\n}\n\nexport type SyncedDb<Database> = Awaited<ReturnType<typeof createSyncedDb<Database>>>;\n"],"mappings":"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","names":["db"]}
1
+ {"version":3,"sources":["../src/memory-db/sqlite-reactive-db.ts","../src/bound-map.ts","../src/sqlite-crdt/crdt-schema.ts","../src/sqlite-crdt/crdt-storage-mutator.ts","../src/sqlite-crdt/make-crdt-table.ts","../src/db-id.ts","../src/devtools-registry.ts","../src/memory-db/memory-db.ts","../src/worker-db/db-worker-client.ts","../src/sync-db.ts"],"sourcesContent":["import sqlite3InitModule, { type Sqlite3Static } from \"@sqlite.org/sqlite-wasm\";\nimport { BoundMap } from \"../bound-map\";\nimport { type Logger, startPerformanceLogger } from \"../logger\";\nimport { type PreparedStatement, SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { createTypedEventTarget, type TypedEvent } from \"../utils\";\n\nlet sqliteModule: Sqlite3Static | null = null;\n\ntype TableName<Database> = keyof Database extends string ? keyof Database : never;\n\ntype SQLiteReactiveDbOptions = {\n snapshot: Uint8Array<ArrayBufferLike>;\n logger: Logger;\n};\n\ntype EventsMap = {\n \"transaction-committed\": undefined;\n \"transaction-rolled-back\": undefined;\n \"any-table-changed\": undefined;\n} & Record<`table:${string}`, void>;\n\nexport function createSQLiteReactiveDb<Database>(opts: SQLiteReactiveDbOptions) {\n return SQLiteReactiveDb.create<Database>(opts);\n}\n\nexport class SQLiteReactiveDb<Database> {\n readonly db: SQLiteDbWrapper<Database>;\n private sqlite3: Sqlite3Static;\n\n private readonly logger: Logger;\n\n private tablesUsedStatement: PreparedStatement<[string], { name: string; isWrite: boolean }> | null = null;\n\n private eventTarget = createTypedEventTarget<EventsMap>();\n\n private constructor(sqlite3: Sqlite3Static, logger: Logger) {\n this.sqlite3 = sqlite3;\n this.logger = logger;\n\n this.db = new SQLiteDbWrapper({\n db: () => new sqlite3.oo1.DB({ filename: \":memory:\" }),\n logger: this.logger,\n loggerPrefix: \"memory\",\n sqlite3,\n });\n }\n\n static async create<Database>(opts: SQLiteReactiveDbOptions) {\n const logger = opts.logger;\n const perf = startPerformanceLogger(logger);\n if (!sqliteModule) {\n sqliteModule = await sqlite3InitModule();\n }\n\n const db = new SQLiteReactiveDb<Database>(sqliteModule, logger);\n\n if (opts.snapshot) {\n db.useSnapshot(opts.snapshot);\n }\n db.registerDbHooks();\n\n perf.logEnd(\"createSQLiteMemoryDb\", \"success\", \"system\");\n\n return db;\n }\n\n private liveQueryStatements = new BoundMap<string, PreparedStatement<any[], unknown>>({\n maxSize: 100,\n onRemove(_, value) {\n value.finalize();\n },\n });\n\n createLiveQuery<TResult>(query: { sql: string; parameters: readonly unknown[] }) {\n const fetchRows = (parameters: readonly unknown[]) => {\n let statement = this.liveQueryStatements.get(query.sql);\n if (!statement) {\n statement = this.db.prepare<any[], any>(query.sql);\n this.liveQueryStatements.set(query.sql, statement);\n }\n return statement.execute(parameters as any) as TResult[];\n };\n\n let rows: TResult[] | null = null;\n\n const getRows = () => {\n if (!rows) {\n rows = fetchRows(query.parameters);\n }\n return rows;\n };\n\n let subscriber: (() => void) | null = null;\n\n let lastParameters: readonly unknown[] = query.parameters;\n const refresh = (parameters?: readonly unknown[]) => {\n if (parameters) {\n lastParameters = parameters;\n }\n rows = fetchRows(lastParameters);\n subscriber?.();\n };\n\n const subscribe = (onchange: () => void) => {\n if (subscriber) {\n throw new Error(\"Subscriber already exists\");\n }\n\n subscriber = onchange;\n const subscription = this.subscribeToQueryChanges({\n sql: query.sql,\n onDataChange: refresh,\n });\n\n return () => {\n subscription.unsubscribe();\n subscriber = null;\n };\n };\n\n return { getRows, refresh, subscribe };\n }\n\n subscribeToQueryChanges(params: { sql: string; onDataChange: () => void }) {\n const { sql, onDataChange } = params;\n\n const tables = this.getTablesUsed(sql);\n const readTables = new Set<string>();\n for (const table of tables) {\n if (!readTables.has(table.name)) {\n readTables.add(table.name);\n } else if (table.isWrite) {\n throw new Error(\"This query writes and reads from the same table. This may cause infinite loops.\");\n }\n }\n\n const notifyDataChange = createDebouncedCallback(() => {\n onDataChange();\n }, 30);\n\n for (const table of readTables) {\n this.eventTarget.addEventListener(`table:${table}`, notifyDataChange);\n }\n this.eventTarget.addEventListener(\"any-table-changed\", notifyDataChange);\n\n return {\n unsubscribe: () => {\n for (const table of readTables) {\n this.eventTarget.removeEventListener(`table:${table}`, notifyDataChange);\n }\n this.eventTarget.removeEventListener(\"any-table-changed\", notifyDataChange);\n },\n };\n }\n\n subscribeToTableChanges(table: string, onChanges: () => void) {\n this.eventTarget.addEventListener(`table:${table}`, onChanges);\n this.eventTarget.addEventListener(\"any-table-changed\", onChanges);\n return {\n unsubscribe: () => {\n this.eventTarget.removeEventListener(`table:${table}`, onChanges);\n this.eventTarget.removeEventListener(\"any-table-changed\", onChanges);\n },\n };\n }\n\n getTablesUsed(query: string) {\n if (!this.tablesUsedStatement) {\n this.tablesUsedStatement = this.db.prepare<[string], { name: string; isWrite: boolean }>(\n \"select t.tbl_name as name, u.wr as isWrite from tables_used(?) as u inner join sqlite_master as t on t.name = u.name where u.schema = 'main'\",\n { loggerLevel: \"system\" },\n );\n }\n\n const tables = this.tablesUsedStatement.execute([query]);\n\n if (tables.length === 0 && query.toLowerCase().includes(\"delete\")) {\n // tables_used function does not work with delete queries that clear entire tables\n tables.push(...this.getClearedTables(query));\n }\n\n return tables;\n }\n\n private getClearedTables(query: string) {\n const operations = this.db.execute<{\n opcode: string;\n p1: number;\n p2: number;\n }>(`EXPLAIN ${query.split(\";\")[0]}`, { loggerLevel: \"system\" }).rows;\n\n const clearedTablesRootPages = new Set<number>();\n for (const operation of operations) {\n if (operation.opcode === \"Clear\" && operation.p2 === 0) {\n clearedTablesRootPages.add(operation.p1);\n }\n }\n\n if (clearedTablesRootPages.size === 0) {\n return [];\n }\n\n const tableNames = this.db.execute<{ name: string; isWrite: boolean }>(\n `select t.tbl_name as name, true as isWrite from sqlite_master as t where t.rootpage in (${Array.from(\n clearedTablesRootPages,\n ).join(\",\")})`,\n { loggerLevel: \"system\" },\n ).rows;\n\n return tableNames;\n }\n\n addEventListener<K extends keyof EventsMap>(type: K, listener: (event: TypedEvent<EventsMap[K]>) => void) {\n this.eventTarget.addEventListener(type, listener);\n }\n\n removeEventListener<K extends keyof EventsMap>(type: K, listener: (event: TypedEvent<EventsMap[K]>) => void) {\n this.eventTarget.removeEventListener(type, listener);\n }\n\n private notifyTableSubscribers(tables: (TableName<Database> | (string & {}))[] | Set<string> | null = null) {\n if (!tables) {\n this.eventTarget.dispatchEvent(\"any-table-changed\", undefined);\n return;\n }\n\n for (const table of tables) {\n this.eventTarget.dispatchEvent(`table:${table}`, undefined);\n }\n }\n\n private registerDbHooks() {\n let updateQueue = new Set<string>();\n\n this.sqlite3.capi.sqlite3_update_hook(\n this.db.ensureDb,\n (_ctx, _opId, _db, table) => {\n updateQueue.add(table);\n },\n 0,\n );\n\n this.sqlite3.capi.sqlite3_rollback_hook(\n this.db.ensureDb,\n () => {\n if (updateQueue.size === 0) {\n return 0;\n }\n\n updateQueue.clear();\n this.eventTarget.dispatchEvent(\"transaction-rolled-back\", undefined);\n\n return 0;\n },\n 0,\n );\n\n this.sqlite3.capi.sqlite3_commit_hook(\n this.db.ensureDb,\n () => {\n if (updateQueue.size === 0) {\n return 0;\n }\n\n const tables = updateQueue;\n updateQueue = new Set<string>();\n this.eventTarget.dispatchEvent(\"transaction-committed\", undefined);\n\n queueMicrotask(() => {\n this.notifyTableSubscribers(tables);\n });\n return 0;\n },\n 0,\n );\n }\n\n createSnapshot() {\n const perf = startPerformanceLogger(this.logger);\n const snapshot = this.sqlite3.capi.sqlite3_js_db_export(this.db.ensureDb);\n perf.logEnd(\"createSnapshot\", `snapshot size: ${snapshot.byteLength}`, \"info\");\n\n return snapshot;\n }\n\n useSnapshot(snapshot: Uint8Array<ArrayBufferLike>) {\n this.db.useSnapshot(snapshot);\n this.notifyTableSubscribers();\n }\n\n dispose() {\n this.liveQueryStatements.clear();\n if (this.tablesUsedStatement) {\n this.tablesUsedStatement.finalize();\n this.tablesUsedStatement = null;\n }\n this.db.close();\n }\n}\n\nfunction createDebouncedCallback<TArgs extends unknown[]>(callback: (...args: TArgs) => void, delay: number) {\n let timeout: unknown | null = null;\n let shouldCallWithoutDelay = true;\n\n return (...args: TArgs) => {\n if (shouldCallWithoutDelay) {\n callback(...args);\n shouldCallWithoutDelay = false;\n return;\n }\n\n const effect = () => {\n timeout = null;\n shouldCallWithoutDelay = true;\n return callback(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout as any);\n }\n\n timeout = setTimeout(effect, delay);\n };\n}\n","interface BoundMapOptions<K, V> {\n maxSize: number;\n onRemove: ((key: K, value: V) => void) | undefined;\n}\n\nexport class BoundMap<K, V> {\n private map = new Map<K, V>();\n private maxSize: number;\n private onRemove: ((key: K, value: V) => void) | undefined;\n\n constructor(opts: BoundMapOptions<K, V>) {\n this.maxSize = opts.maxSize;\n this.onRemove = opts.onRemove;\n }\n\n set = (key: K, value: V) => {\n if (this.onRemove && this.map.has(key)) {\n const old = this.map.get(key) as V;\n this.map.set(key, value);\n this.onRemove(key, old);\n } else {\n this.map.set(key, value);\n }\n if (this.map.size > this.maxSize) {\n const firstKey = this.map.keys().next().value as K;\n this.delete(firstKey);\n }\n };\n\n get = (key: K): V | undefined => {\n return this.map.get(key);\n };\n\n delete = (key: K) => {\n if (this.onRemove && this.map.has(key)) {\n const value = this.map.get(key) as V;\n this.map.delete(key);\n this.onRemove(key, value);\n } else {\n this.map.delete(key);\n }\n };\n\n clear = () => {\n const onRemove = this.onRemove;\n if (onRemove) {\n this.map.forEach((value, key) => {\n onRemove(key, value);\n });\n }\n this.map.clear();\n };\n}\n","import type { ColumnType } from \"kysely\";\nimport type { Migrations } from \"../migrations/migrator\";\n\nexport type CrdtTableConfig = {\n baseTableName: string;\n crdtTableName: string;\n};\n\nexport function createSyncDbSchema({ migrations }: { migrations: Migrations }) {\n return new CrdtSchemaBuilder({ tables: [], migrations });\n}\n\nexport interface CreateCrdtSchemaOptions {\n tables: CrdtTableConfig[];\n migrations: Migrations;\n}\n\n// biome-ignore lint/complexity/noBannedTypes: required generic\nclass CrdtSchemaBuilder<ClientDB = {}, ServerDB = {}, MutationsDB = {}>\n implements SyncDbSchema<ClientDB, ServerDB, MutationsDB>\n{\n constructor(private config: CreateCrdtSchemaOptions) {}\n\n get tablesConfig() {\n return this.config.tables;\n }\n\n get migrations() {\n return this.config.migrations;\n }\n\n get \"~clientSchema\"() {\n console.warn(\"~clientSchema should not be accessed on the client\");\n return null as any;\n }\n\n get \"~serverSchema\"() {\n console.warn(\"~serverSchema should not be accessed on the server\");\n return null as any;\n }\n\n get \"~mutationsSchema\"() {\n console.warn(\"~mutationsSchema should not be accessed on the client\");\n return null as any;\n }\n\n addTable<Table extends Record<string, unknown>>() {\n const withConfig = <const CrdtTable extends string, const BaseTable extends string>({\n baseTableName,\n crdtTableName,\n }: {\n baseTableName: BaseTable;\n crdtTableName: CrdtTable;\n }) => {\n this.config.tables.push({ baseTableName, crdtTableName });\n return new CrdtSchemaBuilder<\n ClientDB & { [K in CrdtTable]: Table } & { [K in BaseTable]: ReadonlyTable<Table> },\n ServerDB & { [K in BaseTable]: ReadonlyTable<Table> },\n MutationsDB & { [K in BaseTable]: Table }\n >(this.config);\n };\n\n return { withConfig };\n }\n\n build() {\n return this as SyncDbSchema<ClientDB, ServerDB, MutationsDB>;\n }\n}\n\n// biome-ignore lint/complexity/noBannedTypes: required generic\nexport interface SyncDbSchema<ClientDB = {}, ServerDB = {}, MutationsDB = {}> {\n get tablesConfig(): CrdtTableConfig[];\n get migrations(): Migrations;\n \"~clientSchema\": ClientDB;\n \"~serverSchema\": ServerDB;\n \"~mutationsSchema\": MutationsDB;\n}\n\ntype ReadonlyTable<Table extends Record<string, unknown>> = {\n [K in keyof Table]: ColumnType<Table[K], never, never>;\n};\n","import type { CrdtStorage, OwnCrdtEvent } from \"./crdt-storage\";\n\nexport type CrdtStorageMutator<Database> = ReturnType<typeof createCrdtStorageMutator<Database>>;\n\ntype CommitEventOptions<Database, Table extends keyof Database & string> =\n | {\n type: \"item-created\";\n dataset: Table;\n item_id: string;\n payload: CreateEventPayload<Database, Table>;\n }\n | {\n type: \"item-updated\";\n dataset: Table;\n item_id: string;\n payload: UpdateEventPayload<Database, Table>;\n }\n | {\n type: \"item-deleted\";\n dataset: Table;\n item_id: string;\n };\n\ntype CreateEventPayload<Database, Table extends keyof Database> = Omit<Database[Table], \"tombstone\">;\ntype UpdateEventPayload<Database, Table extends keyof Database> = Omit<Partial<Database[Table]>, \"id\" | \"tombstone\">;\n\nexport function createCrdtStorageMutator<Database>({ storage }: { storage: CrdtStorage }) {\n const mapToStorageEvent = (event: CommitEventOptions<Database, keyof Database & string>): OwnCrdtEvent => {\n switch (event.type) {\n case \"item-created\":\n return {\n type: \"item-created\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-updated\":\n return {\n type: \"item-updated\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-deleted\":\n return {\n type: \"item-deleted\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: \"{}\",\n };\n }\n };\n\n const enqueueEvents = (events: CommitEventOptions<Database, keyof Database & string>[]) => {\n storage.enqueueOwnEvents(events.map(mapToStorageEvent));\n };\n\n const createEvent = <Table extends keyof Database & string>(event: CommitEventOptions<Database, Table>) => {\n return event;\n };\n\n const enqueueEvent = (event: CommitEventOptions<Database, keyof Database & string>) => {\n storage.enqueueOwnEvents([mapToStorageEvent(event)]);\n };\n\n return {\n enqueueEvents,\n createEvent,\n enqueueEvent,\n };\n}\n","import type { SQLiteReactiveDb } from \"../memory-db/sqlite-reactive-db\";\nimport type { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { quoteId } from \"../utils\";\nimport type { CrdtStorage } from \"./crdt-storage\";\n\nexport function makeCrdtTable({\n db,\n baseTableName,\n crdtTableName,\n}: {\n db: SQLiteDbWrapper<any>;\n baseTableName: string;\n crdtTableName: string;\n}) {\n const tableSchema = db.dbSchema[baseTableName];\n\n if (!tableSchema) {\n throw new Error(`Table ${baseTableName} not found`);\n }\n\n const columns = new Map(tableSchema.columns.map((c) => [c.name, c]));\n\n const idColumn = columns.get(\"id\");\n if (!idColumn) {\n throw new Error(\n `Table \"${baseTableName}\" is missing a required \"id\" column. CRDT tables must have an \"id\" column to identify items.`,\n );\n }\n if (idColumn.dataType.toUpperCase() !== \"TEXT\") {\n throw new Error(\n `Table \"${baseTableName}\": \"id\" column must be of type TEXT, got \"${idColumn.dataType}\". CRDT item IDs are stored as strings.`,\n );\n }\n\n const tombstoneColumn = columns.get(\"tombstone\");\n if (!tombstoneColumn) {\n throw new Error(\n `Table \"${baseTableName}\" is missing a required \"tombstone\" column. CRDT tables must have a \"tombstone\" INTEGER column for soft deletes.`,\n );\n }\n const tombstoneType = tombstoneColumn.dataType.toUpperCase();\n if (tombstoneType !== \"INTEGER\" && tombstoneType !== \"BOOLEAN\") {\n throw new Error(\n `Table \"${baseTableName}\": \"tombstone\" column must be of type INTEGER or BOOLEAN, got \"${tombstoneColumn.dataType}\". It is compared as 0/1 for soft deletes.`,\n );\n }\n\n db.execute(\n `\ncreate view ${quoteId(crdtTableName)} as\nselect * from ${quoteId(baseTableName)}\nwhere tombstone = 0;`,\n { loggerLevel: \"system\" },\n );\n\n const allColumnNames = tableSchema.columns.map((column) => column.name);\n\n const jsonPayload = (from: \"new\" | \"old\") =>\n `'{'||${allColumnNames.map((col) => `'\"${col}\":'||json_quote(${from}.${quoteId(col)})`).join(\"||','||\")}||'}'`;\n\n db.execute(\n `\ncreate trigger ${quoteId(`${crdtTableName}_created`)}\ninstead of insert on ${quoteId(crdtTableName)}\nfor each row\nbegin\nselect handle_item_created('${baseTableName}', ${jsonPayload(\"new\")});\nend;\n`,\n { loggerLevel: \"system\" },\n );\n\n db.execute(\n `\ncreate trigger ${quoteId(`${crdtTableName}_updated`)}\ninstead of update on ${quoteId(crdtTableName)}\nfor each row\nbegin\nselect handle_item_updated(\n '${baseTableName}',\n ${jsonPayload(\"old\")},\n ${jsonPayload(\"new\")}\n);\nend;\n`,\n { loggerLevel: \"system\" },\n );\n\n db.execute(\n `\ncreate trigger ${quoteId(`${crdtTableName}_deleted`)}\ninstead of delete on ${quoteId(crdtTableName)}\nfor each row\nwhen old.tombstone = 0\nbegin\nselect handle_item_deleted('${baseTableName}', old.id);\nend;\n`,\n { loggerLevel: \"system\" },\n );\n}\n\nexport function registerCrdtFunctions({\n reactiveDb,\n storage,\n}: {\n reactiveDb: SQLiteReactiveDb<any>;\n storage: CrdtStorage;\n}) {\n let eventApplied = false;\n\n reactiveDb.db.createScalarFunction({\n name: \"handle_item_created\",\n deterministic: false,\n directOnly: false,\n innocuous: false,\n callback: (dataset: string, payloadRaw: string) => {\n const payload = JSON.parse(payloadRaw) as { id: string };\n\n storage.applyOwnEvent(\n {\n type: \"item-created\",\n dataset,\n item_id: payload.id,\n payload: payloadRaw,\n },\n {\n wrapInTransaction: false,\n },\n );\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.db.createScalarFunction({\n name: \"handle_item_updated\",\n deterministic: false,\n directOnly: false,\n innocuous: false,\n callback: (dataset: string, oldPayloadRaw: string, newPayloadRaw: string) => {\n if (oldPayloadRaw === newPayloadRaw) {\n return undefined;\n }\n\n const tableSchema = reactiveDb.db.dbSchema[dataset];\n\n if (!tableSchema?.columns) {\n throw new Error(`Schema not found for dataset: ${dataset}`);\n }\n\n const oldPayload = JSON.parse(oldPayloadRaw);\n const newPayload = JSON.parse(newPayloadRaw);\n\n let hasDiff = false;\n const updatePayload = {} as Record<string, unknown>;\n\n for (const column of tableSchema.columns) {\n const oldValue = oldPayload[column.name];\n const newValue = newPayload[column.name];\n if (oldValue === newValue) {\n continue;\n }\n\n if (column.name === \"id\") {\n throw new Error(\n `Cannot update the \"id\" column of an item. It is used to identify the item and must be immutable.`,\n );\n }\n\n hasDiff = true;\n updatePayload[column.name] = newValue;\n }\n\n if (!hasDiff) {\n return;\n }\n\n storage.applyOwnEvent(\n {\n type: \"item-updated\",\n dataset,\n item_id: oldPayload.id,\n payload: JSON.stringify(updatePayload),\n },\n {\n wrapInTransaction: false,\n },\n );\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.db.createScalarFunction({\n name: \"handle_item_deleted\",\n deterministic: false,\n directOnly: false,\n innocuous: false,\n callback: (dataset: string, itemId: string) => {\n storage.applyOwnEvent(\n {\n type: \"item-deleted\",\n dataset,\n item_id: itemId,\n payload: \"{}\",\n },\n {\n wrapInTransaction: false,\n },\n );\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.addEventListener(\"transaction-committed\", () => {\n if (eventApplied) {\n eventApplied = false;\n storage.dispatchEventsApplied();\n }\n });\n\n reactiveDb.addEventListener(\"transaction-rolled-back\", () => {\n eventApplied = false;\n });\n}\n","const dbIdRegex = /^[a-zA-Z][a-zA-Z\\-0-9]{2,63}$/;\n\nexport function validateDbId(dbId: string) {\n if (!dbIdRegex.test(dbId)) {\n throw new Error(\"Invalid dbId. Must be between 3 and 64 characters long and start with a letter.\");\n }\n}\n","import type { SyncedDb } from \"./sync-db\";\n\nconst devtoolsRegistrySymbol = Symbol.for(\"@sqlite-sync/devtools\");\n\nexport type SQLiteSyncDevtoolsInstance = {\n instanceId: string;\n dbId: string;\n createdAt: number;\n instance: SyncedDb<any>;\n};\n\nexport type SQLiteSyncDevtoolsSnapshot = {\n instances: readonly SQLiteSyncDevtoolsInstance[];\n};\n\nexport type SQLiteSyncDevtoolsRegistry = {\n version: 1;\n instances: Map<string, SQLiteSyncDevtoolsInstance>;\n subscribe(listener: () => void): () => void;\n getSnapshot(): SQLiteSyncDevtoolsSnapshot;\n register(instance: SQLiteSyncDevtoolsInstance): () => void;\n};\n\ntype RegistryGlobal = typeof globalThis & {\n [key: symbol]: SQLiteSyncDevtoolsRegistry | undefined;\n};\n\nfunction createSQLiteSyncDevtoolsRegistry(): SQLiteSyncDevtoolsRegistry {\n const instances = new Map<string, SQLiteSyncDevtoolsInstance>();\n const listeners = new Set<() => void>();\n let snapshot: SQLiteSyncDevtoolsSnapshot = {\n instances: [],\n };\n\n const notify = () => {\n for (const listener of listeners) {\n listener();\n }\n };\n\n const updateSnapshot = () => {\n snapshot = {\n instances: Array.from(instances.values()),\n };\n notify();\n };\n\n return {\n version: 1,\n instances,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n getSnapshot() {\n return snapshot;\n },\n register(instance) {\n instances.set(instance.instanceId, instance);\n updateSnapshot();\n\n let isUnregistered = false;\n return () => {\n if (isUnregistered) return;\n isUnregistered = true;\n\n if (!instances.delete(instance.instanceId)) return;\n updateSnapshot();\n };\n },\n };\n}\n\nexport function getOrCreateSQLiteSyncDevtoolsRegistry(): SQLiteSyncDevtoolsRegistry {\n const registryGlobal = globalThis as RegistryGlobal;\n\n registryGlobal[devtoolsRegistrySymbol] ??= createSQLiteSyncDevtoolsRegistry();\n\n return registryGlobal[devtoolsRegistrySymbol];\n}\n","import { xxhash } from \"../hash\";\nimport type { HLCCounter } from \"../hlc\";\nimport type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { applyMemoryDbSchema, type MemoryDbSchema, memoryDbConfig } from \"../migrations/system-schema\";\nimport type { CrdtTableConfig } from \"../sqlite-crdt/crdt-schema\";\nimport { createCrdtStorage } from \"../sqlite-crdt/crdt-storage\";\nimport { makeCrdtTable, registerCrdtFunctions } from \"../sqlite-crdt/make-crdt-table\";\nimport type { StoredValue } from \"../sqlite-crdt/stored-value\";\nimport type { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport type { SQLiteReactiveDb } from \"./sqlite-reactive-db\";\n\ntype MemoryDbOptions<Database> = {\n nodeId: string;\n migrator: SyncDbMigrator;\n reactiveDb: SQLiteReactiveDb<Database>;\n hlcCounter: HLCCounter;\n crdtTables: CrdtTableConfig[];\n initializeSchema?: boolean;\n initialSyncId?: number;\n eventHlcAccumulator?: StoredValue<string>;\n};\n\nexport async function createMemoryDb<Database>({\n nodeId,\n migrator,\n reactiveDb: _reactiveDb,\n hlcCounter,\n crdtTables,\n initializeSchema = true,\n initialSyncId,\n eventHlcAccumulator,\n}: MemoryDbOptions<Database>) {\n await xxhash.ensureLoaded();\n\n const reactiveDb = _reactiveDb as unknown as SQLiteReactiveDb<MemoryDbSchema>;\n const db = reactiveDb.db;\n\n if (initializeSchema) {\n applyMemoryDbSchema(db);\n for (const table of crdtTables) {\n makeCrdtTable({\n db,\n baseTableName: table.baseTableName,\n crdtTableName: table.crdtTableName,\n });\n }\n }\n\n const crdtStorage = createCrdtStorage({\n nodeId,\n initialLocalSyncId: initialSyncId ?? getCurrentSyncId(db),\n hlc: hlcCounter,\n migrator,\n db,\n dbConfig: memoryDbConfig,\n eventHlcAccumulator,\n });\n\n registerCrdtFunctions({\n reactiveDb,\n storage: crdtStorage,\n });\n\n return {\n crdtStorage,\n };\n}\n\nfunction getCurrentSyncId(db: SQLiteDbWrapper<MemoryDbSchema>) {\n return (\n db.execute<{ syncId: number }>(\"SELECT coalesce(max(sync_id), 0) AS syncId FROM persisted_crdt_events\", {\n loggerLevel: \"system\",\n }).rows[0]?.syncId ?? 0\n );\n}\n","import {\n createDeferredPromise,\n createTypedEventTarget,\n type DeferredPromise,\n noop,\n type TypedEventTarget,\n} from \"../utils\";\nimport type {\n AsyncRpc,\n WorkerBroadcastChannels,\n WorkerConfig,\n WorkerErrorResponseMessage,\n WorkerInitMessage,\n WorkerNotificationMessage,\n WorkerRequestMessage,\n WorkerRequestMethod,\n WorkerResponseMessage,\n WorkerRpc,\n WorkerState,\n} from \"./worker-common\";\nimport { isWorkerErrorResponseMessage, isWorkerNotificationMessage, isWorkerResponseMessage } from \"./worker-common\";\n\ntype NotificationEvents = {\n [K in WorkerNotificationMessage[\"notificationType\"]]: Extract<WorkerNotificationMessage, { notificationType: K }>;\n};\n\nexport const createWorkerDbClient = async ({\n broadcastChannels,\n worker,\n config,\n}: {\n broadcastChannels: WorkerBroadcastChannels;\n worker: Worker;\n config: WorkerConfig;\n}) => {\n const eventTarget = createTypedEventTarget<NotificationEvents>();\n const workerRequestsMap = new Map<string, DeferredPromise<unknown>>();\n let isDisposed = false;\n\n const queryWorker = <TMethod extends WorkerRequestMethod>(\n method: TMethod,\n args: Parameters<WorkerRpc[TMethod]>,\n ): Promise<Awaited<ReturnType<WorkerRpc[TMethod]>>> => {\n if (isDisposed) {\n return Promise.reject(new Error(\"Worker client disposed\"));\n }\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<unknown>({\n timeout: 30_000,\n onTimeout: () => workerRequestsMap.delete(requestId),\n });\n workerRequestsMap.set(requestId, promise);\n\n const request: WorkerRequestMessage<TMethod> = {\n type: \"request\",\n requestId,\n method,\n args,\n };\n\n broadcastChannels.requests.postMessage(request);\n\n return promise.promise as Promise<Awaited<ReturnType<WorkerRpc[TMethod]>>>;\n };\n\n const handleWorkerResponse = (message: WorkerResponseMessage) => {\n const promise = workerRequestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n\n promise.resolve(message.data);\n workerRequestsMap.delete(message.requestId);\n };\n\n const handleWorkerError = (message: WorkerErrorResponseMessage) => {\n const promise = workerRequestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n\n promise.reject(new Error(message.error));\n workerRequestsMap.delete(message.requestId);\n };\n\n broadcastChannels.responses.onmessage = (event) => {\n const message = event.data;\n\n if (isWorkerResponseMessage(message)) {\n handleWorkerResponse(message);\n } else if (isWorkerErrorResponseMessage(message)) {\n handleWorkerError(message);\n } else if (isWorkerNotificationMessage(message)) {\n eventTarget.dispatchEvent(message.notificationType, message);\n }\n };\n\n const rpc: AsyncRpc<WorkerRpc> = {\n execute: (query) => queryWorker(\"execute\", [query]),\n getSnapshot: () => queryWorker(\"getSnapshot\", []),\n pushTabEvents: (request) => queryWorker(\"pushTabEvents\", [request]),\n pullEvents: (params) => queryWorker(\"pullEvents\", [params]),\n postState: () => queryWorker(\"postState\", []),\n goOnline: () => queryWorker(\"goOnline\", []),\n goOffline: () => queryWorker(\"goOffline\", []),\n requestReload: (options) => queryWorker(\"requestReload\", [options]),\n };\n\n const statePromise = awaitWorkerState(eventTarget);\n postWorkerConfig(worker, config);\n // On first tab, the worker may not have its BroadcastChannel listener ready yet,\n // so this request can be silently dropped and timeout. The worker will independently\n // post state at the end of init. For subsequent tabs, this triggers the already-running\n // worker to send its current state.\n rpc.postState().catch(noop);\n\n let workerState = await statePromise;\n\n eventTarget.addEventListener(\"state-changed\", (event) => {\n workerState = event.payload.state;\n });\n\n const dispose = () => {\n isDisposed = true;\n broadcastChannels.responses.onmessage = null;\n for (const [id, deferred] of workerRequestsMap) {\n deferred.reject(new Error(\"Worker client disposed\"));\n workerRequestsMap.delete(id);\n }\n };\n\n return {\n ...rpc,\n subscribe: eventTarget.addEventListener,\n getState: () => workerState,\n dispose,\n };\n};\n\nfunction awaitWorkerState(eventTarget: TypedEventTarget<NotificationEvents>) {\n const promise = createDeferredPromise<WorkerState>({ timeout: 15_000 });\n\n const subscription = eventTarget.addEventListener(\"state-changed\", (event) => {\n promise.resolve(event.payload.state);\n subscription.unsubscribe();\n });\n\n return promise.promise;\n}\n\nfunction postWorkerConfig(worker: Worker, config: WorkerConfig) {\n const configMessage: WorkerInitMessage = {\n type: \"init\",\n config,\n };\n worker.postMessage(configMessage);\n}\n","import { validateDbId } from \"./db-id\";\nimport { getOrCreateSQLiteSyncDevtoolsRegistry } from \"./devtools-registry\";\nimport { HLCCounter } from \"./hlc\";\nimport { type Logger, startPerformanceLogger } from \"./logger\";\nimport { createMemoryDb } from \"./memory-db/memory-db\";\nimport { createSQLiteReactiveDb } from \"./memory-db/sqlite-reactive-db\";\nimport type { SyncDbMigrator } from \"./migrations/migrator\";\nimport type { SyncDbSchema } from \"./sqlite-crdt/crdt-schema\";\nimport { createCrdtSyncRemoteSource } from \"./sqlite-crdt/crdt-sync-remote-source\";\nimport { createStoredValue } from \"./sqlite-crdt/stored-value\";\nimport { createDeferredPromise, generateId } from \"./utils\";\nimport { createWorkerDbClient } from \"./worker-db/db-worker-client\";\nimport { createBroadcastChannels, syncDbClientLockName } from \"./worker-db/worker-common\";\n\ntype SyncedDbOptions<Database, Props = undefined> = {\n dbId: string;\n worker: Worker;\n workerProps: Props;\n syncDbSchema: SyncDbSchema<Database>;\n};\n\nconst defaultLogger: Logger = (type, message, level = \"info\") => {\n const logMessage = `[${type}] ${message}`;\n switch (level) {\n case \"info\":\n console.log(logMessage);\n break;\n case \"warning\":\n console.warn(logMessage);\n break;\n case \"error\":\n console.error(logMessage);\n break;\n case \"trace\":\n console.trace(logMessage);\n break;\n }\n};\n\nexport async function createSyncedDb<Database, Props = undefined>(options: SyncedDbOptions<Database, Props>) {\n validateDbId(options.dbId);\n\n const perf = startPerformanceLogger(defaultLogger);\n\n const instanceId = generateId();\n const tabId = generateId();\n\n const broadcastChannels = createBroadcastChannels(options.dbId);\n\n const clientLockAcquired = createDeferredPromise<void>();\n const clientLockRelease = createDeferredPromise<void>();\n navigator.locks.request(`${syncDbClientLockName}-${options.dbId}`, { mode: \"shared\" }, () => {\n clientLockAcquired.resolve();\n return clientLockRelease.promise;\n });\n await clientLockAcquired.promise;\n\n const workerClient = await createWorkerDbClient({\n worker: options.worker,\n config: {\n clientId: generateId(),\n dbId: options.dbId,\n props: options.workerProps as never,\n },\n broadcastChannels,\n });\n\n const hlcCounter = new HLCCounter(tabId, () => Date.now());\n\n const workerClientSnapshot = await workerClient.getSnapshot();\n const reactiveDb = await createSQLiteReactiveDb<Database>({\n snapshot: workerClientSnapshot.file,\n logger: defaultLogger,\n });\n\n const memoryDbMigrator: SyncDbMigrator = {\n currentSchemaVersion: workerClientSnapshot.schemaVersion,\n latestSchemaVersion: workerClientSnapshot.schemaVersion,\n migrateDbToLatest: () => {\n throw new Error(\"Memory DB migrations are not implemented\");\n },\n migrateEvent: (event, targetVersion) => {\n if (event.schema_version === targetVersion) {\n return event;\n }\n throw new Error(\"Memory DB migrations are not implemented\");\n },\n migrateEvents: (events) => events,\n };\n const { crdtStorage } = await createMemoryDb({\n nodeId: tabId,\n migrator: memoryDbMigrator,\n reactiveDb: reactiveDb,\n hlcCounter,\n crdtTables: options.syncDbSchema.tablesConfig,\n });\n\n const pullSyncId = createStoredValue({\n initialValue: workerClientSnapshot.syncId,\n });\n const pushSyncId = createStoredValue({\n initialValue: 0,\n });\n const tabRemoteSource = createCrdtSyncRemoteSource({\n bufferSize: 500,\n pullSyncId,\n pushSyncId,\n storage: crdtStorage,\n nodeId: tabId,\n migrator: memoryDbMigrator,\n remoteFactory: ({ onEventsAvailable }) => {\n const subscription = workerClient.subscribe(\"new-event-chunk-applied\", (event) => {\n onEventsAvailable({ newSyncId: event.payload.newSyncId, remoteEventHlcSum: event.payload.eventHlcSum });\n });\n\n return {\n pullEvents: (request) => workerClient.pullEvents(request),\n pushEvents: (request) => workerClient.pushTabEvents(request),\n disconnect: () => {\n subscription.unsubscribe();\n },\n };\n },\n });\n tabRemoteSource.goOnline();\n\n const reloadRequestedSubscription = workerClient.subscribe(\"reload-requested\", () => {\n globalThis.location?.reload();\n });\n\n perf.logEnd(\"createSyncedDb\", \"initialized\", \"info\");\n\n let isDisposed = false;\n let unregisterDevtools: (() => void) | undefined;\n const dispose = async () => {\n if (isDisposed) return;\n isDisposed = true;\n\n unregisterDevtools?.();\n reloadRequestedSubscription.unsubscribe();\n clientLockRelease.resolve();\n await tabRemoteSource.dispose();\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n workerClient.dispose();\n reactiveDb.dispose();\n };\n\n const syncedDb = {\n db: {\n execute: reactiveDb.db.execute.bind(reactiveDb.db),\n executeKysely: reactiveDb.db.executeKysely.bind(reactiveDb.db),\n executeTransaction: reactiveDb.db.executeTransaction.bind(reactiveDb.db),\n createLiveQuery: reactiveDb.createLiveQuery.bind(reactiveDb),\n },\n state: {\n getState: workerClient.getState.bind(workerClient),\n subscribe: (onChange: () => void) => {\n const { unsubscribe } = workerClient.subscribe(\"state-changed\", onChange);\n return unsubscribe;\n },\n goOnline: workerClient.goOnline.bind(workerClient),\n goOffline: workerClient.goOffline.bind(workerClient),\n },\n /**\n * Ask the elected worker to broadcast a page reload to all tabs for this dbId.\n *\n * With `clean: true` the worker durably records a reset request epoch before\n * broadcasting, so the worker elected on the next startup initializes with\n * `clearOnInit: true` and wipes the persisted DB. Destructive — use as a\n * recovery path when the durable worker DB may be de-synced.\n *\n * Pending in-memory tab events are not preserved, and the returned promise\n * may never settle in the caller — the page typically unloads first.\n */\n requestReload: async (options: { clean: boolean }) => {\n await workerClient.requestReload(options);\n // Primary path: this tab receives the worker's \"reload-requested\" broadcast\n // like every other tab. Fallback in case the broadcast is missed — in the\n // normal case the page is already unloading and this timeout never fires.\n setTimeout(() => globalThis.location?.reload(), 250);\n },\n subscribe: workerClient.subscribe,\n dispose,\n _internal: {\n executeAsync: workerClient.execute.bind(workerClient),\n getMemoryQueryTables: reactiveDb.getTablesUsed.bind(reactiveDb),\n crdtTableNames: options.syncDbSchema.tablesConfig.map((table) => table.crdtTableName),\n crdtTablesConfig: options.syncDbSchema.tablesConfig,\n schemaVersion: workerClientSnapshot.schemaVersion,\n migrationVersions: Object.keys(options.syncDbSchema.migrations)\n .map(Number)\n .sort((a, b) => a - b),\n },\n };\n\n unregisterDevtools = getOrCreateSQLiteSyncDevtoolsRegistry().register({\n instanceId,\n dbId: options.dbId,\n createdAt: Date.now(),\n instance: syncedDb,\n });\n\n return syncedDb;\n}\n\nexport type SyncedDb<Database> = Awaited<ReturnType<typeof createSyncedDb<Database>>>;\n"],"mappings":"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","names":["options"]}
@@ -1,4 +1,4 @@
1
- import { m as Migrations, B as CrdtEventType } from './crdt-sync-remote-source-idoIjMcs.js';
1
+ import { n as Migrations, X as CrdtEventType } from './crdt-sync-remote-source-Da77s4k0.js';
2
2
  import { ColumnType } from 'kysely';
3
3
 
4
4
  type CrdtTableConfig = {
@@ -60,4 +60,22 @@ type ReadonlyTable<Table extends Record<string, unknown>> = {
60
60
  [K in keyof Table]: ColumnType<Table[K], never, never>;
61
61
  };
62
62
 
63
- export { type CreateCrdtSchemaOptions as C, type SyncDbSchema as S, createSyncDbSchema as c };
63
+ type ResetRequest = {
64
+ epoch: string;
65
+ requestedAt: number;
66
+ };
67
+ /** Durable async key-value storage for reset state. Injectable for tests. */
68
+ type ResetStore = {
69
+ get: <T>(key: string) => Promise<T | undefined>;
70
+ set: (key: string, value: unknown) => Promise<void>;
71
+ delete: (key: string) => Promise<void>;
72
+ };
73
+ /**
74
+ * A clean reset is a recovery action for a de-sync detected now. If the reload
75
+ * never happens (broadcast lost, tab crashed mid-reload, browser killed the page),
76
+ * the request must not fire on an arbitrary later cold start and silently wipe
77
+ * local-only writes accumulated since.
78
+ */
79
+ declare const RESET_REQUEST_TTL_MS: number;
80
+
81
+ export { type CrdtTableConfig as C, RESET_REQUEST_TTL_MS as R, type SyncDbSchema as S, type CreateCrdtSchemaOptions as a, type ResetRequest as b, createSyncDbSchema as c, type ResetStore as d };
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a5 as EventsPullResponse, y as EventsPushResponse } from './crdt-sync-remote-source-idoIjMcs.js';
2
- export { Y as jsonSafeParse } from './crdt-sync-remote-source-idoIjMcs.js';
1
+ import { ah as EventsPullResponse, O as EventsPushResponse } from './crdt-sync-remote-source-Da77s4k0.js';
2
+ export { a8 as jsonSafeParse } from './crdt-sync-remote-source-Da77s4k0.js';
3
3
  import { z } from 'zod';
4
4
  import 'kysely';
5
5
  import '@sqlite.org/sqlite-wasm';
@@ -21,6 +21,7 @@ declare const syncServerZodSchema: {
21
21
  type: z.ZodEnum<{
22
22
  "item-created": "item-created";
23
23
  "item-updated": "item-updated";
24
+ "item-deleted": "item-deleted";
24
25
  }>;
25
26
  dataset: z.ZodString;
26
27
  item_id: z.ZodString;
@@ -42,6 +43,7 @@ declare const syncServerZodSchema: {
42
43
  type: z.ZodEnum<{
43
44
  "item-created": "item-created";
44
45
  "item-updated": "item-updated";
46
+ "item-deleted": "item-deleted";
45
47
  }>;
46
48
  dataset: z.ZodString;
47
49
  item_id: z.ZodString;
@@ -60,6 +62,11 @@ type SyncServerMessage = {
60
62
  } | {
61
63
  type: "events-applied";
62
64
  newSyncId: number;
65
+ /**
66
+ * Remote's HLC checksum after applying up to `newSyncId`. Used by clients
67
+ * to detect de-sync when they are caught up.
68
+ */
69
+ eventHlcSum: string | null;
63
70
  };
64
71
  type SyncServerRequest = z.infer<typeof syncServerZodSchema.request>;
65
72
  type ExtractSyncServerRequest<T extends SyncServerRequest["type"]> = Extract<SyncServerRequest, {
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  jsonSafeParse
3
- } from "./chunk-UGF5IU53.js";
3
+ } from "./chunk-NHT3ELMN.js";
4
4
 
5
5
  // src/server/server-common.ts
6
6
  import { z } from "zod";
@@ -18,7 +18,7 @@ var pushEventsZodSchema = z.object({
18
18
  z.object({
19
19
  schema_version: z.number(),
20
20
  timestamp: z.string(),
21
- type: z.enum(["item-created", "item-updated"]),
21
+ type: z.enum(["item-created", "item-updated", "item-deleted"]),
22
22
  dataset: z.string(),
23
23
  item_id: z.string(),
24
24
  payload: z.string()
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/server-common.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { EventsPushResponse } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { EventsPullResponse } from \"../worker-db/worker-common\";\n\nconst pullEventsZodSchema = z.object({\n type: z.literal(\"pull-events\"),\n requestId: z.string(),\n afterSyncId: z.number(),\n excludeNodeId: z.string().optional(),\n});\nconst pushEventsZodSchema = z.object({\n type: z.literal(\"push-events\"),\n requestId: z.string(),\n nodeId: z.string(),\n events: z.array(\n z.object({\n schema_version: z.number(),\n timestamp: z.string(),\n type: z.enum([\"item-created\", \"item-updated\"]),\n dataset: z.string(),\n item_id: z.string(),\n payload: z.string(),\n }),\n ),\n});\n\nexport const syncServerZodSchema = {\n pullEvents: pullEventsZodSchema,\n pushEvents: pushEventsZodSchema,\n request: z.discriminatedUnion(\"type\", [pullEventsZodSchema, pushEventsZodSchema]),\n};\n\nexport type SyncServerMessage =\n | {\n type: \"events-pull-response\";\n requestId: string;\n data: EventsPullResponse;\n }\n | {\n type: \"events-push-response\";\n requestId: string;\n data: EventsPushResponse;\n }\n | {\n type: \"events-applied\";\n newSyncId: number;\n };\n\nexport type SyncServerRequest = z.infer<typeof syncServerZodSchema.request>;\n\nexport type ExtractSyncServerRequest<T extends SyncServerRequest[\"type\"]> = Extract<SyncServerRequest, { type: T }>;\n"],"mappings":";;;;;AAAA,SAAS,SAAS;AAIlB,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,WAAW,EAAE,OAAO;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,eAAe,EAAE,OAAO,EAAE,SAAS;AACrC,CAAC;AACD,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,WAAW,EAAE,OAAO;AAAA,EACpB,QAAQ,EAAE,OAAO;AAAA,EACjB,QAAQ,EAAE;AAAA,IACR,EAAE,OAAO;AAAA,MACP,gBAAgB,EAAE,OAAO;AAAA,MACzB,WAAW,EAAE,OAAO;AAAA,MACpB,MAAM,EAAE,KAAK,CAAC,gBAAgB,cAAc,CAAC;AAAA,MAC7C,SAAS,EAAE,OAAO;AAAA,MAClB,SAAS,EAAE,OAAO;AAAA,MAClB,SAAS,EAAE,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AACF,CAAC;AAEM,IAAM,sBAAsB;AAAA,EACjC,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,SAAS,EAAE,mBAAmB,QAAQ,CAAC,qBAAqB,mBAAmB,CAAC;AAClF;","names":[]}
1
+ {"version":3,"sources":["../src/server/server-common.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { EventsPushResponse } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { EventsPullResponse } from \"../worker-db/worker-common\";\n\nconst pullEventsZodSchema = z.object({\n type: z.literal(\"pull-events\"),\n requestId: z.string(),\n afterSyncId: z.number(),\n excludeNodeId: z.string().optional(),\n});\nconst pushEventsZodSchema = z.object({\n type: z.literal(\"push-events\"),\n requestId: z.string(),\n nodeId: z.string(),\n events: z.array(\n z.object({\n schema_version: z.number(),\n timestamp: z.string(),\n type: z.enum([\"item-created\", \"item-updated\", \"item-deleted\"]),\n dataset: z.string(),\n item_id: z.string(),\n payload: z.string(),\n }),\n ),\n});\n\nexport const syncServerZodSchema = {\n pullEvents: pullEventsZodSchema,\n pushEvents: pushEventsZodSchema,\n request: z.discriminatedUnion(\"type\", [pullEventsZodSchema, pushEventsZodSchema]),\n};\n\nexport type SyncServerMessage =\n | {\n type: \"events-pull-response\";\n requestId: string;\n data: EventsPullResponse;\n }\n | {\n type: \"events-push-response\";\n requestId: string;\n data: EventsPushResponse;\n }\n | {\n type: \"events-applied\";\n newSyncId: number;\n /**\n * Remote's HLC checksum after applying up to `newSyncId`. Used by clients\n * to detect de-sync when they are caught up.\n */\n eventHlcSum: string | null;\n };\n\nexport type SyncServerRequest = z.infer<typeof syncServerZodSchema.request>;\n\nexport type ExtractSyncServerRequest<T extends SyncServerRequest[\"type\"]> = Extract<SyncServerRequest, { type: T }>;\n"],"mappings":";;;;;AAAA,SAAS,SAAS;AAIlB,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,WAAW,EAAE,OAAO;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,eAAe,EAAE,OAAO,EAAE,SAAS;AACrC,CAAC;AACD,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,WAAW,EAAE,OAAO;AAAA,EACpB,QAAQ,EAAE,OAAO;AAAA,EACjB,QAAQ,EAAE;AAAA,IACR,EAAE,OAAO;AAAA,MACP,gBAAgB,EAAE,OAAO;AAAA,MACzB,WAAW,EAAE,OAAO;AAAA,MACpB,MAAM,EAAE,KAAK,CAAC,gBAAgB,gBAAgB,cAAc,CAAC;AAAA,MAC7D,SAAS,EAAE,OAAO;AAAA,MAClB,SAAS,EAAE,OAAO;AAAA,MAClB,SAAS,EAAE,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AACF,CAAC;AAEM,IAAM,sBAAsB;AAAA,EACjC,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,SAAS,EAAE,mBAAmB,QAAQ,CAAC,qBAAqB,mBAAmB,CAAC;AAClF;","names":[]}
package/dist/worker.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { a3 as CreateRemoteSourceFactory, a2 as WorkerConfig, L as Logger } from './crdt-sync-remote-source-idoIjMcs.js';
2
- export { w as EventsPullRequest, x as EventsPushRequest, y as EventsPushResponse, a4 as GetEventsBatch } from './crdt-sync-remote-source-idoIjMcs.js';
3
- import { S as SyncDbSchema } from './crdt-schema-DQ1cYsFE.js';
1
+ import { af as CreateRemoteSourceFactory, ae as WorkerConfig, L as Logger } from './crdt-sync-remote-source-Da77s4k0.js';
2
+ export { J as EventsPullRequest, N as EventsPushRequest, O as EventsPushResponse, ag as GetEventsBatch } from './crdt-sync-remote-source-Da77s4k0.js';
3
+ import { S as SyncDbSchema, d as ResetStore } from './reset-state-0LGwO78x.js';
4
4
  import 'kysely';
5
5
  import '@sqlite.org/sqlite-wasm';
6
6
 
@@ -15,6 +15,14 @@ type WorkerOptions = {
15
15
  logger?: Logger;
16
16
  createRemoteSource?: CreateRemoteSourceFactory;
17
17
  workerConfig?: WorkerConfig;
18
+ /** Durable storage for reset state. Defaults to an IndexedDB-backed store. */
19
+ resetStore?: ResetStore;
20
+ /**
21
+ * App-provided storage version, combined with the library's internal storage
22
+ * version. Bump it when deploying a code change that old persisted local DBs
23
+ * cannot survive — on mismatch the elected worker wipes the local DB on startup.
24
+ */
25
+ storageVersion?: string;
18
26
  };
19
27
  declare function startDbWorker(opts: WorkerOptions): Promise<void>;
20
28