@sqlite-sync/core 0.12.0 → 0.12.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.
@@ -1,4 +1,4 @@
1
- import { a as createDeferredPromise, m as tryCatchAsync, o as createTypedEventTarget, s as ensureSingletonExecution } from "./crdt-table-schema-2w_AIpsV.js";
1
+ import { c as ensureSingletonExecution, h as tryCatchAsync, o as createDeferredPromise, s as createTypedEventTarget } from "./crdt-table-schema-BRm_WdcW.js";
2
2
  //#region src/hlc.ts
3
3
  const MAX_COUNTER = 36 ** 5 - 1;
4
4
  const DEFAULT_MAX_DRIFT_MS = 216e5;
@@ -540,4 +540,4 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
540
540
  //#endregion
541
541
  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 };
542
542
 
543
- //# sourceMappingURL=crdt-sync-remote-source-Q6P6OiL0.js.map
543
+ //# sourceMappingURL=crdt-sync-remote-source-CPXzmSIS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"crdt-sync-remote-source-Q6P6OiL0.js","names":[],"sources":["../src/hlc.ts","../src/schema/validate-crdt-event.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","import type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { createDeferredPromise, 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 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;\nconst REQUEST_TIMEOUT_MS = 10_000;\nconst RECONNECT_BASE_DELAY_MS = 1_000;\nconst RECONNECT_MAX_DELAY_MS = 30_000;\n// Backoff restarts only after a connection stays up this long, so a remote that accepts and then drops\n// connections right away is not hammered.\nconst RECONNECT_MIN_UPTIME_MS = 5_000;\n// Pings a remote that has sent nothing for this long, to catch connections that died without closing.\nconst HEARTBEAT_IDLE_MS = 25_000;\n\nexport type CrdtSyncRemoteSource = ReturnType<typeof createCrdtSyncRemoteSource>;\n\nexport type EventsAvailable = {\n newSyncId: number;\n remoteEventHlcSum: string | null;\n};\n\n/**\n * Opens one connection to the remote. Each call must return a new source: the library calls `disconnect` on every\n * returned source exactly once. Call `onClosed` when the connection ends on its own, at any time including during\n * this call; the library then reconnects with backoff. Requests may wait for the connection to open and should\n * reject once it closes. The library times requests out and shows the remote as online after the first one succeeds.\n * With `ping`, the library also checks idle connections and replaces one whose ping fails or times out. It calls `ping`\n * only on an online connection, one at a time, and never after the connection closes; a ping does not need to settle\n * once the source has closed.\n */\nexport type CreateRemoteSourceFactory = (opts: {\n onEventsAvailable: (event: EventsAvailable) => void;\n onClosed: (error?: unknown) => void;\n}) => RemoteSource;\n\ntype RemoteSource = {\n pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;\n pushEvents: (request: EventsPushRequest) => Promise<EventsPushResponse>;\n ping?: () => Promise<void>;\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: \"connecting\";\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 | \"CONNECTION_LOST\"\n | \"NETWORK_OFFLINE\"\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 const previous = remoteState;\n remoteState = { ...remoteState, ...state } as RemoteSourceState;\n if (\n remoteState.type !== previous.type ||\n remoteState.deSynced !== previous.deSynced ||\n remoteState.schemaVersionMismatched !== previous.schemaVersionMismatched\n ) {\n eventTarget.dispatchEvent(\"state-changed\", remoteState.type);\n }\n };\n\n let current: Connection | null = null;\n let disposed = false;\n let wantsOnline = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let reconnectAttempt = 0;\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, { reconnect = false } = {}) => {\n if (current !== connection) {\n return false;\n }\n current = null;\n if (reconnect && scheduleReconnect()) {\n patchRemoteState({ type: \"connecting\" });\n } else {\n patchRemoteState({ type: \"offline\", reason });\n }\n connection.close();\n return true;\n };\n\n const cancelReconnect = () => {\n clearTimeout(reconnectTimer);\n reconnectTimer = undefined;\n };\n\n // Retrying after a schema mismatch would fail the same way.\n const scheduleReconnect = () => {\n if (!wantsOnline || current || reconnectTimer || remoteState.schemaVersionMismatched) {\n return false;\n }\n const maxDelay = Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** reconnectAttempt);\n reconnectAttempt++;\n const delay = maxDelay / 2 + (Math.random() * maxDelay) / 2;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined;\n void connect();\n }, delay);\n return true;\n };\n\n type Connection = ReturnType<typeof createConnection>;\n const createConnection = (factory: CreateRemoteSourceFactory) => {\n const controller = new AbortController();\n const { signal } = controller;\n const ready = createDeferredPromise<void>();\n let source: RemoteSource | null = null;\n let establishedAt: number | null = null;\n let lastActivityAt = Date.now();\n let heartbeatTimer: ReturnType<typeof setTimeout> | undefined;\n\n const untilClosed = <T>(operation: () => Promise<T>, timeoutMs?: number) =>\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 const timeout =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => reject(new Error(`Remote request timed out after ${timeoutMs}ms`)), timeoutMs);\n operation()\n .then(resolve, reject)\n .finally(() => {\n clearTimeout(timeout);\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n\n const request = async <T>(operation: () => Promise<T>) => {\n const response = await untilClosed(operation, REQUEST_TIMEOUT_MS);\n lastActivityAt = Date.now();\n if (establishedAt === null && !signal.aborted) {\n establishedAt = Date.now();\n patchRemoteState({ type: \"online\", schemaVersionMismatched: false });\n ready.resolve();\n scheduleHeartbeat();\n }\n return response;\n };\n\n const fail = (reason: OfflineReason, { reconnect = true } = {}) => {\n if (current !== connection) {\n return;\n }\n if (establishedAt !== null && Date.now() - establishedAt >= RECONNECT_MIN_UPTIME_MS) {\n reconnectAttempt = 0;\n }\n closeConnection(connection, establishedAt === null ? \"INITIALIZATION_FAILED\" : reason, { reconnect });\n };\n\n // A close during the factory call aborts before `source` is set, so the abort check below disconnects it.\n const start = () => {\n if (signal.aborted) {\n return;\n }\n\n let created: RemoteSource;\n try {\n created = factory({\n onEventsAvailable: ({ newSyncId, remoteEventHlcSum }) => {\n lastActivityAt = Date.now();\n pull({ remoteSyncId: newSyncId, remoteEventHlcSum, includeSelf: false });\n },\n onClosed: (error) => {\n if (signal.aborted) {\n return;\n }\n if (error !== undefined) {\n console.warn(\"Remote connection closed\", error);\n }\n fail(\"CONNECTION_LOST\");\n },\n });\n } catch (error) {\n console.warn(\"Failed to create remote source\", error);\n fail(\"INITIALIZATION_FAILED\");\n return;\n }\n\n if (signal.aborted) {\n disconnectSource(created);\n return;\n }\n source = created;\n void sync();\n };\n\n const scheduleHeartbeat = () => {\n const ping = source?.ping?.bind(source);\n if (!ping || signal.aborted) {\n return;\n }\n heartbeatTimer = setTimeout(\n async () => {\n if (Date.now() - lastActivityAt >= HEARTBEAT_IDLE_MS) {\n try {\n await request(ping);\n } catch (error) {\n if (!signal.aborted) {\n console.warn(\"Remote did not answer a ping\", error);\n fail(\"CONNECTION_LOST\");\n }\n return;\n }\n }\n scheduleHeartbeat();\n },\n Math.max(0, lastActivityAt + HEARTBEAT_IDLE_MS - Date.now()),\n );\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 fail(\"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 request(() =>\n activeSource.pullEvents({\n ...opts,\n afterSyncId,\n }),\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.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 request(() =>\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 } catch (error) {\n if (!signal.aborted) {\n console.error(\"Error pushing events. Going offline.\", error);\n fail(\"REMOTE_PUSH_ERROR\");\n }\n return;\n }\n\n if (signal.aborted) {\n return;\n }\n\n if (!response.ok) {\n // The remote would reject the same batch again, so only an explicit sync retries it.\n console.error(\"Remote rejected pushed events. Going offline.\");\n fail(\"REMOTE_PUSH_ERROR\", { reconnect: false });\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 clearTimeout(heartbeatTimer);\n ready.resolve();\n if (source) {\n disconnectSource(source);\n }\n };\n\n const connection = { ready: ready.promise, start, close, push, sync };\n return connection;\n };\n\n const connect = (): 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 cancelReconnect();\n wantsOnline = true;\n const connection = createConnection(remoteFactory);\n current = connection;\n patchRemoteState({ type: \"connecting\" });\n connection.start();\n return connection.ready;\n };\n\n const goOnline = () => {\n reconnectAttempt = 0;\n return connect();\n };\n\n // Unlike goOffline, leaves syncWithRemote free to reconnect, but does not retry on its own: the caller\n // knows the network is gone and calls syncWithRemote once it is back.\n const dropConnection = (reason: OfflineReason) => {\n cancelReconnect();\n if (current) {\n closeConnection(current, reason);\n } else if (remoteState.type === \"connecting\") {\n patchRemoteState({ type: \"offline\", reason });\n }\n };\n\n const goOffline = async (reason: OfflineReason) => {\n wantsOnline = false;\n dropConnection(reason);\n };\n\n // Reconnects right away, skipping any backoff, unless goOffline or a schema mismatch took us offline.\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.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.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 dropConnection,\n syncWithRemote,\n getState,\n dispose,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"crdt-sync-remote-source-CPXzmSIS.js","names":[],"sources":["../src/hlc.ts","../src/schema/validate-crdt-event.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","import type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { createDeferredPromise, 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 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;\nconst REQUEST_TIMEOUT_MS = 10_000;\nconst RECONNECT_BASE_DELAY_MS = 1_000;\nconst RECONNECT_MAX_DELAY_MS = 30_000;\n// Backoff restarts only after a connection stays up this long, so a remote that accepts and then drops\n// connections right away is not hammered.\nconst RECONNECT_MIN_UPTIME_MS = 5_000;\n// Pings a remote that has sent nothing for this long, to catch connections that died without closing.\nconst HEARTBEAT_IDLE_MS = 25_000;\n\nexport type CrdtSyncRemoteSource = ReturnType<typeof createCrdtSyncRemoteSource>;\n\nexport type EventsAvailable = {\n newSyncId: number;\n remoteEventHlcSum: string | null;\n};\n\n/**\n * Opens one connection to the remote. Each call must return a new source: the library calls `disconnect` on every\n * returned source exactly once. Call `onClosed` when the connection ends on its own, at any time including during\n * this call; the library then reconnects with backoff. Requests may wait for the connection to open and should\n * reject once it closes. The library times requests out and shows the remote as online after the first one succeeds.\n * With `ping`, the library also checks idle connections and replaces one whose ping fails or times out. It calls `ping`\n * only on an online connection, one at a time, and never after the connection closes; a ping does not need to settle\n * once the source has closed.\n */\nexport type CreateRemoteSourceFactory = (opts: {\n onEventsAvailable: (event: EventsAvailable) => void;\n onClosed: (error?: unknown) => void;\n}) => RemoteSource;\n\ntype RemoteSource = {\n pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;\n pushEvents: (request: EventsPushRequest) => Promise<EventsPushResponse>;\n ping?: () => Promise<void>;\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: \"connecting\";\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 | \"CONNECTION_LOST\"\n | \"NETWORK_OFFLINE\"\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 const previous = remoteState;\n remoteState = { ...remoteState, ...state } as RemoteSourceState;\n if (\n remoteState.type !== previous.type ||\n remoteState.deSynced !== previous.deSynced ||\n remoteState.schemaVersionMismatched !== previous.schemaVersionMismatched\n ) {\n eventTarget.dispatchEvent(\"state-changed\", remoteState.type);\n }\n };\n\n let current: Connection | null = null;\n let disposed = false;\n let wantsOnline = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let reconnectAttempt = 0;\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, { reconnect = false } = {}) => {\n if (current !== connection) {\n return false;\n }\n current = null;\n if (reconnect && scheduleReconnect()) {\n patchRemoteState({ type: \"connecting\" });\n } else {\n patchRemoteState({ type: \"offline\", reason });\n }\n connection.close();\n return true;\n };\n\n const cancelReconnect = () => {\n clearTimeout(reconnectTimer);\n reconnectTimer = undefined;\n };\n\n // Retrying after a schema mismatch would fail the same way.\n const scheduleReconnect = () => {\n if (!wantsOnline || current || reconnectTimer || remoteState.schemaVersionMismatched) {\n return false;\n }\n const maxDelay = Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** reconnectAttempt);\n reconnectAttempt++;\n const delay = maxDelay / 2 + (Math.random() * maxDelay) / 2;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined;\n void connect();\n }, delay);\n return true;\n };\n\n type Connection = ReturnType<typeof createConnection>;\n const createConnection = (factory: CreateRemoteSourceFactory) => {\n const controller = new AbortController();\n const { signal } = controller;\n const ready = createDeferredPromise<void>();\n let source: RemoteSource | null = null;\n let establishedAt: number | null = null;\n let lastActivityAt = Date.now();\n let heartbeatTimer: ReturnType<typeof setTimeout> | undefined;\n\n const untilClosed = <T>(operation: () => Promise<T>, timeoutMs?: number) =>\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 const timeout =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => reject(new Error(`Remote request timed out after ${timeoutMs}ms`)), timeoutMs);\n operation()\n .then(resolve, reject)\n .finally(() => {\n clearTimeout(timeout);\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n\n const request = async <T>(operation: () => Promise<T>) => {\n const response = await untilClosed(operation, REQUEST_TIMEOUT_MS);\n lastActivityAt = Date.now();\n if (establishedAt === null && !signal.aborted) {\n establishedAt = Date.now();\n patchRemoteState({ type: \"online\", schemaVersionMismatched: false });\n ready.resolve();\n scheduleHeartbeat();\n }\n return response;\n };\n\n const fail = (reason: OfflineReason, { reconnect = true } = {}) => {\n if (current !== connection) {\n return;\n }\n if (establishedAt !== null && Date.now() - establishedAt >= RECONNECT_MIN_UPTIME_MS) {\n reconnectAttempt = 0;\n }\n closeConnection(connection, establishedAt === null ? \"INITIALIZATION_FAILED\" : reason, { reconnect });\n };\n\n // A close during the factory call aborts before `source` is set, so the abort check below disconnects it.\n const start = () => {\n if (signal.aborted) {\n return;\n }\n\n let created: RemoteSource;\n try {\n created = factory({\n onEventsAvailable: ({ newSyncId, remoteEventHlcSum }) => {\n lastActivityAt = Date.now();\n pull({ remoteSyncId: newSyncId, remoteEventHlcSum, includeSelf: false });\n },\n onClosed: (error) => {\n if (signal.aborted) {\n return;\n }\n if (error !== undefined) {\n console.warn(\"Remote connection closed\", error);\n }\n fail(\"CONNECTION_LOST\");\n },\n });\n } catch (error) {\n console.warn(\"Failed to create remote source\", error);\n fail(\"INITIALIZATION_FAILED\");\n return;\n }\n\n if (signal.aborted) {\n disconnectSource(created);\n return;\n }\n source = created;\n void sync();\n };\n\n const scheduleHeartbeat = () => {\n const ping = source?.ping?.bind(source);\n if (!ping || signal.aborted) {\n return;\n }\n heartbeatTimer = setTimeout(\n async () => {\n if (Date.now() - lastActivityAt >= HEARTBEAT_IDLE_MS) {\n try {\n await request(ping);\n } catch (error) {\n if (!signal.aborted) {\n console.warn(\"Remote did not answer a ping\", error);\n fail(\"CONNECTION_LOST\");\n }\n return;\n }\n }\n scheduleHeartbeat();\n },\n Math.max(0, lastActivityAt + HEARTBEAT_IDLE_MS - Date.now()),\n );\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 fail(\"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 request(() =>\n activeSource.pullEvents({\n ...opts,\n afterSyncId,\n }),\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.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 request(() =>\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 } catch (error) {\n if (!signal.aborted) {\n console.error(\"Error pushing events. Going offline.\", error);\n fail(\"REMOTE_PUSH_ERROR\");\n }\n return;\n }\n\n if (signal.aborted) {\n return;\n }\n\n if (!response.ok) {\n // The remote would reject the same batch again, so only an explicit sync retries it.\n console.error(\"Remote rejected pushed events. Going offline.\");\n fail(\"REMOTE_PUSH_ERROR\", { reconnect: false });\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 clearTimeout(heartbeatTimer);\n ready.resolve();\n if (source) {\n disconnectSource(source);\n }\n };\n\n const connection = { ready: ready.promise, start, close, push, sync };\n return connection;\n };\n\n const connect = (): 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 cancelReconnect();\n wantsOnline = true;\n const connection = createConnection(remoteFactory);\n current = connection;\n patchRemoteState({ type: \"connecting\" });\n connection.start();\n return connection.ready;\n };\n\n const goOnline = () => {\n reconnectAttempt = 0;\n return connect();\n };\n\n // Unlike goOffline, leaves syncWithRemote free to reconnect, but does not retry on its own: the caller\n // knows the network is gone and calls syncWithRemote once it is back.\n const dropConnection = (reason: OfflineReason) => {\n cancelReconnect();\n if (current) {\n closeConnection(current, reason);\n } else if (remoteState.type === \"connecting\") {\n patchRemoteState({ type: \"offline\", reason });\n }\n };\n\n const goOffline = async (reason: OfflineReason) => {\n wantsOnline = false;\n dropConnection(reason);\n };\n\n // Reconnects right away, skipping any backoff, unless goOffline or a schema mismatch took us offline.\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.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.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 dropConnection,\n syncWithRemote,\n getState,\n dispose,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n};\n"],"mappings":";;AAMA,MAAM,cAAc,MAAM,IAAI;AAC9B,MAAa,uBAAuB;AAEpC,IAAa,aAAb,MAAwB;CACtB;CACA;CACA;CAEA;CACA;CAEA,YAAY,QAAgB,cAA4B,WAAmB,sBAAsB;EAC/F,KAAK,YAAY,aAAa;EAC9B,KAAK,UAAU;EACf,KAAK,SAAS;EACd,KAAK,eAAe;EACpB,KAAK,WAAW;CAClB;CAEA,gBAAqB;EACnB,OAAO;GACL,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,QAAQ,KAAK;EACf;CACF;CAEA,aAAkB;EAChB,MAAM,MAAM,KAAK,aAAa;EAE9B,IAAI,MAAM,KAAK,WAAW;GACxB,KAAK,YAAY;GACjB,KAAK,UAAU;GACf,OAAO,KAAK,cAAc;EAC5B;EAEA,KAAK;EACL,KAAK,gBAAgB;EACrB,OAAO,KAAK,cAAc;CAC5B;;CAGA,WAAW,KAAU;EACnB,IAAI,IAAI,YAAY,KAAK,WAAW;GAClC,KAAK,YAAY,IAAI;GACrB,KAAK,UAAU,IAAI;EACrB,OAAO,IAAI,IAAI,cAAc,KAAK,WAChC,KAAK,UAAU,KAAK,IAAI,KAAK,SAAS,IAAI,OAAO;CAErD;CAEA,SAAS,KAAU;EACjB,MAAM,MAAM,KAAK,aAAa;EAC9B,IAAI,IAAI,YAAY,MAAM,KAAK,UAAU;GACvC,QAAQ,KACN,8CAA8C,IAAI,UAAU,UAAU,IAAI,UAAU,IAAI,YAAY,IAAI,IAC1G;GACA;EACF;EAEA,IAAI,KAAK,cAAc,IAAI,WACzB,KAAK,UAAU,KAAK,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI;OAChD,IAAI,KAAK,YAAY,IAAI,WAC9B,KAAK;OACA;GACL,KAAK,YAAY,IAAI;GACrB,KAAK,UAAU,IAAI,UAAU;EAC/B;EACA,KAAK,gBAAgB;CACvB;CAEA,kBAA0B;EACxB,IAAI,KAAK,UAAU,aAAa;GAC9B,KAAK,aAAa;GAClB,KAAK,UAAU;EACjB;CACF;AACF;AAEA,SAAgB,aAAa,KAAU;CACrC,OAAO,GAAG,IAAI,UAAU,SAAS,CAAC,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,IAAI,QAAQ,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,IAAI;AAC3G;AAEA,SAAgB,eAAe,YAAyB;CACtD,MAAM,QAAQ,WAAW,MAAM,GAAG;CAClC,IAAI,MAAM,SAAS,GACjB,MAAM,IAAI,MAAM,yEAAyE,MAAM,QAAQ;CAGzG,MAAM,YAAY,SAAS,MAAM,IAAI,EAAE;CACvC,MAAM,UAAU,SAAS,MAAM,IAAI,EAAE;CAErC,IAAI,OAAO,MAAM,SAAS,KAAK,OAAO,MAAM,OAAO,GACjD,MAAM,IAAI,MAAM,iCAAiC,MAAM,GAAG,YAAY,MAAM,IAAI;CAGlF,OAAO;EACL;EACA;EACA,QAAQ,MAAM,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG;CACjC;AACF;AAEA,SAAgB,WAAW,KAAU,KAAU;CAC7C,IAAI,IAAI,cAAc,IAAI,WAAW;EACnC,IAAI,IAAI,YAAY,IAAI,SAAS;GAC/B,IAAI,IAAI,WAAW,IAAI,QACrB,OAAO;GAET,OAAO,IAAI,SAAS,IAAI,SAAS,KAAK;EACxC;EACA,OAAO,IAAI,UAAU,IAAI;CAC3B;CACA,OAAO,IAAI,YAAY,IAAI;AAC7B;;;AChHA,IAAa,2BAAb,cAA8C,MAAM;CAC7B;CAArB,YAAY,QAA2B;EACrC,MAAM,wBAAwB,OAAO,KAAK,IAAI,GAAG;EAD9B,KAAA,SAAA;EAEnB,KAAK,OAAO;CACd;AACF;AAEA,MAAM,iBAAiB;CAAC;CAAgB;CAAgB;AAAc;AAEtE,SAAgB,qBACd,QACA,OAC8B;CAC9B,MAAM,SAAmB,CAAC;CAE1B,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO;EAAE,SAAS;EAAO,QAAQ,CAAC,yBAAyB;CAAE;CAG/D,MAAM,OAAO,MAAM;CACnB,MAAM,UAAU,MAAM;CACtB,MAAM,SAAS,MAAM;CAErB,IAAI,UAAU,MAAM;CACpB,IAAI,qBAAqB;CACzB,IAAI,OAAO,YAAY,UACrB,IAAI;EACF,UAAU,KAAK,MAAM,OAAO;CAC9B,QAAQ;EACN,qBAAqB;EACrB,OAAO,KAAK,iCAAiC;CAC/C;CAGF,IAAI,CAAC,gBAAgB,IAAI,GACvB,OAAO,KAAK,uBAAuB,OAAO,IAAI,EAAE,EAAE;CAEpD,IAAI,OAAO,YAAY,UACrB,OAAO,KAAK,gCAAgC;CAE9C,IAAI,OAAO,WAAW,UACpB,OAAO,KAAK,gCAAgC;CAE9C,IAAI,CAAC,sBAAsB,CAAC,SAAS,OAAO,GAC1C,OAAO,KAAK,iCAAiC;CAG/C,MAAM,eAAe,OAAO,YAAY,WAAW,oBAAoB,QAAQ,OAAO,IAAI;CAC1F,IAAI,OAAO,YAAY,YAAY,CAAC,cAClC,OAAO,KAAK,oBAAoB,QAAQ,EAAE;CAG5C,IAAI,gBAAgB,IAAI,KAAK,gBAAgB,SAAS,OAAO,GAC3D,QAAQ,MAAR;EACE,KAAK,gBAAgB;GACnB,MAAM,SAAS,aAAa,MAAM,gBAAgB,SAAS,EAAE,OAAO,eAAe,CAAC;GACpF,IAAI,CAAC,OAAO,SACV,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK,UAAU,YAAY,OAAO,CAAC;GAElE,IAAI,OAAO,WAAW,YAAY,QAAQ,WAAW,QAAQ,OAAO,QAClE,OAAO,KAAK,4CAA4C,OAAO,EAAE;GAEnE;EACF;EACA,KAAK,gBAAgB;GACnB,MAAM,SAAS,aAAa,MAAM,gBAAgB,SAAS,EAAE,OAAO,eAAe,CAAC;GACpF,IAAI,CAAC,OAAO,SACV,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK,UAAU,YAAY,OAAO,CAAC;GAElE;EACF;EACA,KAAK,gBACH,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAChC,OAAO,KAAK,yDAAyD;CAG3E;CAGF,IAAI,OAAO,SAAS,GAClB,OAAO;EAAE,SAAS;EAAO;CAAO;CAGlC,OAAO;EACL,SAAS;EACT,OAAO;GACC;GACN,SAAS,cAAc,iBAAkB;GACzC,SAAS;GACA;EACX;CACF;AACF;AAEA,SAAS,oBACP,QACA,SAC0D;CAC1D,MAAM,SAAS,OAAO,aAAa,MAChC,gBAAgB,YAAY,kBAAkB,WAAW,YAAY,kBAAkB,OAC1F;CACA,IAAI,QAAQ;EACV,MAAM,QAAQ,OAAO,OAAO,OAAO;EACnC,OAAO,QAAQ;GAAE;GAAO,eAAe,OAAO;EAAc,IAAI;CAClE;CACA,MAAM,QAAQ,OAAO,OAAO,OAAO,QAAQ,OAAO,IAAI,OAAO,OAAO,WAAW,KAAA;CAC/E,OAAO,QAAQ;EAAE;EAAO,eAAe;CAAQ,IAAI;AACrD;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,gBAAgB,OAAwC;CAC/D,OAAO,OAAO,UAAU,YAAY,eAAe,SAAS,KAAsB;AACpF;;;ACtFA,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;AAC3B,MAAM,0BAA0B;AAChC,MAAM,yBAAyB;AAG/B,MAAM,0BAA0B;AAEhC,MAAM,oBAAoB;AA8B1B,IAAa,6BAAb,cAAgD,MAAM;CAE3C;CACA;CAFT,YACE,qBACA,oBACA;EACA,MAAM,mCAAmC,oBAAoB,YAAY,oBAAoB;EAHtF,KAAA,sBAAA;EACA,KAAA,qBAAA;EAGP,KAAK,OAAO;CACd;AACF;AA+BA,MAAa,8BAA8B,EACzC,YACA,SACA,UACA,YACA,YACA,QACA,oBACgC;CAChC,MAAM,cAAc,uBASjB;CAEH,IAAI,cAAiC;EACnC,MAAM;EACN,QAAQ;EACR,UAAU;EACV,yBAAyB;CAC3B;CAEA,MAAM,oBAAoB,UAAsC;EAC9D,MAAM,WAAW;EACjB,cAAc;GAAE,GAAG;GAAa,GAAG;EAAM;EACzC,IACE,YAAY,SAAS,SAAS,QAC9B,YAAY,aAAa,SAAS,YAClC,YAAY,4BAA4B,SAAS,yBAEjD,YAAY,cAAc,iBAAiB,YAAY,IAAI;CAE/D;CAEA,IAAI,UAA6B;CACjC,IAAI,WAAW;CACf,IAAI,cAAc;CAClB,IAAI;CACJ,IAAI,mBAAmB;CAEvB,MAAM,oBAAoB,WAAyB;EACjD,MAAM,WAAW,iBAAiB;GAChC,QAAQ,MAAM,2CAA2C,uBAAuB,GAAG;EACrF,GAAG,sBAAsB;EACzB,cAAmB,YAAY,MAAM,OAAO,aAAa,CAAC,CAAC,CAAC,MAAM,WAAW;GAC3E,aAAa,QAAQ;GACrB,IAAI,CAAC,OAAO,SACV,QAAQ,KAAK,gDAAgD,OAAO,KAAK;EAE7E,CAAC;CACH;CAEA,MAAM,mBAAmB,YAAwB,QAAuB,EAAE,YAAY,UAAU,CAAC,MAAM;EACrG,IAAI,YAAY,YACd,OAAO;EAET,UAAU;EACV,IAAI,aAAa,kBAAkB,GACjC,iBAAiB,EAAE,MAAM,aAAa,CAAC;OAEvC,iBAAiB;GAAE,MAAM;GAAW;EAAO,CAAC;EAE9C,WAAW,MAAM;EACjB,OAAO;CACT;CAEA,MAAM,wBAAwB;EAC5B,aAAa,cAAc;EAC3B,iBAAiB,KAAA;CACnB;CAGA,MAAM,0BAA0B;EAC9B,IAAI,CAAC,eAAe,WAAW,kBAAkB,YAAY,yBAC3D,OAAO;EAET,MAAM,WAAW,KAAK,IAAI,wBAAwB,0BAA0B,KAAK,gBAAgB;EACjG;EACA,MAAM,QAAQ,WAAW,IAAK,KAAK,OAAO,IAAI,WAAY;EAC1D,iBAAiB,iBAAiB;GAChC,iBAAiB,KAAA;GACjB,QAAa;EACf,GAAG,KAAK;EACR,OAAO;CACT;CAGA,MAAM,oBAAoB,YAAuC;EAC/D,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,EAAE,WAAW;EACnB,MAAM,QAAQ,sBAA4B;EAC1C,IAAI,SAA8B;EAClC,IAAI,gBAA+B;EACnC,IAAI,iBAAiB,KAAK,IAAI;EAC9B,IAAI;EAEJ,MAAM,eAAkB,WAA6B,cACnD,IAAI,SAAY,SAAS,WAAW;GAClC,MAAM,gBAAgB,uBAAO,IAAI,MAAM,0BAA0B,CAAC;GAClE,IAAI,OAAO,SAAS;IAClB,QAAQ;IACR;GACF;GACA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;GACxD,MAAM,UACJ,cAAc,KAAA,IACV,KAAA,IACA,iBAAiB,uBAAO,IAAI,MAAM,kCAAkC,UAAU,GAAG,CAAC,GAAG,SAAS;GACpG,UAAU,CAAC,CACR,KAAK,SAAS,MAAM,CAAC,CACrB,cAAc;IACb,aAAa,OAAO;IACpB,OAAO,oBAAoB,SAAS,OAAO;GAC7C,CAAC;EACL,CAAC;EAEH,MAAM,UAAU,OAAU,cAAgC;GACxD,MAAM,WAAW,MAAM,YAAY,WAAW,kBAAkB;GAChE,iBAAiB,KAAK,IAAI;GAC1B,IAAI,kBAAkB,QAAQ,CAAC,OAAO,SAAS;IAC7C,gBAAgB,KAAK,IAAI;IACzB,iBAAiB;KAAE,MAAM;KAAU,yBAAyB;IAAM,CAAC;IACnE,MAAM,QAAQ;IACd,kBAAkB;GACpB;GACA,OAAO;EACT;EAEA,MAAM,QAAQ,QAAuB,EAAE,YAAY,SAAS,CAAC,MAAM;GACjE,IAAI,YAAY,YACd;GAEF,IAAI,kBAAkB,QAAQ,KAAK,IAAI,IAAI,iBAAiB,yBAC1D,mBAAmB;GAErB,gBAAgB,YAAY,kBAAkB,OAAO,0BAA0B,QAAQ,EAAE,UAAU,CAAC;EACtG;EAGA,MAAM,cAAc;GAClB,IAAI,OAAO,SACT;GAGF,IAAI;GACJ,IAAI;IACF,UAAU,QAAQ;KAChB,oBAAoB,EAAE,WAAW,wBAAwB;MACvD,iBAAiB,KAAK,IAAI;MAC1B,KAAK;OAAE,cAAc;OAAW;OAAmB,aAAa;MAAM,CAAC;KACzE;KACA,WAAW,UAAU;MACnB,IAAI,OAAO,SACT;MAEF,IAAI,UAAU,KAAA,GACZ,QAAQ,KAAK,4BAA4B,KAAK;MAEhD,KAAK,iBAAiB;KACxB;IACF,CAAC;GACH,SAAS,OAAO;IACd,QAAQ,KAAK,kCAAkC,KAAK;IACpD,KAAK,uBAAuB;IAC5B;GACF;GAEA,IAAI,OAAO,SAAS;IAClB,iBAAiB,OAAO;IACxB;GACF;GACA,SAAS;GACT,KAAU;EACZ;EAEA,MAAM,0BAA0B;GAC9B,MAAM,OAAO,QAAQ,MAAM,KAAK,MAAM;GACtC,IAAI,CAAC,QAAQ,OAAO,SAClB;GAEF,iBAAiB,WACf,YAAY;IACV,IAAI,KAAK,IAAI,IAAI,kBAAkB,mBACjC,IAAI;KACF,MAAM,QAAQ,IAAI;IACpB,SAAS,OAAO;KACd,IAAI,CAAC,OAAO,SAAS;MACnB,QAAQ,KAAK,gCAAgC,KAAK;MAClD,KAAK,iBAAiB;KACxB;KACA;IACF;IAEF,kBAAkB;GACpB,GACA,KAAK,IAAI,GAAG,iBAAiB,oBAAoB,KAAK,IAAI,CAAC,CAC7D;EACF;EAGA,IAAI,aAAgC;EACpC,MAAM,QAAQ,YAIO;GACnB,IAAI,CAAC,UAAU,OAAO,SACpB,OAAO,QAAQ,QAAQ;GAEzB,MAAM,eAAe;GAErB,MAAM,eAAe,SAAS;GAE9B,IAAI,iBAAiB,KAAA,KAAa,gBAAgB,WAAW,SAAS;IAKpE,uBAAuB,cAAc,SAAS,qBAAqB,IAAI;IACvE,OAAO,QAAQ,QAAQ;GACzB;GAEA,IAAI,YAAY;IACd,IAAI,iBAAiB,KAAA,MAAc,CAAC,WAAW,mBAAmB,WAAW,kBAAkB,eAC7F,WAAW,kBAAkB;IAE/B,OAAO,WAAW;GACpB;GAEA,MAAM,WAAuB;IAAE,SAAS,QAAQ,QAAQ;IAAG,iBAAiB;GAAK;GACjF,SAAS,UAAU,cAAc,cAAc;IAC7C,aAAa,WAAW;IACxB,eAAe,SAAS,cAAc,KAAA,IAAY;GACpD,CAAC,CAAC,CACC,OAAO,UAAU;IAChB,IAAI,OAAO,SACT;IAEF,QAAQ,MAAM,wCAAwC,KAAK;IAC3D,KAAK,mBAAmB;GAC1B,CAAC,CAAC,CACD,cAAc;IACb,IAAI,eAAe,UACjB,aAAa;IAGf,MAAM,aAAa,SAAS;IAC5B,IAAI,cAAc,aAAa,WAAW,SACxC,KAAK,EAAE,cAAc,WAAW,CAAC;GAErC,CAAC;GACH,aAAa;GACb,OAAO,SAAS;EAClB;EAEA,MAAM,gBAAgB,OAAO,cAA4B,SAA4B;GACnF,IAAI,UAAU;GACd,IAAI,cAAc,KAAK;GACvB,OAAO,SAAS;IACd,MAAM,WAAW,MAAM,cACrB,aAAa,WAAW;KACtB,GAAG;KACH;IACF,CAAC,CACH;IACA,IAAI,OAAO,SACT;IAEF,UAAU,SAAS;IACnB,cAAc,SAAS;IAEvB,IAAI,SAAS,QAAQ;KACnB,MAAM,EAAE,cAAc,QAAQ,oBAC5B,SAAS,OAAO,KAAK,MAAM;MACzB,IAAI,EAAE,iBAAiB,SAAS,sBAAsB;OACpD,YAAY,cAAc,kCAAkC;QAC1D,qBAAqB,EAAE;QACvB,oBAAoB,SAAS;OAC/B,CAAC;OACD,IAAI,CAAC,YAAY,yBACf,iBAAiB,EAAE,yBAAyB,KAAK,CAAC;OAEpD,MAAM,IAAI,2BAA2B,EAAE,gBAAgB,SAAS,oBAAoB;MACtF;MACA,OAAO;KACT,CAAC,CACH;KACA,MAAM,kBAAkB,SAAS;KACjC,IAAI,OAAO,SACT;IAEJ;IACA,IAAI,SAAS,cAAc,WAAW,SACpC;IAEF,IAAI,SAAS,aAAa,WAAW,SACnC,WAAW,UAAU,SAAS;GAElC;EACF;EAEA,MAAM,OAAO,yBAAyB,YAAY;GAChD,OAAO,UAAU,CAAC,OAAO,SAAS;IAChC,MAAM,eAAe;IACrB,MAAM,cAAc,QAAQ,eAAe;KACzC,QAAQ;KACR,aAAa,WAAW;KACxB,eAAe;KACf,OAAO;IACT,CAAC;IACD,IAAI,YAAY,OAAO,WAAW,GAChC;IAGF,IAAI;IACJ,IAAI;KACF,WAAW,MAAM,cACf,aAAa,WAAW;MACtB;MACA,QAAQ,YAAY,OAAO,KAAK,WAAW;OACzC,gBAAgB,MAAM;OACtB,WAAW,MAAM;OACjB,MAAM,MAAM;OACZ,SAAS,MAAM;OACf,SAAS,MAAM;OACf,SAAS,MAAM;MACjB,EAAE;KACJ,CAAC,CACH;IACF,SAAS,OAAO;KACd,IAAI,CAAC,OAAO,SAAS;MACnB,QAAQ,MAAM,wCAAwC,KAAK;MAC3D,KAAK,mBAAmB;KAC1B;KACA;IACF;IAEA,IAAI,OAAO,SACT;IAGF,IAAI,CAAC,SAAS,IAAI;KAEhB,QAAQ,MAAM,+CAA+C;KAC7D,KAAK,qBAAqB,EAAE,WAAW,MAAM,CAAC;KAC9C;IACF;IAEA,WAAW,UAAU,YAAY;IAOjC,IACE,SAAS,iBAAiB,KAAA,KAC1B,SAAS,gBAAgB,KAAA,KACzB,SAAS,gBAAgB,WAAW,WACpC,SAAS,cAAc,WAAW,SAElC,WAAW,UAAU,SAAS;IAEhC,IAAI,CAAC,YAAY,SACf;GAEJ;EACF,CAAC;EAGD,MAAM,OAAO,YAAY;GACvB,MAAM,YAAY;GAClB,MAAM,KAAK;GACX,MAAM,KAAK,QAAQ;GACnB,MAAM,KAAK;EACb;EAEA,MAAM,cAAc;GAClB,WAAW,MAAM;GACjB,aAAa,cAAc;GAC3B,MAAM,QAAQ;GACd,IAAI,QACF,iBAAiB,MAAM;EAE3B;EAEA,MAAM,aAAa;GAAE,OAAO,MAAM;GAAS;GAAO;GAAO;GAAM;EAAK;EACpE,OAAO;CACT;CAEA,MAAM,gBAA+B;EACnC,IAAI,UACF,OAAO,QAAQ,QAAQ;EAEzB,IAAI,SACF,OAAO,QAAQ;EAEjB,IAAI,CAAC,eAAe;GAClB,QAAQ,KAAK,oDAAoD;GACjE,iBAAiB;IAAE,MAAM;IAAW,QAAQ;GAAkB,CAAC;GAC/D,OAAO,QAAQ,QAAQ;EACzB;EAEA,gBAAgB;EAChB,cAAc;EACd,MAAM,aAAa,iBAAiB,aAAa;EACjD,UAAU;EACV,iBAAiB,EAAE,MAAM,aAAa,CAAC;EACvC,WAAW,MAAM;EACjB,OAAO,WAAW;CACpB;CAEA,MAAM,iBAAiB;EACrB,mBAAmB;EACnB,OAAO,QAAQ;CACjB;CAIA,MAAM,kBAAkB,WAA0B;EAChD,gBAAgB;EAChB,IAAI,SACF,gBAAgB,SAAS,MAAM;OAC1B,IAAI,YAAY,SAAS,cAC9B,iBAAiB;GAAE,MAAM;GAAW;EAAO,CAAC;CAEhD;CAEA,MAAM,YAAY,OAAO,WAA0B;EACjD,cAAc;EACd,eAAe,MAAM;CACvB;CAGA,MAAM,iBAAiB,YAAY;EACjC,IAAI,SACF,MAAM,QAAQ,KAAK;OACd,IAAI,eAAe,CAAC,YAAY,yBACrC,MAAM,SAAS;CAEnB;CAKA,MAAM,0BAA0B,cAAsB,sBAAqC;EAEzF,IAAI,sBAAsB,MACxB;EAMF,IAAI,iBAAiB,WAAW,SAC9B;EAMF,IAAI,CAAC,QAAQ,iBAAiB,WAAW,OAAO,GAC9C;EAGF,MAAM,mBAAmB,QAAQ,uBAAuB;EACxD,IAAI,qBAAqB,MACvB;EAGF,IAAI,qBAAqB,mBAEvB;EAGF,YAAY,cAAc,oBAAoB,EAAE,QAAQ,oBAAoB,CAAC;EAC7E,QAAQ,KACN,4CAA4C,aAAa,uBAAuB,iBAAiB,aAAa,kBAAkB,0DAClI;EACA,IAAI,CAAC,YAAY,UACf,iBAAiB,EAAE,UAAU,KAAK,CAAC;CAEvC;CAEA,MAAM,4BAA4B,QAAQ,iBAAiB,wBAAwB;EACjF,SAAS,KAAK;CAChB,CAAC;CAED,MAAM,qCAAqC,QAAQ,iBAAiB,mCAAmC;EACrG,YAAY,cAAc,oBAAoB,EAAE,QAAQ,8BAA8B,CAAC;EACvF,IAAI,CAAC,YAAY,UACf,iBAAiB,EAAE,UAAU,KAAK,CAAC;CAEvC,CAAC;CAED,MAAM,kBAA+B;EACnC,aAAa,YAAY;EACzB,UAAU,YAAY;EACtB,yBAAyB,YAAY;CACvC;CAEA,MAAM,UAAU,YAAY;EAC1B,WAAW;EACX,0BAA0B,YAAY;EACtC,mCAAmC,YAAY;EAC/C,MAAM,UAAU,cAAc;CAChC;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA,kBAAkB,YAAY;EAC9B,qBAAqB,YAAY;CACnC;AACF"}
@@ -23,6 +23,23 @@ function rejectTimeout(reject, opts) {
23
23
  reject(/* @__PURE__ */ new Error(`Promise timed out after ${opts.timeout}ms`));
24
24
  tryCatch(() => opts?.onTimeout?.());
25
25
  }
26
+ /**
27
+ * Resolves once the Web Lock is held and keeps holding it until the returned release function is called.
28
+ */
29
+ async function acquireLock(name, options) {
30
+ const acquired = createDeferredPromise();
31
+ const released = createDeferredPromise();
32
+ const request = navigator.locks.request(name, options, () => {
33
+ acquired.resolve();
34
+ return released.promise;
35
+ });
36
+ request.catch((error) => acquired.reject(error instanceof Error ? error : new Error(String(error))));
37
+ await acquired.promise;
38
+ return async () => {
39
+ released.resolve();
40
+ await request;
41
+ };
42
+ }
26
43
  const generateId = () => {
27
44
  return crypto.randomUUID();
28
45
  };
@@ -145,6 +162,6 @@ function isNoOpCrdtEventPayload(payload) {
145
162
  return payload === CRDT_EVENT_NO_OP_PAYLOAD;
146
163
  }
147
164
  //#endregion
148
- export { createDeferredPromise as a, generateId as c, parseTableName as d, quoteId as f, TypedEvent as i, jsonSafeParse as l, tryCatchAsync as m, isNoOpCrdtEventPayload as n, createTypedEventTarget as o, tryCatch as p, TypedBroadcastChannel as r, ensureSingletonExecution as s, CRDT_EVENT_NO_OP_PAYLOAD as t, noop as u };
165
+ export { acquireLock as a, ensureSingletonExecution as c, noop as d, parseTableName as f, tryCatchAsync as h, TypedEvent as i, generateId as l, tryCatch as m, isNoOpCrdtEventPayload as n, createDeferredPromise as o, quoteId as p, TypedBroadcastChannel as r, createTypedEventTarget as s, CRDT_EVENT_NO_OP_PAYLOAD as t, jsonSafeParse as u };
149
166
 
150
- //# sourceMappingURL=crdt-table-schema-2w_AIpsV.js.map
167
+ //# sourceMappingURL=crdt-table-schema-BRm_WdcW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt-table-schema-BRm_WdcW.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\n/**\n * Resolves once the Web Lock is held and keeps holding it until the returned release function is called.\n */\nexport async function acquireLock(name: string, options: LockOptions) {\n const acquired = createDeferredPromise<void>();\n const released = createDeferredPromise<void>();\n const request = navigator.locks.request(name, options, () => {\n acquired.resolve();\n return released.promise;\n });\n void request.catch((error: unknown) => acquired.reject(error instanceof Error ? error : new Error(String(error))));\n await acquired.promise;\n\n return async () => {\n released.resolve();\n await request;\n };\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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"}
@@ -415,6 +415,10 @@ type TypedStatement<TParams extends Record<string, unknown>, TResult> = {
415
415
  };
416
416
  type KyselyStatementFactory<TParams extends Record<string, unknown>, TDatabase, TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>> = (kysely: Kysely<TDatabase>, params: ParamsGetter<TParams>) => TQuery;
417
417
  type KyselyQueryFactory<TDatabase, TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>> = (kysely: Kysely<TDatabase>) => TQuery;
418
+ declare function compileKyselyStatement<TParams extends Record<string, unknown>, TDatabase>(factory: KyselyStatementFactory<TParams, TDatabase, Compilable<unknown>>): {
419
+ sql: string;
420
+ bindParameters: (parameters: TParams) => unknown[];
421
+ };
418
422
  //#endregion
419
423
  //#region src/sqlite-kv-store.d.ts
420
424
  type KvStoreItem = {
@@ -1224,5 +1228,5 @@ type ResetStore = {
1224
1228
  */
1225
1229
  declare const RESET_REQUEST_TTL_MS: number;
1226
1230
  //#endregion
1227
- 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 };
1228
- //# sourceMappingURL=index-3zVvNWRd.d.ts.map
1231
+ export { createExportData as $, ReadonlyTable as $t, PendingCrdtEvent as A, CrdtUpdateLogPayload as An, ExecuteParams as At, DefinedSyncSchema as B, LogLevel as Bt, WorkerConfig as C, StoredValue as Cn, jsonSafeParse as Ct, CrdtStorageMutator as D, CrdtEventStatus as Dn, KvStoreItem as Dt, createCrdtSyncProducer as E, CrdtEventOrigin as En, tryCatchAsync as Et, verifySyncSchema as F, PreparedStatement as Ft, SQLiteReactiveDb as G, TableMetadata as Gt, AdmitClientEventsResult as H, startPerformanceLogger as Ht, CrdtEventValidationError as I, QueryBuilderOutput as It, parametersAreEqual as J, HLCCounter as Jt, SharedLiveQuery as K, introspectDb as Kt, NewCrdtEventValidationResult as L, SQLiteDbWrapper as Lt, createSQLiteCrdtApplyFunction as M, isNoOpCrdtEventPayload as Mn, InternalSQLiteWrapper as Mt, SchemaVerificationIssue as N, dummyKysely as Nn, KyselyQueryFactory as Nt, SnapshotOptions as O, CrdtEventType as On, createKvStoreTableQuery as Ot, formatSchemaVerificationIssues as P, KyselyStatementFactory as Pt, SyncedDbExport as Q, CrdtTableConfig as Qt, validateNewCrdtEvent as R, SQLiteTransactionWrapper as Rt, EventsPullResponse as S, createMigrator as Sn, generateId as St, WorkerState as T, CRDT_EVENT_NO_OP_PAYLOAD as Tn, tryCatch as Tt, ClientDatasetRejectionReason as U, ColumnMetadata as Ut, defineSyncSchema as V, Logger as Vt, admitClientEvents as W, DatabaseIntrospection as Wt, ImportDataOptions as X, deserializeHLC as Xt, xxhash as Y, compareHLC as Yt, ImportDataResult as Z, serializeHLC as Zt, CreateRemoteSourceFactory as _, MigratableEvent as _n, TypedBroadcastChannel as _t, SyncedDbDatabase as a, ColumnKind as an, OwnCrdtSnapshot as at, EventsPushResponse as b, SyncDbMigrator as bn, createDeferredPromise as bt, SyncedDbTransaction as c, InferRow as cn, SystemMigration as ct, CRDT_CHANGE_INTENTS_TABLE as d, SyncSchemaTables as dn, applyWorkerDbSchema as dt, SyncDbSchema as en, createImportData as et, crdtViewColumnsFromTable as f, TableBuilder as fn, baseSystemMigrations as ft, CrdtSyncRemoteSource as g, t as gn, DistributiveOmit as gt, makeCrdtTable as h, WriteOrigin as hn, DeferredPromise as ht, SyncedDb as i, ColumnBuilder as in, OwnCrdtEvent as it, createCrdtApplyFunction as j, PersistedCrdtEvent as jn, ExecuteResult as jt, createCrdtStorageMutator as k, CrdtUpdateLogItem as kn, createSQLiteKvStore as kt, UnsafeSyncedDbSqlExecutor as l, PayloadValidationResult as ln, SystemMigrationContext as lt, drainCrdtChangeIntents as m, TableOptions as mn, runSystemMigrations as mt, ResetRequest as n, AnyColumnBuilder as nn, CrdtStorage as nt, SyncedDbOptions as o, ColumnMeta as on, createCrdtStorage as ot, createCrdtViewStatements as p, TableColumns as pn, createSystemDbConfig as pt, SharedLiveQuerySnapshot as q, HLC as qt, ResetStore as r, AnyTableBuilder as rn, GetEventsBatch as rt, SyncedDbSqlExecutor as s, ExportImportMode as sn, SystemDbConfig as st, RESET_REQUEST_TTL_MS as t, AiAccess as tn, CrdtChangeIntent as tt, createSyncedDb as u, SqliteStorageType as un, applyMemoryDbSchema as ut, EventsPullRequest as v, Migrations as vn, TypedEvent as vt, WorkerNotificationMessage as w, createStoredValue as wn, quoteId as wt, createCrdtSyncRemoteSource as x, createMigrations as xn, createTypedEventTarget as xt, EventsPushRequest as y, MigrationsDb as yn, TypedEventTarget as yt, DefineSyncSchemaConfig as z, compileKyselyStatement as zt };
1232
+ //# sourceMappingURL=index-BHsKLqcK.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-3zVvNWRd.js";
1
+ import { $ as createExportData, $t as ReadonlyTable, A as PendingCrdtEvent, An as CrdtUpdateLogPayload, At as ExecuteParams, B as DefinedSyncSchema, Bt as LogLevel, C as WorkerConfig, Cn as StoredValue, Ct as jsonSafeParse, D as CrdtStorageMutator, Dn as CrdtEventStatus, Dt as KvStoreItem, E as createCrdtSyncProducer, En as CrdtEventOrigin, Et as tryCatchAsync, F as verifySyncSchema, Ft as PreparedStatement, G as SQLiteReactiveDb, Gt as TableMetadata, H as AdmitClientEventsResult, Ht as startPerformanceLogger, I as CrdtEventValidationError, It as QueryBuilderOutput, J as parametersAreEqual, Jt as HLCCounter, K as SharedLiveQuery, Kt as introspectDb, L as NewCrdtEventValidationResult, Lt as SQLiteDbWrapper, M as createSQLiteCrdtApplyFunction, Mn as isNoOpCrdtEventPayload, N as SchemaVerificationIssue, Nn as dummyKysely, Nt as KyselyQueryFactory, O as SnapshotOptions, On as CrdtEventType, Ot as createKvStoreTableQuery, P as formatSchemaVerificationIssues, Q as SyncedDbExport, Qt as CrdtTableConfig, R as validateNewCrdtEvent, Rt as SQLiteTransactionWrapper, Sn as createMigrator, St as generateId, T as WorkerState, Tn as CRDT_EVENT_NO_OP_PAYLOAD, Tt as tryCatch, Ut as ColumnMetadata, V as defineSyncSchema, Vt as Logger, W as admitClientEvents, Wt as DatabaseIntrospection, X as ImportDataOptions, Xt as deserializeHLC, Y as xxhash, Yt as compareHLC, Z as ImportDataResult, Zt as serializeHLC, _n as MigratableEvent, _t as TypedBroadcastChannel, a as SyncedDbDatabase, an as ColumnKind, at as OwnCrdtSnapshot, b as EventsPushResponse, bn as SyncDbMigrator, bt as createDeferredPromise, c as SyncedDbTransaction, cn as InferRow, ct as SystemMigration, dn as SyncSchemaTables, dt as applyWorkerDbSchema, en as SyncDbSchema, et as createImportData, fn as TableBuilder, g as CrdtSyncRemoteSource, gn as t, gt as DistributiveOmit, h as makeCrdtTable, hn as WriteOrigin, ht as DeferredPromise, i as SyncedDb, in as ColumnBuilder, it as OwnCrdtEvent, j as createCrdtApplyFunction, jn as PersistedCrdtEvent, jt as ExecuteResult, k as createCrdtStorageMutator, kn as CrdtUpdateLogItem, kt as createSQLiteKvStore, l as UnsafeSyncedDbSqlExecutor, ln as PayloadValidationResult, lt as SystemMigrationContext, mn as TableOptions, n as ResetRequest, nn as AnyColumnBuilder, nt as CrdtStorage, o as SyncedDbOptions, on as ColumnMeta, ot as createCrdtStorage, pn as TableColumns, q as SharedLiveQuerySnapshot, qt as HLC, r as ResetStore, rn as AnyTableBuilder, s as SyncedDbSqlExecutor, sn as ExportImportMode, st as SystemDbConfig, t as RESET_REQUEST_TTL_MS, tn as AiAccess, u as createSyncedDb, un as SqliteStorageType, ut as applyMemoryDbSchema, v as EventsPullRequest, vn as Migrations, vt as TypedEvent, w as WorkerNotificationMessage, wn as createStoredValue, wt as quoteId, x as createCrdtSyncRemoteSource, xn as createMigrations, xt as createTypedEventTarget, y as EventsPushRequest, yn as MigrationsDb, yt as TypedEventTarget, z as DefineSyncSchemaConfig } from "./index-BHsKLqcK.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 };