@sqlite-sync/core 0.12.3 → 0.13.1

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.
Files changed (44) hide show
  1. package/dist/{reset-state-t0tTGqoh.js → crdt-sync-producer-BY-WobgO.js} +55 -241
  2. package/dist/crdt-sync-producer-BY-WobgO.js.map +1 -0
  3. package/dist/{crdt-sync-remote-source-CPXzmSIS.js → crdt-sync-remote-source-yeTEkOjH.js} +344 -12
  4. package/dist/crdt-sync-remote-source-yeTEkOjH.js.map +1 -0
  5. package/dist/{crdt-table-schema-BRm_WdcW.js → crdt-table-schema-D6Yf61Ex.js} +37 -13
  6. package/dist/crdt-table-schema-D6Yf61Ex.js.map +1 -0
  7. package/dist/{index-BHsKLqcK.d.ts → index-CH4lf3iX.d.ts} +166 -72
  8. package/dist/index.common-C8W9VKSY.js +276 -0
  9. package/dist/index.common-C8W9VKSY.js.map +1 -0
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +20 -677
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.native.d.ts +2 -0
  14. package/dist/index.native.js +9 -0
  15. package/dist/internal.d.ts +1 -1
  16. package/dist/internal.js +2 -2
  17. package/dist/local.d.ts +48 -0
  18. package/dist/local.js +255 -0
  19. package/dist/local.js.map +1 -0
  20. package/dist/{make-crdt-table-C6l0F-o_.js → make-crdt-table-DYCXOYB7.js} +13 -14
  21. package/dist/make-crdt-table-DYCXOYB7.js.map +1 -0
  22. package/dist/reset-state-CPOi_45B.js +220 -0
  23. package/dist/reset-state-CPOi_45B.js.map +1 -0
  24. package/dist/server.d.ts +8 -3
  25. package/dist/server.js +2 -2
  26. package/dist/server.js.map +1 -1
  27. package/dist/sqlite-reactive-db-DLS1xRZp.js +296 -0
  28. package/dist/sqlite-reactive-db-DLS1xRZp.js.map +1 -0
  29. package/dist/synced-db-database-B5M7MrBD.js +81 -0
  30. package/dist/synced-db-database-B5M7MrBD.js.map +1 -0
  31. package/dist/{system-schema-Dvl_BLId.js → system-schema-CXf3FmxN.js} +142 -58
  32. package/dist/system-schema-CXf3FmxN.js.map +1 -0
  33. package/dist/worker.d.ts +2 -10
  34. package/dist/worker.js +11 -177
  35. package/dist/worker.js.map +1 -1
  36. package/dist/ws-remote-source-BbPWrHB6.js +161 -0
  37. package/dist/ws-remote-source-BbPWrHB6.js.map +1 -0
  38. package/dist/ws-remote-source-DFBhDc6Z.d.ts +12 -0
  39. package/package.json +9 -1
  40. package/dist/crdt-sync-remote-source-CPXzmSIS.js.map +0 -1
  41. package/dist/crdt-table-schema-BRm_WdcW.js.map +0 -1
  42. package/dist/make-crdt-table-C6l0F-o_.js.map +0 -1
  43. package/dist/reset-state-t0tTGqoh.js.map +0 -1
  44. package/dist/system-schema-Dvl_BLId.js.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/bound-map.ts","../src/memory-db/sqlite-reactive-db.ts","../src/schema/define-sync-schema.ts","../src/schema/table-builder.ts","../src/sqlite-crdt/crdt-storage-mutator.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":["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 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 private mutationVersion = 0;\n private tableMutationVersions = new Map<string, number>();\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, \"system\");\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 let rowsVersion = 0;\n\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 rowsVersion = this.mutationVersion;\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 // Tables may have changed while nothing was subscribed, e.g. between React's render and commit.\n if (rows && this.mutationVersion !== rowsVersion) {\n 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 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\n return () => {\n entry.listeners.delete(onchange);\n\n if (entry.listeners.size === 0) {\n entry.unsubscribeFromLiveQuery?.();\n entry.unsubscribeFromLiveQuery = null;\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 getTableMutationVersion(table: string) {\n return this.tableMutationVersions.get(table) ?? 0;\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 this.tableMutationVersions.set(table, ++this.mutationVersion);\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.mutationVersion++;\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","import type { Migrations } from \"../migrations/migrator\";\nimport type { CrdtTableConfig, ReadonlyTable, SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport { buildWriteOriginByName } from \"./admit-client-events\";\nimport type {\n AnyTableBuilder,\n InferRow,\n SyncSchemaTables,\n TableBuilder,\n TableColumns,\n WriteOrigin,\n} from \"./table-builder\";\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// Names sqlite-sync uses for its own tables/views, so a user schema can't shadow them.\nconst RESERVED_TABLE_NAMES = new Set<string>([\n \"change_history\",\n \"crdt_events\",\n \"worker.crdt_events\",\n \"persisted_crdt_events\",\n \"crdt_update_log\",\n \"crdt_change_intents\",\n \"worker.kv\",\n]);\n\ntype RowOf<Table extends AnyTableBuilder> =\n Table extends TableBuilder<infer Cols extends TableColumns, any, WriteOrigin> ? 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 {\n [K in keyof Tables]: Tables[K][\"~writeOrigin\"] extends \"server\"\n ? ReadonlyTable<RowOf<Tables[K]>>\n : RowOf<Tables[K]>;\n } & {\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]: 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 /** Write origin keyed by crdt and base table name. */\n writeOriginByName: ReadonlyMap<string, WriteOrigin>;\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 ...(table.exportImport === \"ignore\" ? { exportImport: \"ignore\" } : {}),\n }));\n\n const seenNames = new Set<string>();\n for (const { crdtTableName, baseTableName } of tablesConfig) {\n for (const name of [crdtTableName, baseTableName]) {\n const normalizedName = name.toLowerCase();\n if (RESERVED_TABLE_NAMES.has(normalizedName)) {\n throw new Error(`Table name \"${name}\" is reserved by sqlite-sync and cannot be used in a sync schema`);\n }\n if (seenNames.has(normalizedName)) {\n throw new Error(`Duplicate table name \"${name}\" in sync schema`);\n }\n seenNames.add(normalizedName);\n }\n }\n\n const writeOriginByName = buildWriteOriginByName({ tables, tablesConfig });\n\n return {\n tables,\n get tablesConfig() {\n return tablesConfig;\n },\n get writeOriginByName() {\n return writeOriginByName;\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\n/**\n * What `@sqlite-sync/ai` may do with a table.\n * `read-only`: documented and queryable; mutations rejected.\n * `hidden`: omitted from the schema doc; reads rejected.\n */\nexport type AiAccess = \"read-write\" | \"read-only\" | \"hidden\";\n\n/**\n * Who may author CRDT events for a table.\n * `\"any\"` (default): every replica. `\"server\"`: client pushes drop these events.\n */\nexport type WriteOrigin = \"any\" | \"server\";\n\n/** Whether backup and restore include a table. */\nexport type ExportImportMode = \"include\" | \"ignore\";\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, WriteOrigin>;\n\nexport type SyncSchemaTables = Record<string, AnyTableBuilder>;\n\nexport type TableOptions<\n BaseName extends string | undefined = string | undefined,\n Writes extends WriteOrigin = \"any\",\n> = {\n /** Override the materialized base table name (defaults to the crdt table name prefixed with \"_\"). */\n baseName?: BaseName;\n description?: string;\n /** Defaults to `\"read-write\"`. See {@link AiAccess}. */\n ai?: AiAccess;\n /** Defaults to `\"any\"`. See {@link WriteOrigin}. */\n writes?: Writes;\n /** Defaults to `\"include\"`. Ignored tables are omitted from export and skipped on import. */\n exportImport?: ExportImportMode;\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<\n Cols extends TableColumns,\n BaseName extends string | undefined = undefined,\n Writes extends WriteOrigin = \"any\",\n> {\n declare readonly \"~baseName\": BaseName;\n declare readonly \"~writeOrigin\": Writes;\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 /** Resolved {@link AiAccess}; defaults to `\"read-write\"`. */\n readonly aiAccess: AiAccess;\n /** Resolved {@link WriteOrigin}; defaults to `\"any\"`. */\n readonly writeOrigin: Writes;\n /** Resolved {@link ExportImportMode}; defaults to `\"include\"`. */\n readonly exportImport: ExportImportMode;\n\n constructor(\n readonly userColumns: Cols,\n options?: TableOptions<BaseName, Writes>,\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 this.aiAccess = options?.ai ?? \"read-write\";\n this.writeOrigin = (options?.writes ?? \"any\") as Writes;\n this.exportImport = options?.exportImport ?? \"include\";\n }\n\n describe(description: string): TableBuilder<Cols, BaseName, Writes> {\n return new TableBuilder<Cols, BaseName, Writes>(this.userColumns, {\n baseName: this.baseName,\n description,\n ai: this.aiAccess,\n writes: this.writeOrigin,\n exportImport: this.exportImport,\n });\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 * and their `tombstone` value is ignored, since applying a create always sets it to 0;\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 (!Object.hasOwn(this.columns, key)) {\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 (Object.hasOwn(payload, name) || 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 = Object.hasOwn(this.columns, name) ? this.columns[name] : undefined;\n if (!meta || (event === \"item-updated\" && name === \"id\") || (event === \"item-created\" && name === \"tombstone\")) {\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: <\n Cols extends TableColumns,\n const BaseName extends string | undefined = undefined,\n const Writes extends WriteOrigin = \"any\",\n >(\n columns: Cols,\n options?: TableOptions<BaseName, Writes>,\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 AnyCommitEventOptions<Database> = {\n [Table in keyof Database & string]: CommitEventOptions<Database, Table>;\n}[keyof Database & string];\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 type SnapshotOptions<Database, Table extends keyof Database & string> = {\n dataset: Table;\n id: string;\n patch: UpdateEventPayload<Database, Table>;\n};\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 applyEvents = (events: AnyCommitEventOptions<Database>[]) => {\n storage.applyOwnEvents(events.map(mapToStorageEvent));\n };\n\n const applyEvent = <Table extends keyof Database & string>(event: CommitEventOptions<Database, Table>) => {\n storage.applyOwnEvents([mapToStorageEvent(event)]);\n };\n\n const applySnapshot = <Table extends keyof Database & string>(snapshot: SnapshotOptions<Database, Table>) => {\n storage.applyOwnSnapshot({\n dataset: snapshot.dataset,\n item_id: snapshot.id,\n patch: { ...snapshot.patch },\n });\n };\n\n return {\n applyEvent,\n applyEvents,\n applySnapshot,\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, registerCrdtIntentDrainer } 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 registerCrdtIntentDrainer({\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} 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 request: WorkerRequestMessage<TMethod> = {\n type: \"request\",\n requestId,\n method,\n args,\n };\n\n try {\n broadcastChannels.requests.postMessage(request);\n } catch (error) {\n return Promise.reject(error);\n }\n\n const promise = createDeferredPromise<unknown>({\n timeout: 30_000,\n onTimeout: () => workerRequestsMap.delete(requestId),\n });\n workerRequestsMap.set(requestId, promise);\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 importData: (data, opts) => queryWorker(\"importData\", [data, opts]),\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 dropConnection: () => queryWorker(\"dropConnection\", []),\n sync: () => queryWorker(\"sync\", []),\n requestReload: (options) => queryWorker(\"requestReload\", [options]),\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 try {\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 { state: workerState, storageGeneration } = await awaitWorkerState(eventTarget);\n\n eventTarget.addEventListener(\"state-changed\", (event) => {\n workerState = event.payload.state;\n storageGeneration = event.payload.storageGeneration;\n });\n\n return {\n ...rpc,\n subscribe: eventTarget.addEventListener,\n getState: () => workerState,\n getStorageGeneration: () => storageGeneration,\n dispose,\n };\n } catch (error) {\n dispose();\n throw error;\n }\n};\n\nfunction awaitWorkerState(eventTarget: TypedEventTarget<NotificationEvents>) {\n const promise = createDeferredPromise<NotificationEvents[\"state-changed\"]>({ timeout: 15_000 });\n\n const subscription = eventTarget.addEventListener(\"state-changed\", (event) => {\n promise.resolve(event.payload);\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 type { Compilable, CompiledQuery } from \"kysely\";\nimport { validateDbId } from \"./db-id\";\nimport { getOrCreateSQLiteSyncDevtoolsRegistry } from \"./devtools-registry\";\nimport { createExportData, type ImportDataOptions, type SyncedDbExport } from \"./export-import\";\nimport { HLCCounter } from \"./hlc\";\nimport { type Logger, startPerformanceLogger } from \"./logger\";\nimport { createMemoryDb } from \"./memory-db/memory-db\";\nimport { createSQLiteReactiveDb, type SharedLiveQuery, type SQLiteReactiveDb } 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 type {\n ExecuteParams,\n ExecuteResult,\n KyselyQueryFactory,\n QueryBuilderOutput,\n SQLiteTransactionWrapper,\n} from \"./sqlite-db-wrapper\";\nimport { acquireLock, generateId } from \"./utils\";\nimport { createWorkerDbClient } from \"./worker-db/db-worker-client\";\nimport { acquireReplicaLock } from \"./worker-db/replica-lock\";\nimport { createBroadcastChannels, syncDbClientLockName } from \"./worker-db/worker-common\";\n\nexport type SyncedDbOptions<Database, Props = undefined> = {\n dbId: string;\n worker: Worker;\n syncDbSchema: SyncDbSchema<Database>;\n} & (undefined extends Props ? { workerProps?: Props } : { workerProps: Props });\n\n/** SQL execution that participates in CRDT intent draining. */\nexport type SyncedDbSqlExecutor<Database> = {\n execute<TResult = unknown>(query: ExecuteParams | string | CompiledQuery<TResult>): ExecuteResult<TResult>;\n executeKysely<TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<Database, TQuery, TResult>,\n ): ExecuteResult<TResult>;\n};\n\n/**\n * SQL execution that deliberately bypasses CRDT intent draining.\n *\n * Mutating a synced view through this executor can leave undrained intents and\n * must only be done by sqlite-sync internals or recovery tooling.\n */\nexport type UnsafeSyncedDbSqlExecutor<Database> = SyncedDbSqlExecutor<Database>;\n\nexport type SyncedDbTransaction<Database> = SyncedDbSqlExecutor<Database> & {\n unsafe: UnsafeSyncedDbSqlExecutor<Database>;\n sql<TResult = unknown>(\n templateOrString: TemplateStringsArray | string,\n ...parameters: unknown[]\n ): ExecuteResult<TResult>;\n};\n\nexport type SyncedDbDatabase<Database> = SyncedDbSqlExecutor<Database> & {\n unsafe: UnsafeSyncedDbSqlExecutor<Database>;\n executeTransaction<TResult>(callback: (tx: SyncedDbTransaction<Database>) => TResult): TResult;\n createLiveQuery<TResult>(query: { sql: string; parameters: readonly unknown[] }): {\n getRows: () => TResult[];\n refresh: (parameters?: readonly unknown[]) => void;\n subscribe: (onchange: () => void) => () => void;\n };\n getSharedLiveQuery<TResult>(query: { sql: string; parameters: readonly unknown[] }): SharedLiveQuery<TResult>;\n};\n\nfunction createSqlExecutor<Database>(\n tx: Pick<SQLiteTransactionWrapper<Database>, \"execute\" | \"executeKysely\">,\n unsafe: boolean,\n): SyncedDbSqlExecutor<Database> {\n return {\n execute: (query) => tx.execute(query, unsafe ? { skipAfterMutatingStatement: true } : undefined),\n executeKysely: (factory) => tx.executeKysely(factory, unsafe ? { skipAfterMutatingStatement: true } : undefined),\n };\n}\n\n/** @internal Exported from this module for focused facade tests, but not from the package entry point. */\nexport function createSyncedDbDatabase<Database>(reactiveDb: SQLiteReactiveDb<Database>): SyncedDbDatabase<Database> {\n const safeExecutor = createSqlExecutor(reactiveDb.db, false);\n const unsafeExecutor = createSqlExecutor(reactiveDb.db, true);\n\n return {\n ...safeExecutor,\n unsafe: unsafeExecutor,\n executeTransaction: (callback) =>\n reactiveDb.db.executeTransaction((tx) => {\n const safeTransactionExecutor = createSqlExecutor(tx, false);\n const unsafeTransactionExecutor = createSqlExecutor(tx, true);\n return callback({\n ...safeTransactionExecutor,\n unsafe: unsafeTransactionExecutor,\n sql: tx.sql.bind(tx),\n });\n }),\n createLiveQuery: reactiveDb.createLiveQuery.bind(reactiveDb),\n getSharedLiveQuery: reactiveDb.getSharedLiveQuery.bind(reactiveDb),\n };\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 let releaseClientLock: (() => Promise<void>) | undefined;\n let disposeWorkerClient: (() => void) | undefined;\n let releaseReplicaLock: (() => Promise<void>) | undefined;\n let disposeReactiveDb: (() => void) | undefined;\n let disposeRemoteSource: (() => Promise<void>) | undefined;\n const cleanupCallbacks: (() => void)[] = [];\n let unregisterDevtools: (() => void) | undefined;\n let isDisposed = false;\n const dispose = async () => {\n if (isDisposed) return;\n isDisposed = true;\n\n unregisterDevtools?.();\n for (const cleanup of cleanupCallbacks) {\n cleanup();\n }\n try {\n await disposeRemoteSource?.();\n } finally {\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n disposeWorkerClient?.();\n try {\n disposeReactiveDb?.();\n } finally {\n await Promise.all([releaseClientLock?.(), releaseReplicaLock?.()]);\n }\n }\n };\n\n try {\n releaseClientLock = await acquireLock(`${syncDbClientLockName}-${options.dbId}`, { mode: \"shared\" });\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 disposeWorkerClient = () => workerClient.dispose();\n\n const hlcCounter = new HLCCounter(tabId, () => Date.now());\n\n releaseReplicaLock = await acquireReplicaLock({ dbId: options.dbId });\n const workerClientSnapshot = await workerClient.getSnapshot();\n const reactiveDb = await createSQLiteReactiveDb<Database>({\n snapshot: workerClientSnapshot.file,\n logger: defaultLogger,\n });\n disposeReactiveDb = () => reactiveDb.dispose();\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 disposeRemoteSource = () => tabRemoteSource.dispose();\n tabRemoteSource.goOnline();\n\n const reloadRequestedSubscription = workerClient.subscribe(\"reload-requested\", () => {\n globalThis.location?.reload();\n });\n\n cleanupCallbacks.push(reloadRequestedSubscription.unsubscribe);\n\n // A worker elected after this tab's snapshot may have wiped the database the snapshot came from.\n const reloadIfStorageReplaced = (storageGeneration: string) => {\n if (storageGeneration !== workerClientSnapshot.storageGeneration) {\n storageReplacedSubscription.unsubscribe();\n globalThis.location?.reload();\n }\n };\n const storageReplacedSubscription = workerClient.subscribe(\"state-changed\", (event) => {\n reloadIfStorageReplaced(event.payload.storageGeneration);\n });\n cleanupCallbacks.push(storageReplacedSubscription.unsubscribe);\n reloadIfStorageReplaced(workerClient.getStorageGeneration());\n\n const sync = () => {\n workerClient.sync().catch((error) => console.warn(\"Failed to start sync\", error));\n };\n\n // Mobile browsers suspend background pages, and the socket can reconnect or die unnoticed meanwhile.\n const syncWhenVisible = () => {\n if (globalThis.document?.visibilityState === \"visible\") {\n sync();\n }\n };\n // Browsers can keep a socket open long after the network is gone.\n const dropConnection = () => {\n workerClient.dropConnection().catch((error) => console.warn(\"Failed to drop connection\", error));\n };\n cleanupCallbacks.push(() => {\n globalThis.document?.removeEventListener(\"visibilitychange\", syncWhenVisible);\n globalThis.removeEventListener?.(\"online\", sync);\n globalThis.removeEventListener?.(\"offline\", dropConnection);\n });\n globalThis.document?.addEventListener(\"visibilitychange\", syncWhenVisible);\n globalThis.addEventListener?.(\"online\", sync);\n globalThis.addEventListener?.(\"offline\", dropConnection);\n\n perf.logEnd(\"createSyncedDb\", \"initialized\", \"info\");\n\n const exportData = createExportData({\n reactiveDb,\n tablesConfig: options.syncDbSchema.tablesConfig,\n schemaVersion: workerClientSnapshot.schemaVersion,\n });\n const importData = (data: SyncedDbExport, opts?: ImportDataOptions) => workerClient.importData(data, opts);\n\n const syncedDb = {\n db: createSyncedDbDatabase(reactiveDb),\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 sync,\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 exportData,\n importData,\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 tables: options.syncDbSchema.tables,\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 } catch (error) {\n await dispose();\n throw error;\n }\n}\n\nexport type SyncedDb<Database> = Awaited<ReturnType<typeof createSyncedDb<Database>>>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/memory-db/wasm-reactive-db.ts","../src/memory-db/memory-db.ts","../src/worker-db/db-worker-client.ts","../src/sync-db.ts"],"sourcesContent":["import { createWasmMemoryDriver } from \"../wasm-driver\";\nimport { SQLiteReactiveDb, type SQLiteReactiveDbOptions } from \"./sqlite-reactive-db\";\n\nexport async function createSQLiteReactiveDb<Database>(opts: Omit<SQLiteReactiveDbOptions, \"driver\">) {\n const driver = await createWasmMemoryDriver();\n return SQLiteReactiveDb.create<Database>({ ...opts, driver });\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, registerCrdtIntentDrainer } from \"../sqlite-crdt/make-crdt-table\";\nimport type { StateHashStore } from \"../sqlite-crdt/state-hash\";\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 stateHash?: StateHashStore;\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 stateHash,\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 stateHash,\n schema: syncDbSchema,\n });\n\n registerCrdtIntentDrainer({\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 WorkerNotificationEvents,\n WorkerRequestMessage,\n WorkerRequestMethod,\n WorkerResponseMessage,\n WorkerRpc,\n} from \"./worker-common\";\nimport { isWorkerErrorResponseMessage, isWorkerNotificationMessage, isWorkerResponseMessage } from \"./worker-common\";\n\ntype NotificationEvents = WorkerNotificationEvents;\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 request: WorkerRequestMessage<TMethod> = {\n type: \"request\",\n requestId,\n method,\n args,\n };\n\n try {\n broadcastChannels.requests.postMessage(request);\n } catch (error) {\n return Promise.reject(error);\n }\n\n const promise = createDeferredPromise<unknown>({\n timeout: 30_000,\n onTimeout: () => workerRequestsMap.delete(requestId),\n });\n workerRequestsMap.set(requestId, promise);\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 importData: (data, opts) => queryWorker(\"importData\", [data, opts]),\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 dropConnection: () => queryWorker(\"dropConnection\", []),\n sync: () => queryWorker(\"sync\", []),\n requestReload: (options) => queryWorker(\"requestReload\", [options]),\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 try {\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 { state: workerState, storageGeneration } = await awaitWorkerState(eventTarget);\n\n eventTarget.addEventListener(\"state-changed\", (event) => {\n workerState = event.payload.state;\n storageGeneration = event.payload.storageGeneration;\n });\n\n return {\n ...rpc,\n subscribe: eventTarget.addEventListener,\n getState: () => workerState,\n getStorageGeneration: () => storageGeneration,\n dispose,\n };\n } catch (error) {\n dispose();\n throw error;\n }\n};\n\nfunction awaitWorkerState(eventTarget: TypedEventTarget<NotificationEvents>) {\n const promise = createDeferredPromise<NotificationEvents[\"state-changed\"]>({ timeout: 15_000 });\n\n const subscription = eventTarget.addEventListener(\"state-changed\", (event) => {\n promise.resolve(event.payload);\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 { createExportData, type ImportDataOptions, type SyncedDbExport } from \"./export-import\";\nimport { HLCCounter } from \"./hlc\";\nimport { type Logger, startPerformanceLogger } from \"./logger\";\nimport { createMemoryDb } from \"./memory-db/memory-db\";\nimport { createSQLiteReactiveDb } from \"./memory-db/wasm-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 { createSyncedDbDatabase } from \"./synced-db-database\";\nimport { acquireLock, generateId } from \"./utils\";\nimport { createWorkerDbClient } from \"./worker-db/db-worker-client\";\nimport { acquireReplicaLock } from \"./worker-db/replica-lock\";\nimport { createBroadcastChannels, syncDbClientLockName } from \"./worker-db/worker-common\";\n\nexport {\n createSyncedDbDatabase,\n type SyncedDbDatabase,\n type SyncedDbSqlExecutor,\n type SyncedDbTransaction,\n type UnsafeSyncedDbSqlExecutor,\n} from \"./synced-db-database\";\n\nexport type SyncedDbOptions<Database, Props = undefined> = {\n dbId: string;\n worker: Worker;\n syncDbSchema: SyncDbSchema<Database>;\n} & (undefined extends Props ? { workerProps?: Props } : { workerProps: Props });\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 let releaseClientLock: (() => Promise<void>) | undefined;\n let disposeWorkerClient: (() => void) | undefined;\n let releaseReplicaLock: (() => Promise<void>) | undefined;\n let disposeReactiveDb: (() => void) | undefined;\n let disposeRemoteSource: (() => Promise<void>) | undefined;\n const cleanupCallbacks: (() => void)[] = [];\n let unregisterDevtools: (() => void) | undefined;\n let isDisposed = false;\n const dispose = async () => {\n if (isDisposed) return;\n isDisposed = true;\n\n unregisterDevtools?.();\n for (const cleanup of cleanupCallbacks) {\n cleanup();\n }\n try {\n await disposeRemoteSource?.();\n } finally {\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n disposeWorkerClient?.();\n try {\n disposeReactiveDb?.();\n } finally {\n await Promise.all([releaseClientLock?.(), releaseReplicaLock?.()]);\n }\n }\n };\n\n try {\n releaseClientLock = await acquireLock(`${syncDbClientLockName}-${options.dbId}`, { mode: \"shared\" });\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 disposeWorkerClient = () => workerClient.dispose();\n\n const hlcCounter = new HLCCounter(tabId, () => Date.now());\n\n releaseReplicaLock = await acquireReplicaLock({ dbId: options.dbId });\n const workerClientSnapshot = await workerClient.getSnapshot();\n const reactiveDb = await createSQLiteReactiveDb<Database>({\n snapshot: workerClientSnapshot.file,\n logger: defaultLogger,\n });\n disposeReactiveDb = () => reactiveDb.dispose();\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, remoteStateHash: event.payload.stateHash ?? null });\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 disposeRemoteSource = () => tabRemoteSource.dispose();\n tabRemoteSource.goOnline();\n\n const reloadRequestedSubscription = workerClient.subscribe(\"reload-requested\", () => {\n globalThis.location?.reload();\n });\n\n cleanupCallbacks.push(reloadRequestedSubscription.unsubscribe);\n\n // A worker elected after this tab's snapshot may have wiped the database the snapshot came from.\n const reloadIfStorageReplaced = (storageGeneration: string) => {\n if (storageGeneration !== workerClientSnapshot.storageGeneration) {\n storageReplacedSubscription.unsubscribe();\n globalThis.location?.reload();\n }\n };\n const storageReplacedSubscription = workerClient.subscribe(\"state-changed\", (event) => {\n reloadIfStorageReplaced(event.payload.storageGeneration);\n });\n cleanupCallbacks.push(storageReplacedSubscription.unsubscribe);\n reloadIfStorageReplaced(workerClient.getStorageGeneration());\n\n const sync = () => {\n workerClient.sync().catch((error) => console.warn(\"Failed to start sync\", error));\n };\n\n // Mobile browsers suspend background pages, and the socket can reconnect or die unnoticed meanwhile.\n const syncWhenVisible = () => {\n if (globalThis.document?.visibilityState === \"visible\") {\n sync();\n }\n };\n // Browsers can keep a socket open long after the network is gone.\n const dropConnection = () => {\n workerClient.dropConnection().catch((error) => console.warn(\"Failed to drop connection\", error));\n };\n cleanupCallbacks.push(() => {\n globalThis.document?.removeEventListener(\"visibilitychange\", syncWhenVisible);\n globalThis.removeEventListener?.(\"online\", sync);\n globalThis.removeEventListener?.(\"offline\", dropConnection);\n });\n globalThis.document?.addEventListener(\"visibilitychange\", syncWhenVisible);\n globalThis.addEventListener?.(\"online\", sync);\n globalThis.addEventListener?.(\"offline\", dropConnection);\n\n perf.logEnd(\"createSyncedDb\", \"initialized\", \"info\");\n\n const exportData = createExportData({\n reactiveDb,\n tablesConfig: options.syncDbSchema.tablesConfig,\n schemaVersion: workerClientSnapshot.schemaVersion,\n });\n const importData = (data: SyncedDbExport, opts?: ImportDataOptions) => workerClient.importData(data, opts);\n\n const syncedDb = {\n db: createSyncedDbDatabase(reactiveDb),\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 sync,\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 exportData,\n importData,\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 tables: options.syncDbSchema.tables,\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 } catch (error) {\n await dispose();\n throw error;\n }\n}\n\nexport type SyncedDb<Database> = Awaited<ReturnType<typeof createSyncedDb<Database>>>;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { $ as parametersAreEqual, $t as ColumnMetadata, A as WorkerState, An as SyncDbMigrator, At as tryCatch, B as verifySyncSchemaWithDriver, Bn as CrdtUpdateLogPayload, Bt as PreparedStatement, C as EventsPullRequest, Cn as TableColumns, Ct as TypedEvent, Dn as MigratableEvent, Dt as generateId, E as createCrdtSyncRemoteSource, En as t, Et as createTypedEventTarget, F as PendingCrdtEvent, Fn as CRDT_EVENT_NO_OP_PAYLOAD, Ft as ExecuteParams, G as DefinedSyncSchema, Gt as SqliteDriver, H as NewCrdtEventValidationResult, Hn as isNoOpCrdtEventPayload, Ht as SQLiteDbWrapper, I as createCrdtApplyFunction, In as CrdtEventOrigin, It as ExecuteResult, Jt as SqliteDriverStatement, K as defineSyncSchema, Kt as SqliteDriverHooks, L as createSQLiteCrdtApplyFunction, Ln as CrdtEventStatus, M as CrdtStorageMutator, Mn as createMigrator, Mt as KvStoreItem, N as SnapshotOptions, Nn as StoredValue, Nt as createKvStoreTableQuery, O as WorkerConfig, On as Migrations, Ot as jsonSafeParse, P as createCrdtStorageMutator, Pn as createStoredValue, Pt as createSQLiteKvStore, Q as SharedLiveQuerySnapshot, Qt as startPerformanceLogger, R as SchemaVerificationIssue, Rn as CrdtEventType, Rt as KyselyQueryFactory, Sn as TableBuilder, St as TypedBroadcastChannel, T as EventsPushResponse, Tn as WriteOrigin, Tt as createDeferredPromise, U as validateNewCrdtEvent, Un as dummyKysely, Ut as SQLiteTransactionWrapper, V as CrdtEventValidationError, Vn as PersistedCrdtEvent, Vt as QueryBuilderOutput, W as DefineSyncSchemaConfig, X as SQLiteReactiveDb, Xt as LogLevel, Y as admitClientEvents, Yt as SqliteDriverValue, Z as SharedLiveQuery, Zt as Logger, _n as ExportImportMode, an as compareHLC, at as createImportData, b as makeCrdtTable, bn as SqliteStorageType, bt as DeferredPromise, c as ResetStore, cn as CrdtTableConfig, dn as AiAccess, dt as createCrdtStorage, en as DatabaseIntrospection, et as xxhash, f as SyncedDbDatabase, fn as AnyColumnBuilder, ft as SystemDbConfig, gn as ColumnMeta, gt as applyWorkerDbSchema, h as UnsafeSyncedDbSqlExecutor, hn as ColumnKind, ht as applyMemoryDbSchema, in as HLCCounter, it as createExportData, j as createCrdtSyncProducer, jn as createMigrations, jt as tryCatchAsync, k as WorkerNotificationMessage, kn as MigrationsDb, kt as quoteId, l as SyncedDb, ln as ReadonlyTable, lt as OwnCrdtEvent, m as SyncedDbTransaction, mn as ColumnBuilder, mt as SystemMigrationContext, nn as introspectDb, nt as ImportDataResult, on as deserializeHLC, p as SyncedDbSqlExecutor, pn as AnyTableBuilder, pt as SystemMigration, q as AdmitClientEventsResult, qt as SqliteDriverRows, rn as HLC, rt as SyncedDbExport, s as ResetRequest, sn as serializeHLC, st as CrdtStorage, tn as TableMetadata, tt as ImportDataOptions, u as SyncedDbOptions, un as SyncDbSchema, ut as OwnCrdtSnapshot, vn as InferRow, w as EventsPushRequest, wn as TableOptions, wt as TypedEventTarget, x as CrdtSyncRemoteSource, xn as SyncSchemaTables, xt as DistributiveOmit, yn as PayloadValidationResult, z as formatSchemaVerificationIssues, zn as CrdtUpdateLogItem } from "./index-CH4lf3iX.js";
2
+ export { type AdmitClientEventsResult, type AiAccess, type AnyColumnBuilder, type AnyTableBuilder, CRDT_EVENT_NO_OP_PAYLOAD, ColumnBuilder, type ColumnKind, type ColumnMeta, type ColumnMetadata, type CrdtEventOrigin, type CrdtEventStatus, type CrdtEventType, CrdtEventValidationError, type CrdtStorage, type CrdtStorageMutator, type CrdtSyncRemoteSource, type CrdtTableConfig, type CrdtUpdateLogItem, type CrdtUpdateLogPayload, type DatabaseIntrospection, type DeferredPromise, type DefineSyncSchemaConfig, type DefinedSyncSchema, type DistributiveOmit, type EventsPullRequest, type EventsPushRequest, type EventsPushResponse, type ExecuteParams, type ExecuteResult, type ExportImportMode, type HLC, HLCCounter, type ImportDataOptions, type ImportDataResult, type InferRow, type KvStoreItem, type KyselyQueryFactory, type LogLevel, type Logger, type MigratableEvent, type Migrations, type MigrationsDb, type NewCrdtEventValidationResult, type OwnCrdtEvent, type OwnCrdtSnapshot, type PayloadValidationResult, type PendingCrdtEvent, type PersistedCrdtEvent, type PreparedStatement, type QueryBuilderOutput, type ReadonlyTable, type ResetRequest, type ResetStore, SQLiteDbWrapper, SQLiteReactiveDb, type SQLiteTransactionWrapper, type SchemaVerificationIssue, type SharedLiveQuery, type SharedLiveQuerySnapshot, type SnapshotOptions, type SqliteDriver, type SqliteDriverHooks, type SqliteDriverRows, type SqliteDriverStatement, type SqliteDriverValue, type SqliteStorageType, type StoredValue, type SyncDbMigrator, type SyncDbSchema, type SyncSchemaTables, type SyncedDb, type SyncedDbDatabase, type SyncedDbExport, type SyncedDbOptions, type SyncedDbSqlExecutor, type SyncedDbTransaction, type SystemDbConfig, type SystemMigration, type SystemMigrationContext, TableBuilder, type TableColumns, type TableMetadata, type TableOptions, TypedBroadcastChannel, TypedEvent, type TypedEventTarget, type UnsafeSyncedDbSqlExecutor, type WorkerConfig, type WorkerNotificationMessage, type WorkerState, type WriteOrigin, admitClientEvents, applyMemoryDbSchema, applyWorkerDbSchema, compareHLC, createCrdtApplyFunction, createCrdtStorage, createCrdtStorageMutator, createCrdtSyncProducer, createCrdtSyncRemoteSource, createDeferredPromise, createExportData, createImportData, createKvStoreTableQuery, createMigrations, createMigrator, createSQLiteCrdtApplyFunction, createSQLiteKvStore, createStoredValue, createTypedEventTarget, defineSyncSchema, deserializeHLC, dummyKysely, formatSchemaVerificationIssues, generateId, introspectDb, isNoOpCrdtEventPayload, jsonSafeParse, makeCrdtTable, parametersAreEqual, quoteId, serializeHLC, startPerformanceLogger, t, tryCatch, tryCatchAsync, validateNewCrdtEvent, verifySyncSchemaWithDriver, xxhash };
@@ -0,0 +1,9 @@
1
+ import { b as dummyKysely, f as createKvStoreTableQuery, h as SQLiteDbWrapper, i as applyWorkerDbSchema, m as createStoredValue, p as createSQLiteKvStore, r as applyMemoryDbSchema, v as startPerformanceLogger, y as introspectDb } from "./system-schema-CXf3FmxN.js";
2
+ import { h as tryCatchAsync, i as TypedEvent, l as generateId, m as tryCatch, n as isNoOpCrdtEventPayload, o as createDeferredPromise, p as quoteId, r as TypedBroadcastChannel, s as createTypedEventTarget, t as CRDT_EVENT_NO_OP_PAYLOAD, u as jsonSafeParse } from "./crdt-table-schema-D6Yf61Ex.js";
3
+ import { a as formatSchemaVerificationIssues, c as createMigrator, i as createSQLiteCrdtApplyFunction, l as createExportData, n as createCrdtStorage, o as verifySyncSchemaWithDriver, r as createCrdtApplyFunction, s as createMigrations, t as createCrdtSyncProducer, u as createImportData } from "./crdt-sync-producer-BY-WobgO.js";
4
+ import { c as deserializeHLC, i as validateNewCrdtEvent, l as serializeHLC, o as HLCCounter, r as CrdtEventValidationError, s as compareHLC, t as createCrdtSyncRemoteSource, u as xxhash } from "./crdt-sync-remote-source-yeTEkOjH.js";
5
+ import { n as parametersAreEqual, t as SQLiteReactiveDb } from "./sqlite-reactive-db-DLS1xRZp.js";
6
+ import { t as admitClientEvents } from "./admit-client-events-BDrJw4SK.js";
7
+ import { a as defineSyncSchema, i as t, n as ColumnBuilder, r as TableBuilder, t as createCrdtStorageMutator } from "./index.common-C8W9VKSY.js";
8
+ import { a as makeCrdtTable } from "./make-crdt-table-DYCXOYB7.js";
9
+ export { CRDT_EVENT_NO_OP_PAYLOAD, ColumnBuilder, CrdtEventValidationError, HLCCounter, SQLiteDbWrapper, SQLiteReactiveDb, TableBuilder, TypedBroadcastChannel, TypedEvent, admitClientEvents, applyMemoryDbSchema, applyWorkerDbSchema, compareHLC, createCrdtApplyFunction, createCrdtStorage, createCrdtStorageMutator, createCrdtSyncProducer, createCrdtSyncRemoteSource, createDeferredPromise, createExportData, createImportData, createKvStoreTableQuery, createMigrations, createMigrator, createSQLiteCrdtApplyFunction, createSQLiteKvStore, createStoredValue, createTypedEventTarget, defineSyncSchema, deserializeHLC, dummyKysely, formatSchemaVerificationIssues, generateId, introspectDb, isNoOpCrdtEventPayload, jsonSafeParse, makeCrdtTable, parametersAreEqual, quoteId, serializeHLC, startPerformanceLogger, t, tryCatch, tryCatchAsync, validateNewCrdtEvent, verifySyncSchemaWithDriver, xxhash };
@@ -1,2 +1,2 @@
1
- import { Mt as InternalSQLiteWrapper, Pt as KyselyStatementFactory, d as CRDT_CHANGE_INTENTS_TABLE, f as crdtViewColumnsFromTable, ft as baseSystemMigrations, m as drainCrdtChangeIntents, mt as runSystemMigrations, p as createCrdtViewStatements, pt as createSystemDbConfig, tt as CrdtChangeIntent, zt as compileKyselyStatement } from "./index-BHsKLqcK.js";
1
+ import { Lt as InternalSQLiteWrapper, Wt as compileKyselyStatement, _ as crdtViewColumnsFromTable, _t as baseSystemMigrations, g as CRDT_CHANGE_INTENTS_TABLE, ot as CrdtChangeIntent, v as createCrdtViewStatements, vt as createSystemDbConfig, y as drainCrdtChangeIntents, yt as runSystemMigrations, zt as KyselyStatementFactory } from "./index-CH4lf3iX.js";
2
2
  export { CRDT_CHANGE_INTENTS_TABLE, type CrdtChangeIntent, type InternalSQLiteWrapper, type KyselyStatementFactory, baseSystemMigrations, compileKyselyStatement, crdtViewColumnsFromTable, createCrdtViewStatements, createSystemDbConfig, drainCrdtChangeIntents, runSystemMigrations };
package/dist/internal.js CHANGED
@@ -1,3 +1,3 @@
1
- import { f as compileKyselyStatement, i as createSystemDbConfig, o as runSystemMigrations, r as baseSystemMigrations } from "./system-schema-Dvl_BLId.js";
2
- import { i as drainCrdtChangeIntents, n as crdtViewColumnsFromTable, r as createCrdtViewStatements, t as CRDT_CHANGE_INTENTS_TABLE } from "./make-crdt-table-C6l0F-o_.js";
1
+ import { a as baseSystemMigrations, g as compileKyselyStatement, l as runSystemMigrations, o as createSystemDbConfig } from "./system-schema-CXf3FmxN.js";
2
+ import { i as drainCrdtChangeIntents, n as crdtViewColumnsFromTable, r as createCrdtViewStatements, t as CRDT_CHANGE_INTENTS_TABLE } from "./make-crdt-table-DYCXOYB7.js";
3
3
  export { CRDT_CHANGE_INTENTS_TABLE, baseSystemMigrations, compileKyselyStatement, crdtViewColumnsFromTable, createCrdtViewStatements, createSystemDbConfig, drainCrdtChangeIntents, runSystemMigrations };
@@ -0,0 +1,48 @@
1
+ import { C as EventsPullRequest, Gt as SqliteDriver, Jt as SqliteDriverStatement, Kt as SqliteDriverHooks, S as CreateRemoteSourceFactory, T as EventsPushResponse, Yt as SqliteDriverValue, Zt as Logger, l as SyncedDb, qt as SqliteDriverRows, un as SyncDbSchema, w as EventsPushRequest } from "./index-CH4lf3iX.js";
2
+ import { t as createWsRemoteSource } from "./ws-remote-source-DFBhDc6Z.js";
3
+ //#region src/local/local-synced-db.d.ts
4
+ type LocalSyncedDbOptions<Database> = {
5
+ dbId: string;
6
+ syncDbSchema: SyncDbSchema<Database>;
7
+ /** Opens the persisted database. Called again after a reset deleted it. */
8
+ openDriver: () => SqliteDriver | Promise<SqliteDriver>;
9
+ /** Deletes the persisted database. Only called while no connection to it is open. */
10
+ deleteDatabase: () => void | Promise<void>;
11
+ createRemoteSource?: CreateRemoteSourceFactory;
12
+ logger?: Logger;
13
+ /**
14
+ * App-provided storage version, combined with the library's internal storage
15
+ * version. Bump it when deploying a code change that old persisted local DBs
16
+ * cannot survive — on mismatch the local DB is deleted on startup.
17
+ */
18
+ storageVersion?: string;
19
+ /**
20
+ * Dev-time drift check: replays the full migration history on a throwaway
21
+ * database opened by this callback and verifies the result matches the schema
22
+ * declared with `defineSyncSchema`. On mismatch startup throws.
23
+ */
24
+ verifySchemaDriver?: () => SqliteDriver | Promise<SqliteDriver>;
25
+ /**
26
+ * Runs event-log garbage collection on startup. Disabled by default. GC keeps the
27
+ * latest event rows and never removes pending events or local rows that have not
28
+ * been pushed to the remote.
29
+ */
30
+ eventLogGc?: boolean;
31
+ /** Runs once when the instance is disposed, including by `requestReload`. */
32
+ onDispose?: () => void;
33
+ };
34
+ type LocalSyncedDb<Database> = SyncedDb<Database> & {
35
+ state: SyncedDb<Database>["state"] & {
36
+ /** Drops the remote connection without retrying; call `sync` once the network is back. */
37
+ dropConnection: () => void;
38
+ };
39
+ };
40
+ /**
41
+ * A synced DB backed by a single persisted SQLite connection, for runtimes with one
42
+ * JS context per database (React Native, Electron, Node). Reads and writes go to the
43
+ * persisted database directly; there is no worker, tab coordination or in-memory copy.
44
+ */
45
+ declare function createLocalSyncedDb<Database>(options: LocalSyncedDbOptions<Database>): Promise<LocalSyncedDb<Database>>;
46
+ //#endregion
47
+ export { type CreateRemoteSourceFactory, type EventsPullRequest, type EventsPushRequest, type EventsPushResponse, type LocalSyncedDb, type LocalSyncedDbOptions, type SqliteDriver, type SqliteDriverHooks, type SqliteDriverRows, type SqliteDriverStatement, type SqliteDriverValue, createLocalSyncedDb, createWsRemoteSource };
48
+ //# sourceMappingURL=local.d.ts.map
package/dist/local.js ADDED
@@ -0,0 +1,255 @@
1
+ import { d as createKvStateHashStore, f as createKvStoreTableQuery, n as applyLocalDbSchema, p as createSQLiteKvStore, s as localDbConfig, t as LOCAL_KV_TABLE_NAME } from "./system-schema-CXf3FmxN.js";
2
+ import { l as generateId, s as createTypedEventTarget } from "./crdt-table-schema-D6Yf61Ex.js";
3
+ import { a as formatSchemaVerificationIssues, c as createMigrator, l as createExportData, n as createCrdtStorage, o as verifySyncSchemaWithDriver, t as createCrdtSyncProducer, u as createImportData } from "./crdt-sync-producer-BY-WobgO.js";
4
+ import { o as HLCCounter, t as createCrdtSyncRemoteSource, u as xxhash } from "./crdt-sync-remote-source-yeTEkOjH.js";
5
+ import { t as SQLiteReactiveDb } from "./sqlite-reactive-db-DLS1xRZp.js";
6
+ import { a as makeCrdtTable, o as registerCrdtIntentDrainer } from "./make-crdt-table-DYCXOYB7.js";
7
+ import { n as getOrCreateSQLiteSyncDevtoolsRegistry, r as validateDbId, t as createSyncedDbDatabase } from "./synced-db-database-B5M7MrBD.js";
8
+ import { i as runWorkerEventLogGc, r as formatStorageVersion, t as createWsRemoteSource } from "./ws-remote-source-BbPWrHB6.js";
9
+ //#region src/local/local-synced-db.ts
10
+ const STORAGE_VERSION_KEY = "storage-version";
11
+ const defaultLogger = (type, message, level = "info") => {
12
+ const logMessage = `[${type}] ${message}`;
13
+ switch (level) {
14
+ case "info":
15
+ console.log(logMessage);
16
+ break;
17
+ case "warning":
18
+ console.warn(logMessage);
19
+ break;
20
+ case "error":
21
+ console.error(logMessage);
22
+ break;
23
+ case "trace": console.trace(logMessage);
24
+ }
25
+ };
26
+ /**
27
+ * A synced DB backed by a single persisted SQLite connection, for runtimes with one
28
+ * JS context per database (React Native, Electron, Node). Reads and writes go to the
29
+ * persisted database directly; there is no worker, tab coordination or in-memory copy.
30
+ */
31
+ async function createLocalSyncedDb(options) {
32
+ validateDbId(options.dbId);
33
+ const logger = options.logger ?? defaultLogger;
34
+ if (options.verifySchemaDriver) {
35
+ const issues = verifySyncSchemaWithDriver(options.syncDbSchema, await options.verifySchemaDriver());
36
+ if (issues.length > 0) throw new Error(formatSchemaVerificationIssues(issues));
37
+ logger("local", "Schema verification passed: migrations match the declared schema", "info");
38
+ }
39
+ await xxhash.ensureLoaded();
40
+ const reactiveDb = await openLocalReactiveDb(options, logger);
41
+ const db = reactiveDb.db;
42
+ try {
43
+ const { kvStore } = applyLocalDbSchema(db);
44
+ const migrator = createMigrator({
45
+ migrations: options.syncDbSchema.migrations,
46
+ schemaVersion: kvStore.createNumberStoredValue("schema-version", -1),
47
+ updateLogTableName: localDbConfig.updateLogTable.fullIdentifier
48
+ });
49
+ migrator.migrateDbToLatest({ startTransaction: (callback) => {
50
+ db.executeTransaction((tx) => callback({ execute: (sql, parameters, meta) => tx.execute({
51
+ sql,
52
+ parameters
53
+ }, meta) }));
54
+ } });
55
+ db.invalidateDbSchema();
56
+ const pullSyncId = kvStore.createNumberStoredValue("pull-sync-id", -1);
57
+ const pushSyncId = kvStore.createNumberStoredValue("push-sync-id", -1);
58
+ const stateHash = createKvStateHashStore(kvStore);
59
+ const storageGeneration = kvStore.createStringStoredValue("storage-generation", "");
60
+ if (!storageGeneration.current) storageGeneration.current = generateId();
61
+ if (options.eventLogGc === true) runWorkerEventLogGc({
62
+ db,
63
+ dbConfig: localDbConfig,
64
+ pushSyncId,
65
+ logger
66
+ });
67
+ for (const table of options.syncDbSchema.tablesConfig) makeCrdtTable({
68
+ db,
69
+ baseTableName: table.baseTableName,
70
+ crdtTableName: table.crdtTableName,
71
+ temp: true
72
+ });
73
+ const nodeId = generateId();
74
+ const crdtStorage = createCrdtStorage({
75
+ nodeId,
76
+ initialLocalSyncId: getMaxSyncId(db),
77
+ migrator,
78
+ hlc: new HLCCounter(nodeId, () => Date.now()),
79
+ db,
80
+ dbConfig: localDbConfig,
81
+ stateHash,
82
+ schema: options.syncDbSchema
83
+ });
84
+ registerCrdtIntentDrainer({
85
+ reactiveDb,
86
+ storage: crdtStorage
87
+ });
88
+ kvStore.set(STORAGE_VERSION_KEY, formatStorageVersion(options.storageVersion));
89
+ const eventTarget = createTypedEventTarget();
90
+ const notify = (notification) => {
91
+ eventTarget.dispatchEvent(notification.notificationType, notification);
92
+ };
93
+ createCrdtSyncProducer({
94
+ storage: crdtStorage,
95
+ broadcastEvents: (chunk) => {
96
+ notify({
97
+ notificationType: "new-event-chunk-applied",
98
+ newSyncId: chunk.newSyncId,
99
+ stateHash: chunk.stateHash
100
+ });
101
+ }
102
+ });
103
+ const remoteSource = createCrdtSyncRemoteSource({
104
+ bufferSize: 75,
105
+ pullSyncId,
106
+ pushSyncId,
107
+ nodeId,
108
+ storage: crdtStorage,
109
+ migrator,
110
+ remoteFactory: options.createRemoteSource
111
+ });
112
+ remoteSource.addEventListener("state-changed", () => {
113
+ notify({
114
+ notificationType: "state-changed",
115
+ state: remoteSource.getState(),
116
+ storageGeneration: storageGeneration.current
117
+ });
118
+ });
119
+ remoteSource.addEventListener("de-sync-detected", (event) => {
120
+ notify({
121
+ notificationType: "de-sync-detected",
122
+ reason: event.payload.reason
123
+ });
124
+ });
125
+ remoteSource.addEventListener("remote-schema-version-mismatch", (event) => {
126
+ notify({
127
+ notificationType: "remote-schema-version-mismatch",
128
+ remoteSchemaVersion: event.payload.remoteSchemaVersion,
129
+ localSchemaVersion: event.payload.localSchemaVersion
130
+ });
131
+ });
132
+ remoteSource.goOnline();
133
+ let unregisterDevtools;
134
+ let disposePromise = null;
135
+ const dispose = () => {
136
+ disposePromise ??= (async () => {
137
+ options.onDispose?.();
138
+ unregisterDevtools?.();
139
+ try {
140
+ await remoteSource.dispose();
141
+ await crdtStorage.internal.processEnqueuedEvents();
142
+ } finally {
143
+ reactiveDb.dispose();
144
+ }
145
+ })();
146
+ return disposePromise;
147
+ };
148
+ const importData = createImportData({
149
+ migrator,
150
+ tablesConfig: options.syncDbSchema.tablesConfig,
151
+ applyEvents: async (events) => {
152
+ crdtStorage.applyOwnEvents(events);
153
+ await crdtStorage.internal.processEnqueuedEvents();
154
+ }
155
+ });
156
+ const sync = () => {
157
+ remoteSource.syncWithRemote().catch((error) => console.warn("Failed to start sync", error));
158
+ };
159
+ const syncedDb = {
160
+ db: createSyncedDbDatabase(reactiveDb),
161
+ state: {
162
+ getState: () => remoteSource.getState(),
163
+ subscribe: (onChange) => {
164
+ const { unsubscribe } = eventTarget.addEventListener("state-changed", onChange);
165
+ return unsubscribe;
166
+ },
167
+ goOnline: () => remoteSource.goOnline(),
168
+ goOffline: () => remoteSource.goOffline("DISCONNECTED"),
169
+ dropConnection: () => remoteSource.dropConnection("NETWORK_OFFLINE"),
170
+ sync
171
+ },
172
+ /**
173
+ * Disposes this instance and notifies `reload-requested` subscribers, which are
174
+ * expected to create a new instance. With `clean: true` the persisted database is
175
+ * deleted first, so the next instance starts empty and re-syncs from the remote.
176
+ * Pending local events that were not pushed yet are lost with a clean reload.
177
+ */
178
+ requestReload: async ({ clean }) => {
179
+ await dispose();
180
+ if (clean) await options.deleteDatabase();
181
+ notify({
182
+ notificationType: "reload-requested",
183
+ reloadEpoch: generateId(),
184
+ clean
185
+ });
186
+ },
187
+ subscribe: eventTarget.addEventListener,
188
+ exportData: createExportData({
189
+ reactiveDb,
190
+ tablesConfig: options.syncDbSchema.tablesConfig,
191
+ schemaVersion: migrator.currentSchemaVersion
192
+ }),
193
+ importData: async (data, opts) => importData(data, opts),
194
+ dispose,
195
+ _internal: {
196
+ executeAsync: async (query) => reactiveDb.db.execute(query),
197
+ getMemoryQueryTables: reactiveDb.getTablesUsed.bind(reactiveDb),
198
+ getSharedLiveQueriesSnapshot: reactiveDb.getSharedLiveQueriesSnapshot.bind(reactiveDb),
199
+ crdtTableNames: options.syncDbSchema.tablesConfig.map((table) => table.crdtTableName),
200
+ crdtTablesConfig: options.syncDbSchema.tablesConfig,
201
+ tables: options.syncDbSchema.tables,
202
+ schemaVersion: migrator.currentSchemaVersion,
203
+ migrationVersions: Object.keys(options.syncDbSchema.migrations).map(Number).sort((a, b) => a - b)
204
+ }
205
+ };
206
+ unregisterDevtools = getOrCreateSQLiteSyncDevtoolsRegistry().register({
207
+ instanceId: generateId(),
208
+ dbId: options.dbId,
209
+ createdAt: Date.now(),
210
+ instance: syncedDb
211
+ });
212
+ return syncedDb;
213
+ } catch (error) {
214
+ reactiveDb.dispose();
215
+ throw error;
216
+ }
217
+ }
218
+ async function openLocalReactiveDb(options, logger) {
219
+ const open = async () => {
220
+ const driver = await options.openDriver();
221
+ const reactiveDb = SQLiteReactiveDb.create({
222
+ driver,
223
+ logger,
224
+ loggerPrefix: "local"
225
+ });
226
+ try {
227
+ reactiveDb.db.execute("PRAGMA journal_mode=WAL", { loggerLevel: "system" });
228
+ reactiveDb.db.execute("PRAGMA synchronous=NORMAL", { loggerLevel: "system" });
229
+ reactiveDb.db.executeKysely((kysely) => createKvStoreTableQuery(kysely.schema, LOCAL_KV_TABLE_NAME), { loggerLevel: "system" });
230
+ } catch (error) {
231
+ reactiveDb.dispose();
232
+ throw error;
233
+ }
234
+ return reactiveDb;
235
+ };
236
+ const reactiveDb = await open();
237
+ const currentVersion = formatStorageVersion(options.storageVersion);
238
+ const storedVersion = createSQLiteKvStore({
239
+ db: reactiveDb.db,
240
+ metaTableName: LOCAL_KV_TABLE_NAME
241
+ }).get(STORAGE_VERSION_KEY);
242
+ if (storedVersion === null || storedVersion === currentVersion) return reactiveDb;
243
+ logger("local", `Storage version mismatch — resetting local DB to ${currentVersion}`, "warning");
244
+ reactiveDb.dispose();
245
+ await options.deleteDatabase();
246
+ return open();
247
+ }
248
+ function getMaxSyncId(db) {
249
+ const [result] = db.executePrepared("get-max-sync-id", {}, (db) => db.selectFrom("crdt_events").select((eb) => eb.fn.max("sync_id").as("sync_id")), { loggerLevel: "system" });
250
+ return result?.sync_id ?? 0;
251
+ }
252
+ //#endregion
253
+ export { createLocalSyncedDb, createWsRemoteSource };
254
+
255
+ //# sourceMappingURL=local.js.map