@instantdb/core 0.22.57 → 0.22.58
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/__tests__/src/Reactor.test.js +97 -21
- package/__tests__/src/utils/PersistedObject.test.ts +383 -0
- package/dist/commonjs/InMemoryStorage.d.ts +11 -0
- package/dist/commonjs/InMemoryStorage.d.ts.map +1 -0
- package/dist/commonjs/InMemoryStorage.js +48 -0
- package/dist/commonjs/InMemoryStorage.js.map +1 -0
- package/dist/commonjs/IndexedDBStorage.d.ts +10 -3
- package/dist/commonjs/IndexedDBStorage.d.ts.map +1 -1
- package/dist/commonjs/IndexedDBStorage.js +254 -8
- package/dist/commonjs/IndexedDBStorage.js.map +1 -1
- package/dist/commonjs/Reactor.d.ts +7 -16
- package/dist/commonjs/Reactor.d.ts.map +1 -1
- package/dist/commonjs/Reactor.js +199 -149
- package/dist/commonjs/Reactor.js.map +1 -1
- package/dist/commonjs/SyncTable.d.ts +3 -1
- package/dist/commonjs/SyncTable.d.ts.map +1 -1
- package/dist/commonjs/SyncTable.js +194 -168
- package/dist/commonjs/SyncTable.js.map +1 -1
- package/dist/commonjs/index.d.ts +2 -1
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +3 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/utils/PersistedObject.d.ts +73 -29
- package/dist/commonjs/utils/PersistedObject.d.ts.map +1 -1
- package/dist/commonjs/utils/PersistedObject.js +366 -77
- package/dist/commonjs/utils/PersistedObject.js.map +1 -1
- package/dist/esm/InMemoryStorage.d.ts +11 -0
- package/dist/esm/InMemoryStorage.d.ts.map +1 -0
- package/dist/esm/InMemoryStorage.js +45 -0
- package/dist/esm/InMemoryStorage.js.map +1 -0
- package/dist/esm/IndexedDBStorage.d.ts +10 -3
- package/dist/esm/IndexedDBStorage.d.ts.map +1 -1
- package/dist/esm/IndexedDBStorage.js +254 -8
- package/dist/esm/IndexedDBStorage.js.map +1 -1
- package/dist/esm/Reactor.d.ts +7 -16
- package/dist/esm/Reactor.d.ts.map +1 -1
- package/dist/esm/Reactor.js +199 -149
- package/dist/esm/Reactor.js.map +1 -1
- package/dist/esm/SyncTable.d.ts +3 -1
- package/dist/esm/SyncTable.d.ts.map +1 -1
- package/dist/esm/SyncTable.js +194 -168
- package/dist/esm/SyncTable.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/PersistedObject.d.ts +73 -29
- package/dist/esm/utils/PersistedObject.d.ts.map +1 -1
- package/dist/esm/utils/PersistedObject.js +364 -76
- package/dist/esm/utils/PersistedObject.js.map +1 -1
- package/dist/standalone/index.js +1978 -1567
- package/dist/standalone/index.umd.cjs +3 -3
- package/package.json +2 -2
- package/src/InMemoryStorage.ts +34 -0
- package/src/IndexedDBStorage.ts +281 -9
- package/src/Reactor.js +212 -184
- package/src/SyncTable.ts +220 -191
- package/src/index.ts +8 -0
- package/src/utils/PersistedObject.ts +480 -109
- package/src/InMemoryStorage.js +0 -14
package/dist/commonjs/Reactor.js
CHANGED
|
@@ -139,30 +139,46 @@ const ignoreLogging = {
|
|
|
139
139
|
'refresh-presence': true,
|
|
140
140
|
'patch-presence': true,
|
|
141
141
|
};
|
|
142
|
-
function
|
|
142
|
+
function querySubFromStorage(x, useDateObjects) {
|
|
143
143
|
var _a;
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
const storeJSON = v.result.store;
|
|
149
|
-
v.result.store = s.fromJSON(Object.assign(Object.assign({}, storeJSON), { useDateObjects: useDateObjects }));
|
|
150
|
-
}
|
|
144
|
+
const v = typeof x === 'string' ? JSON.parse(x) : x;
|
|
145
|
+
if ((_a = v === null || v === void 0 ? void 0 : v.result) === null || _a === void 0 ? void 0 : _a.store) {
|
|
146
|
+
const storeJSON = v.result.store;
|
|
147
|
+
v.result.store = s.fromJSON(Object.assign(Object.assign({}, storeJSON), { useDateObjects: useDateObjects }));
|
|
151
148
|
}
|
|
152
|
-
return
|
|
149
|
+
return v;
|
|
153
150
|
}
|
|
154
|
-
function
|
|
151
|
+
function querySubToStorage(_key, sub) {
|
|
155
152
|
var _a;
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
153
|
+
const jsonSub = Object.assign({}, sub);
|
|
154
|
+
if ((_a = sub.result) === null || _a === void 0 ? void 0 : _a.store) {
|
|
155
|
+
jsonSub.result = Object.assign(Object.assign({}, sub.result), { store: s.toJSON(sub.result.store) });
|
|
156
|
+
}
|
|
157
|
+
return jsonSub;
|
|
158
|
+
}
|
|
159
|
+
function kvFromStorage(key, x) {
|
|
160
|
+
switch (key) {
|
|
161
|
+
case 'pendingMutations':
|
|
162
|
+
return new Map(typeof x === 'string' ? JSON.parse(x) : x);
|
|
163
|
+
default:
|
|
164
|
+
return x;
|
|
164
165
|
}
|
|
165
|
-
|
|
166
|
+
}
|
|
167
|
+
function kvToStorage(key, x) {
|
|
168
|
+
switch (key) {
|
|
169
|
+
case 'pendingMutations':
|
|
170
|
+
return [...x.entries()];
|
|
171
|
+
default:
|
|
172
|
+
return x;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function onMergeQuerySub(_k, storageSub, inMemorySub) {
|
|
176
|
+
const storageResult = storageSub === null || storageSub === void 0 ? void 0 : storageSub.result;
|
|
177
|
+
const memoryResult = inMemorySub === null || inMemorySub === void 0 ? void 0 : inMemorySub.result;
|
|
178
|
+
if (storageResult && !memoryResult && inMemorySub) {
|
|
179
|
+
inMemorySub.result = storageResult;
|
|
180
|
+
}
|
|
181
|
+
return inMemorySub || storageSub;
|
|
166
182
|
}
|
|
167
183
|
function sortedMutationEntries(entries) {
|
|
168
184
|
return [...entries].sort((a, b) => {
|
|
@@ -217,65 +233,27 @@ class Reactor {
|
|
|
217
233
|
this._beforeUnloadCbs = [];
|
|
218
234
|
this._dataForQueryCache = {};
|
|
219
235
|
this._inFlightMutationEventIds = new Set();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
});
|
|
238
|
-
// Consider a storageSub with no corresponding inMemorySub
|
|
239
|
-
// This means that at least at this point,
|
|
240
|
-
// the user has not asked to subscribe to the query.
|
|
241
|
-
// We may _still_ want to add it, because in just a
|
|
242
|
-
// few milliseconds, the user will ask to subscribe to the
|
|
243
|
-
// query.
|
|
244
|
-
// For now, we can't really tell if the user will ask to subscribe
|
|
245
|
-
// or not. So for now let's just add the first 10 queries from storage.
|
|
246
|
-
// Eventually, we could be smarter about this. For example,
|
|
247
|
-
// we can keep usage information about which queries are popular.
|
|
248
|
-
const storageKsToAdd = Object.keys(storageSubs)
|
|
249
|
-
.filter((k) => !inMemorySubs[k])
|
|
250
|
-
.sort((a, b) => {
|
|
251
|
-
var _a, _b;
|
|
252
|
-
// Sort by lastAccessed, newest first
|
|
253
|
-
const aTime = ((_a = storageSubs[a]) === null || _a === void 0 ? void 0 : _a.lastAccessed) || 0;
|
|
254
|
-
const bTime = ((_b = storageSubs[b]) === null || _b === void 0 ? void 0 : _b.lastAccessed) || 0;
|
|
255
|
-
return bTime - aTime;
|
|
256
|
-
})
|
|
257
|
-
.slice(0, this.queryCacheLimit);
|
|
258
|
-
storageKsToAdd.forEach((k) => {
|
|
259
|
-
ret[k] = storageSubs[k];
|
|
260
|
-
});
|
|
261
|
-
// Okay, now we have merged our querySubs
|
|
262
|
-
this.querySubs.set((_) => ret);
|
|
263
|
-
this.loadedNotifyAll();
|
|
264
|
-
};
|
|
265
|
-
/**
|
|
266
|
-
* merge pendingMutations from storage and in memory. Has a side effect of
|
|
267
|
-
* sending mutations that were stored but not acked
|
|
268
|
-
*/
|
|
269
|
-
this._onMergePendingMutations = (storageMuts, inMemoryMuts) => {
|
|
270
|
-
const ret = new Map([...storageMuts.entries(), ...inMemoryMuts.entries()]);
|
|
271
|
-
this.pendingMutations.set((_) => ret);
|
|
272
|
-
this.loadedNotifyAll();
|
|
273
|
-
const rewrittenStorageMuts = this._rewriteMutationsSorted(this.attrs, storageMuts);
|
|
274
|
-
rewrittenStorageMuts.forEach(([k, mut]) => {
|
|
275
|
-
if (!inMemoryMuts.has(k) && !mut['tx-id']) {
|
|
276
|
-
this._sendMutation(k, mut);
|
|
236
|
+
this._onMergeKv = (key, storageV, inMemoryV) => {
|
|
237
|
+
var _a, _b;
|
|
238
|
+
switch (key) {
|
|
239
|
+
case 'pendingMutations': {
|
|
240
|
+
const storageEntries = (_a = storageV === null || storageV === void 0 ? void 0 : storageV.entries()) !== null && _a !== void 0 ? _a : [];
|
|
241
|
+
const inMemoryEntries = (_b = inMemoryV === null || inMemoryV === void 0 ? void 0 : inMemoryV.entries()) !== null && _b !== void 0 ? _b : [];
|
|
242
|
+
const muts = new Map([...storageEntries, ...inMemoryEntries]);
|
|
243
|
+
const rewrittenStorageMuts = storageV
|
|
244
|
+
? this._rewriteMutationsSorted(this.attrs, storageV)
|
|
245
|
+
: [];
|
|
246
|
+
rewrittenStorageMuts.forEach(([k, mut]) => {
|
|
247
|
+
var _a;
|
|
248
|
+
if (!((_a = inMemoryV === null || inMemoryV === void 0 ? void 0 : inMemoryV.pendingMutations) === null || _a === void 0 ? void 0 : _a.has(k)) && !mut['tx-id']) {
|
|
249
|
+
this._sendMutation(k, mut);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
return muts;
|
|
277
253
|
}
|
|
278
|
-
|
|
254
|
+
default:
|
|
255
|
+
return inMemoryV || storageV;
|
|
256
|
+
}
|
|
279
257
|
};
|
|
280
258
|
// ---------------------------
|
|
281
259
|
// Queries
|
|
@@ -334,7 +312,7 @@ class Reactor {
|
|
|
334
312
|
*/
|
|
335
313
|
this.pushOps = (txSteps, error) => {
|
|
336
314
|
const eventId = (0, uuid_ts_1.default)();
|
|
337
|
-
const mutations = [...this.
|
|
315
|
+
const mutations = [...this._pendingMutations().values()];
|
|
338
316
|
const order = Math.max(0, ...mutations.map((mut) => mut.order || 0)) + 1;
|
|
339
317
|
const mutation = {
|
|
340
318
|
op: 'transact',
|
|
@@ -343,9 +321,8 @@ class Reactor {
|
|
|
343
321
|
error,
|
|
344
322
|
order,
|
|
345
323
|
};
|
|
346
|
-
this.
|
|
324
|
+
this._updatePendingMutations((prev) => {
|
|
347
325
|
prev.set(eventId, mutation);
|
|
348
|
-
return prev;
|
|
349
326
|
});
|
|
350
327
|
const dfd = new Deferred_js_1.Deferred();
|
|
351
328
|
this.mutationDeferredStore.set(eventId, dfd);
|
|
@@ -474,7 +451,7 @@ class Reactor {
|
|
|
474
451
|
}));
|
|
475
452
|
}
|
|
476
453
|
this._initStorage(Storage);
|
|
477
|
-
this._syncTable = new SyncTable_ts_1.SyncTable(this._trySendAuthed.bind(this), this.
|
|
454
|
+
this._syncTable = new SyncTable_ts_1.SyncTable(this._trySendAuthed.bind(this), new Storage(this.config.appId, 'syncSubs'), {
|
|
478
455
|
useDateObjects: this.config.useDateObjects,
|
|
479
456
|
}, this._log, (triples) => s.createStore(this.attrs, triples, this.config.enableCardinalityInference, this._linkIndex, this.config.useDateObjects));
|
|
480
457
|
this._oauthCallbackResponse = this._oauthLoginInit();
|
|
@@ -513,21 +490,55 @@ class Reactor {
|
|
|
513
490
|
_reactorStats() {
|
|
514
491
|
return {
|
|
515
492
|
inFlightMutationCount: this._inFlightMutationEventIds.size,
|
|
516
|
-
pendingMutationCount: this.
|
|
493
|
+
pendingMutationCount: this._pendingMutations().size,
|
|
517
494
|
transportType: this._transportType,
|
|
518
495
|
};
|
|
519
496
|
}
|
|
497
|
+
_onQuerySubLoaded(hash) {
|
|
498
|
+
this.kv
|
|
499
|
+
.waitForKeyToLoad('pendingMutations')
|
|
500
|
+
.then(() => this.notifyOne(hash));
|
|
501
|
+
}
|
|
520
502
|
_initStorage(Storage) {
|
|
521
|
-
this.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
503
|
+
this.querySubs = new PersistedObject_ts_1.PersistedObject({
|
|
504
|
+
persister: new Storage(this.config.appId, 'querySubs'),
|
|
505
|
+
merge: onMergeQuerySub,
|
|
506
|
+
serialize: querySubToStorage,
|
|
507
|
+
parse: (_key, x) => querySubFromStorage(x, this.config.useDateObjects),
|
|
508
|
+
// objectSize
|
|
509
|
+
objectSize: (x) => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = x.result) === null || _a === void 0 ? void 0 : _a.store) === null || _b === void 0 ? void 0 : _b.triples) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0; },
|
|
510
|
+
logger: this._log,
|
|
511
|
+
preloadEntryCount: 10,
|
|
512
|
+
gc: {
|
|
513
|
+
maxAgeMs: 1000 * 60 * 60 * 24 * 7 * 52, // 1 year
|
|
514
|
+
maxEntries: 1000,
|
|
515
|
+
// Size of each query is the number of triples
|
|
516
|
+
maxSize: 1000000, // 1 million triples
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
this.querySubs.onKeyLoaded = (k) => this._onQuerySubLoaded(k);
|
|
520
|
+
this.kv = new PersistedObject_ts_1.PersistedObject({
|
|
521
|
+
persister: new Storage(this.config.appId, 'kv'),
|
|
522
|
+
merge: this._onMergeKv,
|
|
523
|
+
serialize: kvToStorage,
|
|
524
|
+
parse: kvFromStorage,
|
|
525
|
+
objectSize: () => 0,
|
|
526
|
+
logger: this._log,
|
|
527
|
+
saveThrottleMs: 100,
|
|
528
|
+
idleCallbackMaxWaitMs: 100,
|
|
529
|
+
// Don't GC the kv store
|
|
530
|
+
gc: null,
|
|
531
|
+
});
|
|
532
|
+
this.kv.onKeyLoaded = (k) => {
|
|
533
|
+
if (k === 'pendingMutations') {
|
|
534
|
+
this.notifyAll();
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
// Trigger immediate load for pendingMutations and currentUser
|
|
538
|
+
this.kv.waitForKeyToLoad('pendingMutations');
|
|
539
|
+
this.kv.waitForKeyToLoad(currentUserKey);
|
|
529
540
|
this._beforeUnloadCbs.push(() => {
|
|
530
|
-
this.
|
|
541
|
+
this.kv.flush();
|
|
531
542
|
this.querySubs.flush();
|
|
532
543
|
});
|
|
533
544
|
}
|
|
@@ -623,18 +634,24 @@ class Reactor {
|
|
|
623
634
|
case 'add-query-ok': {
|
|
624
635
|
const { q, result } = msg;
|
|
625
636
|
const hash = (0, weakHash_ts_1.default)(q);
|
|
637
|
+
if (!this._hasQueryListeners() && !this.querySubs.currentValue[hash]) {
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
626
640
|
const pageInfo = (_d = (_c = result === null || result === void 0 ? void 0 : result[0]) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d['page-info'];
|
|
627
641
|
const aggregate = (_f = (_e = result === null || result === void 0 ? void 0 : result[0]) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f['aggregate'];
|
|
628
642
|
const triples = (0, instaqlResult_js_1.extractTriples)(result);
|
|
629
643
|
const store = s.createStore(this.attrs, triples, enableCardinalityInference, this._linkIndex, this.config.useDateObjects);
|
|
630
|
-
this.querySubs.
|
|
644
|
+
this.querySubs.updateInPlace((prev) => {
|
|
645
|
+
if (!prev[hash]) {
|
|
646
|
+
this._log.info('Missing value in querySubs', { hash, q });
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
631
649
|
prev[hash].result = {
|
|
632
650
|
store,
|
|
633
651
|
pageInfo,
|
|
634
652
|
aggregate,
|
|
635
653
|
processedTxId: msg['processed-tx-id'],
|
|
636
654
|
};
|
|
637
|
-
return prev;
|
|
638
655
|
});
|
|
639
656
|
this._cleanupPendingMutationsQueries();
|
|
640
657
|
this.notifyOne(hash);
|
|
@@ -665,12 +682,14 @@ class Reactor {
|
|
|
665
682
|
this._setAttrs(attrs);
|
|
666
683
|
}
|
|
667
684
|
this._cleanupPendingMutationsTimeout();
|
|
668
|
-
const rewrittenMutations = this._rewriteMutations(this.attrs, this.
|
|
669
|
-
if (rewrittenMutations !== this.
|
|
685
|
+
const rewrittenMutations = this._rewriteMutations(this.attrs, this._pendingMutations(), processedTxId);
|
|
686
|
+
if (rewrittenMutations !== this._pendingMutations()) {
|
|
670
687
|
// We know we've changed the mutations to fix the attr ids and removed
|
|
671
688
|
// processed attrs, so we'll persist those changes to prevent optimisticAttrs
|
|
672
689
|
// from using old attr definitions
|
|
673
|
-
this.
|
|
690
|
+
this.kv.updateInPlace((prev) => {
|
|
691
|
+
prev.pendingMutations = rewrittenMutations;
|
|
692
|
+
});
|
|
674
693
|
}
|
|
675
694
|
const mutations = sortedMutationEntries(rewrittenMutations.entries());
|
|
676
695
|
const updates = computations.map((x) => {
|
|
@@ -683,12 +702,15 @@ class Reactor {
|
|
|
683
702
|
const newStore = this._applyOptimisticUpdates(store, mutations, processedTxId);
|
|
684
703
|
const pageInfo = (_b = (_a = result === null || result === void 0 ? void 0 : result[0]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b['page-info'];
|
|
685
704
|
const aggregate = (_d = (_c = result === null || result === void 0 ? void 0 : result[0]) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d['aggregate'];
|
|
686
|
-
return { hash, store: newStore, pageInfo, aggregate };
|
|
705
|
+
return { q, hash, store: newStore, pageInfo, aggregate };
|
|
687
706
|
});
|
|
688
|
-
updates.forEach(({ hash, store, pageInfo, aggregate }) => {
|
|
689
|
-
this.querySubs.
|
|
707
|
+
updates.forEach(({ hash, q, store, pageInfo, aggregate }) => {
|
|
708
|
+
this.querySubs.updateInPlace((prev) => {
|
|
709
|
+
if (!prev[hash]) {
|
|
710
|
+
this._log.error('Missing value in querySubs', { hash, q });
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
690
713
|
prev[hash].result = { store, pageInfo, aggregate, processedTxId };
|
|
691
|
-
return prev;
|
|
692
714
|
});
|
|
693
715
|
});
|
|
694
716
|
this._cleanupPendingMutationsQueries();
|
|
@@ -700,15 +722,14 @@ class Reactor {
|
|
|
700
722
|
case 'transact-ok': {
|
|
701
723
|
const { 'client-event-id': eventId, 'tx-id': txId } = msg;
|
|
702
724
|
this._inFlightMutationEventIds.delete(eventId);
|
|
703
|
-
const muts = this._rewriteMutations(this.attrs, this.
|
|
725
|
+
const muts = this._rewriteMutations(this.attrs, this._pendingMutations());
|
|
704
726
|
const prevMutation = muts.get(eventId);
|
|
705
727
|
if (!prevMutation) {
|
|
706
728
|
break;
|
|
707
729
|
}
|
|
708
730
|
// update pendingMutation with server-side tx-id
|
|
709
|
-
this.
|
|
731
|
+
this._updatePendingMutations((prev) => {
|
|
710
732
|
prev.set(eventId, Object.assign(Object.assign({}, prev.get(eventId)), { 'tx-id': txId, confirmed: Date.now() }));
|
|
711
|
-
return prev;
|
|
712
733
|
});
|
|
713
734
|
const newAttrs = prevMutation['tx-steps']
|
|
714
735
|
.filter(([action, ..._args]) => action === 'add-attr')
|
|
@@ -775,15 +796,27 @@ class Reactor {
|
|
|
775
796
|
break;
|
|
776
797
|
}
|
|
777
798
|
}
|
|
799
|
+
_pendingMutations() {
|
|
800
|
+
var _a;
|
|
801
|
+
return (_a = this.kv.currentValue.pendingMutations) !== null && _a !== void 0 ? _a : new Map();
|
|
802
|
+
}
|
|
803
|
+
_updatePendingMutations(f) {
|
|
804
|
+
this.kv.updateInPlace((prev) => {
|
|
805
|
+
var _a;
|
|
806
|
+
const muts = (_a = prev.pendingMutations) !== null && _a !== void 0 ? _a : new Map();
|
|
807
|
+
prev.pendingMutations = muts;
|
|
808
|
+
f(muts);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
778
811
|
/**
|
|
779
812
|
* @param {'timeout' | 'error'} status
|
|
780
813
|
* @param {string} eventId
|
|
781
814
|
* @param {{message: string, type?: string, status?: number, hint?: unknown}} errorMsg
|
|
782
815
|
*/
|
|
783
816
|
_handleMutationError(status, eventId, errorMsg) {
|
|
784
|
-
const mut = this.
|
|
817
|
+
const mut = this._pendingMutations().get(eventId);
|
|
785
818
|
if (mut && (status !== 'timeout' || !mut['tx-id'])) {
|
|
786
|
-
this.
|
|
819
|
+
this._updatePendingMutations((prev) => {
|
|
787
820
|
prev.delete(eventId);
|
|
788
821
|
return prev;
|
|
789
822
|
});
|
|
@@ -804,7 +837,7 @@ class Reactor {
|
|
|
804
837
|
const eventId = msg['client-event-id'];
|
|
805
838
|
// This might not be a mutation, but it can't hurt to delete it
|
|
806
839
|
this._inFlightMutationEventIds.delete(eventId);
|
|
807
|
-
const prevMutation = this.
|
|
840
|
+
const prevMutation = this._pendingMutations().get(eventId);
|
|
808
841
|
const errorMessage = {
|
|
809
842
|
message: msg.message || 'Uh-oh, something went wrong. Ping Joe & Stopa.',
|
|
810
843
|
};
|
|
@@ -871,10 +904,9 @@ class Reactor {
|
|
|
871
904
|
}
|
|
872
905
|
_startQuerySub(q, hash) {
|
|
873
906
|
const eventId = (0, uuid_ts_1.default)();
|
|
874
|
-
this.querySubs.
|
|
907
|
+
this.querySubs.updateInPlace((prev) => {
|
|
875
908
|
prev[hash] = prev[hash] || { q, result: null, eventId };
|
|
876
909
|
prev[hash].lastAccessed = Date.now();
|
|
877
|
-
return prev;
|
|
878
910
|
});
|
|
879
911
|
this._trySendAuthed(eventId, { op: 'add-query', q });
|
|
880
912
|
return eventId;
|
|
@@ -948,13 +980,18 @@ class Reactor {
|
|
|
948
980
|
this.queryCbs[hash] = this.queryCbs[hash].filter((r) => r.cb !== cb);
|
|
949
981
|
this._cleanupQuery(q, hash);
|
|
950
982
|
}
|
|
951
|
-
|
|
983
|
+
_hasQueryListeners(hash) {
|
|
952
984
|
var _a, _b;
|
|
953
|
-
|
|
985
|
+
return !!(((_a = this.queryCbs[hash]) === null || _a === void 0 ? void 0 : _a.length) || ((_b = this.queryOnceDfds[hash]) === null || _b === void 0 ? void 0 : _b.length));
|
|
986
|
+
}
|
|
987
|
+
_cleanupQuery(q, hash) {
|
|
988
|
+
const hasListeners = this._hasQueryListeners(hash);
|
|
954
989
|
if (hasListeners)
|
|
955
990
|
return;
|
|
956
991
|
delete this.queryCbs[hash];
|
|
957
992
|
delete this.queryOnceDfds[hash];
|
|
993
|
+
delete this._dataForQueryCache[hash];
|
|
994
|
+
this.querySubs.unloadKey(hash);
|
|
958
995
|
this._trySendAuthed((0, uuid_ts_1.default)(), { op: 'remove-query', q });
|
|
959
996
|
}
|
|
960
997
|
// When we `pushTx`, it's possible that we don't yet have `this.attrs`
|
|
@@ -971,6 +1008,8 @@ class Reactor {
|
|
|
971
1008
|
_rewriteMutations(attrs, muts, processedTxId) {
|
|
972
1009
|
if (!attrs)
|
|
973
1010
|
return muts;
|
|
1011
|
+
if (!muts)
|
|
1012
|
+
return new Map();
|
|
974
1013
|
const findExistingAttr = (attr) => {
|
|
975
1014
|
const [_, etype, label] = attr['forward-identity'];
|
|
976
1015
|
const existing = instaml.getAttrByFwdIdentName(attrs, etype, label);
|
|
@@ -1043,9 +1082,7 @@ class Reactor {
|
|
|
1043
1082
|
// Transact
|
|
1044
1083
|
optimisticAttrs() {
|
|
1045
1084
|
var _a;
|
|
1046
|
-
const pendingMutationSteps = [
|
|
1047
|
-
...this.pendingMutations.currentValue.values(),
|
|
1048
|
-
] // hack due to Map()
|
|
1085
|
+
const pendingMutationSteps = [...this._pendingMutations().values()] // hack due to Map()
|
|
1049
1086
|
.flatMap((x) => x['tx-steps']);
|
|
1050
1087
|
const deletedAttrIds = new Set(pendingMutationSteps
|
|
1051
1088
|
.filter(([action, _attr]) => action === 'delete-attr')
|
|
@@ -1077,12 +1114,12 @@ class Reactor {
|
|
|
1077
1114
|
}
|
|
1078
1115
|
if (!this.querySubs)
|
|
1079
1116
|
return;
|
|
1080
|
-
if (!this.pendingMutations)
|
|
1117
|
+
if (!this.kv.currentValue.pendingMutations)
|
|
1081
1118
|
return;
|
|
1082
1119
|
const querySubVersion = this.querySubs.version();
|
|
1083
1120
|
const querySubs = this.querySubs.currentValue;
|
|
1084
|
-
const pendingMutationsVersion = this.
|
|
1085
|
-
const pendingMutations = this.
|
|
1121
|
+
const pendingMutationsVersion = this.kv.version();
|
|
1122
|
+
const pendingMutations = this._pendingMutations();
|
|
1086
1123
|
const { q, result } = querySubs[hash] || {};
|
|
1087
1124
|
if (!result)
|
|
1088
1125
|
return;
|
|
@@ -1114,13 +1151,17 @@ class Reactor {
|
|
|
1114
1151
|
/** Re-compute all subscriptions */
|
|
1115
1152
|
notifyAll() {
|
|
1116
1153
|
Object.keys(this.queryCbs).forEach((hash) => {
|
|
1117
|
-
this.
|
|
1154
|
+
this.querySubs
|
|
1155
|
+
.waitForKeyToLoad(hash)
|
|
1156
|
+
.then(() => this.notifyOne(hash))
|
|
1157
|
+
.catch(() => this.notifyOne(hash));
|
|
1118
1158
|
});
|
|
1119
1159
|
}
|
|
1120
1160
|
loadedNotifyAll() {
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1161
|
+
this.kv
|
|
1162
|
+
.waitForKeyToLoad('pendingMutations')
|
|
1163
|
+
.then(() => this.notifyAll())
|
|
1164
|
+
.catch(() => this.notifyAll());
|
|
1124
1165
|
}
|
|
1125
1166
|
shutdown() {
|
|
1126
1167
|
var _a;
|
|
@@ -1148,7 +1189,7 @@ class Reactor {
|
|
|
1148
1189
|
}
|
|
1149
1190
|
const timeoutMs = Math.max(6000, Math.min(this._inFlightMutationEventIds.size + 1,
|
|
1150
1191
|
// Defensive code in case we don't clean up in flight mutation event ids
|
|
1151
|
-
this.
|
|
1192
|
+
this._pendingMutations().size + 1) * 6000);
|
|
1152
1193
|
if (!this._isOnline) {
|
|
1153
1194
|
this._finishTransaction('enqueued', eventId);
|
|
1154
1195
|
}
|
|
@@ -1185,7 +1226,7 @@ class Reactor {
|
|
|
1185
1226
|
.forEach(({ eventId, q }) => {
|
|
1186
1227
|
this._trySendAuthed(eventId, { op: 'add-query', q });
|
|
1187
1228
|
});
|
|
1188
|
-
const muts = this._rewriteMutationsSorted(this.attrs, this.
|
|
1229
|
+
const muts = this._rewriteMutationsSorted(this.attrs, this._pendingMutations());
|
|
1189
1230
|
muts.forEach(([eventId, mut]) => {
|
|
1190
1231
|
if (!mut['tx-id']) {
|
|
1191
1232
|
this._sendMutation(eventId, mut);
|
|
@@ -1203,13 +1244,12 @@ class Reactor {
|
|
|
1203
1244
|
minProcessedTxId = Math.min(minProcessedTxId, result === null || result === void 0 ? void 0 : result.processedTxId);
|
|
1204
1245
|
}
|
|
1205
1246
|
}
|
|
1206
|
-
this.
|
|
1247
|
+
this._updatePendingMutations((prev) => {
|
|
1207
1248
|
for (const [eventId, mut] of Array.from(prev.entries())) {
|
|
1208
1249
|
if (mut['tx-id'] && mut['tx-id'] <= minProcessedTxId) {
|
|
1209
1250
|
prev.delete(eventId);
|
|
1210
1251
|
}
|
|
1211
1252
|
}
|
|
1212
|
-
return prev;
|
|
1213
1253
|
});
|
|
1214
1254
|
}
|
|
1215
1255
|
/**
|
|
@@ -1218,19 +1258,17 @@ class Reactor {
|
|
|
1218
1258
|
* unaffected by this mutation and it’s safe to delete it from local queue
|
|
1219
1259
|
*/
|
|
1220
1260
|
_cleanupPendingMutationsTimeout() {
|
|
1221
|
-
if (this.
|
|
1222
|
-
this._pendingMutationCleanupThreshold) {
|
|
1261
|
+
if (this._pendingMutations().size < this._pendingMutationCleanupThreshold) {
|
|
1223
1262
|
return;
|
|
1224
1263
|
}
|
|
1225
1264
|
const now = Date.now();
|
|
1226
|
-
this.
|
|
1265
|
+
this._updatePendingMutations((prev) => {
|
|
1227
1266
|
for (const [eventId, mut] of Array.from(prev.entries())) {
|
|
1228
1267
|
if (mut.confirmed &&
|
|
1229
1268
|
mut.confirmed + this._pendingTxCleanupTimeout < now) {
|
|
1230
1269
|
prev.delete(eventId);
|
|
1231
1270
|
}
|
|
1232
1271
|
}
|
|
1233
|
-
return prev;
|
|
1234
1272
|
});
|
|
1235
1273
|
}
|
|
1236
1274
|
_trySendAuthed(...args) {
|
|
@@ -1306,23 +1344,24 @@ class Reactor {
|
|
|
1306
1344
|
*
|
|
1307
1345
|
* Note: If the user deletes their local storage, this id will change.
|
|
1308
1346
|
*
|
|
1309
|
-
* We use this._localIdPromises to ensure that we only generate a local
|
|
1310
|
-
* id once, even if multiple callers call this function concurrently.
|
|
1311
1347
|
*/
|
|
1312
1348
|
getLocalId(name) {
|
|
1313
1349
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1314
1350
|
const k = `localToken_${name}`;
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1351
|
+
if (this.kv.currentValue[k]) {
|
|
1352
|
+
return this.kv.currentValue[k];
|
|
1353
|
+
}
|
|
1354
|
+
const current = yield this.kv.waitForKeyToLoad(k);
|
|
1355
|
+
if (current) {
|
|
1356
|
+
return current;
|
|
1320
1357
|
}
|
|
1321
1358
|
const newId = (0, uuid_ts_1.default)();
|
|
1322
|
-
this.
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1359
|
+
this.kv.updateInPlace((prev) => {
|
|
1360
|
+
if (prev[k])
|
|
1361
|
+
return;
|
|
1362
|
+
prev[k] = newId;
|
|
1363
|
+
});
|
|
1364
|
+
return yield this.kv.waitForKeyToLoad(k);
|
|
1326
1365
|
});
|
|
1327
1366
|
}
|
|
1328
1367
|
// ----
|
|
@@ -1497,7 +1536,10 @@ class Reactor {
|
|
|
1497
1536
|
}
|
|
1498
1537
|
setCurrentUser(user) {
|
|
1499
1538
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1500
|
-
|
|
1539
|
+
this.kv.updateInPlace((prev) => {
|
|
1540
|
+
prev[currentUserKey] = user;
|
|
1541
|
+
});
|
|
1542
|
+
yield this.kv.waitForKeyToLoad(currentUserKey);
|
|
1501
1543
|
});
|
|
1502
1544
|
}
|
|
1503
1545
|
getCurrentUserCached() {
|
|
@@ -1505,7 +1547,7 @@ class Reactor {
|
|
|
1505
1547
|
}
|
|
1506
1548
|
_getCurrentUser() {
|
|
1507
1549
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1508
|
-
const user = yield this.
|
|
1550
|
+
const user = yield this.kv.waitForKeyToLoad(currentUserKey);
|
|
1509
1551
|
return typeof user === 'string' ? JSON.parse(user) : user;
|
|
1510
1552
|
});
|
|
1511
1553
|
}
|
|
@@ -1517,16 +1559,25 @@ class Reactor {
|
|
|
1517
1559
|
this._currentUserCached = Object.assign({ isLoading: false }, errorV);
|
|
1518
1560
|
return errorV;
|
|
1519
1561
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1562
|
+
try {
|
|
1563
|
+
const user = yield this._getCurrentUser();
|
|
1564
|
+
const userV = { user: user, error: undefined };
|
|
1565
|
+
this._currentUserCached = Object.assign({ isLoading: false }, userV);
|
|
1566
|
+
return userV;
|
|
1567
|
+
}
|
|
1568
|
+
catch (e) {
|
|
1569
|
+
return {
|
|
1570
|
+
user: undefined,
|
|
1571
|
+
isLoading: false,
|
|
1572
|
+
error: { message: (e === null || e === void 0 ? void 0 : e.message) || 'Error loading user' },
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1524
1575
|
});
|
|
1525
1576
|
}
|
|
1526
1577
|
_hasCurrentUser() {
|
|
1527
1578
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1528
|
-
const user = yield this.
|
|
1529
|
-
return JSON.parse(user) != null;
|
|
1579
|
+
const user = yield this.kv.waitForKeyToLoad(currentUserKey);
|
|
1580
|
+
return typeof user === 'string' ? JSON.parse(user) != null : user != null;
|
|
1530
1581
|
});
|
|
1531
1582
|
}
|
|
1532
1583
|
changeCurrentUser(newUser) {
|
|
@@ -1554,11 +1605,10 @@ class Reactor {
|
|
|
1554
1605
|
const newV = { error: undefined, user: newUser };
|
|
1555
1606
|
this._currentUserCached = Object.assign({ isLoading: false }, newV);
|
|
1556
1607
|
this._dataForQueryCache = {};
|
|
1557
|
-
this.querySubs.
|
|
1608
|
+
this.querySubs.updateInPlace((prev) => {
|
|
1558
1609
|
Object.keys(prev).forEach((k) => {
|
|
1559
1610
|
delete prev[k].result;
|
|
1560
1611
|
});
|
|
1561
|
-
return prev;
|
|
1562
1612
|
});
|
|
1563
1613
|
this._reconnectTimeoutMs = 0;
|
|
1564
1614
|
this._transport.close();
|