@laboverwire/stitch 0.4.3 → 0.5.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/CHANGELOG.md +40 -0
- package/README.md +8 -5
- package/dist/index.d.ts +1 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +1 -1
- package/dist/react/provider.d.ts +2 -2
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/store.d.ts +2 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +190 -724
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +23 -161
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -6
- package/dist/types.js.map +1 -1
- package/package.json +16 -7
- package/dist/internal-utils.d.ts +0 -4
- package/dist/internal-utils.d.ts.map +0 -1
- package/dist/internal-utils.js +0 -20
- package/dist/internal-utils.js.map +0 -1
- package/dist/internal-wasm-error.d.ts +0 -9
- package/dist/internal-wasm-error.d.ts.map +0 -1
- package/dist/internal-wasm-error.js +0 -48
- package/dist/internal-wasm-error.js.map +0 -1
- package/dist/memory-store.d.ts +0 -3
- package/dist/memory-store.d.ts.map +0 -1
- package/dist/memory-store.js +0 -572
- package/dist/memory-store.js.map +0 -1
- package/dist/offline-queue.d.ts +0 -6
- package/dist/offline-queue.d.ts.map +0 -1
- package/dist/offline-queue.js +0 -363
- package/dist/offline-queue.js.map +0 -1
- package/dist/persistence-layer.d.ts +0 -7
- package/dist/persistence-layer.d.ts.map +0 -1
- package/dist/persistence-layer.js +0 -409
- package/dist/persistence-layer.js.map +0 -1
- package/dist/remote-sync-layer.d.ts +0 -8
- package/dist/remote-sync-layer.d.ts.map +0 -1
- package/dist/remote-sync-layer.js +0 -439
- package/dist/remote-sync-layer.js.map +0 -1
- package/dist/sync-engine.d.ts +0 -3
- package/dist/sync-engine.d.ts.map +0 -1
- package/dist/sync-engine.js +0 -643
- package/dist/sync-engine.js.map +0 -1
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { wrapWasmError, isCorruptionError } from "./internal-wasm-error.js";
|
|
2
|
-
const PENDING_SYNC_DEFINITION = {
|
|
3
|
-
fields: [
|
|
4
|
-
{ name: 'id', type: 'string', required: true },
|
|
5
|
-
{ name: 'op', type: 'string', required: true },
|
|
6
|
-
{ name: 'entity', type: 'string', required: true },
|
|
7
|
-
{ name: 'entityId', type: 'string', required: true },
|
|
8
|
-
{ name: 'scopeId', type: 'string', required: true },
|
|
9
|
-
{ name: 'userId', type: 'string', required: true },
|
|
10
|
-
{ name: 'data', type: 'object' },
|
|
11
|
-
{ name: 'createdAt', type: 'number' },
|
|
12
|
-
],
|
|
13
|
-
indexes: ['scopeId', 'entity'],
|
|
14
|
-
};
|
|
15
|
-
class PersistenceLayerImpl {
|
|
16
|
-
db = null;
|
|
17
|
-
dbName = null;
|
|
18
|
-
dbRecovering = false;
|
|
19
|
-
_dbNeedsRecovery = false;
|
|
20
|
-
shuttingDown = false;
|
|
21
|
-
_opQueue = Promise.resolve();
|
|
22
|
-
config;
|
|
23
|
-
allSyncedEntities;
|
|
24
|
-
allLocalEntities;
|
|
25
|
-
entitySubscriptions = new Map();
|
|
26
|
-
wasmSubscriptionIds = new Map();
|
|
27
|
-
suppressEntityNotifications = false;
|
|
28
|
-
constructor(config) {
|
|
29
|
-
this.config = config;
|
|
30
|
-
this.allSyncedEntities = [
|
|
31
|
-
config.scope.rootEntity,
|
|
32
|
-
...config.scope.childEntities,
|
|
33
|
-
...(config.topLevelEntities?.map((t) => t.entity) ?? []),
|
|
34
|
-
];
|
|
35
|
-
this.allLocalEntities = Object.keys(config.localOnlyEntities ?? {});
|
|
36
|
-
}
|
|
37
|
-
get isOpen() {
|
|
38
|
-
return this.db !== null;
|
|
39
|
-
}
|
|
40
|
-
async open(dbName) {
|
|
41
|
-
if (this.db)
|
|
42
|
-
return;
|
|
43
|
-
const wasmMod = await import('mqdb-wasm');
|
|
44
|
-
try {
|
|
45
|
-
await wasmMod.default();
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
throw wrapWasmError('init', err);
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
this.db = await wasmMod.Database.openPersistent(dbName);
|
|
52
|
-
this.dbName = dbName;
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
throw wrapWasmError(`openPersistent:${dbName}`, err);
|
|
56
|
-
}
|
|
57
|
-
await this.setupSchemas();
|
|
58
|
-
this.setupWasmSubscriptions();
|
|
59
|
-
}
|
|
60
|
-
close() {
|
|
61
|
-
this.shuttingDown = true;
|
|
62
|
-
this._opQueue = Promise.resolve();
|
|
63
|
-
this.unsubscribeAllWasm();
|
|
64
|
-
this.entitySubscriptions.clear();
|
|
65
|
-
if (this.db) {
|
|
66
|
-
try {
|
|
67
|
-
this.db.free();
|
|
68
|
-
}
|
|
69
|
-
catch { }
|
|
70
|
-
}
|
|
71
|
-
this.db = null;
|
|
72
|
-
this.shuttingDown = false;
|
|
73
|
-
}
|
|
74
|
-
notifyCorruption() {
|
|
75
|
-
this._dbNeedsRecovery = true;
|
|
76
|
-
this.serialized('proactiveRecovery', async () => { }).catch(() => { });
|
|
77
|
-
}
|
|
78
|
-
setSuppressNotifications(suppress) {
|
|
79
|
-
this.suppressEntityNotifications = suppress;
|
|
80
|
-
}
|
|
81
|
-
serialized(label, fn, timeoutMs = 10000) {
|
|
82
|
-
if (this.shuttingDown)
|
|
83
|
-
return Promise.reject(new Error('Service shutting down'));
|
|
84
|
-
const result = this._opQueue.then(async () => {
|
|
85
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
86
|
-
if (this._dbNeedsRecovery) {
|
|
87
|
-
this._dbNeedsRecovery = false;
|
|
88
|
-
await this.recoverDb();
|
|
89
|
-
}
|
|
90
|
-
let timer;
|
|
91
|
-
const timeout = new Promise((_, reject) => {
|
|
92
|
-
timer = setTimeout(() => {
|
|
93
|
-
this._dbNeedsRecovery = true;
|
|
94
|
-
reject(new Error(`serialized('${label}') timed out after ${timeoutMs}ms`));
|
|
95
|
-
}, timeoutMs);
|
|
96
|
-
});
|
|
97
|
-
return Promise.race([fn(), timeout]).finally(() => {
|
|
98
|
-
clearTimeout(timer);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
this._opQueue = result.then(() => undefined, () => undefined);
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
async create(entity, data) {
|
|
105
|
-
await this.serialized('create:' + entity, async () => {
|
|
106
|
-
if (!this.db)
|
|
107
|
-
throw new Error('Database not open');
|
|
108
|
-
try {
|
|
109
|
-
await this.db.create(entity, data);
|
|
110
|
-
}
|
|
111
|
-
catch (err) {
|
|
112
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
113
|
-
try {
|
|
114
|
-
await this.db.create(entity, data);
|
|
115
|
-
}
|
|
116
|
-
catch (retryErr) {
|
|
117
|
-
throw wrapWasmError(`create:${entity}`, retryErr);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
throw wrapWasmError(`create:${entity}`, err);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
async read(entity, id) {
|
|
127
|
-
return this.serialized('read:' + entity, async () => {
|
|
128
|
-
if (!this.db)
|
|
129
|
-
throw new Error('Database not open');
|
|
130
|
-
try {
|
|
131
|
-
return (await this.db.read(entity, id));
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
135
|
-
try {
|
|
136
|
-
return (await this.db.read(entity, id));
|
|
137
|
-
}
|
|
138
|
-
catch (retryErr) {
|
|
139
|
-
throw wrapWasmError(`read:${entity}`, retryErr);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
throw wrapWasmError(`read:${entity}`, err);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
async update(entity, id, data) {
|
|
147
|
-
await this.serialized('update:' + entity, async () => {
|
|
148
|
-
if (!this.db)
|
|
149
|
-
throw new Error('Database not open');
|
|
150
|
-
try {
|
|
151
|
-
await this.db.update(entity, id, data);
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
155
|
-
try {
|
|
156
|
-
await this.db.update(entity, id, data);
|
|
157
|
-
}
|
|
158
|
-
catch (retryErr) {
|
|
159
|
-
throw wrapWasmError(`update:${entity}`, retryErr);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
throw wrapWasmError(`update:${entity}`, err);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
async delete(entity, id) {
|
|
169
|
-
await this.serialized('delete:' + entity, async () => {
|
|
170
|
-
if (!this.db)
|
|
171
|
-
throw new Error('Database not open');
|
|
172
|
-
try {
|
|
173
|
-
await this.db.delete(entity, id);
|
|
174
|
-
}
|
|
175
|
-
catch (err) {
|
|
176
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
177
|
-
try {
|
|
178
|
-
await this.db.delete(entity, id);
|
|
179
|
-
}
|
|
180
|
-
catch (retryErr) {
|
|
181
|
-
throw wrapWasmError(`delete:${entity}`, retryErr);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
throw wrapWasmError(`delete:${entity}`, err);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
async list(entity, options) {
|
|
191
|
-
return this.serialized('list:' + entity, async () => {
|
|
192
|
-
if (!this.db)
|
|
193
|
-
return [];
|
|
194
|
-
try {
|
|
195
|
-
return (await this.db.list(entity, options ?? {}));
|
|
196
|
-
}
|
|
197
|
-
catch (err) {
|
|
198
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
199
|
-
try {
|
|
200
|
-
return (await this.db.list(entity, options ?? {}));
|
|
201
|
-
}
|
|
202
|
-
catch {
|
|
203
|
-
return [];
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
throw wrapWasmError(`list:${entity}`, err);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
async count(entity, options) {
|
|
211
|
-
return this.serialized('count:' + entity, async () => {
|
|
212
|
-
if (!this.db)
|
|
213
|
-
return 0;
|
|
214
|
-
try {
|
|
215
|
-
return await this.db.count(entity, options ?? {});
|
|
216
|
-
}
|
|
217
|
-
catch (err) {
|
|
218
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
219
|
-
try {
|
|
220
|
-
return await this.db.count(entity, options ?? {});
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
return 0;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return 0;
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
subscribe(entity, callback) {
|
|
231
|
-
let set = this.entitySubscriptions.get(entity);
|
|
232
|
-
if (!set) {
|
|
233
|
-
set = new Set();
|
|
234
|
-
this.entitySubscriptions.set(entity, set);
|
|
235
|
-
}
|
|
236
|
-
set.add(callback);
|
|
237
|
-
return () => {
|
|
238
|
-
const current = this.entitySubscriptions.get(entity);
|
|
239
|
-
if (!current)
|
|
240
|
-
return;
|
|
241
|
-
current.delete(callback);
|
|
242
|
-
if (current.size === 0)
|
|
243
|
-
this.entitySubscriptions.delete(entity);
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
notifyAllEntitySubscribers() {
|
|
247
|
-
for (const entity of this.entitySubscriptions.keys()) {
|
|
248
|
-
this.notifyEntitySubscribers(entity, null, 'update');
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
async readLocalState(entity, id) {
|
|
252
|
-
return this.serialized('readLocalState', async () => {
|
|
253
|
-
if (!this.db)
|
|
254
|
-
return null;
|
|
255
|
-
try {
|
|
256
|
-
return (await this.db.read(entity, id));
|
|
257
|
-
}
|
|
258
|
-
catch (err) {
|
|
259
|
-
if (this.isDbCorrupted(err) && (await this.recoverDb())) {
|
|
260
|
-
try {
|
|
261
|
-
return (await this.db.read(entity, id));
|
|
262
|
-
}
|
|
263
|
-
catch {
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
async updateLocalState(entity, id, fields) {
|
|
272
|
-
await this.serialized('updateLocalState', async () => {
|
|
273
|
-
if (!this.db)
|
|
274
|
-
return;
|
|
275
|
-
try {
|
|
276
|
-
await this.db.update(entity, id, fields);
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
try {
|
|
280
|
-
await this.db.create(entity, { id, ...fields });
|
|
281
|
-
}
|
|
282
|
-
catch { }
|
|
283
|
-
}
|
|
284
|
-
}, 5000);
|
|
285
|
-
}
|
|
286
|
-
notifyEntitySubscribers(entity, data, op) {
|
|
287
|
-
const subscribers = this.entitySubscriptions.get(entity);
|
|
288
|
-
if (subscribers) {
|
|
289
|
-
for (const callback of subscribers) {
|
|
290
|
-
callback(data, op);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
parseOperationFromEvent(operation) {
|
|
295
|
-
if (!operation)
|
|
296
|
-
return null;
|
|
297
|
-
if (operation === 'create')
|
|
298
|
-
return 'insert';
|
|
299
|
-
if (operation === 'update')
|
|
300
|
-
return 'update';
|
|
301
|
-
if (operation === 'delete')
|
|
302
|
-
return 'delete';
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
async setupSchemas() {
|
|
306
|
-
if (!this.db)
|
|
307
|
-
return;
|
|
308
|
-
const db = this.db;
|
|
309
|
-
const { entities, localOnlyEntities } = this.config;
|
|
310
|
-
const pendingSyncDef = localOnlyEntities?.['pending_sync'];
|
|
311
|
-
const schemaDefs = [
|
|
312
|
-
...Object.entries(entities),
|
|
313
|
-
...Object.entries(localOnlyEntities ?? {}),
|
|
314
|
-
];
|
|
315
|
-
if (!pendingSyncDef) {
|
|
316
|
-
schemaDefs.push(['pending_sync', PENDING_SYNC_DEFINITION]);
|
|
317
|
-
}
|
|
318
|
-
await Promise.all(schemaDefs.map(async ([entity, def]) => {
|
|
319
|
-
try {
|
|
320
|
-
await db.addSchemaAsync(entity, def);
|
|
321
|
-
}
|
|
322
|
-
catch (err) {
|
|
323
|
-
throw wrapWasmError(`addSchema:${entity}`, err);
|
|
324
|
-
}
|
|
325
|
-
}));
|
|
326
|
-
await Promise.all(schemaDefs.flatMap(([entity, def]) => [
|
|
327
|
-
...(def.foreignKeys ?? []).map(async (fk) => {
|
|
328
|
-
try {
|
|
329
|
-
await db.addForeignKeyAsync(entity, fk.field, fk.references, 'id', fk.onDelete);
|
|
330
|
-
}
|
|
331
|
-
catch (err) {
|
|
332
|
-
throw wrapWasmError(`addForeignKey:${entity}.${fk.field}`, err);
|
|
333
|
-
}
|
|
334
|
-
}),
|
|
335
|
-
...(def.indexes ?? []).map(async (field) => {
|
|
336
|
-
try {
|
|
337
|
-
await db.addIndexAsync(entity, [field]);
|
|
338
|
-
}
|
|
339
|
-
catch (err) {
|
|
340
|
-
throw wrapWasmError(`addIndex:${entity}.${field}`, err);
|
|
341
|
-
}
|
|
342
|
-
}),
|
|
343
|
-
]));
|
|
344
|
-
}
|
|
345
|
-
unsubscribeAllWasm() {
|
|
346
|
-
if (this.db) {
|
|
347
|
-
for (const subId of this.wasmSubscriptionIds.values()) {
|
|
348
|
-
try {
|
|
349
|
-
this.db.unsubscribe(subId);
|
|
350
|
-
}
|
|
351
|
-
catch { }
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
this.wasmSubscriptionIds.clear();
|
|
355
|
-
}
|
|
356
|
-
setupWasmSubscriptions() {
|
|
357
|
-
if (!this.db)
|
|
358
|
-
return;
|
|
359
|
-
this.unsubscribeAllWasm();
|
|
360
|
-
const allEntities = [...this.allSyncedEntities, ...this.allLocalEntities];
|
|
361
|
-
for (const entity of allEntities) {
|
|
362
|
-
let subId;
|
|
363
|
-
try {
|
|
364
|
-
subId = this.db.subscribe('#', entity, (event) => {
|
|
365
|
-
if (this.suppressEntityNotifications)
|
|
366
|
-
return;
|
|
367
|
-
const evt = event;
|
|
368
|
-
const op = this.parseOperationFromEvent(evt.operation);
|
|
369
|
-
if (op) {
|
|
370
|
-
this.notifyEntitySubscribers(entity, evt.data, op);
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
catch (err) {
|
|
375
|
-
throw wrapWasmError(`subscribe:${entity}`, err);
|
|
376
|
-
}
|
|
377
|
-
this.wasmSubscriptionIds.set(entity, subId);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
isDbCorrupted(err) {
|
|
381
|
-
return isCorruptionError(err, ['index out of bounds', 'database is busy']);
|
|
382
|
-
}
|
|
383
|
-
async recoverDb() {
|
|
384
|
-
if (this.dbRecovering)
|
|
385
|
-
return false;
|
|
386
|
-
this.dbRecovering = true;
|
|
387
|
-
const oldDb = this.db;
|
|
388
|
-
try {
|
|
389
|
-
if (!this.dbName)
|
|
390
|
-
return false;
|
|
391
|
-
const wasmMod = await import('mqdb-wasm');
|
|
392
|
-
this.db = await wasmMod.Database.openPersistent(this.dbName);
|
|
393
|
-
await this.setupSchemas();
|
|
394
|
-
this.setupWasmSubscriptions();
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
this.db = oldDb;
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
finally {
|
|
402
|
-
this.dbRecovering = false;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
export function createPersistenceLayer(config) {
|
|
407
|
-
return new PersistenceLayerImpl(config);
|
|
408
|
-
}
|
|
409
|
-
//# sourceMappingURL=persistence-layer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"persistence-layer.js","sourceRoot":"","sources":["../src/persistence-layer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from 'mqdb-wasm';\nimport type { StoreConfig, PersistenceLayer, EntityDefinition } from './types.ts';\nimport { wrapWasmError, isCorruptionError } from './internal-wasm-error.ts';\n\nconst PENDING_SYNC_DEFINITION: EntityDefinition = {\n fields: [\n { name: 'id', type: 'string', required: true },\n { name: 'op', type: 'string', required: true },\n { name: 'entity', type: 'string', required: true },\n { name: 'entityId', type: 'string', required: true },\n { name: 'scopeId', type: 'string', required: true },\n { name: 'userId', type: 'string', required: true },\n { name: 'data', type: 'object' },\n { name: 'createdAt', type: 'number' },\n ],\n indexes: ['scopeId', 'entity'],\n};\n\ntype EntitySubscriptionCallback = (entity: unknown, op: 'insert' | 'update' | 'delete') => void;\n\nclass PersistenceLayerImpl implements PersistenceLayer {\n private db: Database | null = null;\n private dbName: string | null = null;\n private dbRecovering = false;\n private _dbNeedsRecovery = false;\n private shuttingDown = false;\n private _opQueue: Promise<void> = Promise.resolve();\n private readonly config: StoreConfig;\n private readonly allSyncedEntities: string[];\n private readonly allLocalEntities: string[];\n private entitySubscriptions: Map<string, Set<EntitySubscriptionCallback>> = new Map();\n private wasmSubscriptionIds: Map<string, string> = new Map();\n private suppressEntityNotifications = false;\n\n constructor(config: StoreConfig) {\n this.config = config;\n this.allSyncedEntities = [\n config.scope.rootEntity,\n ...config.scope.childEntities,\n ...(config.topLevelEntities?.map((t) => t.entity) ?? []),\n ];\n this.allLocalEntities = Object.keys(config.localOnlyEntities ?? {});\n }\n\n get isOpen(): boolean {\n return this.db !== null;\n }\n\n async open(dbName: string): Promise<void> {\n if (this.db) return;\n const wasmMod = await import('mqdb-wasm');\n try {\n await wasmMod.default();\n } catch (err) {\n throw wrapWasmError('init', err);\n }\n try {\n this.db = await wasmMod.Database.openPersistent(dbName);\n this.dbName = dbName;\n } catch (err) {\n throw wrapWasmError(`openPersistent:${dbName}`, err);\n }\n await this.setupSchemas();\n this.setupWasmSubscriptions();\n }\n\n close(): void {\n this.shuttingDown = true;\n this._opQueue = Promise.resolve();\n this.unsubscribeAllWasm();\n this.entitySubscriptions.clear();\n if (this.db) {\n try {\n this.db.free();\n } catch {}\n }\n this.db = null;\n this.shuttingDown = false;\n }\n\n notifyCorruption(): void {\n this._dbNeedsRecovery = true;\n this.serialized('proactiveRecovery', async () => {}).catch(() => {});\n }\n\n setSuppressNotifications(suppress: boolean): void {\n this.suppressEntityNotifications = suppress;\n }\n\n private serialized<T>(label: string, fn: () => Promise<T>, timeoutMs = 10000): Promise<T> {\n if (this.shuttingDown) return Promise.reject(new Error('Service shutting down'));\n const result = this._opQueue.then(async () => {\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n\n if (this._dbNeedsRecovery) {\n this._dbNeedsRecovery = false;\n await this.recoverDb();\n }\n\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n this._dbNeedsRecovery = true;\n reject(new Error(`serialized('${label}') timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n return Promise.race([fn(), timeout]).finally(() => {\n clearTimeout(timer);\n });\n });\n this._opQueue = result.then(\n () => undefined,\n () => undefined\n );\n return result;\n }\n\n async create(entity: string, data: Record<string, unknown>): Promise<void> {\n await this.serialized('create:' + entity, async () => {\n if (!this.db) throw new Error('Database not open');\n try {\n await this.db.create(entity, data);\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n await this.db!.create(entity, data);\n } catch (retryErr) {\n throw wrapWasmError(`create:${entity}`, retryErr);\n }\n } else {\n throw wrapWasmError(`create:${entity}`, err);\n }\n }\n });\n }\n\n async read(entity: string, id: string): Promise<Record<string, unknown>> {\n return this.serialized('read:' + entity, async () => {\n if (!this.db) throw new Error('Database not open');\n try {\n return (await this.db.read(entity, id)) as Record<string, unknown>;\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n return (await this.db!.read(entity, id)) as Record<string, unknown>;\n } catch (retryErr) {\n throw wrapWasmError(`read:${entity}`, retryErr);\n }\n }\n throw wrapWasmError(`read:${entity}`, err);\n }\n });\n }\n\n async update(entity: string, id: string, data: Record<string, unknown>): Promise<void> {\n await this.serialized('update:' + entity, async () => {\n if (!this.db) throw new Error('Database not open');\n try {\n await this.db.update(entity, id, data);\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n await this.db!.update(entity, id, data);\n } catch (retryErr) {\n throw wrapWasmError(`update:${entity}`, retryErr);\n }\n } else {\n throw wrapWasmError(`update:${entity}`, err);\n }\n }\n });\n }\n\n async delete(entity: string, id: string): Promise<void> {\n await this.serialized('delete:' + entity, async () => {\n if (!this.db) throw new Error('Database not open');\n try {\n await this.db.delete(entity, id);\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n await this.db!.delete(entity, id);\n } catch (retryErr) {\n throw wrapWasmError(`delete:${entity}`, retryErr);\n }\n } else {\n throw wrapWasmError(`delete:${entity}`, err);\n }\n }\n });\n }\n\n async list(\n entity: string,\n options?: Record<string, unknown>\n ): Promise<Record<string, unknown>[]> {\n return this.serialized('list:' + entity, async () => {\n if (!this.db) return [];\n try {\n return (await this.db.list(entity, options ?? {})) as Record<string, unknown>[];\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n return (await this.db!.list(entity, options ?? {})) as Record<string, unknown>[];\n } catch {\n return [];\n }\n }\n throw wrapWasmError(`list:${entity}`, err);\n }\n });\n }\n\n async count(entity: string, options?: Record<string, unknown>): Promise<number> {\n return this.serialized('count:' + entity, async () => {\n if (!this.db) return 0;\n try {\n return await this.db.count(entity, options ?? {});\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n return await this.db!.count(entity, options ?? {});\n } catch {\n return 0;\n }\n }\n return 0;\n }\n });\n }\n\n subscribe(\n entity: string,\n callback: (data: unknown, op: 'insert' | 'update' | 'delete') => void\n ): () => void {\n let set = this.entitySubscriptions.get(entity);\n if (!set) {\n set = new Set();\n this.entitySubscriptions.set(entity, set);\n }\n set.add(callback);\n return () => {\n const current = this.entitySubscriptions.get(entity);\n if (!current) return;\n current.delete(callback);\n if (current.size === 0) this.entitySubscriptions.delete(entity);\n };\n }\n\n notifyAllEntitySubscribers(): void {\n for (const entity of this.entitySubscriptions.keys()) {\n this.notifyEntitySubscribers(entity, null, 'update');\n }\n }\n\n async readLocalState(entity: string, id: string): Promise<Record<string, unknown> | null> {\n return this.serialized('readLocalState', async () => {\n if (!this.db) return null;\n try {\n return (await this.db.read(entity, id)) as Record<string, unknown>;\n } catch (err) {\n if (this.isDbCorrupted(err) && (await this.recoverDb())) {\n try {\n return (await this.db!.read(entity, id)) as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n return null;\n }\n });\n }\n\n async updateLocalState(\n entity: string,\n id: string,\n fields: Record<string, unknown>\n ): Promise<void> {\n await this.serialized(\n 'updateLocalState',\n async () => {\n if (!this.db) return;\n try {\n await this.db.update(entity, id, fields);\n } catch {\n try {\n await this.db.create(entity, { id, ...fields });\n } catch {}\n }\n },\n 5000\n );\n }\n\n private notifyEntitySubscribers(\n entity: string,\n data: unknown,\n op: 'insert' | 'update' | 'delete'\n ): void {\n const subscribers = this.entitySubscriptions.get(entity);\n if (subscribers) {\n for (const callback of subscribers) {\n callback(data, op);\n }\n }\n }\n\n private parseOperationFromEvent(\n operation: string | undefined\n ): 'insert' | 'update' | 'delete' | null {\n if (!operation) return null;\n if (operation === 'create') return 'insert';\n if (operation === 'update') return 'update';\n if (operation === 'delete') return 'delete';\n return null;\n }\n\n private async setupSchemas(): Promise<void> {\n if (!this.db) return;\n const db = this.db;\n\n const { entities, localOnlyEntities } = this.config;\n const pendingSyncDef = localOnlyEntities?.['pending_sync'];\n\n const schemaDefs: Array<[string, EntityDefinition]> = [\n ...Object.entries(entities),\n ...Object.entries(localOnlyEntities ?? {}),\n ];\n if (!pendingSyncDef) {\n schemaDefs.push(['pending_sync', PENDING_SYNC_DEFINITION]);\n }\n\n await Promise.all(\n schemaDefs.map(async ([entity, def]) => {\n try {\n await db.addSchemaAsync(entity, def);\n } catch (err) {\n throw wrapWasmError(`addSchema:${entity}`, err);\n }\n })\n );\n\n await Promise.all(\n schemaDefs.flatMap(([entity, def]) => [\n ...(def.foreignKeys ?? []).map(async (fk) => {\n try {\n await db.addForeignKeyAsync(entity, fk.field, fk.references, 'id', fk.onDelete);\n } catch (err) {\n throw wrapWasmError(`addForeignKey:${entity}.${fk.field}`, err);\n }\n }),\n ...(def.indexes ?? []).map(async (field) => {\n try {\n await db.addIndexAsync(entity, [field]);\n } catch (err) {\n throw wrapWasmError(`addIndex:${entity}.${field}`, err);\n }\n }),\n ])\n );\n }\n\n private unsubscribeAllWasm(): void {\n if (this.db) {\n for (const subId of this.wasmSubscriptionIds.values()) {\n try {\n this.db.unsubscribe(subId);\n } catch {}\n }\n }\n this.wasmSubscriptionIds.clear();\n }\n\n private setupWasmSubscriptions(): void {\n if (!this.db) return;\n this.unsubscribeAllWasm();\n\n const allEntities = [...this.allSyncedEntities, ...this.allLocalEntities];\n\n for (const entity of allEntities) {\n let subId: string;\n try {\n subId = this.db.subscribe('#', entity, (event: unknown) => {\n if (this.suppressEntityNotifications) return;\n const evt = event as { operation?: string; data?: unknown };\n const op = this.parseOperationFromEvent(evt.operation);\n if (op) {\n this.notifyEntitySubscribers(entity, evt.data, op);\n }\n });\n } catch (err) {\n throw wrapWasmError(`subscribe:${entity}`, err);\n }\n this.wasmSubscriptionIds.set(entity, subId);\n }\n }\n\n private isDbCorrupted(err: unknown): boolean {\n return isCorruptionError(err, ['index out of bounds', 'database is busy']);\n }\n\n private async recoverDb(): Promise<boolean> {\n if (this.dbRecovering) return false;\n this.dbRecovering = true;\n const oldDb = this.db;\n try {\n if (!this.dbName) return false;\n const wasmMod = await import('mqdb-wasm');\n this.db = await wasmMod.Database.openPersistent(this.dbName);\n await this.setupSchemas();\n this.setupWasmSubscriptions();\n return true;\n } catch {\n this.db = oldDb;\n return false;\n } finally {\n this.dbRecovering = false;\n }\n }\n}\n\nexport function createPersistenceLayer(config: StoreConfig): PersistenceLayer & {\n notifyCorruption(): void;\n setSuppressNotifications(suppress: boolean): void;\n notifyAllEntitySubscribers(): void;\n} {\n return new PersistenceLayerImpl(config);\n}\n"]}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { StoreConfig, SyncMutation, ConnectionStatus, RemoteSyncLayer } from './types.ts';
|
|
2
|
-
export declare function createRemoteSyncLayer(config: StoreConfig): RemoteSyncLayer & {
|
|
3
|
-
setMutationCallback(cb: (mutation: SyncMutation) => void): void;
|
|
4
|
-
setConnectionChangeCallback(cb: (status: ConnectionStatus) => void): void;
|
|
5
|
-
setOnConnectedCallback(cb: () => void): void;
|
|
6
|
-
readonly isInitialSyncDone: boolean;
|
|
7
|
-
};
|
|
8
|
-
//# sourceMappingURL=remote-sync-layer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remote-sync-layer.d.ts","sourceRoot":"","sources":["../src/remote-sync-layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEZ,gBAAgB,EAGhB,eAAe,EAGhB,MAAM,YAAY,CAAC;AAuepB,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,GAAG;IAC5E,mBAAmB,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI,CAAC;IAChE,2BAA2B,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,sBAAsB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;CACrC,CAEA"}
|