@sqlite-sync/core 0.10.0 → 0.10.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.
@@ -247,6 +247,7 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
247
247
  };
248
248
  let current = null;
249
249
  let disposed = false;
250
+ let wantsOnline = false;
250
251
  const disconnectSource = (source) => {
251
252
  const watchdog = setTimeout(() => {
252
253
  console.error(`Remote source did not disconnect within ${DISCONNECT_WATCHDOG_MS}ms`);
@@ -448,16 +449,19 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
448
449
  });
449
450
  return Promise.resolve();
450
451
  }
452
+ wantsOnline = true;
451
453
  const connection = createConnection(remoteFactory);
452
454
  current = connection;
453
455
  patchRemoteState({ type: "pending" });
454
456
  return connection.ready;
455
457
  };
456
458
  const goOffline = async (reason) => {
459
+ wantsOnline = false;
457
460
  if (current) closeConnection(current, reason);
458
461
  };
459
462
  const syncWithRemote = async () => {
460
- await current?.sync();
463
+ if (current) await current.sync();
464
+ else if (wantsOnline && !remoteState.schemaVersionMismatched) await goOnline();
461
465
  };
462
466
  const checkRemoteConsistency = (remoteSyncId, remoteEventHlcSum) => {
463
467
  if (remoteEventHlcSum === null) return;
@@ -501,4 +505,4 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
501
505
  //#endregion
502
506
  export { HLCCounter as a, serializeHLC as c, DEFAULT_MAX_DRIFT_MS as i, CrdtEventValidationError as n, compareHLC as o, validateNewCrdtEvent as r, deserializeHLC as s, createCrdtSyncRemoteSource as t };
503
507
 
504
- //# sourceMappingURL=crdt-sync-remote-source-Dtvyrk0R.js.map
508
+ //# sourceMappingURL=crdt-sync-remote-source-CugbiosG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt-sync-remote-source-CugbiosG.js","names":[],"sources":["../src/hlc.ts","../src/schema/validate-crdt-event.ts","../src/sqlite-crdt/retry-remote-operation.ts","../src/sqlite-crdt/crdt-sync-remote-source.ts"],"sourcesContent":["export interface HLC {\n timestamp: number;\n counter: number;\n nodeId: string;\n}\n\nconst MAX_COUNTER = 36 ** 5 - 1; // 60,466,175 — max value that fits in 5-char base36\nexport const DEFAULT_MAX_DRIFT_MS = 6 * 60 * 60 * 1000; // 6 hours\n\nexport class HLCCounter {\n private timestamp: number;\n private counter: number;\n private nodeId: string;\n\n private readonly getTimestamp: () => number;\n private readonly maxDrift: number;\n\n constructor(nodeId: string, getTimestamp: () => number, maxDrift: number = DEFAULT_MAX_DRIFT_MS) {\n this.timestamp = getTimestamp();\n this.counter = 0;\n this.nodeId = nodeId;\n this.getTimestamp = getTimestamp;\n this.maxDrift = maxDrift;\n }\n\n getCurrentHLC(): HLC {\n return {\n timestamp: this.timestamp,\n counter: this.counter,\n nodeId: this.nodeId,\n };\n }\n\n getNextHLC(): HLC {\n const now = this.getTimestamp();\n\n if (now > this.timestamp) {\n this.timestamp = now;\n this.counter = 0;\n return this.getCurrentHLC();\n }\n\n this.counter++;\n this.rollOverCounter();\n return this.getCurrentHLC();\n }\n\n /** Restore already-persisted causal state, regardless of wall-clock drift. */\n restoreHLC(hlc: HLC) {\n if (hlc.timestamp > this.timestamp) {\n this.timestamp = hlc.timestamp;\n this.counter = hlc.counter;\n } else if (hlc.timestamp === this.timestamp) {\n this.counter = Math.max(this.counter, hlc.counter);\n }\n }\n\n mergeHLC(hlc: HLC) {\n const now = this.getTimestamp();\n if (hlc.timestamp - now > this.maxDrift) {\n console.warn(\n `HLC: ignoring far-future timestamp (remote=${hlc.timestamp}, local=${now}, drift=${hlc.timestamp - now}ms)`,\n );\n return;\n }\n\n if (this.timestamp === hlc.timestamp) {\n this.counter = Math.max(this.counter, hlc.counter) + 1;\n } else if (this.timestamp > hlc.timestamp) {\n this.counter++;\n } else {\n this.timestamp = hlc.timestamp;\n this.counter = hlc.counter + 1;\n }\n this.rollOverCounter();\n }\n\n private rollOverCounter() {\n if (this.counter > MAX_COUNTER) {\n this.timestamp += 1;\n this.counter = 0;\n }\n }\n}\n\nexport function serializeHLC(hlc: HLC) {\n return `${hlc.timestamp.toString().padStart(15, \"0\")}:${hlc.counter.toString(36).padStart(5, \"0\")}:${hlc.nodeId}`;\n}\n\nexport function deserializeHLC(serialized: string): HLC {\n const parts = serialized.split(\":\");\n if (parts.length < 3) {\n throw new Error(`Invalid HLC format: expected at least 3 colon-separated segments, got ${parts.length}`);\n }\n\n const timestamp = parseInt(parts[0], 10);\n const counter = parseInt(parts[1], 36);\n\n if (Number.isNaN(timestamp) || Number.isNaN(counter)) {\n throw new Error(`Invalid HLC values: timestamp=${parts[0]}, counter=${parts[1]}`);\n }\n\n return {\n timestamp,\n counter,\n nodeId: parts.slice(2).join(\":\"),\n };\n}\n\nexport function compareHLC(one: HLC, two: HLC) {\n if (one.timestamp === two.timestamp) {\n if (one.counter === two.counter) {\n if (one.nodeId === two.nodeId) {\n return 0;\n }\n return one.nodeId < two.nodeId ? -1 : 1;\n }\n return one.counter - two.counter;\n }\n return one.timestamp - two.timestamp;\n}\n","import type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport type { CrdtEventType, NewCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport type { AnyTableBuilder } from \"./table-builder\";\n\nexport type NewCrdtEventValidationResult =\n | { success: true; event: NewCrdtEvent }\n | { success: false; errors: string[] };\n\nexport class CrdtEventValidationError extends Error {\n constructor(readonly errors: string[]) {\n super(`Invalid CRDT events: ${errors.join(\"; \")}`);\n this.name = \"CrdtEventValidationError\";\n }\n}\n\nconst crdtEventTypes = [\"item-created\", \"item-updated\", \"item-deleted\"] as const;\n\nexport function validateNewCrdtEvent(\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">,\n input: unknown,\n): NewCrdtEventValidationResult {\n const errors: string[] = [];\n\n if (!isRecord(input)) {\n return { success: false, errors: [\"Event must be an object\"] } satisfies NewCrdtEventValidationResult;\n }\n\n const type = input.type;\n const dataset = input.dataset;\n const itemId = input.item_id;\n\n let payload = input.payload;\n let payloadParseFailed = false;\n if (typeof payload === \"string\") {\n try {\n payload = JSON.parse(payload);\n } catch {\n payloadParseFailed = true;\n errors.push(`Event payload is not valid JSON`);\n }\n }\n\n if (!isCrdtEventType(type)) {\n errors.push(`Invalid event type \"${String(type)}\"`);\n }\n if (typeof dataset !== \"string\") {\n errors.push(`Event dataset must be a string`);\n }\n if (typeof itemId !== \"string\") {\n errors.push(`Event item_id must be a string`);\n }\n if (!payloadParseFailed && !isRecord(payload)) {\n errors.push(`Event payload must be an object`);\n }\n\n const datasetMatch = typeof dataset === \"string\" ? findTableForDataset(schema, dataset) : null;\n if (typeof dataset === \"string\" && !datasetMatch) {\n errors.push(`Unknown dataset \"${dataset}\"`);\n }\n\n if (isCrdtEventType(type) && datasetMatch && isRecord(payload)) {\n switch (type) {\n case \"item-created\": {\n const result = datasetMatch.table.validatePayload(payload, { event: \"item-created\" });\n if (!result.success) {\n errors.push(...result.errors.map((error) => `payload: ${error}`));\n }\n if (typeof itemId === \"string\" && \"id\" in payload && payload.id !== itemId) {\n errors.push(`payload: Column \"id\" must match item_id \"${itemId}\"`);\n }\n break;\n }\n case \"item-updated\": {\n const result = datasetMatch.table.validatePayload(payload, { event: \"item-updated\" });\n if (!result.success) {\n errors.push(...result.errors.map((error) => `payload: ${error}`));\n }\n break;\n }\n case \"item-deleted\":\n if (Object.keys(payload).length > 0) {\n errors.push(`payload: item-deleted events must have an empty payload`);\n }\n break;\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors } satisfies NewCrdtEventValidationResult;\n }\n\n return {\n success: true,\n event: {\n type: type as CrdtEventType,\n dataset: datasetMatch?.baseTableName ?? (dataset as string),\n item_id: itemId as string,\n payload: payload as Record<string, unknown>,\n },\n } satisfies NewCrdtEventValidationResult;\n}\n\nfunction findTableForDataset(\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">,\n dataset: string,\n): { table: AnyTableBuilder; baseTableName: string } | null {\n const config = schema.tablesConfig.find(\n (tableConfig) => tableConfig.baseTableName === dataset || tableConfig.crdtTableName === dataset,\n );\n if (config) {\n const table = schema.tables[config.crdtTableName];\n return table ? { table, baseTableName: config.baseTableName } : null;\n }\n const table = Object.hasOwn(schema.tables, dataset) ? schema.tables[dataset] : undefined;\n return table ? { table, baseTableName: dataset } : null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCrdtEventType(value: unknown): value is CrdtEventType {\n return typeof value === \"string\" && crdtEventTypes.includes(value as CrdtEventType);\n}\n","type RetryOptions = {\n maxAttempts: number;\n backoffBaseMs: number;\n backoffExponent: number;\n backoffJitterMs: number;\n timeoutMs: number;\n shouldRetry?: (error: unknown) => boolean;\n};\n\nexport const REMOTE_RETRY_OPTIONS: RetryOptions = {\n maxAttempts: 3,\n backoffBaseMs: 100,\n backoffExponent: 1.5,\n backoffJitterMs: 150,\n timeoutMs: 10000,\n};\n\nclass RetryTimeoutError extends Error {\n constructor(\n message: string,\n public previous?: unknown,\n ) {\n super(message);\n this.name = \"TimeoutError\";\n }\n}\n\nconst applyJitter = (delayMs: number, maxJitterMs: number): number => {\n const jitter = Math.random() * maxJitterMs * (Math.random() > 0.5 ? 1 : -1);\n return Math.max(0, delayMs + jitter);\n};\n\nconst delay = (delayMs: number) => new Promise((resolve) => setTimeout(resolve, delayMs));\n\nconst withTimeout = async <T>(operation: () => Promise<T>, timeoutMs: number, previousError?: unknown): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n return await Promise.race([\n operation(),\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new RetryTimeoutError(\"Remote operation timed out\", previousError)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n};\n\nexport const retryRemoteOperation = async <T>(operation: () => Promise<T>, options: RetryOptions): Promise<T> => {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {\n try {\n return await withTimeout(operation, options.timeoutMs, lastError);\n } catch (error) {\n lastError = error;\n\n if (attempt >= options.maxAttempts || options.shouldRetry?.(error) === false) {\n throw error;\n }\n\n const backoffDelay = applyJitter(\n options.backoffBaseMs * options.backoffExponent ** (attempt - 1),\n options.backoffJitterMs,\n );\n if (backoffDelay > 0) {\n await delay(backoffDelay);\n }\n }\n }\n\n throw lastError;\n};\n","import type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { createTypedEventTarget, ensureSingletonExecution, tryCatchAsync } from \"../utils\";\nimport type { EventsPullResponse, WorkerState } from \"../worker-db/worker-common\";\nimport type { PendingCrdtEvent } from \"./apply-crdt-event\";\nimport type { CrdtStorage } from \"./crdt-storage\";\nimport { REMOTE_RETRY_OPTIONS, retryRemoteOperation } from \"./retry-remote-operation\";\nimport type { StoredValue } from \"./stored-value\";\n\ntype CrdtSyncRemoteSourceConfig = {\n bufferSize: number;\n storage: CrdtStorage;\n migrator: SyncDbMigrator;\n pullSyncId: StoredValue<number>;\n pushSyncId: StoredValue<number>;\n nodeId: string;\n remoteFactory?: CreateRemoteSourceFactory;\n};\n\nexport type EventsPullRequest = {\n afterSyncId: number;\n excludeNodeId?: string;\n};\n\nexport const PUSH_BATCH_SIZE = 75;\nexport const MAX_PUSH_EVENTS = 100;\n\nexport type EventsPushRequest = {\n nodeId: string;\n events: (PendingCrdtEvent & { schema_version: number })[];\n};\nexport type EventsPushResponse = {\n ok: boolean;\n /** Remote sync_id right before the pushed events were enqueued. */\n beforeSyncId?: number;\n /** Remote sync_id right after the pushed events were enqueued. */\n afterSyncId?: number;\n};\n\nconst DISCONNECT_WATCHDOG_MS = 15_000;\n\nexport type CrdtSyncRemoteSource = ReturnType<typeof createCrdtSyncRemoteSource>;\n\nexport type EventsAvailable = {\n newSyncId: number;\n remoteEventHlcSum: string | null;\n};\n\n/**\n * Opens a connection to the remote. Each call must return an independent source: the library calls\n * `disconnect` on every returned source exactly once, including one that resolves after `signal`\n * has aborted. `signal` aborts when the connection attempt is cancelled or the connection is closed.\n * Call `onReconnected` when the transport re-establishes itself without a new source: broadcasts\n * sent while it was down are lost, so the library pulls and pushes to catch up.\n */\nexport type CreateRemoteSourceFactory = (opts: {\n onEventsAvailable: (event: EventsAvailable) => void;\n onReconnected: () => void;\n signal: AbortSignal;\n}) => RemoteSource | Promise<RemoteSource>;\n\ntype RemoteSource = {\n pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;\n pushEvents: (request: EventsPushRequest) => Promise<EventsPushResponse>;\n disconnect?: () => void | Promise<void>;\n};\n\nexport class SchemaVersionMismatchError extends Error {\n constructor(\n public remoteSchemaVersion: number,\n public localSchemaVersion: number,\n ) {\n super(`Schema version mismatch: remote ${remoteSchemaVersion} != local ${localSchemaVersion}`);\n this.name = \"SchemaVersionMismatchError\";\n }\n}\n\ntype RemoteSourceState =\n | {\n type: \"pending\";\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n }\n | {\n type: \"offline\";\n reason: OfflineReason;\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n }\n | {\n type: \"online\";\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n };\n\nexport type OfflineReason =\n | \"NOT_INITIALIZED\"\n | \"INITIALIZATION_FAILED\"\n | \"REMOTE_PUSH_ERROR\"\n | \"REMOTE_PULL_ERROR\"\n | \"DISCONNECTED\";\n\nexport type DeSyncDetectedReason = \"CHECKSUM_MISMATCH\" | \"ERROR_APPLYING_REMOTE_EVENT\";\n\nexport const createCrdtSyncRemoteSource = ({\n bufferSize,\n storage,\n migrator,\n pullSyncId,\n pushSyncId,\n nodeId,\n remoteFactory,\n}: CrdtSyncRemoteSourceConfig) => {\n const eventTarget = createTypedEventTarget<{\n \"state-changed\": RemoteSourceState[\"type\"];\n \"de-sync-detected\": {\n reason: DeSyncDetectedReason;\n };\n \"remote-schema-version-mismatch\": {\n remoteSchemaVersion: number;\n localSchemaVersion: number;\n };\n }>();\n\n let remoteState: RemoteSourceState = {\n type: \"offline\",\n reason: \"NOT_INITIALIZED\",\n deSynced: false,\n schemaVersionMismatched: false,\n };\n\n const patchRemoteState = (state: Partial<RemoteSourceState>) => {\n remoteState = { ...remoteState, ...state } as RemoteSourceState;\n eventTarget.dispatchEvent(\"state-changed\", remoteState.type);\n };\n\n let current: Connection | null = null;\n let disposed = false;\n let wantsOnline = false;\n\n const disconnectSource = (source: RemoteSource) => {\n const watchdog = setTimeout(() => {\n console.error(`Remote source did not disconnect within ${DISCONNECT_WATCHDOG_MS}ms`);\n }, DISCONNECT_WATCHDOG_MS);\n void tryCatchAsync(async () => await source.disconnect?.()).then((result) => {\n clearTimeout(watchdog);\n if (!result.success) {\n console.warn(\"Error while disconnecting from remote source\", result.error);\n }\n });\n };\n\n const closeConnection = (connection: Connection, reason: OfflineReason) => {\n if (current !== connection) {\n return;\n }\n current = null;\n patchRemoteState({ type: \"offline\", reason });\n connection.close();\n };\n\n type Connection = ReturnType<typeof createConnection>;\n const createConnection = (factory: CreateRemoteSourceFactory) => {\n const controller = new AbortController();\n const { signal } = controller;\n let source: RemoteSource | null = null;\n\n const untilClosed = <T>(operation: () => Promise<T>) =>\n new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(new Error(\"Remote connection closed\"));\n if (signal.aborted) {\n onAbort();\n return;\n }\n signal.addEventListener(\"abort\", onAbort, { once: true });\n operation()\n .then(resolve, reject)\n .finally(() => signal.removeEventListener(\"abort\", onAbort));\n });\n\n const retryOptions = { ...REMOTE_RETRY_OPTIONS, shouldRetry: () => !signal.aborted };\n\n const connect = async () => {\n if (signal.aborted) {\n return;\n }\n\n const result = await tryCatchAsync(\n async () =>\n await factory({\n onEventsAvailable: ({ newSyncId, remoteEventHlcSum }) => {\n pull({ remoteSyncId: newSyncId, remoteEventHlcSum, includeSelf: false });\n },\n onReconnected: () => {\n void sync();\n },\n signal,\n }),\n );\n\n if (signal.aborted) {\n if (result.success) {\n disconnectSource(result.data);\n }\n return;\n }\n\n if (!result.success) {\n console.warn(\"Failed to create remote source\", result.error);\n closeConnection(connection, \"INITIALIZATION_FAILED\");\n return;\n }\n\n source = result.data;\n patchRemoteState({ type: \"online\", deSynced: false, schemaVersionMismatched: false });\n void sync();\n };\n\n type ActivePull = { promise: Promise<void>; requestedSyncId: number | null };\n let activePull: ActivePull | null = null;\n const pull = (request?: {\n remoteSyncId?: number;\n remoteEventHlcSum?: string | null;\n includeSelf?: boolean;\n }): Promise<void> => {\n if (!source || signal.aborted) {\n return Promise.resolve();\n }\n const activeSource = source;\n\n const remoteSyncId = request?.remoteSyncId;\n\n if (remoteSyncId !== undefined && remoteSyncId <= pullSyncId.current) {\n // We are already caught up to this broadcast, so there is nothing to pull.\n // This is the quiescent moment to verify we have not diverged from the\n // remote (the check is a no-op unless we are exactly aligned: remoteSyncId\n // === pullSyncId.current).\n checkRemoteConsistency(remoteSyncId, request?.remoteEventHlcSum ?? null);\n return Promise.resolve();\n }\n\n if (activePull) {\n if (remoteSyncId !== undefined && (!activePull.requestedSyncId || activePull.requestedSyncId < remoteSyncId)) {\n activePull.requestedSyncId = remoteSyncId;\n }\n return activePull.promise;\n }\n\n const nextPull: ActivePull = { promise: Promise.resolve(), requestedSyncId: null };\n nextPull.promise = pullAllEvents(activeSource, {\n afterSyncId: pullSyncId.current,\n excludeNodeId: request?.includeSelf ? undefined : nodeId,\n })\n .catch((error) => {\n if (signal.aborted) {\n return;\n }\n console.error(\"Error pulling events. Going offline.\", error);\n closeConnection(connection, \"REMOTE_PULL_ERROR\");\n })\n .finally(() => {\n if (activePull === nextPull) {\n activePull = null;\n }\n\n const nextTarget = nextPull.requestedSyncId;\n if (nextTarget && nextTarget > pullSyncId.current) {\n pull({ remoteSyncId: nextTarget });\n }\n });\n activePull = nextPull;\n return nextPull.promise;\n };\n\n const pullAllEvents = async (activeSource: RemoteSource, opts: EventsPullRequest) => {\n let hasMore = true;\n let afterSyncId = opts.afterSyncId;\n while (hasMore) {\n const response = await retryRemoteOperation(\n () =>\n untilClosed(() =>\n activeSource.pullEvents({\n ...opts,\n afterSyncId,\n }),\n ),\n retryOptions,\n );\n if (signal.aborted) {\n return;\n }\n hasMore = response.hasMore;\n afterSyncId = response.nextSyncId;\n\n if (response.events) {\n const { processed } = storage.enqueueRemoteEvents(\n response.events.map((x) => {\n if (x.schema_version > migrator.currentSchemaVersion) {\n eventTarget.dispatchEvent(\"remote-schema-version-mismatch\", {\n remoteSchemaVersion: x.schema_version,\n localSchemaVersion: migrator.currentSchemaVersion,\n });\n if (remoteState.type === \"online\" && !remoteState.schemaVersionMismatched) {\n patchRemoteState({ schemaVersionMismatched: true });\n }\n throw new SchemaVersionMismatchError(x.schema_version, migrator.currentSchemaVersion);\n }\n return x;\n }),\n );\n await untilClosed(() => processed);\n if (signal.aborted) {\n return;\n }\n }\n if (response.nextSyncId <= pullSyncId.current) {\n break;\n }\n if (response.nextSyncId > pullSyncId.current) {\n pullSyncId.current = response.nextSyncId;\n }\n }\n };\n\n const push = ensureSingletonExecution(async () => {\n while (source && !signal.aborted) {\n const activeSource = source;\n const eventsBatch = storage.getEventsBatch({\n status: \"applied\",\n afterSyncId: pushSyncId.current,\n excludeOrigin: \"remote\",\n limit: bufferSize,\n });\n if (eventsBatch.events.length === 0) {\n break;\n }\n\n let response: EventsPushResponse;\n try {\n response = await retryRemoteOperation(\n () =>\n untilClosed(() =>\n activeSource.pushEvents({\n nodeId,\n events: eventsBatch.events.map((event) => ({\n schema_version: event.schema_version,\n timestamp: event.timestamp,\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n payload: event.payload,\n })),\n }),\n ),\n retryOptions,\n );\n } catch (error) {\n if (!signal.aborted) {\n console.error(\"Error pushing events. Going offline.\", error);\n closeConnection(connection, \"REMOTE_PUSH_ERROR\");\n }\n return;\n }\n\n if (signal.aborted) {\n return;\n }\n\n if (!response.ok) {\n console.error(\"Remote rejected pushed events. Going offline.\");\n closeConnection(connection, \"REMOTE_PUSH_ERROR\");\n return;\n }\n\n pushSyncId.current = eventsBatch.nextSyncId;\n\n // Fast-forward the pull cursor: the remote assigns sync ids for the pushed\n // events synchronously, so (beforeSyncId, afterSyncId] contains only this\n // node's own events. If we are caught up to at least beforeSyncId, the skipped\n // range (pullSyncId, afterSyncId] contains only our own events, so there is\n // nothing to pull up to afterSyncId.\n if (\n response.beforeSyncId !== undefined &&\n response.afterSyncId !== undefined &&\n response.beforeSyncId <= pullSyncId.current &&\n response.afterSyncId > pullSyncId.current\n ) {\n pullSyncId.current = response.afterSyncId;\n }\n if (!eventsBatch.hasMore) {\n break;\n }\n }\n });\n\n // Waits out in-flight operations first: they may have started before the caller's request.\n const sync = async () => {\n await activePull?.promise;\n await pull();\n await push.promise();\n await push();\n };\n\n const close = () => {\n controller.abort();\n if (source) {\n disconnectSource(source);\n }\n };\n\n const ready = Promise.resolve()\n .then(() => untilClosed(connect))\n .catch((error) => {\n if (!signal.aborted) {\n throw error;\n }\n });\n\n const connection = { ready, close, push, sync };\n return connection;\n };\n\n const goOnline = (): Promise<void> => {\n if (disposed) {\n return Promise.resolve();\n }\n if (current) {\n return current.ready;\n }\n if (!remoteFactory) {\n console.warn(\"Remote source factory not provided. Going offline.\");\n patchRemoteState({ type: \"offline\", reason: \"NOT_INITIALIZED\" });\n return Promise.resolve();\n }\n\n wantsOnline = true;\n const connection = createConnection(remoteFactory);\n current = connection;\n patchRemoteState({ type: \"pending\" });\n return connection.ready;\n };\n\n const goOffline = async (reason: OfflineReason) => {\n wantsOnline = false;\n if (current) {\n closeConnection(current, reason);\n }\n };\n\n // Reconnects only when a connection error, not goOffline, took us offline. Retrying after a schema\n // mismatch would fail the same way.\n const syncWithRemote = async () => {\n if (current) {\n await current.sync();\n } else if (wantsOnline && !remoteState.schemaVersionMismatched) {\n await goOnline();\n }\n };\n\n // De-sync detection: when we are exactly caught up to the remote's broadcast\n // sync id and fully quiescent, our applied-event set must equal the remote's,\n // so our HLC checksums must match. A mismatch means the nodes have diverged.\n const checkRemoteConsistency = (remoteSyncId: number, remoteEventHlcSum: string | null) => {\n // A remote with no accumulator gives us nothing to compare against.\n if (remoteEventHlcSum === null) {\n return;\n }\n\n // Only meaningful when we are exactly caught up: if we are behind we still\n // need to pull; if we are ahead our state covers events the remote checksum\n // does not.\n if (remoteSyncId !== pullSyncId.current) {\n return;\n }\n\n // Quiescence: the accumulator only matches the remote's when nothing is left\n // to apply locally and no local applied events are still waiting to be pushed\n // (those are in our accumulator but the remote has not seen them yet).\n if (!storage.checkIsQuiescent(pushSyncId.current)) {\n return;\n }\n\n const localEventHlcSum = storage.getEventHlcAccumulator();\n if (localEventHlcSum === null) {\n return;\n }\n\n if (localEventHlcSum === remoteEventHlcSum) {\n // No de-sync detected.\n return;\n }\n\n eventTarget.dispatchEvent(\"de-sync-detected\", { reason: \"CHECKSUM_MISMATCH\" });\n console.warn(\n `[sqlite-sync] De-sync detected at syncId ${remoteSyncId}: local HLC checksum ${localEventHlcSum} != remote ${remoteEventHlcSum}. Local and remote have diverged despite being caught up.`,\n );\n if (remoteState.type === \"online\" && !remoteState.deSynced) {\n patchRemoteState({ deSynced: true });\n }\n };\n\n const eventsAppliedSubscription = storage.addEventListener(\"events-applied\", () => {\n current?.push();\n });\n\n const remoteEventApplyFailedSubscription = storage.addEventListener(\"remote-event-apply-failed\", () => {\n eventTarget.dispatchEvent(\"de-sync-detected\", { reason: \"ERROR_APPLYING_REMOTE_EVENT\" });\n if (remoteState.type === \"online\" && !remoteState.deSynced) {\n patchRemoteState({ deSynced: true });\n }\n });\n\n const getState = (): WorkerState => ({\n remoteState: remoteState.type,\n deSynced: remoteState.deSynced,\n schemaVersionMismatched: remoteState.schemaVersionMismatched,\n });\n\n const dispose = async () => {\n disposed = true;\n eventsAppliedSubscription.unsubscribe();\n remoteEventApplyFailedSubscription.unsubscribe();\n await goOffline(\"DISCONNECTED\");\n };\n\n return {\n goOnline,\n goOffline,\n syncWithRemote,\n getState,\n dispose,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n};\n"],"mappings":"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"}
@@ -935,6 +935,8 @@ type WorkerNotificationMessage = {
935
935
  } | {
936
936
  notificationType: "state-changed";
937
937
  state: WorkerState;
938
+ /** Id of the worker's persisted database. A wiped database gets a new one. */
939
+ storageGeneration: string;
938
940
  } | {
939
941
  notificationType: "reload-requested";
940
942
  reloadEpoch: string;
@@ -1153,6 +1155,7 @@ declare function createSyncedDb<Database, Props = undefined>(options: SyncedDbOp
1153
1155
  "state-changed": {
1154
1156
  notificationType: "state-changed";
1155
1157
  state: WorkerState;
1158
+ storageGeneration: string;
1156
1159
  };
1157
1160
  "de-sync-detected": {
1158
1161
  notificationType: "de-sync-detected";
@@ -1217,4 +1220,4 @@ type ResetStore = {
1217
1220
  declare const RESET_REQUEST_TTL_MS: number;
1218
1221
  //#endregion
1219
1222
  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
1223
+ //# sourceMappingURL=index-D0jdmcYF.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
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";
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-D0jdmcYF.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,6 +1,6 @@
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";
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-DNPkxGim.js";
2
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";
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-CugbiosG.js";
4
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
5
  import { n as buildWriteOriginByName, t as admitClientEvents } from "./admit-client-events-BDrJw4SK.js";
6
6
  import { a as makeCrdtTable, o as registerCrdtIntentDrainer } from "./make-crdt-table-D8VQ-ptj.js";
@@ -740,9 +740,10 @@ const createWorkerDbClient = async ({ broadcastChannels, worker, config }) => {
740
740
  const statePromise = awaitWorkerState(eventTarget);
741
741
  postWorkerConfig(worker, config);
742
742
  rpc.postState().catch(noop);
743
- let workerState = await statePromise;
743
+ let { state: workerState, storageGeneration } = await statePromise;
744
744
  eventTarget.addEventListener("state-changed", (event) => {
745
745
  workerState = event.payload.state;
746
+ storageGeneration = event.payload.storageGeneration;
746
747
  });
747
748
  const dispose = () => {
748
749
  isDisposed = true;
@@ -756,13 +757,14 @@ const createWorkerDbClient = async ({ broadcastChannels, worker, config }) => {
756
757
  ...rpc,
757
758
  subscribe: eventTarget.addEventListener,
758
759
  getState: () => workerState,
760
+ getStorageGeneration: () => storageGeneration,
759
761
  dispose
760
762
  };
761
763
  };
762
764
  function awaitWorkerState(eventTarget) {
763
765
  const promise = createDeferredPromise({ timeout: 15e3 });
764
766
  const subscription = eventTarget.addEventListener("state-changed", (event) => {
765
- promise.resolve(event.payload.state);
767
+ promise.resolve(event.payload);
766
768
  subscription.unsubscribe();
767
769
  });
768
770
  return promise.promise;
@@ -894,6 +896,16 @@ async function createSyncedDb(options) {
894
896
  const reloadRequestedSubscription = workerClient.subscribe("reload-requested", () => {
895
897
  globalThis.location?.reload();
896
898
  });
899
+ const reloadIfStorageReplaced = (storageGeneration) => {
900
+ if (storageGeneration !== workerClientSnapshot.storageGeneration) {
901
+ storageReplacedSubscription.unsubscribe();
902
+ globalThis.location?.reload();
903
+ }
904
+ };
905
+ const storageReplacedSubscription = workerClient.subscribe("state-changed", (event) => {
906
+ reloadIfStorageReplaced(event.payload.storageGeneration);
907
+ });
908
+ reloadIfStorageReplaced(workerClient.getStorageGeneration());
897
909
  const sync = () => {
898
910
  workerClient.sync().catch((error) => console.warn("Failed to start sync", error));
899
911
  };
@@ -901,6 +913,7 @@ async function createSyncedDb(options) {
901
913
  if (globalThis.document?.visibilityState === "visible") sync();
902
914
  };
903
915
  globalThis.document?.addEventListener("visibilitychange", syncWhenVisible);
916
+ globalThis.addEventListener?.("online", sync);
904
917
  perf.logEnd("createSyncedDb", "initialized", "info");
905
918
  const exportData = createExportData({
906
919
  reactiveDb,
@@ -915,7 +928,9 @@ async function createSyncedDb(options) {
915
928
  isDisposed = true;
916
929
  unregisterDevtools?.();
917
930
  globalThis.document?.removeEventListener("visibilitychange", syncWhenVisible);
931
+ globalThis.removeEventListener?.("online", sync);
918
932
  reloadRequestedSubscription.unsubscribe();
933
+ storageReplacedSubscription.unsubscribe();
919
934
  clientLockRelease.resolve();
920
935
  await tabRemoteSource.dispose();
921
936
  broadcastChannels.requests.close();