@sqlite-sync/core 0.5.0 → 0.7.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/{chunk-NZDRISFS.js → chunk-LBYL5L4O.js} +372 -35
- package/dist/chunk-LBYL5L4O.js.map +1 -0
- package/dist/{crdt-sync-remote-source-CJrVzW-B.d.ts → crdt-sync-remote-source-BFl7v5_I.d.ts} +135 -16
- package/dist/index.d.ts +70 -5
- package/dist/index.js +230 -74
- package/dist/index.js.map +1 -1
- package/dist/reset-state-D6UslXaj.d.ts +19 -0
- package/dist/server.d.ts +2 -2
- package/dist/worker.d.ts +10 -3
- package/dist/worker.js +12 -2
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-NZDRISFS.js.map +0 -1
- package/dist/reset-state-Comk7IC0.d.ts +0 -81
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/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\ntype LiveQuery<TResult> = {\n getRows: () => TResult[];\n refresh: () => void;\n subscribe: (onchange: () => void) => () => void;\n};\n\nexport type SharedLiveQuery<TResult> = LiveQuery<TResult> & {\n readonly sql: string;\n readonly parameters: readonly unknown[];\n getSubscriberCount: () => number;\n};\n\ntype SharedLiveQueryEntry<TResult> = SharedLiveQuery<TResult> & {\n listeners: Set<() => void>;\n unsubscribeFromLiveQuery: (() => void) | null;\n cleanupTimeout: ReturnType<typeof setTimeout> | null;\n};\n\nexport type SharedLiveQuerySnapshot = {\n sql: string;\n parameters: readonly unknown[];\n subscriberCount: number;\n};\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 private sharedLiveQueries = new Map<string, SharedLiveQueryEntry<unknown>[]>();\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 getSharedLiveQuery<TResult>(query: { sql: string; parameters: readonly unknown[] }) {\n const existingEntry = this.sharedLiveQueries\n .get(query.sql)\n ?.find((entry) => parametersAreEqual(entry.parameters, query.parameters));\n if (existingEntry) {\n if (existingEntry.listeners.size === 0) {\n this.scheduleSharedLiveQueryCleanup(existingEntry);\n }\n return existingEntry as SharedLiveQuery<TResult>;\n }\n\n const liveQuery = this.createLiveQuery<TResult>(query);\n let hasDetachedFromLiveQuery = false;\n const entry: SharedLiveQueryEntry<TResult> = {\n sql: query.sql,\n parameters: query.parameters,\n listeners: new Set(),\n unsubscribeFromLiveQuery: null,\n cleanupTimeout: null,\n getRows: () => liveQuery.getRows(),\n refresh: () => {\n liveQuery.refresh();\n },\n getSubscriberCount: () => entry.listeners.size,\n subscribe: (onchange) => {\n this.cancelSharedLiveQueryCleanup(entry);\n this.registerSharedLiveQueryEntry(entry as SharedLiveQueryEntry<unknown>);\n entry.listeners.add(onchange);\n\n if (!entry.unsubscribeFromLiveQuery) {\n entry.unsubscribeFromLiveQuery = liveQuery.subscribe(() => {\n for (const listener of entry.listeners) {\n listener();\n }\n });\n\n // Rows may have gone stale while no subscriber was listening\n // for table changes.\n if (hasDetachedFromLiveQuery) {\n hasDetachedFromLiveQuery = false;\n liveQuery.refresh();\n }\n }\n\n return () => {\n entry.listeners.delete(onchange);\n\n if (entry.listeners.size === 0) {\n entry.unsubscribeFromLiveQuery?.();\n entry.unsubscribeFromLiveQuery = null;\n hasDetachedFromLiveQuery = true;\n this.scheduleSharedLiveQueryCleanup(entry);\n }\n };\n },\n };\n\n this.registerSharedLiveQueryEntry(entry as SharedLiveQueryEntry<unknown>);\n\n // Evict the entry if no subscriber attaches by the next tick.\n this.scheduleSharedLiveQueryCleanup(entry as SharedLiveQueryEntry<unknown>);\n\n return entry;\n }\n\n private registerSharedLiveQueryEntry(entry: SharedLiveQueryEntry<unknown>) {\n const matchingSqlEntries = this.sharedLiveQueries.get(entry.sql) ?? [];\n if (!matchingSqlEntries.includes(entry)) {\n matchingSqlEntries.push(entry);\n this.sharedLiveQueries.set(entry.sql, matchingSqlEntries);\n }\n }\n\n getSharedLiveQueriesSnapshot(): SharedLiveQuerySnapshot[] {\n const snapshot: SharedLiveQuerySnapshot[] = [];\n for (const entries of this.sharedLiveQueries.values()) {\n for (const entry of entries) {\n snapshot.push({\n sql: entry.sql,\n parameters: entry.parameters,\n subscriberCount: entry.listeners.size,\n });\n }\n }\n return snapshot;\n }\n\n private scheduleSharedLiveQueryCleanup(entry: SharedLiveQueryEntry<unknown>) {\n this.cancelSharedLiveQueryCleanup(entry);\n entry.cleanupTimeout = setTimeout(() => {\n entry.cleanupTimeout = null;\n this.releaseSharedLiveQuery(entry);\n }, 0);\n }\n\n private cancelSharedLiveQueryCleanup(entry: SharedLiveQueryEntry<unknown>) {\n if (entry.cleanupTimeout) {\n clearTimeout(entry.cleanupTimeout);\n entry.cleanupTimeout = null;\n }\n }\n\n private releaseSharedLiveQuery(entry: SharedLiveQueryEntry<unknown>) {\n if (entry.listeners.size > 0 || entry.unsubscribeFromLiveQuery) {\n return;\n }\n\n const matchingSqlEntries = this.sharedLiveQueries.get(entry.sql);\n if (!matchingSqlEntries) {\n return;\n }\n\n const nextEntries = matchingSqlEntries.filter((candidate) => candidate !== entry);\n if (nextEntries.length > 0) {\n this.sharedLiveQueries.set(entry.sql, nextEntries);\n } else {\n this.sharedLiveQueries.delete(entry.sql);\n }\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 for (const entries of this.sharedLiveQueries.values()) {\n for (const entry of entries) {\n this.cancelSharedLiveQueryCleanup(entry);\n entry.unsubscribeFromLiveQuery?.();\n entry.unsubscribeFromLiveQuery = null;\n entry.listeners.clear();\n }\n }\n this.sharedLiveQueries.clear();\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\nexport function parametersAreEqual(a: readonly unknown[] | undefined, b: readonly unknown[] | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) {\n return false;\n }\n }\n return true;\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 CrdtTable]: ReadonlyTable<Table> } & { [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 getSharedLiveQuery: reactiveDb.getSharedLiveQuery.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 getSharedLiveQueriesSnapshot: reactiveDb.getSharedLiveQueriesSnapshot.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":["entry","options"]}
|
|
1
|
+
{"version":3,"sources":["../src/memory-db/sqlite-reactive-db.ts","../src/bound-map.ts","../src/schema/define-sync-schema.ts","../src/schema/table-builder.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\ntype LiveQuery<TResult> = {\n getRows: () => TResult[];\n refresh: () => void;\n subscribe: (onchange: () => void) => () => void;\n};\n\nexport type SharedLiveQuery<TResult> = LiveQuery<TResult> & {\n readonly sql: string;\n readonly parameters: readonly unknown[];\n getSubscriberCount: () => number;\n};\n\ntype SharedLiveQueryEntry<TResult> = SharedLiveQuery<TResult> & {\n listeners: Set<() => void>;\n unsubscribeFromLiveQuery: (() => void) | null;\n cleanupTimeout: ReturnType<typeof setTimeout> | null;\n};\n\nexport type SharedLiveQuerySnapshot = {\n sql: string;\n parameters: readonly unknown[];\n subscriberCount: number;\n};\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 private sharedLiveQueries = new Map<string, SharedLiveQueryEntry<unknown>[]>();\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 getSharedLiveQuery<TResult>(query: { sql: string; parameters: readonly unknown[] }) {\n const existingEntry = this.sharedLiveQueries\n .get(query.sql)\n ?.find((entry) => parametersAreEqual(entry.parameters, query.parameters));\n if (existingEntry) {\n if (existingEntry.listeners.size === 0) {\n this.scheduleSharedLiveQueryCleanup(existingEntry);\n }\n return existingEntry as SharedLiveQuery<TResult>;\n }\n\n const liveQuery = this.createLiveQuery<TResult>(query);\n let hasDetachedFromLiveQuery = false;\n const entry: SharedLiveQueryEntry<TResult> = {\n sql: query.sql,\n parameters: query.parameters,\n listeners: new Set(),\n unsubscribeFromLiveQuery: null,\n cleanupTimeout: null,\n getRows: () => liveQuery.getRows(),\n refresh: () => {\n liveQuery.refresh();\n },\n getSubscriberCount: () => entry.listeners.size,\n subscribe: (onchange) => {\n this.cancelSharedLiveQueryCleanup(entry);\n this.registerSharedLiveQueryEntry(entry as SharedLiveQueryEntry<unknown>);\n entry.listeners.add(onchange);\n\n if (!entry.unsubscribeFromLiveQuery) {\n entry.unsubscribeFromLiveQuery = liveQuery.subscribe(() => {\n for (const listener of entry.listeners) {\n listener();\n }\n });\n\n // Rows may have gone stale while no subscriber was listening\n // for table changes.\n if (hasDetachedFromLiveQuery) {\n hasDetachedFromLiveQuery = false;\n liveQuery.refresh();\n }\n }\n\n return () => {\n entry.listeners.delete(onchange);\n\n if (entry.listeners.size === 0) {\n entry.unsubscribeFromLiveQuery?.();\n entry.unsubscribeFromLiveQuery = null;\n hasDetachedFromLiveQuery = true;\n this.scheduleSharedLiveQueryCleanup(entry);\n }\n };\n },\n };\n\n this.registerSharedLiveQueryEntry(entry as SharedLiveQueryEntry<unknown>);\n\n // Evict the entry if no subscriber attaches by the next tick.\n this.scheduleSharedLiveQueryCleanup(entry as SharedLiveQueryEntry<unknown>);\n\n return entry;\n }\n\n private registerSharedLiveQueryEntry(entry: SharedLiveQueryEntry<unknown>) {\n const matchingSqlEntries = this.sharedLiveQueries.get(entry.sql) ?? [];\n if (!matchingSqlEntries.includes(entry)) {\n matchingSqlEntries.push(entry);\n this.sharedLiveQueries.set(entry.sql, matchingSqlEntries);\n }\n }\n\n getSharedLiveQueriesSnapshot(): SharedLiveQuerySnapshot[] {\n const snapshot: SharedLiveQuerySnapshot[] = [];\n for (const entries of this.sharedLiveQueries.values()) {\n for (const entry of entries) {\n snapshot.push({\n sql: entry.sql,\n parameters: entry.parameters,\n subscriberCount: entry.listeners.size,\n });\n }\n }\n return snapshot;\n }\n\n private scheduleSharedLiveQueryCleanup(entry: SharedLiveQueryEntry<unknown>) {\n this.cancelSharedLiveQueryCleanup(entry);\n entry.cleanupTimeout = setTimeout(() => {\n entry.cleanupTimeout = null;\n this.releaseSharedLiveQuery(entry);\n }, 0);\n }\n\n private cancelSharedLiveQueryCleanup(entry: SharedLiveQueryEntry<unknown>) {\n if (entry.cleanupTimeout) {\n clearTimeout(entry.cleanupTimeout);\n entry.cleanupTimeout = null;\n }\n }\n\n private releaseSharedLiveQuery(entry: SharedLiveQueryEntry<unknown>) {\n if (entry.listeners.size > 0 || entry.unsubscribeFromLiveQuery) {\n return;\n }\n\n const matchingSqlEntries = this.sharedLiveQueries.get(entry.sql);\n if (!matchingSqlEntries) {\n return;\n }\n\n const nextEntries = matchingSqlEntries.filter((candidate) => candidate !== entry);\n if (nextEntries.length > 0) {\n this.sharedLiveQueries.set(entry.sql, nextEntries);\n } else {\n this.sharedLiveQueries.delete(entry.sql);\n }\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 for (const entries of this.sharedLiveQueries.values()) {\n for (const entry of entries) {\n this.cancelSharedLiveQueryCleanup(entry);\n entry.unsubscribeFromLiveQuery?.();\n entry.unsubscribeFromLiveQuery = null;\n entry.listeners.clear();\n }\n }\n this.sharedLiveQueries.clear();\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\nexport function parametersAreEqual(a: readonly unknown[] | undefined, b: readonly unknown[] | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) {\n return false;\n }\n }\n return true;\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 { Migrations } from \"../migrations/migrator\";\nimport type { CrdtTableConfig, ReadonlyTable, SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport type { AnyTableBuilder, InferRow, SyncSchemaTables, TableBuilder, TableColumns } from \"./table-builder\";\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\ntype RowOf<Table extends AnyTableBuilder> =\n Table extends TableBuilder<infer Cols extends TableColumns, any> ? InferRow<Cols> : never;\n\n// Falls back to the \"_\" convention unless ~baseName is a string *literal* — inference can\n// resolve an omitted override to its constraint (string | undefined) rather than undefined.\ntype BaseNameOf<CrdtName, Table extends AnyTableBuilder> = string extends Table[\"~baseName\"]\n ? `_${CrdtName & string}`\n : [Table[\"~baseName\"]] extends [string]\n ? Table[\"~baseName\"]\n : `_${CrdtName & string}`;\n\ntype ClientSchemaOf<Tables extends SyncSchemaTables> = Simplify<\n { [K in keyof Tables]: RowOf<Tables[K]> } & {\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;\n }\n>;\n\ntype ServerSchemaOf<Tables extends SyncSchemaTables> = Simplify<\n { [K in keyof Tables]: ReadonlyTable<RowOf<Tables[K]>> } & {\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;\n }\n>;\n\ntype MutationsSchemaOf<Tables extends SyncSchemaTables> = Simplify<{\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: RowOf<Tables[K]>;\n}>;\n\nexport interface DefinedSyncSchema<Tables extends SyncSchemaTables>\n extends SyncDbSchema<ClientSchemaOf<Tables>, ServerSchemaOf<Tables>, MutationsSchemaOf<Tables>> {\n /** The table builders, for type extraction (`typeof schema.tables.item.$row`) and runtime metadata. */\n tables: Tables;\n}\n\nexport type DefineSyncSchemaConfig<Tables extends SyncSchemaTables> = {\n tables: Tables;\n /** Full migration history (including the initial createTable version), built with `createMigrations`. */\n migrations: Migrations;\n};\n\n/**\n * Define a sync database schema from `t.table()` builders.\n *\n * Record keys are the crdt (view) table names; base table names default to the key\n * prefixed with \"_\", overridable per table via `t.table(cols, { baseName })`.\n */\nexport function defineSyncSchema<Tables extends SyncSchemaTables>({\n tables,\n migrations,\n}: DefineSyncSchemaConfig<Tables>): DefinedSyncSchema<Tables> {\n const tablesConfig: CrdtTableConfig[] = Object.entries(tables).map(([crdtTableName, table]) => ({\n crdtTableName,\n baseTableName: table.baseName ?? `_${crdtTableName}`,\n }));\n\n const seenNames = new Set<string>();\n for (const { crdtTableName, baseTableName } of tablesConfig) {\n for (const name of [crdtTableName, baseTableName]) {\n if (seenNames.has(name)) {\n throw new Error(`Duplicate table name \"${name}\" in sync schema`);\n }\n seenNames.add(name);\n }\n }\n\n return {\n tables,\n get tablesConfig() {\n return tablesConfig;\n },\n get migrations() {\n return migrations;\n },\n get \"~clientSchema\"(): never {\n throw new Error(\"~clientSchema is type-only and cannot be accessed at runtime\");\n },\n get \"~serverSchema\"(): never {\n throw new Error(\"~serverSchema is type-only and cannot be accessed at runtime\");\n },\n get \"~mutationsSchema\"(): never {\n throw new Error(\"~mutationsSchema is type-only and cannot be accessed at runtime\");\n },\n };\n}\n","export type ColumnKind = \"text\" | \"integer\" | \"real\" | \"boolean\" | \"enum\";\n\nexport type SqliteStorageType = \"text\" | \"integer\" | \"real\";\n\nexport type ColumnMeta = {\n kind: ColumnKind;\n /** Storage type used in generated DDL. Booleans store as integer 0/1. */\n sqlType: SqliteStorageType;\n nullable: boolean;\n hasDefault: boolean;\n /** Declared default in JS form (booleans as booleans). */\n defaultValue?: unknown;\n /** Allowed values for `enum` columns. */\n enumValues?: readonly string[];\n description?: string;\n};\n\nexport class ColumnBuilder<Value, HasDefault extends boolean = false> {\n declare readonly \"~types\": { value: Value; hasDefault: HasDefault };\n\n constructor(readonly meta: ColumnMeta) {}\n\n /** Allow SQL NULL; widens the value type with `null`. */\n nullable(): ColumnBuilder<Value | null, HasDefault> {\n return new ColumnBuilder({ ...this.meta, nullable: true });\n }\n\n /** Declare a column default. The value is used both in DDL and when migrating older events. */\n default(value: Value): ColumnBuilder<Value, true> {\n return new ColumnBuilder({ ...this.meta, hasDefault: true, defaultValue: value });\n }\n\n /** Narrow the TypeScript value type without changing storage, e.g. `t.text().$type<\"movie\" | \"tv\">()`. */\n $type<Narrowed extends Value>(): ColumnBuilder<Narrowed, HasDefault> {\n return this as unknown as ColumnBuilder<Narrowed, HasDefault>;\n }\n\n /** Human/AI-readable description, surfaced in generated schema docs. */\n describe(description: string): ColumnBuilder<Value, HasDefault> {\n return new ColumnBuilder({ ...this.meta, description });\n }\n}\n\nexport type AnyColumnBuilder = ColumnBuilder<any, boolean>;\n\nexport type TableColumns = Record<string, AnyColumnBuilder>;\n\nexport type AnyTableBuilder = TableBuilder<any, any>;\n\nexport type SyncSchemaTables = Record<string, AnyTableBuilder>;\n\nexport type TableOptions<BaseName extends string | undefined = string | undefined> = {\n /** Override the materialized base table name (defaults to the crdt table name prefixed with \"_\"). */\n baseName?: BaseName;\n description?: string;\n};\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\ntype ColumnValue<Column> = Column extends ColumnBuilder<infer Value, boolean> ? Value : never;\n\nexport type InferRow<Cols extends TableColumns> = Simplify<\n { id: string } & { [K in keyof Cols]: ColumnValue<Cols[K]> } & { tombstone?: boolean }\n>;\n\nexport type PayloadValidationResult = { success: true } | { success: false; errors: string[] };\n\nconst RESERVED_COLUMNS = [\"id\", \"tombstone\"] as const;\n\nconst idColumnMeta: ColumnMeta = {\n kind: \"text\",\n sqlType: \"text\",\n nullable: false,\n hasDefault: false,\n description: \"Unique immutable item id\",\n};\n\nconst tombstoneColumnMeta: ColumnMeta = {\n kind: \"boolean\",\n sqlType: \"integer\",\n nullable: false,\n hasDefault: true,\n defaultValue: false,\n description: \"Soft-delete marker\",\n};\n\nexport class TableBuilder<Cols extends TableColumns, BaseName extends string | undefined = undefined> {\n declare readonly \"~baseName\": BaseName;\n\n /** All columns in DDL order, including the auto-injected `id` and `tombstone`. */\n readonly columns: Record<string, ColumnMeta>;\n readonly baseName: BaseName;\n readonly description: string | undefined;\n\n constructor(\n readonly userColumns: Cols,\n options?: TableOptions<BaseName>,\n ) {\n for (const reserved of RESERVED_COLUMNS) {\n if (reserved in userColumns) {\n throw new Error(`Column \"${reserved}\" is added automatically to every synced table and cannot be declared`);\n }\n }\n\n this.columns = {\n id: idColumnMeta,\n ...Object.fromEntries(Object.entries(userColumns).map(([name, column]) => [name, column.meta])),\n tombstone: tombstoneColumnMeta,\n };\n this.baseName = options?.baseName as BaseName;\n this.description = options?.description;\n }\n\n describe(description: string): TableBuilder<Cols, BaseName> {\n return new TableBuilder(this.userColumns, { baseName: this.baseName, description });\n }\n\n /** Type-only: the row shape returned by queries. Do not access at runtime. */\n get $row(): InferRow<Cols> {\n return null as never;\n }\n\n /**\n * Validate a CRDT event payload against the declared columns.\n * `item-created` payloads must contain every column without a default (nullable columns may be omitted);\n * `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.\n */\n validatePayload(\n payload: Record<string, unknown>,\n options?: { event?: \"item-created\" | \"item-updated\" },\n ): PayloadValidationResult {\n const event = options?.event ?? \"item-created\";\n const errors: string[] = [];\n\n for (const key of Object.keys(payload)) {\n if (!(key in this.columns)) {\n errors.push(`Unknown column \"${key}\"`);\n }\n }\n\n if (event === \"item-updated\") {\n if (\"id\" in payload) {\n errors.push(`Column \"id\" is immutable and cannot appear in an update payload`);\n }\n if (payload.tombstone === true || payload.tombstone === 1) {\n errors.push(`Column \"tombstone\" cannot be set to true/1 in an update payload; use an item-deleted event`);\n }\n }\n\n if (event === \"item-created\") {\n for (const [name, meta] of Object.entries(this.columns)) {\n if (name in payload || name === \"tombstone\" || meta.nullable || meta.hasDefault) {\n continue;\n }\n errors.push(`Missing required column \"${name}\"`);\n }\n }\n\n for (const [name, value] of Object.entries(payload)) {\n const meta = this.columns[name];\n if (!meta || (event === \"item-updated\" && name === \"id\")) {\n continue;\n }\n const error = validateValue(name, value, meta);\n if (error) {\n errors.push(error);\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n }\n}\n\nfunction describeValue(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n return typeof value;\n}\n\nfunction validateValue(name: string, value: unknown, meta: ColumnMeta): string | null {\n if (value === null) {\n return meta.nullable ? null : `Column \"${name}\" is not nullable, got null`;\n }\n\n switch (meta.kind) {\n case \"text\":\n return typeof value === \"string\" ? null : `Column \"${name}\" expects text, got ${describeValue(value)}`;\n case \"integer\":\n return typeof value === \"number\" && Number.isInteger(value)\n ? null\n : `Column \"${name}\" expects an integer, got ${describeValue(value)}`;\n case \"real\":\n return typeof value === \"number\" && Number.isFinite(value)\n ? null\n : `Column \"${name}\" expects a number, got ${describeValue(value)}`;\n case \"boolean\":\n return typeof value === \"boolean\" || value === 0 || value === 1\n ? null\n : `Column \"${name}\" expects a boolean (true/false or 0/1), got ${describeValue(value)}`;\n case \"enum\": {\n if (typeof value !== \"string\") {\n return `Column \"${name}\" expects one of ${formatEnumValues(meta)}, got ${describeValue(value)}`;\n }\n return meta.enumValues?.includes(value)\n ? null\n : `Column \"${name}\" expects one of ${formatEnumValues(meta)}, got \"${value}\"`;\n }\n }\n}\n\nfunction formatEnumValues(meta: ColumnMeta): string {\n return (meta.enumValues ?? []).map((value) => `\"${value}\"`).join(\" | \");\n}\n\nfunction column<Value>(kind: ColumnKind, sqlType: SqliteStorageType): ColumnBuilder<Value> {\n return new ColumnBuilder<Value>({ kind, sqlType, nullable: false, hasDefault: false });\n}\n\nexport const t = {\n text: () => column<string>(\"text\", \"text\"),\n integer: () => column<number>(\"integer\", \"integer\"),\n real: () => column<number>(\"real\", \"real\"),\n /** Stored as INTEGER 0/1; always bind booleans as 0/1 when writing raw SQL. */\n boolean: () => column<boolean>(\"boolean\", \"integer\"),\n /**\n * Union of string literals stored as TEXT, e.g. `t.enum([\"movie\", \"tv\"])`. Unlike `$type`, the\n * allowed values are kept as runtime metadata, so payloads are validated against them.\n * For an open-ended set, use `t.text().$type<\"idle\" | (string & {})>()` instead.\n */\n enum: <const Values extends readonly [string, ...string[]]>(values: Values) =>\n new ColumnBuilder<Values[number]>({\n kind: \"enum\",\n sqlType: \"text\",\n nullable: false,\n hasDefault: false,\n enumValues: values,\n }),\n table: <Cols extends TableColumns, const BaseName extends string | undefined = undefined>(\n columns: Cols,\n options?: TableOptions<BaseName>,\n ) => new TableBuilder(columns, options),\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 { InternalCrdtStorage } 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: InternalCrdtStorage;\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.internal.applyOwnEventFromTransaction(reactiveDb.db, {\n type: \"item-created\",\n dataset,\n item_id: payload.id,\n payload: payloadRaw,\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.internal.applyOwnEventFromTransaction(reactiveDb.db, {\n type: \"item-updated\",\n dataset,\n item_id: oldPayload.id,\n payload: JSON.stringify(updatePayload),\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.internal.applyOwnEventFromTransaction(reactiveDb.db, {\n type: \"item-deleted\",\n dataset,\n item_id: itemId,\n payload: \"{}\",\n });\n\n eventApplied = true;\n return undefined;\n },\n });\n\n reactiveDb.addEventListener(\"transaction-committed\", () => {\n if (eventApplied) {\n eventApplied = false;\n void storage.internal.processEnqueuedEvents();\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, SyncDbSchema } 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 syncDbSchema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">;\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 syncDbSchema,\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 schema: syncDbSchema,\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 syncDbSchema: options.syncDbSchema,\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 getSharedLiveQuery: reactiveDb.getSharedLiveQuery.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 getSharedLiveQueriesSnapshot: reactiveDb.getSharedLiveQueriesSnapshot.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":["entry","column","column","options"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type ResetRequest = {
|
|
2
|
+
epoch: string;
|
|
3
|
+
requestedAt: number;
|
|
4
|
+
};
|
|
5
|
+
/** Durable async key-value storage for reset state. Injectable for tests. */
|
|
6
|
+
type ResetStore = {
|
|
7
|
+
get: <T>(key: string) => Promise<T | undefined>;
|
|
8
|
+
set: (key: string, value: unknown) => Promise<void>;
|
|
9
|
+
delete: (key: string) => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* A clean reset is a recovery action for a de-sync detected now. If the reload
|
|
13
|
+
* never happens (broadcast lost, tab crashed mid-reload, browser killed the page),
|
|
14
|
+
* the request must not fire on an arbitrary later cold start and silently wipe
|
|
15
|
+
* local-only writes accumulated since.
|
|
16
|
+
*/
|
|
17
|
+
declare const RESET_REQUEST_TTL_MS: number;
|
|
18
|
+
|
|
19
|
+
export { RESET_REQUEST_TTL_MS as R, type ResetRequest as a, type ResetStore as b };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { aB as EventsPullResponse, aa as EventsPushResponse } from './crdt-sync-remote-source-BFl7v5_I.js';
|
|
2
|
+
export { ar as jsonSafeParse } from './crdt-sync-remote-source-BFl7v5_I.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import 'kysely';
|
|
5
5
|
import '@sqlite.org/sqlite-wasm';
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
import {
|
|
1
|
+
import { az as CreateRemoteSourceFactory, ax as WorkerConfig, c as SyncDbSchema, L as Logger } from './crdt-sync-remote-source-BFl7v5_I.js';
|
|
2
|
+
export { a8 as EventsPullRequest, a9 as EventsPushRequest, aa as EventsPushResponse, aA as GetEventsBatch } from './crdt-sync-remote-source-BFl7v5_I.js';
|
|
3
|
+
import { b as ResetStore } from './reset-state-D6UslXaj.js';
|
|
4
4
|
import 'kysely';
|
|
5
5
|
import '@sqlite.org/sqlite-wasm';
|
|
6
6
|
|
|
@@ -25,6 +25,13 @@ type WorkerOptions = {
|
|
|
25
25
|
* cannot survive — on mismatch the elected worker wipes the local DB on startup.
|
|
26
26
|
*/
|
|
27
27
|
storageVersion?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Dev-time drift check: replays the full migration history on a throwaway
|
|
30
|
+
* in-memory database and verifies the result matches the schema declared with
|
|
31
|
+
* `defineSyncSchema`. On mismatch the worker throws and refuses to start.
|
|
32
|
+
* Enable in development only, e.g. `verifySchema: import.meta.env.DEV`.
|
|
33
|
+
*/
|
|
34
|
+
verifySchema?: boolean;
|
|
28
35
|
};
|
|
29
36
|
declare function startDbWorker(opts: WorkerOptions): Promise<void>;
|
|
30
37
|
|
package/dist/worker.js
CHANGED
|
@@ -9,13 +9,15 @@ import {
|
|
|
9
9
|
createMigrator,
|
|
10
10
|
createReloadRequestHandler,
|
|
11
11
|
createResetStateStore,
|
|
12
|
+
formatSchemaVerificationIssues,
|
|
12
13
|
isWorkerInitMessage,
|
|
13
14
|
isWorkerRequestMessage,
|
|
14
15
|
syncDbClientLockName,
|
|
15
16
|
syncDbWorkerLockName,
|
|
17
|
+
verifySyncSchema,
|
|
16
18
|
workerDbConfig,
|
|
17
19
|
xxhash
|
|
18
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-LBYL5L4O.js";
|
|
19
21
|
import {
|
|
20
22
|
createDeferredPromise,
|
|
21
23
|
jsonSafeParse
|
|
@@ -155,6 +157,13 @@ var defaultLogger = (type, message, level = "info") => {
|
|
|
155
157
|
async function createDbWorker(config, opts) {
|
|
156
158
|
const broadcastChannels = createBroadcastChannels(config.dbId);
|
|
157
159
|
const logger = opts.logger ?? defaultLogger;
|
|
160
|
+
if (opts.verifySchema) {
|
|
161
|
+
const issues = await verifySyncSchema(opts.syncDbSchema);
|
|
162
|
+
if (issues.length > 0) {
|
|
163
|
+
throw new Error(formatSchemaVerificationIssues(issues));
|
|
164
|
+
}
|
|
165
|
+
logger("worker", "Schema verification passed: migrations match the declared schema", "info");
|
|
166
|
+
}
|
|
158
167
|
const [sqlite3] = await Promise.all([sqlite3InitModule(), xxhash.ensureLoaded()]);
|
|
159
168
|
const resetStore = opts.resetStore ?? createIdbResetStore();
|
|
160
169
|
const resetState = createResetStateStore({
|
|
@@ -226,7 +235,8 @@ async function createDbWorker(config, opts) {
|
|
|
226
235
|
},
|
|
227
236
|
db,
|
|
228
237
|
dbConfig: workerDbConfig,
|
|
229
|
-
eventHlcAccumulator: kvStore.createStringStoredValue("crdt.consistency.event_hlc_sum.v2", "")
|
|
238
|
+
eventHlcAccumulator: kvStore.createStringStoredValue("crdt.consistency.event_hlc_sum.v2", ""),
|
|
239
|
+
schema: opts.syncDbSchema
|
|
230
240
|
});
|
|
231
241
|
createCrdtSyncProducer({
|
|
232
242
|
storage: crdtStorage,
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/web-socket/ws-remote-source.ts","../src/worker-db/db-worker.ts","../src/worker-db/storage-version.ts"],"sourcesContent":["import type { SyncServerMessage, SyncServerRequest } from \"../server\";\nimport type { CreateRemoteSourceFactory } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport { createDeferredPromise, type DeferredPromise, jsonSafeParse } from \"../utils\";\nimport type { EventsPullRequest, EventsPushRequest, EventsPushResponse, GetEventsBatch } from \"../worker\";\n\ntype WsRemoteSourceConfig = {\n createWebSocket: () => Pick<WebSocket, \"onmessage\" | \"close\" | \"addEventListener\"> & {\n send: (data: string) => void;\n };\n};\n\nexport const createWsRemoteSource = ({ createWebSocket }: WsRemoteSourceConfig): CreateRemoteSourceFactory => {\n return async ({ onEventsAvailable }) => {\n const socket = createWebSocket();\n\n const openPromise = createDeferredPromise<void>({\n timeout: 5000,\n onTimeout: () => {\n socket.close();\n },\n });\n socket.addEventListener(\"open\", () => {\n openPromise.resolve(undefined);\n });\n await openPromise.promise;\n\n const requestsMap = new Map<string, DeferredPromise<unknown>>();\n\n const pushEvents = async (request: EventsPushRequest): Promise<EventsPushResponse> => {\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<EventsPushResponse>({ timeout: 5000 });\n requestsMap.set(requestId, promise as DeferredPromise<unknown>);\n\n const wsRequest: SyncServerRequest = {\n type: \"push-events\",\n requestId,\n nodeId: request.nodeId,\n events: request.events,\n };\n socket.send(JSON.stringify(wsRequest));\n\n return promise.promise;\n };\n\n const pullEvents = async (request: EventsPullRequest): Promise<GetEventsBatch> => {\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<GetEventsBatch>({ timeout: 2000 });\n requestsMap.set(requestId, promise as DeferredPromise<unknown>);\n\n const wsRequest: SyncServerRequest = {\n type: \"pull-events\",\n requestId,\n afterSyncId: request.afterSyncId,\n excludeNodeId: request.excludeNodeId,\n };\n socket.send(JSON.stringify(wsRequest));\n\n return promise.promise;\n };\n\n socket.onmessage = (event) => {\n const result = jsonSafeParse<SyncServerMessage>(event.data);\n\n if (!result.success || !(\"type\" in result.data) || !result.data.type) {\n return;\n }\n\n const message = result.data;\n\n switch (message.type) {\n case \"events-pull-response\": {\n const promise = requestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n promise.resolve(message.data);\n requestsMap.delete(message.requestId);\n break;\n }\n case \"events-push-response\": {\n const promise = requestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n promise.resolve(message.data);\n requestsMap.delete(message.requestId);\n break;\n }\n case \"events-applied\":\n onEventsAvailable({ newSyncId: message.newSyncId, remoteEventHlcSum: message.eventHlcSum });\n break;\n default:\n message satisfies never;\n return;\n }\n };\n\n return {\n pushEvents,\n pullEvents,\n disconnect: () => {\n socket.close();\n },\n };\n };\n};\n","import sqlite3InitModule from \"@sqlite.org/sqlite-wasm\";\nimport { xxhash } from \"../hash\";\nimport type { Logger } from \"../logger\";\nimport { createMigrator, type SyncDbMigrator } from \"../migrations/migrator\";\nimport { applyWorkerDbSchema, type WorkerDbSchema, workerDbConfig } from \"../migrations/system-schema\";\nimport type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport { type CrdtStorage, createCrdtStorage } from \"../sqlite-crdt/crdt-storage\";\nimport { createCrdtSyncProducer } from \"../sqlite-crdt/crdt-sync-producer\";\nimport { type CreateRemoteSourceFactory, createCrdtSyncRemoteSource } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { CrdtEventStatus } from \"../sqlite-crdt/crdt-table-schema\";\nimport { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport type { KvStore } from \"../sqlite-kv-store\";\nimport { createDeferredPromise } from \"../utils\";\nimport { createIdbResetStore, createReloadRequestHandler, createResetStateStore, type ResetStore } from \"./reset-state\";\nimport { createStorageVersionStore } from \"./storage-version\";\nimport {\n createBroadcastChannels,\n isWorkerInitMessage,\n isWorkerRequestMessage,\n syncDbClientLockName,\n syncDbWorkerLockName,\n type WorkerConfig,\n type WorkerErrorResponseMessage,\n type WorkerNotificationMessage,\n type WorkerResponseMessage,\n type WorkerRpc,\n} from \"./worker-common\";\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\nasync function createDbWorker(config: WorkerConfig, opts: WorkerOptions) {\n const broadcastChannels = createBroadcastChannels(config.dbId);\n const logger = opts.logger ?? defaultLogger;\n\n const [sqlite3] = await Promise.all([sqlite3InitModule(), xxhash.ensureLoaded()]);\n\n const resetStore = opts.resetStore ?? createIdbResetStore();\n const resetState = createResetStateStore({\n store: resetStore,\n dbId: config.dbId,\n });\n const storageVersion = createStorageVersionStore({\n store: resetStore,\n dbId: config.dbId,\n appStorageVersion: opts.storageVersion,\n });\n const [pendingReset, isVersionMismatch] = await Promise.all([\n resetState.resolvePendingReset(),\n storageVersion.isVersionMismatch(),\n ]);\n\n if (isVersionMismatch) {\n logger(\"worker\", `Storage version mismatch — resetting local DB to ${storageVersion.currentVersion}`, \"warning\");\n }\n\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: config.dbId,\n directory: `.${config.dbId}`,\n clearOnInit: !!pendingReset || isVersionMismatch,\n initialCapacity: 8,\n });\n\n const db = new SQLiteDbWrapper<WorkerDbSchema>({\n db: () => new pool.OpfsSAHPoolDb(`/${config.dbId}-main.db`),\n logger: logger,\n loggerPrefix: \"worker\",\n sqlite3,\n });\n\n db.execute(\"PRAGMA locking_mode=exclusive\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA journal_mode=WAL\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA synchronous=NORMAL\", { loggerLevel: \"system\" });\n\n db.execute(`ATTACH DATABASE '/${config.dbId}-worker.db' as worker`, { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.locking_mode=exclusive\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.journal_mode=WAL\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.synchronous=NORMAL\", { loggerLevel: \"system\" });\n\n const { kvStore } = applyWorkerDbSchema(db);\n\n const migrator = createMigrator({\n migrations: opts.syncDbSchema.migrations,\n schemaVersion: kvStore.createNumberStoredValue(\"schema-version\", -1),\n updateLogTableName: workerDbConfig.updateLogTable.fullIdentifier,\n });\n migrator.migrateDbToLatest({\n startTransaction: (callback) => {\n db.executeTransaction((tx) =>\n callback({ execute: (sql, parameters, meta) => tx.execute({ sql, parameters }, meta) }),\n );\n },\n });\n db.invalidateDbSchema();\n\n // Record the applied reset epoch / storage version only after the wiped DB\n // initialized successfully, so a failed init can be retried by a later\n // elected worker, while a later election does not wipe again.\n if (pendingReset) {\n await resetState.markResetApplied(pendingReset.epoch);\n }\n if (isVersionMismatch) {\n await storageVersion.markCurrentVersionApplied();\n }\n\n const crdtStorage = createCrdtStorage({\n nodeId: config.clientId,\n initialLocalSyncId: getMaxSyncId(db, \"none\"),\n migrator,\n hlc: {\n getNextHLC() {\n throw new Error(\"Worker DB should not call getNextHLC\");\n },\n mergeHLC() {\n return;\n },\n },\n db,\n dbConfig: workerDbConfig,\n eventHlcAccumulator: kvStore.createStringStoredValue(\"crdt.consistency.event_hlc_sum.v2\", \"\"),\n });\n\n createCrdtSyncProducer({\n storage: crdtStorage,\n broadcastEvents: (chunk) => {\n broadcastChannels.responses.postMessage({\n notificationType: \"new-event-chunk-applied\",\n newSyncId: chunk.newSyncId,\n eventHlcSum: chunk.eventHlcSum,\n });\n },\n });\n\n const remoteSource = createRemoteSource({\n kvStore,\n crdtStorage,\n migrator,\n clientId: config.clientId,\n remoteFactory: opts.createRemoteSource,\n });\n remoteSource.goOnline();\n\n const broadcastNotification = (notification: WorkerNotificationMessage) => {\n broadcastChannels.responses.postMessage(notification);\n };\n\n const postState = () => {\n broadcastNotification({\n notificationType: \"state-changed\",\n state: remoteSource.getState(),\n });\n };\n const stateChangedSubscription = remoteSource.addEventListener(\"state-changed\", () => {\n postState();\n });\n const deSyncDetectedSubscription = remoteSource.addEventListener(\"de-sync-detected\", (event) => {\n broadcastNotification({\n notificationType: \"de-sync-detected\",\n reason: event.payload.reason,\n });\n });\n const remoteSchemaVersionMismatchSubscription = remoteSource.addEventListener(\n \"remote-schema-version-mismatch\",\n (event) => {\n broadcastNotification({\n notificationType: \"remote-schema-version-mismatch\",\n remoteSchemaVersion: event.payload.remoteSchemaVersion,\n localSchemaVersion: event.payload.localSchemaVersion,\n });\n },\n );\n\n const rpcTarget: WorkerRpc = {\n execute: (query) => db.execute(query),\n getSnapshot: () => {\n const appliedSyncId = getMaxSyncId(db, \"pending\");\n db.execute(\"PRAGMA journal_mode=off\", { loggerLevel: \"system\" });\n const file = db.createSnapshot();\n db.execute(\"PRAGMA journal_mode=WAL\", { loggerLevel: \"system\" });\n return {\n file,\n syncId: appliedSyncId,\n schemaVersion: migrator.currentSchemaVersion,\n };\n },\n postState,\n pushTabEvents: (request) => {\n const { beforeSyncId, afterSyncId } = crdtStorage.enqueueLocalEvents(request.events, request.nodeId);\n return {\n ok: true,\n beforeSyncId,\n afterSyncId,\n };\n },\n pullEvents: (request) => {\n return crdtStorage.getEventsBatch({\n afterSyncId: request.afterSyncId,\n status: \"applied\",\n excludeNodeId: request.excludeNodeId ?? \"\",\n limit: 100,\n });\n },\n goOnline: () => remoteSource.goOnline(),\n goOffline: () => remoteSource.goOffline(\"DISCONNECTED\"),\n requestReload: createReloadRequestHandler({\n resetState,\n broadcast: broadcastNotification,\n }),\n };\n\n broadcastChannels.requests.onmessage = (event) => {\n const message = event.data;\n\n if (!isWorkerRequestMessage(message)) {\n return;\n }\n\n const sendError = (error: unknown) => {\n const response: WorkerErrorResponseMessage = {\n type: \"error-response\",\n requestId: message.requestId,\n error: error instanceof Error ? error.message : String(error),\n };\n broadcastChannels.responses.postMessage(response);\n };\n\n try {\n const method = rpcTarget[message.method] as () => ReturnType<WorkerRpc[keyof WorkerRpc]>;\n const data = method.apply(null, message.args as []);\n\n if (data instanceof Promise) {\n data\n .then((result) => {\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data: result,\n };\n broadcastChannels.responses.postMessage(response);\n })\n .catch(sendError);\n } else {\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data,\n };\n broadcastChannels.responses.postMessage(response);\n }\n } catch (error) {\n sendError(error);\n }\n };\n\n rpcTarget.postState();\n\n return async () => {\n stateChangedSubscription.unsubscribe();\n deSyncDetectedSubscription.unsubscribe();\n remoteSchemaVersionMismatchSubscription.unsubscribe();\n await remoteSource.dispose();\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n db.close();\n };\n}\n\ntype InitRemoteOptions = {\n kvStore: KvStore;\n clientId: string;\n crdtStorage: CrdtStorage;\n migrator: SyncDbMigrator;\n remoteFactory?: CreateRemoteSourceFactory;\n};\n\nfunction createRemoteSource({ kvStore, clientId, crdtStorage, migrator, remoteFactory }: InitRemoteOptions) {\n return createCrdtSyncRemoteSource({\n bufferSize: 50,\n pullSyncId: kvStore.createNumberStoredValue(\"pull-sync-id\", -1),\n pushSyncId: kvStore.createNumberStoredValue(\"push-sync-id\", -1),\n nodeId: clientId,\n storage: crdtStorage,\n migrator,\n remoteFactory,\n });\n}\n\nexport async function getWorkerConfig<Props = never>(): Promise<WorkerConfig<Props>> {\n let configSet = false;\n const responsePromise = createDeferredPromise<WorkerConfig>();\n\n self.onmessage = (event: MessageEvent<unknown>) => {\n if (configSet) {\n console.error(\"Worker config already set\");\n return;\n }\n\n const message = event.data;\n if (!isWorkerInitMessage(message)) {\n return;\n }\n\n responsePromise.resolve(message.config);\n configSet = true;\n };\n\n return responsePromise.promise;\n}\n\ntype WorkerOptions = {\n syncDbSchema: SyncDbSchema;\n logger?: Logger;\n createRemoteSource?: CreateRemoteSourceFactory;\n workerConfig?: WorkerConfig;\n /** Durable storage for reset state. Defaults to an IndexedDB-backed store. */\n resetStore?: ResetStore;\n /**\n * App-provided storage version, combined with the library's internal storage\n * version. Bump it when deploying a code change that old persisted local DBs\n * cannot survive — on mismatch the elected worker wipes the local DB on startup.\n */\n storageVersion?: string;\n};\n\nexport async function startDbWorker(opts: WorkerOptions) {\n const config = opts.workerConfig ?? (await getWorkerConfig());\n\n await navigator.locks.request(`${syncDbWorkerLockName}-${config.dbId}`, { mode: \"exclusive\" }, async (lock) => {\n if (!lock) {\n return;\n }\n\n const cleanup = await createDbWorker(config, opts);\n\n const clientLockName = `${syncDbClientLockName}-${config.dbId}`;\n await new Promise<void>((resolve) => {\n const interval = setInterval(async () => {\n const { held } = await navigator.locks.query();\n const hasClients = held?.some((l) => l.name === clientLockName && l.mode === \"shared\");\n if (!hasClients) {\n clearInterval(interval);\n resolve();\n }\n }, 5_000);\n });\n\n await cleanup();\n });\n\n self.close();\n}\n\nfunction getMaxSyncId(db: SQLiteDbWrapper<WorkerDbSchema>, excludingStatus: \"none\" | \"pending\") {\n const [result] = db.executePrepared(\n \"get-max-sync-id\",\n { excludingStatus: excludingStatus as CrdtEventStatus },\n (db, params) =>\n db\n .selectFrom(\"worker.crdt_events\")\n .where(\"status\", \"!=\", params(\"excludingStatus\"))\n .select((eb) => eb.fn.max(\"sync_id\").as(\"sync_id\")),\n { loggerLevel: \"system\" },\n );\n\n return result?.sync_id ?? 0;\n}\n","import type { ResetStore } from \"./reset-state\";\n\n/**\n * Internal persisted-storage format version. Bump when the library changes\n * the worker DB layout in a way old persisted databases cannot survive —\n * every client resets its local DB on the next worker start.\n */\nexport const LIB_STORAGE_VERSION = 1;\n\nconst storageVersionKey = (dbId: string) => `sqlite-sync-storage-version-${dbId}`;\n\nexport function formatStorageVersion(appStorageVersion: string | undefined): string {\n return appStorageVersion === undefined\n ? `lib-v${LIB_STORAGE_VERSION}`\n : `lib-v${LIB_STORAGE_VERSION}:app-${appStorageVersion}`;\n}\n\ntype StorageVersionStoreOptions = {\n store: ResetStore;\n dbId: string;\n /** Dev-provided app storage version, combined with the internal lib version. */\n appStorageVersion?: string;\n};\n\nexport type StorageVersionStore = ReturnType<typeof createStorageVersionStore>;\n\n/**\n * Worker-owned durable storage version. The current version combines the\n * internal lib version with the dev-provided app version; when the stored\n * version does not match (including a missing record), the elected worker\n * initializes with `clearOnInit: true`. Wiping on a missing record is\n * harmless for fresh installs and correctly resets databases persisted\n * before versioning existed.\n */\nexport function createStorageVersionStore({ store, dbId, appStorageVersion }: StorageVersionStoreOptions) {\n const key = storageVersionKey(dbId);\n const currentVersion = formatStorageVersion(appStorageVersion);\n\n return {\n currentVersion,\n async isVersionMismatch(): Promise<boolean> {\n const storedVersion = await store.get<string>(key);\n return storedVersion !== currentVersion;\n },\n /**\n * Record the current version. Must be called only after the worker has\n * successfully initialized with the wiped DB, so a failed init can be\n * retried by a later elected worker.\n */\n async markCurrentVersionApplied(): Promise<void> {\n await store.set(key, currentVersion);\n },\n };\n}\n"],"mappings":"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","names":["db"]}
|
|
1
|
+
{"version":3,"sources":["../src/web-socket/ws-remote-source.ts","../src/worker-db/db-worker.ts","../src/worker-db/storage-version.ts"],"sourcesContent":["import type { SyncServerMessage, SyncServerRequest } from \"../server\";\nimport type { CreateRemoteSourceFactory } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport { createDeferredPromise, type DeferredPromise, jsonSafeParse } from \"../utils\";\nimport type { EventsPullRequest, EventsPushRequest, EventsPushResponse, GetEventsBatch } from \"../worker\";\n\ntype WsRemoteSourceConfig = {\n createWebSocket: () => Pick<WebSocket, \"onmessage\" | \"close\" | \"addEventListener\"> & {\n send: (data: string) => void;\n };\n};\n\nexport const createWsRemoteSource = ({ createWebSocket }: WsRemoteSourceConfig): CreateRemoteSourceFactory => {\n return async ({ onEventsAvailable }) => {\n const socket = createWebSocket();\n\n const openPromise = createDeferredPromise<void>({\n timeout: 5000,\n onTimeout: () => {\n socket.close();\n },\n });\n socket.addEventListener(\"open\", () => {\n openPromise.resolve(undefined);\n });\n await openPromise.promise;\n\n const requestsMap = new Map<string, DeferredPromise<unknown>>();\n\n const pushEvents = async (request: EventsPushRequest): Promise<EventsPushResponse> => {\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<EventsPushResponse>({ timeout: 5000 });\n requestsMap.set(requestId, promise as DeferredPromise<unknown>);\n\n const wsRequest: SyncServerRequest = {\n type: \"push-events\",\n requestId,\n nodeId: request.nodeId,\n events: request.events,\n };\n socket.send(JSON.stringify(wsRequest));\n\n return promise.promise;\n };\n\n const pullEvents = async (request: EventsPullRequest): Promise<GetEventsBatch> => {\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<GetEventsBatch>({ timeout: 2000 });\n requestsMap.set(requestId, promise as DeferredPromise<unknown>);\n\n const wsRequest: SyncServerRequest = {\n type: \"pull-events\",\n requestId,\n afterSyncId: request.afterSyncId,\n excludeNodeId: request.excludeNodeId,\n };\n socket.send(JSON.stringify(wsRequest));\n\n return promise.promise;\n };\n\n socket.onmessage = (event) => {\n const result = jsonSafeParse<SyncServerMessage>(event.data);\n\n if (!result.success || !(\"type\" in result.data) || !result.data.type) {\n return;\n }\n\n const message = result.data;\n\n switch (message.type) {\n case \"events-pull-response\": {\n const promise = requestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n promise.resolve(message.data);\n requestsMap.delete(message.requestId);\n break;\n }\n case \"events-push-response\": {\n const promise = requestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n promise.resolve(message.data);\n requestsMap.delete(message.requestId);\n break;\n }\n case \"events-applied\":\n onEventsAvailable({ newSyncId: message.newSyncId, remoteEventHlcSum: message.eventHlcSum });\n break;\n default:\n message satisfies never;\n return;\n }\n };\n\n return {\n pushEvents,\n pullEvents,\n disconnect: () => {\n socket.close();\n },\n };\n };\n};\n","import sqlite3InitModule from \"@sqlite.org/sqlite-wasm\";\nimport { xxhash } from \"../hash\";\nimport type { Logger } from \"../logger\";\nimport { createMigrator, type SyncDbMigrator } from \"../migrations/migrator\";\nimport { applyWorkerDbSchema, type WorkerDbSchema, workerDbConfig } from \"../migrations/system-schema\";\nimport { formatSchemaVerificationIssues, verifySyncSchema } from \"../schema/verify-sync-schema\";\nimport type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport { type CrdtStorage, createCrdtStorage } from \"../sqlite-crdt/crdt-storage\";\nimport { createCrdtSyncProducer } from \"../sqlite-crdt/crdt-sync-producer\";\nimport { type CreateRemoteSourceFactory, createCrdtSyncRemoteSource } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { CrdtEventStatus } from \"../sqlite-crdt/crdt-table-schema\";\nimport { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport type { KvStore } from \"../sqlite-kv-store\";\nimport { createDeferredPromise } from \"../utils\";\nimport { createIdbResetStore, createReloadRequestHandler, createResetStateStore, type ResetStore } from \"./reset-state\";\nimport { createStorageVersionStore } from \"./storage-version\";\nimport {\n createBroadcastChannels,\n isWorkerInitMessage,\n isWorkerRequestMessage,\n syncDbClientLockName,\n syncDbWorkerLockName,\n type WorkerConfig,\n type WorkerErrorResponseMessage,\n type WorkerNotificationMessage,\n type WorkerResponseMessage,\n type WorkerRpc,\n} from \"./worker-common\";\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\nasync function createDbWorker(config: WorkerConfig, opts: WorkerOptions) {\n const broadcastChannels = createBroadcastChannels(config.dbId);\n const logger = opts.logger ?? defaultLogger;\n\n if (opts.verifySchema) {\n const issues = await verifySyncSchema(opts.syncDbSchema);\n if (issues.length > 0) {\n throw new Error(formatSchemaVerificationIssues(issues));\n }\n logger(\"worker\", \"Schema verification passed: migrations match the declared schema\", \"info\");\n }\n\n const [sqlite3] = await Promise.all([sqlite3InitModule(), xxhash.ensureLoaded()]);\n\n const resetStore = opts.resetStore ?? createIdbResetStore();\n const resetState = createResetStateStore({\n store: resetStore,\n dbId: config.dbId,\n });\n const storageVersion = createStorageVersionStore({\n store: resetStore,\n dbId: config.dbId,\n appStorageVersion: opts.storageVersion,\n });\n const [pendingReset, isVersionMismatch] = await Promise.all([\n resetState.resolvePendingReset(),\n storageVersion.isVersionMismatch(),\n ]);\n\n if (isVersionMismatch) {\n logger(\"worker\", `Storage version mismatch — resetting local DB to ${storageVersion.currentVersion}`, \"warning\");\n }\n\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: config.dbId,\n directory: `.${config.dbId}`,\n clearOnInit: !!pendingReset || isVersionMismatch,\n initialCapacity: 8,\n });\n\n const db = new SQLiteDbWrapper<WorkerDbSchema>({\n db: () => new pool.OpfsSAHPoolDb(`/${config.dbId}-main.db`),\n logger: logger,\n loggerPrefix: \"worker\",\n sqlite3,\n });\n\n db.execute(\"PRAGMA locking_mode=exclusive\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA journal_mode=WAL\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA synchronous=NORMAL\", { loggerLevel: \"system\" });\n\n db.execute(`ATTACH DATABASE '/${config.dbId}-worker.db' as worker`, { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.locking_mode=exclusive\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.journal_mode=WAL\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.synchronous=NORMAL\", { loggerLevel: \"system\" });\n\n const { kvStore } = applyWorkerDbSchema(db);\n\n const migrator = createMigrator({\n migrations: opts.syncDbSchema.migrations,\n schemaVersion: kvStore.createNumberStoredValue(\"schema-version\", -1),\n updateLogTableName: workerDbConfig.updateLogTable.fullIdentifier,\n });\n migrator.migrateDbToLatest({\n startTransaction: (callback) => {\n db.executeTransaction((tx) =>\n callback({ execute: (sql, parameters, meta) => tx.execute({ sql, parameters }, meta) }),\n );\n },\n });\n db.invalidateDbSchema();\n\n // Record the applied reset epoch / storage version only after the wiped DB\n // initialized successfully, so a failed init can be retried by a later\n // elected worker, while a later election does not wipe again.\n if (pendingReset) {\n await resetState.markResetApplied(pendingReset.epoch);\n }\n if (isVersionMismatch) {\n await storageVersion.markCurrentVersionApplied();\n }\n\n const crdtStorage = createCrdtStorage({\n nodeId: config.clientId,\n initialLocalSyncId: getMaxSyncId(db, \"none\"),\n migrator,\n hlc: {\n getNextHLC() {\n throw new Error(\"Worker DB should not call getNextHLC\");\n },\n mergeHLC() {\n return;\n },\n },\n db,\n dbConfig: workerDbConfig,\n eventHlcAccumulator: kvStore.createStringStoredValue(\"crdt.consistency.event_hlc_sum.v2\", \"\"),\n schema: opts.syncDbSchema,\n });\n\n createCrdtSyncProducer({\n storage: crdtStorage,\n broadcastEvents: (chunk) => {\n broadcastChannels.responses.postMessage({\n notificationType: \"new-event-chunk-applied\",\n newSyncId: chunk.newSyncId,\n eventHlcSum: chunk.eventHlcSum,\n });\n },\n });\n\n const remoteSource = createRemoteSource({\n kvStore,\n crdtStorage,\n migrator,\n clientId: config.clientId,\n remoteFactory: opts.createRemoteSource,\n });\n remoteSource.goOnline();\n\n const broadcastNotification = (notification: WorkerNotificationMessage) => {\n broadcastChannels.responses.postMessage(notification);\n };\n\n const postState = () => {\n broadcastNotification({\n notificationType: \"state-changed\",\n state: remoteSource.getState(),\n });\n };\n const stateChangedSubscription = remoteSource.addEventListener(\"state-changed\", () => {\n postState();\n });\n const deSyncDetectedSubscription = remoteSource.addEventListener(\"de-sync-detected\", (event) => {\n broadcastNotification({\n notificationType: \"de-sync-detected\",\n reason: event.payload.reason,\n });\n });\n const remoteSchemaVersionMismatchSubscription = remoteSource.addEventListener(\n \"remote-schema-version-mismatch\",\n (event) => {\n broadcastNotification({\n notificationType: \"remote-schema-version-mismatch\",\n remoteSchemaVersion: event.payload.remoteSchemaVersion,\n localSchemaVersion: event.payload.localSchemaVersion,\n });\n },\n );\n\n const rpcTarget: WorkerRpc = {\n execute: (query) => db.execute(query),\n getSnapshot: () => {\n const appliedSyncId = getMaxSyncId(db, \"pending\");\n db.execute(\"PRAGMA journal_mode=off\", { loggerLevel: \"system\" });\n const file = db.createSnapshot();\n db.execute(\"PRAGMA journal_mode=WAL\", { loggerLevel: \"system\" });\n return {\n file,\n syncId: appliedSyncId,\n schemaVersion: migrator.currentSchemaVersion,\n };\n },\n postState,\n pushTabEvents: (request) => {\n const { beforeSyncId, afterSyncId } = crdtStorage.enqueueLocalEvents(request.events, request.nodeId);\n return {\n ok: true,\n beforeSyncId,\n afterSyncId,\n };\n },\n pullEvents: (request) => {\n return crdtStorage.getEventsBatch({\n afterSyncId: request.afterSyncId,\n status: \"applied\",\n excludeNodeId: request.excludeNodeId ?? \"\",\n limit: 100,\n });\n },\n goOnline: () => remoteSource.goOnline(),\n goOffline: () => remoteSource.goOffline(\"DISCONNECTED\"),\n requestReload: createReloadRequestHandler({\n resetState,\n broadcast: broadcastNotification,\n }),\n };\n\n broadcastChannels.requests.onmessage = (event) => {\n const message = event.data;\n\n if (!isWorkerRequestMessage(message)) {\n return;\n }\n\n const sendError = (error: unknown) => {\n const response: WorkerErrorResponseMessage = {\n type: \"error-response\",\n requestId: message.requestId,\n error: error instanceof Error ? error.message : String(error),\n };\n broadcastChannels.responses.postMessage(response);\n };\n\n try {\n const method = rpcTarget[message.method] as () => ReturnType<WorkerRpc[keyof WorkerRpc]>;\n const data = method.apply(null, message.args as []);\n\n if (data instanceof Promise) {\n data\n .then((result) => {\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data: result,\n };\n broadcastChannels.responses.postMessage(response);\n })\n .catch(sendError);\n } else {\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data,\n };\n broadcastChannels.responses.postMessage(response);\n }\n } catch (error) {\n sendError(error);\n }\n };\n\n rpcTarget.postState();\n\n return async () => {\n stateChangedSubscription.unsubscribe();\n deSyncDetectedSubscription.unsubscribe();\n remoteSchemaVersionMismatchSubscription.unsubscribe();\n await remoteSource.dispose();\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n db.close();\n };\n}\n\ntype InitRemoteOptions = {\n kvStore: KvStore;\n clientId: string;\n crdtStorage: CrdtStorage;\n migrator: SyncDbMigrator;\n remoteFactory?: CreateRemoteSourceFactory;\n};\n\nfunction createRemoteSource({ kvStore, clientId, crdtStorage, migrator, remoteFactory }: InitRemoteOptions) {\n return createCrdtSyncRemoteSource({\n bufferSize: 50,\n pullSyncId: kvStore.createNumberStoredValue(\"pull-sync-id\", -1),\n pushSyncId: kvStore.createNumberStoredValue(\"push-sync-id\", -1),\n nodeId: clientId,\n storage: crdtStorage,\n migrator,\n remoteFactory,\n });\n}\n\nexport async function getWorkerConfig<Props = never>(): Promise<WorkerConfig<Props>> {\n let configSet = false;\n const responsePromise = createDeferredPromise<WorkerConfig>();\n\n self.onmessage = (event: MessageEvent<unknown>) => {\n if (configSet) {\n console.error(\"Worker config already set\");\n return;\n }\n\n const message = event.data;\n if (!isWorkerInitMessage(message)) {\n return;\n }\n\n responsePromise.resolve(message.config);\n configSet = true;\n };\n\n return responsePromise.promise;\n}\n\ntype WorkerOptions = {\n syncDbSchema: SyncDbSchema;\n logger?: Logger;\n createRemoteSource?: CreateRemoteSourceFactory;\n workerConfig?: WorkerConfig;\n /** Durable storage for reset state. Defaults to an IndexedDB-backed store. */\n resetStore?: ResetStore;\n /**\n * App-provided storage version, combined with the library's internal storage\n * version. Bump it when deploying a code change that old persisted local DBs\n * cannot survive — on mismatch the elected worker wipes the local DB on startup.\n */\n storageVersion?: string;\n /**\n * Dev-time drift check: replays the full migration history on a throwaway\n * in-memory database and verifies the result matches the schema declared with\n * `defineSyncSchema`. On mismatch the worker throws and refuses to start.\n * Enable in development only, e.g. `verifySchema: import.meta.env.DEV`.\n */\n verifySchema?: boolean;\n};\n\nexport async function startDbWorker(opts: WorkerOptions) {\n const config = opts.workerConfig ?? (await getWorkerConfig());\n\n await navigator.locks.request(`${syncDbWorkerLockName}-${config.dbId}`, { mode: \"exclusive\" }, async (lock) => {\n if (!lock) {\n return;\n }\n\n const cleanup = await createDbWorker(config, opts);\n\n const clientLockName = `${syncDbClientLockName}-${config.dbId}`;\n await new Promise<void>((resolve) => {\n const interval = setInterval(async () => {\n const { held } = await navigator.locks.query();\n const hasClients = held?.some((l) => l.name === clientLockName && l.mode === \"shared\");\n if (!hasClients) {\n clearInterval(interval);\n resolve();\n }\n }, 5_000);\n });\n\n await cleanup();\n });\n\n self.close();\n}\n\nfunction getMaxSyncId(db: SQLiteDbWrapper<WorkerDbSchema>, excludingStatus: \"none\" | \"pending\") {\n const [result] = db.executePrepared(\n \"get-max-sync-id\",\n { excludingStatus: excludingStatus as CrdtEventStatus },\n (db, params) =>\n db\n .selectFrom(\"worker.crdt_events\")\n .where(\"status\", \"!=\", params(\"excludingStatus\"))\n .select((eb) => eb.fn.max(\"sync_id\").as(\"sync_id\")),\n { loggerLevel: \"system\" },\n );\n\n return result?.sync_id ?? 0;\n}\n","import type { ResetStore } from \"./reset-state\";\n\n/**\n * Internal persisted-storage format version. Bump when the library changes\n * the worker DB layout in a way old persisted databases cannot survive —\n * every client resets its local DB on the next worker start.\n */\nexport const LIB_STORAGE_VERSION = 1;\n\nconst storageVersionKey = (dbId: string) => `sqlite-sync-storage-version-${dbId}`;\n\nexport function formatStorageVersion(appStorageVersion: string | undefined): string {\n return appStorageVersion === undefined\n ? `lib-v${LIB_STORAGE_VERSION}`\n : `lib-v${LIB_STORAGE_VERSION}:app-${appStorageVersion}`;\n}\n\ntype StorageVersionStoreOptions = {\n store: ResetStore;\n dbId: string;\n /** Dev-provided app storage version, combined with the internal lib version. */\n appStorageVersion?: string;\n};\n\nexport type StorageVersionStore = ReturnType<typeof createStorageVersionStore>;\n\n/**\n * Worker-owned durable storage version. The current version combines the\n * internal lib version with the dev-provided app version; when the stored\n * version does not match (including a missing record), the elected worker\n * initializes with `clearOnInit: true`. Wiping on a missing record is\n * harmless for fresh installs and correctly resets databases persisted\n * before versioning existed.\n */\nexport function createStorageVersionStore({ store, dbId, appStorageVersion }: StorageVersionStoreOptions) {\n const key = storageVersionKey(dbId);\n const currentVersion = formatStorageVersion(appStorageVersion);\n\n return {\n currentVersion,\n async isVersionMismatch(): Promise<boolean> {\n const storedVersion = await store.get<string>(key);\n return storedVersion !== currentVersion;\n },\n /**\n * Record the current version. Must be called only after the worker has\n * successfully initialized with the wiped DB, so a failed init can be\n * retried by a later elected worker.\n */\n async markCurrentVersionApplied(): Promise<void> {\n await store.set(key, currentVersion);\n },\n };\n}\n"],"mappings":"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","names":["db"]}
|