@sqlite-sync/core 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,14 +1,20 @@
1
1
  import * as kysely from 'kysely';
2
- import { Kysely, SchemaModule, SelectQueryBuilder } from 'kysely';
3
- import { S as SQLiteDbWrapper, L as Logger, T as TypedEvent, a as SQLiteTransactionWrapper, b as StoredValue, C as CrdtStorage, P as PersistedCrdtEvent, G as GetEventsOptions, E as ExecuteParams, c as ExecuteResult, Q as QueryBuilderOutput, K as KyselyQueryFactory, W as WorkerState } from './crdt-sync-remote-source-idoIjMcs.js';
4
- export { z as CrdtEventOrigin, A as CrdtEventStatus, B as CrdtEventType, u as CrdtSyncRemoteSource, F as CrdtUpdateLogItem, I as CrdtUpdateLogPayload, D as DatabaseIntrospection, U as DeferredPromise, V as DistributiveOmit, w as EventsPullRequest, x as EventsPushRequest, y as EventsPushResponse, H as HLC, f as HLCCounter, h as LogLevel, M as MigratableEvent, m as Migrations, n as MigrationsDb, r as PendingCrdtEvent, N as PreparedStatement, o as SyncDbMigrator, g as TableMetadata, _ as TypedBroadcastChannel, $ as TypedEventTarget, a2 as WorkerConfig, d as compareHLC, p as createCrdtApplyFunction, t as createCrdtStorage, v as createCrdtSyncRemoteSource, O as createDeferredPromise, k as createMigrations, l as createMigrator, q as createSQLiteCrdtApplyFunction, J as createStoredValue, R as createTypedEventTarget, e as deserializeHLC, X as generateId, i as introspectDb, Y as jsonSafeParse, Z as quoteId, s as serializeHLC, j as startPerformanceLogger, a0 as tryCatch, a1 as tryCatchAsync } from './crdt-sync-remote-source-idoIjMcs.js';
5
- import { S as SyncDbSchema } from './crdt-schema-DQ1cYsFE.js';
6
- export { C as CreateCrdtSchemaOptions, c as createSyncDbSchema } from './crdt-schema-DQ1cYsFE.js';
2
+ import { Kysely, SchemaModule } from 'kysely';
3
+ import { S as SQLiteDbWrapper, L as Logger, T as TypedEvent, I as InternalSQLiteWrapper, a as StoredValue, C as CrdtStorage, E as ExecuteParams, b as ExecuteResult, Q as QueryBuilderOutput, K as KyselyQueryFactory, c as SQLiteTransactionWrapper, W as WorkerState, D as DeSyncDetectedReason } from './crdt-sync-remote-source-Na9Uicu6.js';
4
+ export { R as CRDT_EVENT_NO_OP_PAYLOAD, U as CrdtEventOrigin, V as CrdtEventStatus, X as CrdtEventType, F as CrdtSyncRemoteSource, Y as CrdtUpdateLogItem, Z as CrdtUpdateLogPayload, g as DatabaseIntrospection, a5 as DeferredPromise, a6 as DistributiveOmit, J as EventsPullRequest, N as EventsPushRequest, O as EventsPushResponse, H as HLC, f as HLCCounter, a1 as KyselyStatementFactory, j as LogLevel, M as MigratableEvent, n as Migrations, o as MigrationsDb, P as PendingCrdtEvent, $ as PersistedCrdtEvent, a2 as PreparedStatement, p as SyncDbMigrator, w as SystemDbConfig, x as SystemMigration, y as SystemMigrationContext, h as TableMetadata, aa as TypedBroadcastChannel, ab as TypedEventTarget, ae as WorkerConfig, q as applyMemoryDbSchema, r as applyWorkerDbSchema, t as baseSystemMigrations, d as compareHLC, z as createCrdtApplyFunction, B as createCrdtStorage, G as createCrdtSyncRemoteSource, a3 as createDeferredPromise, l as createMigrations, m as createMigrator, A as createSQLiteCrdtApplyFunction, a0 as createStoredValue, u as createSystemDbConfig, a4 as createTypedEventTarget, e as deserializeHLC, a7 as generateId, i as introspectDb, _ as isNoOpCrdtEventPayload, a8 as jsonSafeParse, a9 as quoteId, v as runSystemMigrations, s as serializeHLC, k as startPerformanceLogger, ac as tryCatch, ad as tryCatchAsync } from './crdt-sync-remote-source-Na9Uicu6.js';
5
+ import { S as SyncDbSchema, C as CrdtTableConfig } from './reset-state-WqgHBSA4.js';
6
+ export { a as CreateCrdtSchemaOptions, R as RESET_REQUEST_TTL_MS, b as ResetRequest, d as ResetStore, c as createSyncDbSchema } from './reset-state-WqgHBSA4.js';
7
7
  import '@sqlite.org/sqlite-wasm';
8
8
 
9
9
  declare const dummyKysely: Kysely<any>;
10
10
 
11
- type TableName<Database> = keyof Database extends string ? keyof Database : never;
11
+ declare function ensureLoaded(): Promise<void>;
12
+ declare function h64(input: string, seed?: bigint): bigint;
13
+ declare const xxhash: {
14
+ ensureLoaded: typeof ensureLoaded;
15
+ h64: typeof h64;
16
+ };
17
+
12
18
  type SQLiteReactiveDbOptions = {
13
19
  snapshot: Uint8Array<ArrayBufferLike>;
14
20
  logger: Logger;
@@ -51,7 +57,7 @@ declare class SQLiteReactiveDb<Database> {
51
57
  private getClearedTables;
52
58
  addEventListener<K extends keyof EventsMap>(type: K, listener: (event: TypedEvent<EventsMap[K]>) => void): void;
53
59
  removeEventListener<K extends keyof EventsMap>(type: K, listener: (event: TypedEvent<EventsMap[K]>) => void): void;
54
- notifyTableSubscribers(tables?: (TableName<Database> | (string & {}))[]): void;
60
+ private notifyTableSubscribers;
55
61
  private registerDbHooks;
56
62
  createSnapshot(): Uint8Array<ArrayBuffer>;
57
63
  useSnapshot(snapshot: Uint8Array<ArrayBufferLike>): void;
@@ -63,8 +69,8 @@ type KvStoreItem = {
63
69
  value: string;
64
70
  };
65
71
  declare function createKvStoreTableQuery(schema: SchemaModule, tableName: string): kysely.CreateTableBuilder<string, "key" | "value">;
66
- declare function createSQLiteKvStore({ db, metaTableName, }: {
67
- db: SQLiteTransactionWrapper<any>;
72
+ declare function createSQLiteKvStore({ db, metaTableName }: {
73
+ db: InternalSQLiteWrapper<any>;
68
74
  metaTableName: string;
69
75
  }): {
70
76
  get: (key: string) => string | null;
@@ -74,27 +80,6 @@ declare function createSQLiteKvStore({ db, metaTableName, }: {
74
80
  createNumberStoredValue: (key: string, defaultValue: number) => StoredValue<number>;
75
81
  };
76
82
 
77
- type SystemMigrationContext = {
78
- eventsTableName: string;
79
- updateLogTableName: string;
80
- execute: (sql: string) => void;
81
- };
82
- type SystemMigration = {
83
- version: number;
84
- up: (ctx: SystemMigrationContext) => void;
85
- };
86
- declare const baseSystemMigrations: SystemMigration[];
87
- declare function runSystemMigrations(opts: {
88
- version: StoredValue<number>;
89
- migrations: SystemMigration[];
90
- eventsTableName: string;
91
- updateLogTableName: string;
92
- execute: (sql: string) => void;
93
- transaction: (callback: () => void) => void;
94
- }): void;
95
- declare function applyWorkerDbSchema(db: SQLiteDbWrapper<any>): void;
96
- declare function applyMemoryDbSchema(db: SQLiteDbWrapper<any>): void;
97
-
98
83
  type CrdtStorageMutator<Database> = ReturnType<typeof createCrdtStorageMutator<Database>>;
99
84
  type CommitEventOptions<Database, Table extends keyof Database & string> = {
100
85
  type: "item-created";
@@ -125,12 +110,11 @@ type CrdtSyncProducer = {
125
110
  storage: CrdtStorage;
126
111
  broadcastEvents: (request: {
127
112
  newSyncId: number;
113
+ eventHlcSum: string | null;
128
114
  }) => void;
129
115
  };
130
116
  declare const createCrdtSyncProducer: ({ storage, broadcastEvents }: CrdtSyncProducer) => void;
131
117
 
132
- declare function applyKyselyEventsBatchFilters(query: SelectQueryBuilder<any, any, PersistedCrdtEvent>, opts: GetEventsOptions): SelectQueryBuilder<any, any, PersistedCrdtEvent>;
133
-
134
118
  declare function makeCrdtTable({ db, baseTableName, crdtTableName, }: {
135
119
  db: SQLiteDbWrapper<any>;
136
120
  baseTableName: string;
@@ -139,7 +123,6 @@ declare function makeCrdtTable({ db, baseTableName, crdtTableName, }: {
139
123
 
140
124
  type SyncedDbOptions<Database, Props = undefined> = {
141
125
  dbId: string;
142
- clearOnInit?: boolean;
143
126
  worker: Worker;
144
127
  workerProps: Props;
145
128
  syncDbSchema: SyncDbSchema<Database>;
@@ -168,11 +151,60 @@ declare function createSyncedDb<Database, Props = undefined>(options: SyncedDbOp
168
151
  goOnline: () => Promise<void>;
169
152
  goOffline: () => Promise<void>;
170
153
  };
154
+ /**
155
+ * Ask the elected worker to broadcast a page reload to all tabs for this dbId.
156
+ *
157
+ * With `clean: true` the worker durably records a reset request epoch before
158
+ * broadcasting, so the worker elected on the next startup initializes with
159
+ * `clearOnInit: true` and wipes the persisted DB. Destructive — use as a
160
+ * recovery path when the durable worker DB may be de-synced.
161
+ *
162
+ * Pending in-memory tab events are not preserved, and the returned promise
163
+ * may never settle in the caller — the page typically unloads first.
164
+ */
165
+ requestReload: (options: {
166
+ clean: boolean;
167
+ }) => Promise<void>;
168
+ subscribe: <K extends "new-event-chunk-applied" | "state-changed" | "reload-requested" | "de-sync-detected" | "remote-schema-version-mismatch">(type: K, listener: (event: TypedEvent<{
169
+ "new-event-chunk-applied": {
170
+ notificationType: "new-event-chunk-applied";
171
+ newSyncId: number;
172
+ eventHlcSum: string | null;
173
+ };
174
+ "state-changed": {
175
+ notificationType: "state-changed";
176
+ state: WorkerState;
177
+ };
178
+ "reload-requested": {
179
+ notificationType: "reload-requested";
180
+ reloadEpoch: string;
181
+ clean: boolean;
182
+ };
183
+ "de-sync-detected": {
184
+ notificationType: "de-sync-detected";
185
+ reason: DeSyncDetectedReason;
186
+ };
187
+ "remote-schema-version-mismatch": {
188
+ notificationType: "remote-schema-version-mismatch";
189
+ remoteSchemaVersion: number;
190
+ localSchemaVersion: number;
191
+ };
192
+ }[K]>) => void) => {
193
+ unsubscribe: () => void;
194
+ };
171
195
  dispose: () => Promise<void>;
172
196
  _internal: {
173
197
  executeAsync: (query: ExecuteParams) => Promise<ExecuteResult<unknown>>;
198
+ getMemoryQueryTables: (query: string) => {
199
+ name: string;
200
+ isWrite: boolean;
201
+ }[];
202
+ crdtTableNames: string[];
203
+ crdtTablesConfig: CrdtTableConfig[];
204
+ schemaVersion: number;
205
+ migrationVersions: number[];
174
206
  };
175
207
  }>;
176
208
  type SyncedDb<Database> = Awaited<ReturnType<typeof createSyncedDb<Database>>>;
177
209
 
178
- export { CrdtStorage, type CrdtStorageMutator, ExecuteParams, ExecuteResult, type KvStoreItem, KyselyQueryFactory, Logger, PersistedCrdtEvent, QueryBuilderOutput, SQLiteDbWrapper, SQLiteReactiveDb, SQLiteTransactionWrapper, StoredValue, SyncDbSchema, type SyncedDb, type SystemMigration, type SystemMigrationContext, TypedEvent, WorkerState, applyKyselyEventsBatchFilters, applyMemoryDbSchema, applyWorkerDbSchema, baseSystemMigrations, createCrdtStorageMutator, createCrdtSyncProducer, createKvStoreTableQuery, createSQLiteKvStore, createSyncedDb, dummyKysely, makeCrdtTable, runSystemMigrations };
210
+ export { CrdtStorage, type CrdtStorageMutator, ExecuteParams, ExecuteResult, InternalSQLiteWrapper, type KvStoreItem, KyselyQueryFactory, Logger, QueryBuilderOutput, SQLiteDbWrapper, SQLiteReactiveDb, SQLiteTransactionWrapper, StoredValue, SyncDbSchema, type SyncedDb, TypedEvent, WorkerState, createCrdtStorageMutator, createCrdtSyncProducer, createKvStoreTableQuery, createSQLiteKvStore, createSyncedDb, dummyKysely, makeCrdtTable, xxhash };
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
+ CRDT_EVENT_NO_OP_PAYLOAD,
2
3
  HLCCounter,
4
+ RESET_REQUEST_TTL_MS,
3
5
  SQLiteDbWrapper,
4
- applyKyselyEventsBatchFilters,
5
6
  applyMemoryDbSchema,
6
7
  applyWorkerDbSchema,
7
8
  baseSystemMigrations,
@@ -17,17 +18,21 @@ import {
17
18
  createSQLiteCrdtApplyFunction,
18
19
  createSQLiteKvStore,
19
20
  createStoredValue,
21
+ createSystemDbConfig,
20
22
  deserializeHLC,
21
23
  dummyKysely,
22
24
  introspectDb,
25
+ isNoOpCrdtEventPayload,
23
26
  isWorkerErrorResponseMessage,
24
27
  isWorkerNotificationMessage,
25
28
  isWorkerResponseMessage,
29
+ memoryDbConfig,
26
30
  runSystemMigrations,
27
31
  serializeHLC,
28
32
  startPerformanceLogger,
29
- syncDbClientLockName
30
- } from "./chunk-627DSM2Q.js";
33
+ syncDbClientLockName,
34
+ xxhash
35
+ } from "./chunk-O4WYEB4H.js";
31
36
  import {
32
37
  TypedBroadcastChannel,
33
38
  TypedEvent,
@@ -35,10 +40,11 @@ import {
35
40
  createTypedEventTarget,
36
41
  generateId,
37
42
  jsonSafeParse,
43
+ noop,
38
44
  quoteId,
39
45
  tryCatch,
40
46
  tryCatchAsync
41
- } from "./chunk-UGF5IU53.js";
47
+ } from "./chunk-NHT3ELMN.js";
42
48
 
43
49
  // src/memory-db/sqlite-reactive-db.ts
44
50
  import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
@@ -245,8 +251,8 @@ var SQLiteReactiveDb = class _SQLiteReactiveDb {
245
251
  removeEventListener(type, listener) {
246
252
  this.eventTarget.removeEventListener(type, listener);
247
253
  }
248
- notifyTableSubscribers(tables = []) {
249
- if (tables.length === 0) {
254
+ notifyTableSubscribers(tables = null) {
255
+ if (!tables) {
250
256
  this.eventTarget.dispatchEvent("any-table-changed", void 0);
251
257
  return;
252
258
  }
@@ -255,7 +261,7 @@ var SQLiteReactiveDb = class _SQLiteReactiveDb {
255
261
  }
256
262
  }
257
263
  registerDbHooks() {
258
- const updateQueue = /* @__PURE__ */ new Set();
264
+ let updateQueue = /* @__PURE__ */ new Set();
259
265
  this.sqlite3.capi.sqlite3_update_hook(
260
266
  this.db.ensureDb,
261
267
  (_ctx, _opId, _db, table) => {
@@ -281,8 +287,8 @@ var SQLiteReactiveDb = class _SQLiteReactiveDb {
281
287
  if (updateQueue.size === 0) {
282
288
  return 0;
283
289
  }
284
- const tables = Array.from(updateQueue);
285
- updateQueue.clear();
290
+ const tables = updateQueue;
291
+ updateQueue = /* @__PURE__ */ new Set();
286
292
  this.eventTarget.dispatchEvent("transaction-committed", void 0);
287
293
  queueMicrotask(() => {
288
294
  this.notifyTableSubscribers(tables);
@@ -531,7 +537,13 @@ function registerCrdtFunctions({
531
537
  directOnly: false,
532
538
  innocuous: false,
533
539
  callback: (dataset, oldPayloadRaw, newPayloadRaw) => {
540
+ if (oldPayloadRaw === newPayloadRaw) {
541
+ return void 0;
542
+ }
534
543
  const tableSchema = reactiveDb.db.dbSchema[dataset];
544
+ if (!tableSchema?.columns) {
545
+ throw new Error(`Schema not found for dataset: ${dataset}`);
546
+ }
535
547
  const oldPayload = JSON.parse(oldPayloadRaw);
536
548
  const newPayload = JSON.parse(newPayloadRaw);
537
549
  let hasDiff = false;
@@ -542,6 +554,11 @@ function registerCrdtFunctions({
542
554
  if (oldValue === newValue) {
543
555
  continue;
544
556
  }
557
+ if (column.name === "id") {
558
+ throw new Error(
559
+ `Cannot update the "id" column of an item. It is used to identify the item and must be immutable.`
560
+ );
561
+ }
545
562
  hasDiff = true;
546
563
  updatePayload[column.name] = newValue;
547
564
  }
@@ -603,40 +620,88 @@ function validateDbId(dbId) {
603
620
  }
604
621
  }
605
622
 
623
+ // src/devtools-registry.ts
624
+ var devtoolsRegistrySymbol = Symbol.for("@sqlite-sync/devtools");
625
+ function createSQLiteSyncDevtoolsRegistry() {
626
+ const instances = /* @__PURE__ */ new Map();
627
+ const listeners = /* @__PURE__ */ new Set();
628
+ let snapshot = {
629
+ instances: []
630
+ };
631
+ const notify = () => {
632
+ for (const listener of listeners) {
633
+ listener();
634
+ }
635
+ };
636
+ const updateSnapshot = () => {
637
+ snapshot = {
638
+ instances: Array.from(instances.values())
639
+ };
640
+ notify();
641
+ };
642
+ return {
643
+ version: 1,
644
+ instances,
645
+ subscribe(listener) {
646
+ listeners.add(listener);
647
+ return () => {
648
+ listeners.delete(listener);
649
+ };
650
+ },
651
+ getSnapshot() {
652
+ return snapshot;
653
+ },
654
+ register(instance) {
655
+ instances.set(instance.instanceId, instance);
656
+ updateSnapshot();
657
+ let isUnregistered = false;
658
+ return () => {
659
+ if (isUnregistered) return;
660
+ isUnregistered = true;
661
+ if (!instances.delete(instance.instanceId)) return;
662
+ updateSnapshot();
663
+ };
664
+ }
665
+ };
666
+ }
667
+ function getOrCreateSQLiteSyncDevtoolsRegistry() {
668
+ const registryGlobal = globalThis;
669
+ registryGlobal[devtoolsRegistrySymbol] ??= createSQLiteSyncDevtoolsRegistry();
670
+ return registryGlobal[devtoolsRegistrySymbol];
671
+ }
672
+
606
673
  // src/memory-db/memory-db.ts
607
674
  async function createMemoryDb({
608
675
  nodeId,
609
676
  migrator,
610
677
  reactiveDb: _reactiveDb,
611
678
  hlcCounter,
612
- crdtTables
679
+ crdtTables,
680
+ initializeSchema = true,
681
+ initialSyncId,
682
+ eventHlcAccumulator
613
683
  }) {
684
+ await xxhash.ensureLoaded();
614
685
  const reactiveDb = _reactiveDb;
615
686
  const db = reactiveDb.db;
616
- applyMemoryDbSchema(db);
617
- for (const table of crdtTables) {
618
- makeCrdtTable({
619
- db,
620
- baseTableName: table.baseTableName,
621
- crdtTableName: table.crdtTableName
622
- });
687
+ if (initializeSchema) {
688
+ applyMemoryDbSchema(db);
689
+ for (const table of crdtTables) {
690
+ makeCrdtTable({
691
+ db,
692
+ baseTableName: table.baseTableName,
693
+ crdtTableName: table.crdtTableName
694
+ });
695
+ }
623
696
  }
624
- const localSyncId = createStoredValue({
625
- initialValue: 0
626
- });
627
697
  const crdtStorage = createCrdtStorage({
628
698
  nodeId,
629
- syncId: localSyncId,
699
+ initialLocalSyncId: initialSyncId ?? getCurrentSyncId(db),
630
700
  hlc: hlcCounter,
631
- persistEvent: (event) => persistEvent(db, event),
632
- getEventsBatch: (opts) => getEventsBatch(db, opts),
633
701
  migrator,
634
- handleCrdtEventApply: createSQLiteCrdtApplyFunction({
635
- db,
636
- updateLogTableName: "crdt_update_log"
637
- }),
638
- updateEvent: (syncId, update) => updateEvent(db, syncId, update),
639
- transaction: (callback) => db.executeTransaction(callback)
702
+ db,
703
+ dbConfig: memoryDbConfig,
704
+ eventHlcAccumulator
640
705
  });
641
706
  registerCrdtFunctions({
642
707
  reactiveDb,
@@ -646,48 +711,10 @@ async function createMemoryDb({
646
711
  crdtStorage
647
712
  };
648
713
  }
649
- function persistEvent(db, event) {
650
- db.executePrepared(
651
- "persist-crdt-event",
652
- event,
653
- (db2, params) => db2.insertInto("persisted_crdt_events").values({
654
- type: params("type"),
655
- dataset: params("dataset"),
656
- item_id: params("item_id"),
657
- payload: params("payload"),
658
- schema_version: params("schema_version"),
659
- sync_id: params("sync_id"),
660
- status: params("status"),
661
- timestamp: params("timestamp"),
662
- origin: params("origin"),
663
- source_node_id: params("source_node_id")
664
- }),
665
- { loggerLevel: "system" }
666
- );
667
- }
668
- function getEventsBatch(db, opts) {
669
- return db.executeKysely(
670
- (db2) => applyKyselyEventsBatchFilters(db2.selectFrom("persisted_crdt_events").selectAll(), {
671
- limit: 50,
672
- ...opts
673
- }),
674
- { loggerLevel: "system" }
675
- ).rows;
676
- }
677
- function updateEvent(db, syncId, update) {
678
- db.executePrepared(
679
- "update-crdt-event",
680
- { syncId, ...update },
681
- (db2, params) => db2.updateTable("persisted_crdt_events").set({
682
- status: params("status"),
683
- schema_version: params("schema_version"),
684
- type: params("type"),
685
- dataset: params("dataset"),
686
- item_id: params("item_id"),
687
- payload: params("payload")
688
- }).where("sync_id", "=", params("syncId")),
689
- { loggerLevel: "system" }
690
- );
714
+ function getCurrentSyncId(db) {
715
+ return db.execute("SELECT coalesce(max(sync_id), 0) AS syncId FROM persisted_crdt_events", {
716
+ loggerLevel: "system"
717
+ }).rows[0]?.syncId ?? 0;
691
718
  }
692
719
 
693
720
  // src/worker-db/db-worker-client.ts
@@ -751,12 +778,12 @@ var createWorkerDbClient = async ({
751
778
  pullEvents: (params) => queryWorker("pullEvents", [params]),
752
779
  postState: () => queryWorker("postState", []),
753
780
  goOnline: () => queryWorker("goOnline", []),
754
- goOffline: () => queryWorker("goOffline", [])
781
+ goOffline: () => queryWorker("goOffline", []),
782
+ requestReload: (options) => queryWorker("requestReload", [options])
755
783
  };
756
784
  const statePromise = awaitWorkerState(eventTarget);
757
785
  postWorkerConfig(worker, config);
758
- rpc.postState().catch(() => {
759
- });
786
+ rpc.postState().catch(noop);
760
787
  let workerState = await statePromise;
761
788
  eventTarget.addEventListener("state-changed", (event) => {
762
789
  workerState = event.payload.state;
@@ -771,19 +798,17 @@ var createWorkerDbClient = async ({
771
798
  };
772
799
  return {
773
800
  ...rpc,
774
- addEventListener: eventTarget.addEventListener,
775
- removeEventListener: eventTarget.removeEventListener,
801
+ subscribe: eventTarget.addEventListener,
776
802
  getState: () => workerState,
777
803
  dispose
778
804
  };
779
805
  };
780
806
  function awaitWorkerState(eventTarget) {
781
807
  const promise = createDeferredPromise({ timeout: 15e3 });
782
- const onStateChanged = (event) => {
808
+ const subscription = eventTarget.addEventListener("state-changed", (event) => {
783
809
  promise.resolve(event.payload.state);
784
- eventTarget.removeEventListener("state-changed", onStateChanged);
785
- };
786
- eventTarget.addEventListener("state-changed", onStateChanged);
810
+ subscription.unsubscribe();
811
+ });
787
812
  return promise.promise;
788
813
  }
789
814
  function postWorkerConfig(worker, config) {
@@ -815,6 +840,7 @@ var defaultLogger = (type, message, level = "info") => {
815
840
  async function createSyncedDb(options) {
816
841
  validateDbId(options.dbId);
817
842
  const perf = startPerformanceLogger(defaultLogger);
843
+ const instanceId = generateId();
818
844
  const tabId = generateId();
819
845
  const broadcastChannels = createBroadcastChannels(options.dbId);
820
846
  const clientLockAcquired = createDeferredPromise();
@@ -829,7 +855,6 @@ async function createSyncedDb(options) {
829
855
  config: {
830
856
  clientId: generateId(),
831
857
  dbId: options.dbId,
832
- clearOnInit: options.clearOnInit,
833
858
  props: options.workerProps
834
859
  },
835
860
  broadcastChannels
@@ -875,25 +900,30 @@ async function createSyncedDb(options) {
875
900
  nodeId: tabId,
876
901
  migrator: memoryDbMigrator,
877
902
  remoteFactory: ({ onEventsAvailable }) => {
878
- const onNewEventChunkApplied = (event) => {
879
- onEventsAvailable(event.payload.newSyncId);
880
- };
881
- workerClient.addEventListener("new-event-chunk-applied", onNewEventChunkApplied);
903
+ const subscription = workerClient.subscribe("new-event-chunk-applied", (event) => {
904
+ onEventsAvailable({ newSyncId: event.payload.newSyncId, remoteEventHlcSum: event.payload.eventHlcSum });
905
+ });
882
906
  return {
883
907
  pullEvents: (request) => workerClient.pullEvents(request),
884
908
  pushEvents: (request) => workerClient.pushTabEvents(request),
885
909
  disconnect: () => {
886
- workerClient.removeEventListener("new-event-chunk-applied", onNewEventChunkApplied);
910
+ subscription.unsubscribe();
887
911
  }
888
912
  };
889
913
  }
890
914
  });
891
915
  tabRemoteSource.goOnline();
916
+ const reloadRequestedSubscription = workerClient.subscribe("reload-requested", () => {
917
+ globalThis.location?.reload();
918
+ });
892
919
  perf.logEnd("createSyncedDb", "initialized", "info");
893
920
  let isDisposed = false;
921
+ let unregisterDevtools;
894
922
  const dispose = async () => {
895
923
  if (isDisposed) return;
896
924
  isDisposed = true;
925
+ unregisterDevtools?.();
926
+ reloadRequestedSubscription.unsubscribe();
897
927
  clientLockRelease.resolve();
898
928
  await tabRemoteSource.dispose();
899
929
  broadcastChannels.requests.close();
@@ -901,7 +931,7 @@ async function createSyncedDb(options) {
901
931
  workerClient.dispose();
902
932
  reactiveDb.dispose();
903
933
  };
904
- return {
934
+ const syncedDb = {
905
935
  db: {
906
936
  execute: reactiveDb.db.execute.bind(reactiveDb.db),
907
937
  executeKysely: reactiveDb.db.executeKysely.bind(reactiveDb.db),
@@ -911,27 +941,54 @@ async function createSyncedDb(options) {
911
941
  state: {
912
942
  getState: workerClient.getState.bind(workerClient),
913
943
  subscribe: (onChange) => {
914
- workerClient.addEventListener("state-changed", onChange);
915
- return () => {
916
- workerClient.removeEventListener("state-changed", onChange);
917
- };
944
+ const { unsubscribe } = workerClient.subscribe("state-changed", onChange);
945
+ return unsubscribe;
918
946
  },
919
947
  goOnline: workerClient.goOnline.bind(workerClient),
920
948
  goOffline: workerClient.goOffline.bind(workerClient)
921
949
  },
950
+ /**
951
+ * Ask the elected worker to broadcast a page reload to all tabs for this dbId.
952
+ *
953
+ * With `clean: true` the worker durably records a reset request epoch before
954
+ * broadcasting, so the worker elected on the next startup initializes with
955
+ * `clearOnInit: true` and wipes the persisted DB. Destructive — use as a
956
+ * recovery path when the durable worker DB may be de-synced.
957
+ *
958
+ * Pending in-memory tab events are not preserved, and the returned promise
959
+ * may never settle in the caller — the page typically unloads first.
960
+ */
961
+ requestReload: async (options2) => {
962
+ await workerClient.requestReload(options2);
963
+ setTimeout(() => globalThis.location?.reload(), 250);
964
+ },
965
+ subscribe: workerClient.subscribe,
922
966
  dispose,
923
967
  _internal: {
924
- executeAsync: workerClient.execute.bind(workerClient)
968
+ executeAsync: workerClient.execute.bind(workerClient),
969
+ getMemoryQueryTables: reactiveDb.getTablesUsed.bind(reactiveDb),
970
+ crdtTableNames: options.syncDbSchema.tablesConfig.map((table) => table.crdtTableName),
971
+ crdtTablesConfig: options.syncDbSchema.tablesConfig,
972
+ schemaVersion: workerClientSnapshot.schemaVersion,
973
+ migrationVersions: Object.keys(options.syncDbSchema.migrations).map(Number).sort((a, b) => a - b)
925
974
  }
926
975
  };
976
+ unregisterDevtools = getOrCreateSQLiteSyncDevtoolsRegistry().register({
977
+ instanceId,
978
+ dbId: options.dbId,
979
+ createdAt: Date.now(),
980
+ instance: syncedDb
981
+ });
982
+ return syncedDb;
927
983
  }
928
984
  export {
985
+ CRDT_EVENT_NO_OP_PAYLOAD,
929
986
  HLCCounter,
987
+ RESET_REQUEST_TTL_MS,
930
988
  SQLiteDbWrapper,
931
989
  SQLiteReactiveDb,
932
990
  TypedBroadcastChannel,
933
991
  TypedEvent,
934
- applyKyselyEventsBatchFilters,
935
992
  applyMemoryDbSchema,
936
993
  applyWorkerDbSchema,
937
994
  baseSystemMigrations,
@@ -950,11 +1007,13 @@ export {
950
1007
  createStoredValue,
951
1008
  createSyncDbSchema,
952
1009
  createSyncedDb,
1010
+ createSystemDbConfig,
953
1011
  createTypedEventTarget,
954
1012
  deserializeHLC,
955
1013
  dummyKysely,
956
1014
  generateId,
957
1015
  introspectDb,
1016
+ isNoOpCrdtEventPayload,
958
1017
  jsonSafeParse,
959
1018
  makeCrdtTable,
960
1019
  quoteId,
@@ -962,6 +1021,7 @@ export {
962
1021
  serializeHLC,
963
1022
  startPerformanceLogger,
964
1023
  tryCatch,
965
- tryCatchAsync
1024
+ tryCatchAsync,
1025
+ xxhash
966
1026
  };
967
1027
  //# sourceMappingURL=index.js.map