@sqlite-sync/core 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt-table-schema-2w_AIpsV.js","names":[],"sources":["../src/utils.ts","../src/sqlite-crdt/crdt-table-schema.ts"],"sourcesContent":["export type DeferredPromise<T> = {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (error: Error) => void;\n};\n\ntype DeferredPromiseOptions = {\n timeout?: number;\n onTimeout?: () => void;\n};\n\nexport function createDeferredPromise<T>(opts?: DeferredPromiseOptions): DeferredPromise<T> {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const promise = new Promise<T>((_resolve, _reject) => {\n resolve = (value) => {\n if (timeoutId) clearTimeout(timeoutId);\n _resolve(value);\n };\n reject = (reason) => {\n if (timeoutId) clearTimeout(timeoutId);\n _reject(reason);\n };\n\n if (opts?.timeout) {\n timeoutId = setTimeout(rejectTimeout, opts.timeout, _reject, opts);\n }\n });\n\n return { promise, resolve, reject };\n}\n\nfunction rejectTimeout(reject: (reason?: unknown) => void, opts: DeferredPromiseOptions) {\n reject(new Error(`Promise timed out after ${opts.timeout}ms`));\n tryCatch(() => opts?.onTimeout?.());\n}\n\nexport const generateId = () => {\n return crypto.randomUUID();\n};\n\nexport type DistributiveOmit<T, K extends keyof T> = T extends any ? Omit<T, K> : never;\n\nexport function ensureSingletonExecution<T, TArgs extends any[]>(\n fn: (...args: TArgs) => Promise<T>,\n opts: { queueReExecution?: boolean } = { queueReExecution: true },\n) {\n let executingPromise: Promise<T> | null = null;\n let shouldReExecute = false;\n\n const wrappedFn = (...args: TArgs) => {\n if (executingPromise) {\n shouldReExecute = true;\n return executingPromise;\n }\n\n executingPromise = fn(...args).finally(() => {\n executingPromise = null;\n\n if (shouldReExecute && opts?.queueReExecution) {\n shouldReExecute = false;\n wrappedFn(...args);\n }\n });\n return executingPromise;\n };\n\n wrappedFn.promise = () => executingPromise;\n wrappedFn.isExecuting = () => !!executingPromise;\n\n return wrappedFn;\n}\n\nexport function createAutoFlushBuffer<T>({ size, flush }: { size: number; flush: (items: T[]) => void }) {\n const buffer: T[] = [];\n\n return {\n add(item: T) {\n buffer.push(item);\n if (buffer.length >= size) {\n flush(buffer);\n buffer.length = 0;\n }\n },\n flush() {\n flush(buffer);\n buffer.length = 0;\n },\n };\n}\n\nexport function createAsyncAutoFlushBuffer<T>({\n size,\n flush,\n}: {\n size: number;\n flush: (items: T[]) => void | Promise<void>;\n}) {\n const buffer: T[] = [];\n\n return {\n async add(item: T) {\n buffer.push(item);\n if (buffer.length >= size) {\n await this.flush();\n }\n },\n async flush() {\n const itemsToFlush = buffer.splice(0);\n if (itemsToFlush.length === 0) {\n return;\n }\n await flush(itemsToFlush);\n },\n };\n}\n\nexport class TypedBroadcastChannel<TMessage> {\n private readonly channel: BroadcastChannel;\n\n constructor(name: string) {\n this.channel = new BroadcastChannel(name);\n }\n\n postMessage(message: TMessage) {\n this.channel.postMessage(message);\n }\n\n set onmessage(callback: ((event: MessageEvent<TMessage>) => void) | null) {\n this.channel.onmessage = callback;\n }\n\n close() {\n this.channel.close();\n }\n}\n\nexport class TypedEvent<T = unknown> extends Event {\n readonly payload: T;\n constructor(type: string, payload: T) {\n super(type);\n this.payload = payload;\n }\n}\n\nexport type TypedEventTarget<T extends Record<string, unknown>> = {\n addEventListener: <K extends keyof T & string>(\n type: K,\n listener: (event: TypedEvent<T[K]>) => void,\n ) => { unsubscribe: () => void };\n removeEventListener: <K extends keyof T & string>(type: K, listener: (event: TypedEvent<T[K]>) => void) => void;\n dispatchEvent: <K extends keyof T & string>(type: K, payload: T[K]) => void;\n};\n\nexport const createTypedEventTarget = <T extends Record<string, unknown>>(): TypedEventTarget<T> => {\n const eventTarget = new EventTarget();\n\n const addEventListener = <K extends keyof T & string>(type: K, listener: (event: TypedEvent<T[K]>) => void) => {\n eventTarget.addEventListener(type, listener as (e: Event) => void);\n\n return {\n unsubscribe: () => {\n eventTarget.removeEventListener(type, listener as (e: Event) => void);\n },\n };\n };\n\n const removeEventListener = <K extends keyof T & string>(type: K, listener: (event: TypedEvent<T[K]>) => void) => {\n eventTarget.removeEventListener(type, listener as (e: Event) => void);\n };\n\n const dispatchEvent = <K extends keyof T & string>(type: K, payload: T[K]) => {\n eventTarget.dispatchEvent(new TypedEvent(type, payload));\n };\n\n return {\n addEventListener,\n removeEventListener,\n dispatchEvent,\n };\n};\n\ntype TryCatchResult<T> =\n | {\n success: true;\n data: T;\n }\n | {\n success: false;\n error: unknown;\n };\n\nexport function tryCatch<T>(fn: () => T): TryCatchResult<T> {\n try {\n return {\n success: true,\n data: fn(),\n };\n } catch (error) {\n return {\n success: false,\n error,\n };\n }\n}\n\nexport async function tryCatchAsync<T>(fn: () => Promise<T>): Promise<TryCatchResult<T>> {\n try {\n return {\n success: true,\n data: await fn(),\n };\n } catch (error) {\n return {\n success: false,\n error,\n };\n }\n}\n\nexport function jsonSafeParse<T>(json: string) {\n return tryCatch(() => JSON.parse(json) as T);\n}\n\n/** Quote a SQLite identifier (table/column name), handling dot-separated schema qualifiers. */\nexport function quoteId(name: string): string {\n return name\n .split(\".\")\n .map((s) => `\"${s.replace(/\"/g, '\"\"')}\"`)\n .join(\".\");\n}\n\nexport function noop(..._args: unknown[]): void {}\n\nexport type ParsedTableName = {\n schema: \"main\" | (string & {});\n table: string;\n fullIdentifier: string;\n};\n\nexport function parseTableName(tableName: string): ParsedTableName {\n if (!tableName?.trim()) {\n throw new Error(\"Parse table: missing table name\");\n }\n const parts = tableName.split(\".\");\n if (parts.length > 2) {\n throw new Error(\"Parse table: too many dot-delimited segments in table name\");\n }\n\n return parts.length === 1\n ? {\n schema: \"main\",\n table: parts[0],\n fullIdentifier: parts[0],\n }\n : {\n schema: parts[0],\n table: parts[1],\n fullIdentifier: `${parts[0]}.${parts[1]}`,\n };\n}\n","import type { SchemaModule } from \"kysely\";\n\nexport type NewCrdtEvent = {\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: Record<string, unknown>;\n};\n\nexport type CrdtEventType = \"item-created\" | \"item-updated\" | \"item-deleted\";\n\nexport type CrdtEventStatus = \"pending\" | \"applied\" | \"failed\" | \"deduped\";\n\n/** Persisted on applied events that were accepted but did not mutate materialized state. */\nexport const CRDT_EVENT_NO_OP_PAYLOAD = \"no-op\";\n\nexport function isNoOpCrdtEventPayload(payload: string) {\n return payload === CRDT_EVENT_NO_OP_PAYLOAD;\n}\n\nexport type CrdtEventOrigin = \"remote\" | \"own\" | \"own-applied\" | \"local\";\n\nexport type PersistedCrdtEvent = {\n schema_version: number;\n sync_id: number;\n status: CrdtEventStatus;\n type: CrdtEventType;\n timestamp: string;\n origin: CrdtEventOrigin;\n source_node_id: string;\n dataset: string;\n item_id: string;\n payload: string;\n};\n\nexport type CrdtUpdateLogItem = {\n dataset: string;\n item_id: string;\n payload: string;\n};\n\nexport type CrdtUpdateLogPayload = Record<string, string>;\n\nexport const crdtSchema = {\n persistedEventsTable: createPersistedEventsTable,\n crdtUpdateLogTable: createCrdtUpdateLogTableQuery,\n};\n\nfunction createPersistedEventsTable(schema: SchemaModule, tableName: string) {\n return schema\n .createTable(tableName)\n .ifNotExists()\n .addColumn(\"sync_id\", \"integer\", (col) => col.notNull().primaryKey())\n .addColumn(\"schema_version\", \"integer\", (col) => col.notNull())\n .addColumn(\"status\", \"text\", (col) => col.notNull())\n .addColumn(\"type\", \"text\", (col) => col.notNull())\n .addColumn(\"timestamp\", \"text\", (col) => col.notNull())\n .addColumn(\"origin\", \"text\", (col) => col.notNull())\n .addColumn(\"dataset\", \"text\", (col) => col.notNull())\n .addColumn(\"item_id\", \"text\", (col) => col.notNull())\n .addColumn(\"payload\", \"text\", (col) => col.notNull());\n}\n\nfunction createCrdtUpdateLogTableQuery(schema: SchemaModule, tableName: string) {\n return schema\n .createTable(tableName)\n .ifNotExists()\n .addColumn(\"dataset\", \"text\", (col) => col.notNull())\n .addColumn(\"item_id\", \"text\", (col) => col.notNull())\n .addColumn(\"payload\", \"text\", (col) => col.notNull())\n .addPrimaryKeyConstraint(`pk_${tableName}`, [\"item_id\", \"dataset\"]);\n}\n"],"mappings":"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"}
@@ -237,7 +237,8 @@ declare class TableBuilder<Cols extends TableColumns, BaseName extends string |
237
237
  get $row(): InferRow<Cols>;
238
238
  /**
239
239
  * Validate a CRDT event payload against the declared columns.
240
- * `item-created` payloads must contain every column without a default (nullable columns may be omitted);
240
+ * `item-created` payloads must contain every column without a default (nullable columns may be omitted)
241
+ * and their `tombstone` value is ignored, since applying a create always sets it to 0;
241
242
  * `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.
242
243
  */
243
244
  validatePayload(payload: Record<string, unknown>, options?: {
@@ -291,7 +292,10 @@ declare class HLCCounter {
291
292
  constructor(nodeId: string, getTimestamp: () => number, maxDrift?: number);
292
293
  getCurrentHLC(): HLC;
293
294
  getNextHLC(): HLC;
295
+ /** Restore already-persisted causal state, regardless of wall-clock drift. */
296
+ restoreHLC(hlc: HLC): void;
294
297
  mergeHLC(hlc: HLC): void;
298
+ private rollOverCounter;
295
299
  }
296
300
  declare function serializeHLC(hlc: HLC): string;
297
301
  declare function deserializeHLC(serialized: string): HLC;
@@ -574,7 +578,7 @@ type EnqueueEventsResult = {
574
578
  /** Resolves when the enqueued events have been processed (applied/deduped/failed). */
575
579
  processed: Promise<void>;
576
580
  };
577
- type StorageHLC = Pick<HLCCounter, "getNextHLC" | "mergeHLC">;
581
+ type StorageHLC = Pick<HLCCounter, "getNextHLC" | "mergeHLC" | "restoreHLC">;
578
582
  type DbSyncerStorage = {
579
583
  nodeId: string;
580
584
  initialLocalSyncId: number;
@@ -766,13 +770,13 @@ type AdmitClientEventsResult<T> = {
766
770
  admitted: T[];
767
771
  skipped: T[];
768
772
  };
769
- type SchemaForAdmitClientEvents = Pick<SyncDbSchema, "tables" | "tablesConfig"> & {
770
- writeOriginByName?: ReadonlyMap<string, WriteOrigin>;
771
- };
773
+ type SchemaForAdmitClientEvents = Pick<SyncDbSchema, "tables" | "tablesConfig">;
774
+ type ClientDatasetRejectionReason = "server-only-dataset" | "undeclared-dataset";
772
775
  /**
773
- * Splits a client push into events the hub should persist and events for tables with
774
- * `{ writes: "server" }`. Unknown datasets stay admitted. Matches either the crdt or the base
775
- * table name.
776
+ * Splits a client push into events the hub should persist and events it must drop. Only
777
+ * datasets that exactly match the base table name of a declared table without
778
+ * `{ writes: "server" }` are admitted; server-only tables, crdt view names, and undeclared
779
+ * datasets are skipped.
776
780
  */
777
781
  declare function admitClientEvents<T extends {
778
782
  dataset: string;
@@ -998,8 +1002,17 @@ type EventsAvailable = {
998
1002
  newSyncId: number;
999
1003
  remoteEventHlcSum: string | null;
1000
1004
  };
1005
+ /**
1006
+ * Opens a connection to the remote. Each call must return an independent source: the library calls
1007
+ * `disconnect` on every returned source exactly once, including one that resolves after `signal`
1008
+ * has aborted. `signal` aborts when the connection attempt is cancelled or the connection is closed.
1009
+ * Call `onReconnected` when the transport re-establishes itself without a new source: broadcasts
1010
+ * sent while it was down are lost, so the library pulls and pushes to catch up.
1011
+ */
1001
1012
  type CreateRemoteSourceFactory = (opts: {
1002
1013
  onEventsAvailable: (event: EventsAvailable) => void;
1014
+ onReconnected: () => void;
1015
+ signal: AbortSignal;
1003
1016
  }) => RemoteSource | Promise<RemoteSource>;
1004
1017
  type RemoteSource = {
1005
1018
  pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;
@@ -1017,7 +1030,6 @@ type RemoteSourceState = {
1017
1030
  schemaVersionMismatched: boolean;
1018
1031
  } | {
1019
1032
  type: "online";
1020
- source: RemoteSource;
1021
1033
  deSynced: boolean;
1022
1034
  schemaVersionMismatched: boolean;
1023
1035
  };
@@ -1025,16 +1037,8 @@ type OfflineReason = "NOT_INITIALIZED" | "INITIALIZATION_FAILED" | "REMOTE_PUSH_
1025
1037
  type DeSyncDetectedReason = "CHECKSUM_MISMATCH" | "ERROR_APPLYING_REMOTE_EVENT";
1026
1038
  declare const createCrdtSyncRemoteSource: ({ bufferSize, storage, migrator, pullSyncId, pushSyncId, nodeId, remoteFactory }: CrdtSyncRemoteSourceConfig) => {
1027
1039
  goOnline: () => Promise<void>;
1028
- goOffline: {
1029
- (reason: OfflineReason): Promise<void>;
1030
- promise(): Promise<void> | null;
1031
- isExecuting(): boolean;
1032
- };
1033
- syncWithRemote: {
1034
- (): Promise<void>;
1035
- promise(): Promise<void> | null;
1036
- isExecuting(): boolean;
1037
- };
1040
+ goOffline: (reason: OfflineReason) => Promise<void>;
1041
+ syncWithRemote: () => Promise<void>;
1038
1042
  getState: () => WorkerState;
1039
1043
  dispose: () => Promise<void>;
1040
1044
  addEventListener: <K extends "state-changed" | "de-sync-detected" | "remote-schema-version-mismatch">(type: K, listener: (event: TypedEvent<{
@@ -1068,10 +1072,15 @@ declare function makeCrdtTable({ db, baseTableName, crdtTableName }: {
1068
1072
  baseTableName: string;
1069
1073
  crdtTableName: string;
1070
1074
  }): void;
1071
- declare function createCrdtViewStatements({ baseTableName, crdtTableName, columnNames }: {
1075
+ type CrdtViewColumn = {
1076
+ name: string;
1077
+ nullable: boolean;
1078
+ };
1079
+ declare function crdtViewColumnsFromTable(table: AnyTableBuilder): CrdtViewColumn[];
1080
+ declare function createCrdtViewStatements({ baseTableName, crdtTableName, columns }: {
1072
1081
  baseTableName: string;
1073
1082
  crdtTableName: string;
1074
- columnNames: string[];
1083
+ columns: CrdtViewColumn[];
1075
1084
  }): string[];
1076
1085
  declare function drainCrdtChangeIntents({ tx, storage }: {
1077
1086
  tx: InternalSQLiteTransactionWrapper<any>;
@@ -1124,6 +1133,7 @@ declare function createSyncedDb<Database, Props = undefined>(options: SyncedDbOp
1124
1133
  subscribe: (onChange: () => void) => () => void;
1125
1134
  goOnline: () => Promise<void>;
1126
1135
  goOffline: () => Promise<void>;
1136
+ sync: () => void;
1127
1137
  };
1128
1138
  /**
1129
1139
  * Ask the elected worker to broadcast a page reload to all tabs for this dbId.
@@ -1180,6 +1190,7 @@ declare function createSyncedDb<Database, Props = undefined>(options: SyncedDbOp
1180
1190
  getSharedLiveQueriesSnapshot: () => SharedLiveQuerySnapshot[];
1181
1191
  crdtTableNames: string[];
1182
1192
  crdtTablesConfig: CrdtTableConfig[];
1193
+ tables: SyncSchemaTables;
1183
1194
  schemaVersion: number;
1184
1195
  migrationVersions: number[];
1185
1196
  };
@@ -1205,5 +1216,5 @@ type ResetStore = {
1205
1216
  */
1206
1217
  declare const RESET_REQUEST_TTL_MS: number;
1207
1218
  //#endregion
1208
- export { CrdtChangeIntent as $, AnyColumnBuilder as $t, createCrdtApplyFunction as A, dummyKysely as An, InternalSQLiteWrapper as At, defineSyncSchema as B, ColumnMetadata as Bt, WorkerNotificationMessage as C, CrdtEventOrigin as Cn, tryCatch as Ct, SnapshotOptions as D, CrdtUpdateLogPayload as Dn, createSQLiteKvStore as Dt, CrdtStorageMutator as E, CrdtUpdateLogItem as En, createKvStoreTableQuery as Et, CrdtEventValidationError as F, SQLiteDbWrapper as Ft, SharedLiveQuerySnapshot as G, HLCCounter as Gt, admitClientEvents as H, TableMetadata as Ht, NewCrdtEventValidationResult as I, SQLiteTransactionWrapper as It, ImportDataOptions as J, serializeHLC as Jt, parametersAreEqual as K, compareHLC as Kt, validateNewCrdtEvent as L, LogLevel as Lt, SchemaVerificationIssue as M, KyselyStatementFactory as Mt, formatSchemaVerificationIssues as N, PreparedStatement as Nt, createCrdtStorageMutator as O, PersistedCrdtEvent as On, ExecuteParams as Ot, verifySyncSchema as P, QueryBuilderOutput as Pt, createImportData as Q, AiAccess as Qt, DefineSyncSchemaConfig as R, Logger as Rt, WorkerConfig as S, CRDT_EVENT_NO_OP_PAYLOAD as Sn, quoteId as St, createCrdtSyncProducer as T, CrdtEventType as Tn, KvStoreItem as Tt, SQLiteReactiveDb as U, introspectDb as Ut, AdmitClientEventsResult as V, DatabaseIntrospection as Vt, SharedLiveQuery as W, HLC as Wt, SyncedDbExport as X, ReadonlyTable as Xt, ImportDataResult as Y, CrdtTableConfig as Yt, createExportData as Z, SyncDbSchema as Zt, EventsPullRequest as _, SyncDbMigrator as _n, TypedEventTarget as _t, SyncedDbDatabase as a, InferRow as an, SystemDbConfig as at, createCrdtSyncRemoteSource as b, StoredValue as bn, generateId as bt, SyncedDbTransaction as c, SyncSchemaTables as cn, applyMemoryDbSchema as ct, CRDT_CHANGE_INTENTS_TABLE as d, TableOptions as dn, createSystemDbConfig as dt, AnyTableBuilder as en, CrdtStorage as et, createCrdtViewStatements as f, WriteOrigin as fn, runSystemMigrations as ft, CreateRemoteSourceFactory as g, MigrationsDb as gn, TypedEvent as gt, CrdtSyncRemoteSource as h, Migrations as hn, TypedBroadcastChannel as ht, SyncedDb as i, ExportImportMode as in, createCrdtStorage as it, createSQLiteCrdtApplyFunction as j, KyselyQueryFactory as jt, PendingCrdtEvent as k, isNoOpCrdtEventPayload as kn, ExecuteResult as kt, UnsafeSyncedDbSqlExecutor as l, TableBuilder as ln, applyWorkerDbSchema as lt, makeCrdtTable as m, MigratableEvent as mn, DistributiveOmit as mt, ResetRequest as n, ColumnKind as nn, OwnCrdtEvent as nt, SyncedDbOptions as o, PayloadValidationResult as on, SystemMigration as ot, drainCrdtChangeIntents as p, t as pn, DeferredPromise as pt, xxhash as q, deserializeHLC as qt, ResetStore as r, ColumnMeta as rn, OwnCrdtSnapshot as rt, SyncedDbSqlExecutor as s, SqliteStorageType as sn, SystemMigrationContext as st, RESET_REQUEST_TTL_MS as t, ColumnBuilder as tn, GetEventsBatch as tt, createSyncedDb as u, TableColumns as un, baseSystemMigrations as ut, EventsPushRequest as v, createMigrations as vn, createDeferredPromise as vt, WorkerState as w, CrdtEventStatus as wn, tryCatchAsync as wt, EventsPullResponse as x, createStoredValue as xn, jsonSafeParse as xt, EventsPushResponse as y, createMigrator as yn, createTypedEventTarget as yt, DefinedSyncSchema as z, startPerformanceLogger as zt };
1209
- //# sourceMappingURL=index-iXdCBXzB.d.ts.map
1219
+ export { createExportData as $, SyncDbSchema as $t, PendingCrdtEvent as A, PersistedCrdtEvent as An, ExecuteParams as At, DefinedSyncSchema as B, Logger as Bt, WorkerConfig as C, createStoredValue as Cn, jsonSafeParse as Ct, CrdtStorageMutator as D, CrdtEventType as Dn, KvStoreItem as Dt, createCrdtSyncProducer as E, CrdtEventStatus as En, tryCatchAsync as Et, verifySyncSchema as F, PreparedStatement as Ft, SQLiteReactiveDb as G, introspectDb as Gt, AdmitClientEventsResult as H, ColumnMetadata as Ht, CrdtEventValidationError as I, QueryBuilderOutput as It, parametersAreEqual as J, compareHLC as Jt, SharedLiveQuery as K, HLC as Kt, NewCrdtEventValidationResult as L, SQLiteDbWrapper as Lt, createSQLiteCrdtApplyFunction as M, dummyKysely as Mn, InternalSQLiteWrapper as Mt, SchemaVerificationIssue as N, KyselyQueryFactory as Nt, SnapshotOptions as O, CrdtUpdateLogItem as On, createKvStoreTableQuery as Ot, formatSchemaVerificationIssues as P, KyselyStatementFactory as Pt, SyncedDbExport as Q, ReadonlyTable as Qt, validateNewCrdtEvent as R, SQLiteTransactionWrapper as Rt, EventsPullResponse as S, StoredValue as Sn, generateId as St, WorkerState as T, CrdtEventOrigin as Tn, tryCatch as Tt, ClientDatasetRejectionReason as U, DatabaseIntrospection as Ut, defineSyncSchema as V, startPerformanceLogger as Vt, admitClientEvents as W, TableMetadata as Wt, ImportDataOptions as X, serializeHLC as Xt, xxhash as Y, deserializeHLC as Yt, ImportDataResult as Z, CrdtTableConfig as Zt, CreateRemoteSourceFactory as _, Migrations as _n, TypedBroadcastChannel as _t, SyncedDbDatabase as a, ColumnMeta as an, OwnCrdtSnapshot as at, EventsPushResponse as b, createMigrations as bn, createDeferredPromise as bt, SyncedDbTransaction as c, PayloadValidationResult as cn, SystemMigration as ct, CRDT_CHANGE_INTENTS_TABLE as d, TableBuilder as dn, applyWorkerDbSchema as dt, AiAccess as en, createImportData as et, crdtViewColumnsFromTable as f, TableColumns as fn, baseSystemMigrations as ft, CrdtSyncRemoteSource as g, MigratableEvent as gn, DistributiveOmit as gt, makeCrdtTable as h, t as hn, DeferredPromise as ht, SyncedDb as i, ColumnKind as in, OwnCrdtEvent as it, createCrdtApplyFunction as j, isNoOpCrdtEventPayload as jn, ExecuteResult as jt, createCrdtStorageMutator as k, CrdtUpdateLogPayload as kn, createSQLiteKvStore as kt, UnsafeSyncedDbSqlExecutor as l, SqliteStorageType as ln, SystemMigrationContext as lt, drainCrdtChangeIntents as m, WriteOrigin as mn, runSystemMigrations as mt, ResetRequest as n, AnyTableBuilder as nn, CrdtStorage as nt, SyncedDbOptions as o, ExportImportMode as on, createCrdtStorage as ot, createCrdtViewStatements as p, TableOptions as pn, createSystemDbConfig as pt, SharedLiveQuerySnapshot as q, HLCCounter as qt, ResetStore as r, ColumnBuilder as rn, GetEventsBatch as rt, SyncedDbSqlExecutor as s, InferRow as sn, SystemDbConfig as st, RESET_REQUEST_TTL_MS as t, AnyColumnBuilder as tn, CrdtChangeIntent as tt, createSyncedDb as u, SyncSchemaTables as un, applyMemoryDbSchema as ut, EventsPullRequest as v, MigrationsDb as vn, TypedEvent as vt, WorkerNotificationMessage as w, CRDT_EVENT_NO_OP_PAYLOAD as wn, quoteId as wt, createCrdtSyncRemoteSource as x, createMigrator as xn, createTypedEventTarget as xt, EventsPushRequest as y, SyncDbMigrator as yn, TypedEventTarget as yt, DefineSyncSchemaConfig as z, LogLevel as zt };
1220
+ //# sourceMappingURL=index-B4s8GFWF.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $t as AnyColumnBuilder, A as createCrdtApplyFunction, An as dummyKysely, B as defineSyncSchema, Bt as ColumnMetadata, C as WorkerNotificationMessage, Cn as CrdtEventOrigin, Ct as tryCatch, D as SnapshotOptions, Dn as CrdtUpdateLogPayload, Dt as createSQLiteKvStore, E as CrdtStorageMutator, En as CrdtUpdateLogItem, Et as createKvStoreTableQuery, F as CrdtEventValidationError, Ft as SQLiteDbWrapper, G as SharedLiveQuerySnapshot, Gt as HLCCounter, H as admitClientEvents, Ht as TableMetadata, I as NewCrdtEventValidationResult, It as SQLiteTransactionWrapper, J as ImportDataOptions, Jt as serializeHLC, K as parametersAreEqual, Kt as compareHLC, L as validateNewCrdtEvent, Lt as LogLevel, M as SchemaVerificationIssue, N as formatSchemaVerificationIssues, Nt as PreparedStatement, O as createCrdtStorageMutator, On as PersistedCrdtEvent, Ot as ExecuteParams, P as verifySyncSchema, Pt as QueryBuilderOutput, Q as createImportData, Qt as AiAccess, R as DefineSyncSchemaConfig, Rt as Logger, S as WorkerConfig, Sn as CRDT_EVENT_NO_OP_PAYLOAD, St as quoteId, T as createCrdtSyncProducer, Tn as CrdtEventType, Tt as KvStoreItem, U as SQLiteReactiveDb, Ut as introspectDb, V as AdmitClientEventsResult, Vt as DatabaseIntrospection, W as SharedLiveQuery, Wt as HLC, X as SyncedDbExport, Xt as ReadonlyTable, Y as ImportDataResult, Yt as CrdtTableConfig, Z as createExportData, Zt as SyncDbSchema, _ as EventsPullRequest, _n as SyncDbMigrator, _t as TypedEventTarget, a as SyncedDbDatabase, an as InferRow, at as SystemDbConfig, b as createCrdtSyncRemoteSource, bn as StoredValue, bt as generateId, c as SyncedDbTransaction, cn as SyncSchemaTables, ct as applyMemoryDbSchema, dn as TableOptions, en as AnyTableBuilder, et as CrdtStorage, fn as WriteOrigin, gn as MigrationsDb, gt as TypedEvent, h as CrdtSyncRemoteSource, hn as Migrations, ht as TypedBroadcastChannel, i as SyncedDb, in as ExportImportMode, it as createCrdtStorage, j as createSQLiteCrdtApplyFunction, jt as KyselyQueryFactory, k as PendingCrdtEvent, kn as isNoOpCrdtEventPayload, kt as ExecuteResult, l as UnsafeSyncedDbSqlExecutor, ln as TableBuilder, lt as applyWorkerDbSchema, m as makeCrdtTable, mn as MigratableEvent, mt as DistributiveOmit, n as ResetRequest, nn as ColumnKind, nt as OwnCrdtEvent, o as SyncedDbOptions, on as PayloadValidationResult, ot as SystemMigration, pn as t, pt as DeferredPromise, q as xxhash, qt as deserializeHLC, r as ResetStore, rn as ColumnMeta, rt as OwnCrdtSnapshot, s as SyncedDbSqlExecutor, sn as SqliteStorageType, st as SystemMigrationContext, t as RESET_REQUEST_TTL_MS, tn as ColumnBuilder, u as createSyncedDb, un as TableColumns, v as EventsPushRequest, vn as createMigrations, vt as createDeferredPromise, w as WorkerState, wn as CrdtEventStatus, wt as tryCatchAsync, xn as createStoredValue, xt as jsonSafeParse, y as EventsPushResponse, yn as createMigrator, yt as createTypedEventTarget, z as DefinedSyncSchema, zt as startPerformanceLogger } from "./index-iXdCBXzB.js";
1
+ import { $ as createExportData, $t as SyncDbSchema, A as PendingCrdtEvent, An as PersistedCrdtEvent, At as ExecuteParams, B as DefinedSyncSchema, Bt as Logger, C as WorkerConfig, Cn as createStoredValue, Ct as jsonSafeParse, D as CrdtStorageMutator, Dn as CrdtEventType, Dt as KvStoreItem, E as createCrdtSyncProducer, En as CrdtEventStatus, Et as tryCatchAsync, F as verifySyncSchema, Ft as PreparedStatement, G as SQLiteReactiveDb, Gt as introspectDb, H as AdmitClientEventsResult, Ht as ColumnMetadata, I as CrdtEventValidationError, It as QueryBuilderOutput, J as parametersAreEqual, Jt as compareHLC, K as SharedLiveQuery, Kt as HLC, L as NewCrdtEventValidationResult, Lt as SQLiteDbWrapper, M as createSQLiteCrdtApplyFunction, Mn as dummyKysely, N as SchemaVerificationIssue, Nt as KyselyQueryFactory, O as SnapshotOptions, On as CrdtUpdateLogItem, Ot as createKvStoreTableQuery, P as formatSchemaVerificationIssues, Q as SyncedDbExport, Qt as ReadonlyTable, R as validateNewCrdtEvent, Rt as SQLiteTransactionWrapper, Sn as StoredValue, St as generateId, T as WorkerState, Tn as CrdtEventOrigin, Tt as tryCatch, Ut as DatabaseIntrospection, V as defineSyncSchema, Vt as startPerformanceLogger, W as admitClientEvents, Wt as TableMetadata, X as ImportDataOptions, Xt as serializeHLC, Y as xxhash, Yt as deserializeHLC, Z as ImportDataResult, Zt as CrdtTableConfig, _n as Migrations, _t as TypedBroadcastChannel, a as SyncedDbDatabase, an as ColumnMeta, at as OwnCrdtSnapshot, b as EventsPushResponse, bn as createMigrations, bt as createDeferredPromise, c as SyncedDbTransaction, cn as PayloadValidationResult, ct as SystemMigration, dn as TableBuilder, dt as applyWorkerDbSchema, en as AiAccess, et as createImportData, fn as TableColumns, g as CrdtSyncRemoteSource, gn as MigratableEvent, gt as DistributiveOmit, h as makeCrdtTable, hn as t, ht as DeferredPromise, i as SyncedDb, in as ColumnKind, it as OwnCrdtEvent, j as createCrdtApplyFunction, jn as isNoOpCrdtEventPayload, jt as ExecuteResult, k as createCrdtStorageMutator, kn as CrdtUpdateLogPayload, kt as createSQLiteKvStore, l as UnsafeSyncedDbSqlExecutor, ln as SqliteStorageType, lt as SystemMigrationContext, mn as WriteOrigin, n as ResetRequest, nn as AnyTableBuilder, nt as CrdtStorage, o as SyncedDbOptions, on as ExportImportMode, ot as createCrdtStorage, pn as TableOptions, q as SharedLiveQuerySnapshot, qt as HLCCounter, r as ResetStore, rn as ColumnBuilder, s as SyncedDbSqlExecutor, sn as InferRow, st as SystemDbConfig, t as RESET_REQUEST_TTL_MS, tn as AnyColumnBuilder, u as createSyncedDb, un as SyncSchemaTables, ut as applyMemoryDbSchema, v as EventsPullRequest, vn as MigrationsDb, vt as TypedEvent, w as WorkerNotificationMessage, wn as CRDT_EVENT_NO_OP_PAYLOAD, wt as quoteId, x as createCrdtSyncRemoteSource, xn as createMigrator, xt as createTypedEventTarget, y as EventsPushRequest, yn as SyncDbMigrator, yt as TypedEventTarget, z as DefineSyncSchemaConfig, zt as LogLevel } from "./index-B4s8GFWF.js";
2
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, RESET_REQUEST_TTL_MS, type ReadonlyTable, type ResetRequest, type ResetStore, SQLiteDbWrapper, SQLiteReactiveDb, type SQLiteTransactionWrapper, type SchemaVerificationIssue, type SharedLiveQuery, type SharedLiveQuerySnapshot, type SnapshotOptions, 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, createSyncedDb, createTypedEventTarget, defineSyncSchema, deserializeHLC, dummyKysely, formatSchemaVerificationIssues, generateId, introspectDb, isNoOpCrdtEventPayload, jsonSafeParse, makeCrdtTable, parametersAreEqual, quoteId, serializeHLC, startPerformanceLogger, t, tryCatch, tryCatchAsync, validateNewCrdtEvent, verifySyncSchema, xxhash };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
- import { A as serializeHLC, C as createMigrator, D as HLCCounter, E as introspectDb, M as createExportData, N as createImportData, O as compareHLC, P as dummyKysely, S as createMigrations, T as startPerformanceLogger, _ as createSQLiteCrdtApplyFunction, a as createBroadcastChannels, b as CrdtEventValidationError, c as isWorkerNotificationMessage, d as syncDbClientLockName, g as createCrdtApplyFunction, h as createCrdtStorage, j as xxhash, k as deserializeHLC, m as createCrdtSyncProducer, o as isWorkerErrorResponseMessage, p as createCrdtSyncRemoteSource, t as RESET_REQUEST_TTL_MS, u as isWorkerResponseMessage, v as formatSchemaVerificationIssues, w as SQLiteDbWrapper, x as validateNewCrdtEvent, y as verifySyncSchema } from "./reset-state-4Hn95CKG.js";
2
- import { c as noop, d as tryCatch, f as tryCatchAsync, i as createTypedEventTarget, n as TypedEvent, o as generateId, r as createDeferredPromise, s as jsonSafeParse, t as TypedBroadcastChannel, u as quoteId } from "./utils-Dr5JwcTP.js";
3
- import { a as memoryDbConfig, c as createKvStoreTableQuery, d as CRDT_EVENT_NO_OP_PAYLOAD, f as isNoOpCrdtEventPayload, l as createSQLiteKvStore, n as applyWorkerDbSchema, t as applyMemoryDbSchema, u as createStoredValue } from "./system-schema-CkglcfTS.js";
4
- import { a as registerCrdtIntentDrainer, i as makeCrdtTable } from "./make-crdt-table-CvpTHmRY.js";
1
+ import { C as introspectDb, D as dummyKysely, E as createImportData, S as startPerformanceLogger, T as createExportData, _ as formatSchemaVerificationIssues, a as createBroadcastChannels, b as createMigrator, c as isWorkerNotificationMessage, d as syncDbClientLockName, g as createSQLiteCrdtApplyFunction, h as createCrdtApplyFunction, m as createCrdtStorage, o as isWorkerErrorResponseMessage, p as createCrdtSyncProducer, t as RESET_REQUEST_TTL_MS, u as isWorkerResponseMessage, v as verifySyncSchema, w as xxhash, x as SQLiteDbWrapper, y as createMigrations } from "./reset-state-BVWEcT_z.js";
2
+ import { a as createDeferredPromise, c as generateId, f as quoteId, i as TypedEvent, l as jsonSafeParse, m as tryCatchAsync, n as isNoOpCrdtEventPayload, o as createTypedEventTarget, p as tryCatch, r as TypedBroadcastChannel, t as CRDT_EVENT_NO_OP_PAYLOAD, u as noop } from "./crdt-table-schema-2w_AIpsV.js";
3
+ import { a as HLCCounter, c as serializeHLC, n as CrdtEventValidationError, o as compareHLC, r as validateNewCrdtEvent, s as deserializeHLC, t as createCrdtSyncRemoteSource } from "./crdt-sync-remote-source-Dtvyrk0R.js";
4
+ import { a as memoryDbConfig, c as createKvStoreTableQuery, l as createSQLiteKvStore, n as applyWorkerDbSchema, t as applyMemoryDbSchema, u as createStoredValue } from "./system-schema-C2JZDv6L.js";
5
+ import { n as buildWriteOriginByName, t as admitClientEvents } from "./admit-client-events-BDrJw4SK.js";
6
+ import { a as makeCrdtTable, o as registerCrdtIntentDrainer } from "./make-crdt-table-D8VQ-ptj.js";
5
7
  import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
6
8
  //#region src/bound-map.ts
7
9
  var BoundMap = class {
@@ -83,12 +85,14 @@ var SQLiteReactiveDb = class SQLiteReactiveDb {
83
85
  });
84
86
  sharedLiveQueries = /* @__PURE__ */ new Map();
85
87
  createLiveQuery(query) {
88
+ let rowsVersion = 0;
86
89
  const fetchRows = (parameters) => {
87
90
  let statement = this.liveQueryStatements.get(query.sql);
88
91
  if (!statement) {
89
92
  statement = this.db.prepare(query.sql);
90
93
  this.liveQueryStatements.set(query.sql, statement);
91
94
  }
95
+ rowsVersion = this.mutationVersion;
92
96
  return statement.execute(parameters);
93
97
  };
94
98
  let rows = null;
@@ -110,6 +114,7 @@ var SQLiteReactiveDb = class SQLiteReactiveDb {
110
114
  sql: query.sql,
111
115
  onDataChange: refresh
112
116
  });
117
+ if (rows && this.mutationVersion !== rowsVersion) refresh();
113
118
  return () => {
114
119
  subscription.unsubscribe();
115
120
  subscriber = null;
@@ -128,7 +133,6 @@ var SQLiteReactiveDb = class SQLiteReactiveDb {
128
133
  return existingEntry;
129
134
  }
130
135
  const liveQuery = this.createLiveQuery(query);
131
- let hasDetachedFromLiveQuery = false;
132
136
  const entry = {
133
137
  sql: query.sql,
134
138
  parameters: query.parameters,
@@ -144,21 +148,14 @@ var SQLiteReactiveDb = class SQLiteReactiveDb {
144
148
  this.cancelSharedLiveQueryCleanup(entry);
145
149
  this.registerSharedLiveQueryEntry(entry);
146
150
  entry.listeners.add(onchange);
147
- if (!entry.unsubscribeFromLiveQuery) {
148
- entry.unsubscribeFromLiveQuery = liveQuery.subscribe(() => {
149
- for (const listener of entry.listeners) listener();
150
- });
151
- if (hasDetachedFromLiveQuery) {
152
- hasDetachedFromLiveQuery = false;
153
- liveQuery.refresh();
154
- }
155
- }
151
+ if (!entry.unsubscribeFromLiveQuery) entry.unsubscribeFromLiveQuery = liveQuery.subscribe(() => {
152
+ for (const listener of entry.listeners) listener();
153
+ });
156
154
  return () => {
157
155
  entry.listeners.delete(onchange);
158
156
  if (entry.listeners.size === 0) {
159
157
  entry.unsubscribeFromLiveQuery?.();
160
158
  entry.unsubscribeFromLiveQuery = null;
161
- hasDetachedFromLiveQuery = true;
162
159
  this.scheduleSharedLiveQueryCleanup(entry);
163
160
  }
164
161
  };
@@ -289,6 +286,7 @@ var SQLiteReactiveDb = class SQLiteReactiveDb {
289
286
  }
290
287
  useSnapshot(snapshot) {
291
288
  this.db.useSnapshot(snapshot);
289
+ this.mutationVersion++;
292
290
  this.notifyTableSubscribers();
293
291
  }
294
292
  dispose() {
@@ -333,33 +331,6 @@ function parametersAreEqual(a, b) {
333
331
  return true;
334
332
  }
335
333
  //#endregion
336
- //#region src/schema/admit-client-events.ts
337
- function buildWriteOriginByName(schema) {
338
- const writeOriginByName = /* @__PURE__ */ new Map();
339
- for (const { crdtTableName, baseTableName } of schema.tablesConfig) {
340
- const writeOrigin = schema.tables[crdtTableName]?.writeOrigin ?? "any";
341
- writeOriginByName.set(crdtTableName, writeOrigin);
342
- writeOriginByName.set(baseTableName, writeOrigin);
343
- }
344
- return writeOriginByName;
345
- }
346
- /**
347
- * Splits a client push into events the hub should persist and events for tables with
348
- * `{ writes: "server" }`. Unknown datasets stay admitted. Matches either the crdt or the base
349
- * table name.
350
- */
351
- function admitClientEvents(opts) {
352
- const writeOriginByName = opts.syncDbSchema.writeOriginByName ?? buildWriteOriginByName(opts.syncDbSchema);
353
- const admitted = [];
354
- const skipped = [];
355
- for (const event of opts.events) if (writeOriginByName.get(event.dataset) === "server") skipped.push(event);
356
- else admitted.push(event);
357
- return {
358
- admitted,
359
- skipped
360
- };
361
- }
362
- //#endregion
363
334
  //#region src/schema/define-sync-schema.ts
364
335
  const RESERVED_TABLE_NAMES = /* @__PURE__ */ new Set([
365
336
  "change_history",
@@ -506,24 +477,25 @@ var TableBuilder = class TableBuilder {
506
477
  }
507
478
  /**
508
479
  * Validate a CRDT event payload against the declared columns.
509
- * `item-created` payloads must contain every column without a default (nullable columns may be omitted);
480
+ * `item-created` payloads must contain every column without a default (nullable columns may be omitted)
481
+ * and their `tombstone` value is ignored, since applying a create always sets it to 0;
510
482
  * `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.
511
483
  */
512
484
  validatePayload(payload, options) {
513
485
  const event = options?.event ?? "item-created";
514
486
  const errors = [];
515
- for (const key of Object.keys(payload)) if (!(key in this.columns)) errors.push(`Unknown column "${key}"`);
487
+ for (const key of Object.keys(payload)) if (!Object.hasOwn(this.columns, key)) errors.push(`Unknown column "${key}"`);
516
488
  if (event === "item-updated") {
517
489
  if ("id" in payload) errors.push(`Column "id" is immutable and cannot appear in an update payload`);
518
490
  if (payload.tombstone === true || payload.tombstone === 1) errors.push(`Column "tombstone" cannot be set to true/1 in an update payload; use an item-deleted event`);
519
491
  }
520
492
  if (event === "item-created") for (const [name, meta] of Object.entries(this.columns)) {
521
- if (name in payload || name === "tombstone" || meta.nullable || meta.hasDefault) continue;
493
+ if (Object.hasOwn(payload, name) || name === "tombstone" || meta.nullable || meta.hasDefault) continue;
522
494
  errors.push(`Missing required column "${name}"`);
523
495
  }
524
496
  for (const [name, value] of Object.entries(payload)) {
525
- const meta = this.columns[name];
526
- if (!meta || event === "item-updated" && name === "id") continue;
497
+ const meta = Object.hasOwn(this.columns, name) ? this.columns[name] : void 0;
498
+ if (!meta || event === "item-updated" && name === "id" || event === "item-created" && name === "tombstone") continue;
527
499
  const error = validateValue(name, value, meta);
528
500
  if (error) errors.push(error);
529
501
  }
@@ -762,6 +734,7 @@ const createWorkerDbClient = async ({ broadcastChannels, worker, config }) => {
762
734
  postState: () => queryWorker("postState", []),
763
735
  goOnline: () => queryWorker("goOnline", []),
764
736
  goOffline: () => queryWorker("goOffline", []),
737
+ sync: () => queryWorker("sync", []),
765
738
  requestReload: (options) => queryWorker("requestReload", [options])
766
739
  };
767
740
  const statePromise = awaitWorkerState(eventTarget);
@@ -921,6 +894,13 @@ async function createSyncedDb(options) {
921
894
  const reloadRequestedSubscription = workerClient.subscribe("reload-requested", () => {
922
895
  globalThis.location?.reload();
923
896
  });
897
+ const sync = () => {
898
+ workerClient.sync().catch((error) => console.warn("Failed to start sync", error));
899
+ };
900
+ const syncWhenVisible = () => {
901
+ if (globalThis.document?.visibilityState === "visible") sync();
902
+ };
903
+ globalThis.document?.addEventListener("visibilitychange", syncWhenVisible);
924
904
  perf.logEnd("createSyncedDb", "initialized", "info");
925
905
  const exportData = createExportData({
926
906
  reactiveDb,
@@ -934,6 +914,7 @@ async function createSyncedDb(options) {
934
914
  if (isDisposed) return;
935
915
  isDisposed = true;
936
916
  unregisterDevtools?.();
917
+ globalThis.document?.removeEventListener("visibilitychange", syncWhenVisible);
937
918
  reloadRequestedSubscription.unsubscribe();
938
919
  clientLockRelease.resolve();
939
920
  await tabRemoteSource.dispose();
@@ -951,7 +932,8 @@ async function createSyncedDb(options) {
951
932
  return unsubscribe;
952
933
  },
953
934
  goOnline: workerClient.goOnline.bind(workerClient),
954
- goOffline: workerClient.goOffline.bind(workerClient)
935
+ goOffline: workerClient.goOffline.bind(workerClient),
936
+ sync
955
937
  },
956
938
  /**
957
939
  * Ask the elected worker to broadcast a page reload to all tabs for this dbId.
@@ -978,6 +960,7 @@ async function createSyncedDb(options) {
978
960
  getSharedLiveQueriesSnapshot: reactiveDb.getSharedLiveQueriesSnapshot.bind(reactiveDb),
979
961
  crdtTableNames: options.syncDbSchema.tablesConfig.map((table) => table.crdtTableName),
980
962
  crdtTablesConfig: options.syncDbSchema.tablesConfig,
963
+ tables: options.syncDbSchema.tables,
981
964
  schemaVersion: workerClientSnapshot.schemaVersion,
982
965
  migrationVersions: Object.keys(options.syncDbSchema.migrations).map(Number).sort((a, b) => a - b)
983
966
  }