@sqlite-sync/core 0.1.1 → 0.3.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/{chunk-UGF5IU53.js → chunk-NHT3ELMN.js} +34 -6
- package/dist/chunk-NHT3ELMN.js.map +1 -0
- package/dist/{chunk-627DSM2Q.js → chunk-RKBTBPNC.js} +566 -177
- package/dist/chunk-RKBTBPNC.js.map +1 -0
- package/dist/{crdt-sync-remote-source-idoIjMcs.d.ts → crdt-sync-remote-source-Da77s4k0.d.ts} +131 -46
- package/dist/index.d.ts +66 -34
- package/dist/index.js +158 -98
- package/dist/index.js.map +1 -1
- package/dist/{crdt-schema-DQ1cYsFE.d.ts → reset-state-0LGwO78x.d.ts} +20 -2
- package/dist/server.d.ts +9 -2
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +11 -3
- package/dist/worker.js +119 -89
- package/dist/worker.js.map +1 -1
- package/package.json +7 -3
- package/dist/chunk-627DSM2Q.js.map +0 -1
- package/dist/chunk-UGF5IU53.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import * as kysely from 'kysely';
|
|
2
|
-
import { Kysely, SchemaModule
|
|
3
|
-
import { S as SQLiteDbWrapper, L as Logger, T as TypedEvent,
|
|
4
|
-
export {
|
|
5
|
-
import { S as SyncDbSchema } from './
|
|
6
|
-
export {
|
|
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-Da77s4k0.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-Da77s4k0.js';
|
|
5
|
+
import { S as SyncDbSchema, C as CrdtTableConfig } from './reset-state-0LGwO78x.js';
|
|
6
|
+
export { a as CreateCrdtSchemaOptions, R as RESET_REQUEST_TTL_MS, b as ResetRequest, d as ResetStore, c as createSyncDbSchema } from './reset-state-0LGwO78x.js';
|
|
7
7
|
import '@sqlite.org/sqlite-wasm';
|
|
8
8
|
|
|
9
9
|
declare const dummyKysely: Kysely<any>;
|
|
10
10
|
|
|
11
|
-
|
|
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
|
|
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:
|
|
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,
|
|
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
|
-
|
|
33
|
+
syncDbClientLockName,
|
|
34
|
+
xxhash
|
|
35
|
+
} from "./chunk-RKBTBPNC.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-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
285
|
-
updateQueue
|
|
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);
|
|
@@ -393,10 +399,10 @@ function createCrdtStorageMutator({ storage }) {
|
|
|
393
399
|
};
|
|
394
400
|
case "item-deleted":
|
|
395
401
|
return {
|
|
396
|
-
type: "item-
|
|
402
|
+
type: "item-deleted",
|
|
397
403
|
dataset: event.dataset,
|
|
398
404
|
item_id: event.item_id,
|
|
399
|
-
payload:
|
|
405
|
+
payload: "{}"
|
|
400
406
|
};
|
|
401
407
|
}
|
|
402
408
|
};
|
|
@@ -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
|
}
|
|
@@ -571,10 +588,10 @@ function registerCrdtFunctions({
|
|
|
571
588
|
callback: (dataset, itemId) => {
|
|
572
589
|
storage.applyOwnEvent(
|
|
573
590
|
{
|
|
574
|
-
type: "item-
|
|
591
|
+
type: "item-deleted",
|
|
575
592
|
dataset,
|
|
576
593
|
item_id: itemId,
|
|
577
|
-
payload:
|
|
594
|
+
payload: "{}"
|
|
578
595
|
},
|
|
579
596
|
{
|
|
580
597
|
wrapInTransaction: false
|
|
@@ -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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
|
650
|
-
db.
|
|
651
|
-
"
|
|
652
|
-
|
|
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
|
-
|
|
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
|
|
808
|
+
const subscription = eventTarget.addEventListener("state-changed", (event) => {
|
|
783
809
|
promise.resolve(event.payload.state);
|
|
784
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|