@arcote.tech/arc 0.7.15 → 0.7.17
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/adapters/event-publisher.d.ts +1 -8
- package/dist/adapters/event-wire.d.ts +28 -22
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/module-sync-coordinator.d.ts +40 -0
- package/dist/adapters/module-sync-coordinator.test.d.ts +2 -0
- package/dist/adapters/query-wire.d.ts +2 -2
- package/dist/context-element/aggregate/aggregate-element.d.ts +0 -10
- package/dist/context-element/view/view.d.ts +0 -10
- package/dist/data-storage/data-storage-master.d.ts +2 -4
- package/dist/data-storage/data-storage-observable.d.ts +2 -4
- package/dist/data-storage/data-storage-observable.test.d.ts +2 -0
- package/dist/data-storage/data-storage.abstract.d.ts +1 -14
- package/dist/data-storage/store-state-master.d.ts +1 -11
- package/dist/index.js +330 -312
- package/dist/model/live-query/diff.d.ts +42 -0
- package/dist/model/live-query/diff.test.d.ts +2 -0
- package/dist/model/live-query/index.d.ts +2 -0
- package/dist/model/live-query/live-query-subscription.d.ts +66 -0
- package/dist/model/scoped-model.d.ts +9 -1
- package/dist/streaming/index.d.ts +1 -1
- package/dist/streaming/streaming-event-publisher.d.ts +8 -10
- package/dist/streaming/streaming-query-cache.d.ts +35 -96
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -119,6 +119,56 @@ class AuthAdapter {
|
|
|
119
119
|
this.scopes.clear();
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
// src/adapters/module-sync-coordinator.ts
|
|
123
|
+
var DEFAULT_TIMEOUT_MS = 8000;
|
|
124
|
+
var provider = null;
|
|
125
|
+
var latestSync = null;
|
|
126
|
+
var syncSeq = 0;
|
|
127
|
+
function registerModuleSyncProvider(fn) {
|
|
128
|
+
provider = fn;
|
|
129
|
+
return () => {
|
|
130
|
+
if (provider === fn)
|
|
131
|
+
provider = null;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function hasModuleSyncProvider() {
|
|
135
|
+
return provider !== null;
|
|
136
|
+
}
|
|
137
|
+
function triggerModuleSync(scope, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
138
|
+
if (!provider) {
|
|
139
|
+
latestSync = Promise.resolve();
|
|
140
|
+
return latestSync;
|
|
141
|
+
}
|
|
142
|
+
const seq = ++syncSeq;
|
|
143
|
+
const run = (async () => {
|
|
144
|
+
try {
|
|
145
|
+
await provider(scope);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.warn("[arc] module sync failed during setToken:", err);
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
const guarded = new Promise((resolve) => {
|
|
151
|
+
let settled = false;
|
|
152
|
+
const done = () => {
|
|
153
|
+
if (settled)
|
|
154
|
+
return;
|
|
155
|
+
settled = true;
|
|
156
|
+
resolve();
|
|
157
|
+
};
|
|
158
|
+
const timer = setTimeout(() => {
|
|
159
|
+
console.warn(`[arc] module sync did not complete within ${timeoutMs}ms; proceeding anyway.`);
|
|
160
|
+
done();
|
|
161
|
+
}, timeoutMs);
|
|
162
|
+
timer?.unref?.();
|
|
163
|
+
run.then(done, done).finally(() => clearTimeout(timer));
|
|
164
|
+
});
|
|
165
|
+
if (seq === syncSeq)
|
|
166
|
+
latestSync = guarded;
|
|
167
|
+
return guarded;
|
|
168
|
+
}
|
|
169
|
+
function awaitModuleSync() {
|
|
170
|
+
return latestSync ?? Promise.resolve();
|
|
171
|
+
}
|
|
122
172
|
// src/adapters/wire.ts
|
|
123
173
|
class Wire {
|
|
124
174
|
baseUrl;
|
|
@@ -211,7 +261,8 @@ class EventWire {
|
|
|
211
261
|
onSyncedCallback;
|
|
212
262
|
reconnectTimeout;
|
|
213
263
|
syncRequested = false;
|
|
214
|
-
|
|
264
|
+
querySubscriptions = new Map;
|
|
265
|
+
querySubCounter = 0;
|
|
215
266
|
enableEventSync;
|
|
216
267
|
constructor(baseUrl, options) {
|
|
217
268
|
this.baseUrl = baseUrl;
|
|
@@ -266,7 +317,7 @@ class EventWire {
|
|
|
266
317
|
this.requestSync();
|
|
267
318
|
}
|
|
268
319
|
this.flushPendingEvents();
|
|
269
|
-
this.
|
|
320
|
+
this.sendAllQuerySubscriptions();
|
|
270
321
|
} else {
|
|
271
322
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
272
323
|
}
|
|
@@ -342,24 +393,29 @@ class EventWire {
|
|
|
342
393
|
onSynced(callback) {
|
|
343
394
|
this.onSyncedCallback = callback;
|
|
344
395
|
}
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
this.
|
|
396
|
+
subscribeQuery(descriptor, scope, callbacks) {
|
|
397
|
+
const subscriptionId = `qs_${this.instanceId}_${++this.querySubCounter}`;
|
|
398
|
+
this.querySubscriptions.set(subscriptionId, {
|
|
399
|
+
descriptor,
|
|
400
|
+
scope,
|
|
401
|
+
callbacks
|
|
402
|
+
});
|
|
348
403
|
if (this.state === "connected" && this.ws) {
|
|
349
404
|
this.ws.send(JSON.stringify({
|
|
350
|
-
type: "subscribe-
|
|
351
|
-
|
|
405
|
+
type: "subscribe-query",
|
|
406
|
+
subscriptionId,
|
|
407
|
+
descriptor,
|
|
352
408
|
scope
|
|
353
409
|
}));
|
|
354
410
|
}
|
|
411
|
+
return subscriptionId;
|
|
355
412
|
}
|
|
356
|
-
|
|
357
|
-
this.
|
|
413
|
+
unsubscribeQuery(subscriptionId) {
|
|
414
|
+
this.querySubscriptions.delete(subscriptionId);
|
|
358
415
|
if (this.state === "connected" && this.ws) {
|
|
359
416
|
this.ws.send(JSON.stringify({
|
|
360
|
-
type: "unsubscribe-
|
|
361
|
-
|
|
362
|
-
scope
|
|
417
|
+
type: "unsubscribe-query",
|
|
418
|
+
subscriptionId
|
|
363
419
|
}));
|
|
364
420
|
}
|
|
365
421
|
}
|
|
@@ -394,17 +450,17 @@ class EventWire {
|
|
|
394
450
|
this.lastHostEventId = message.lastHostEventId;
|
|
395
451
|
}
|
|
396
452
|
break;
|
|
397
|
-
case "
|
|
398
|
-
const sub = this.
|
|
453
|
+
case "query-snapshot": {
|
|
454
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
399
455
|
if (sub) {
|
|
400
|
-
sub.onSnapshot(message.
|
|
456
|
+
sub.callbacks.onSnapshot(message.result ?? null);
|
|
401
457
|
}
|
|
402
458
|
break;
|
|
403
459
|
}
|
|
404
|
-
case "
|
|
405
|
-
const sub = this.
|
|
460
|
+
case "query-changes": {
|
|
461
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
406
462
|
if (sub && Array.isArray(message.changes)) {
|
|
407
|
-
sub.onChanges(message.changes);
|
|
463
|
+
sub.callbacks.onChanges(message.changes);
|
|
408
464
|
}
|
|
409
465
|
break;
|
|
410
466
|
}
|
|
@@ -432,17 +488,15 @@ class EventWire {
|
|
|
432
488
|
this.pendingEvents = [];
|
|
433
489
|
}
|
|
434
490
|
}
|
|
435
|
-
|
|
491
|
+
sendAllQuerySubscriptions() {
|
|
436
492
|
if (!this.ws || this.state !== "connected")
|
|
437
493
|
return;
|
|
438
|
-
for (const
|
|
439
|
-
const sepIdx = key.indexOf(":");
|
|
440
|
-
const scope = key.slice(0, sepIdx);
|
|
441
|
-
const element = key.slice(sepIdx + 1);
|
|
494
|
+
for (const [subscriptionId, sub] of this.querySubscriptions) {
|
|
442
495
|
this.ws.send(JSON.stringify({
|
|
443
|
-
type: "subscribe-
|
|
444
|
-
|
|
445
|
-
|
|
496
|
+
type: "subscribe-query",
|
|
497
|
+
subscriptionId,
|
|
498
|
+
descriptor: sub.descriptor,
|
|
499
|
+
scope: sub.scope
|
|
446
500
|
}));
|
|
447
501
|
}
|
|
448
502
|
}
|
|
@@ -484,19 +538,12 @@ class LocalEventPublisher {
|
|
|
484
538
|
views = [];
|
|
485
539
|
syncCallback;
|
|
486
540
|
subscribers = new Map;
|
|
487
|
-
viewChangesCallbacks = new Set;
|
|
488
541
|
constructor(dataStorage) {
|
|
489
542
|
this.dataStorage = dataStorage;
|
|
490
543
|
}
|
|
491
544
|
onPublish(callback) {
|
|
492
545
|
this.syncCallback = callback;
|
|
493
546
|
}
|
|
494
|
-
onViewChanges(callback) {
|
|
495
|
-
this.viewChangesCallbacks.add(callback);
|
|
496
|
-
return () => {
|
|
497
|
-
this.viewChangesCallbacks.delete(callback);
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
547
|
registerViews(views) {
|
|
501
548
|
this.views = views;
|
|
502
549
|
}
|
|
@@ -564,19 +611,7 @@ class LocalEventPublisher {
|
|
|
564
611
|
});
|
|
565
612
|
const viewChanges = await this.collectViewChanges(event);
|
|
566
613
|
allChanges.push(...viewChanges);
|
|
567
|
-
|
|
568
|
-
const committed = await this.dataStorage.commitChanges(allChanges, {
|
|
569
|
-
captureRowsFor: viewStoreNames
|
|
570
|
-
});
|
|
571
|
-
if (committed.length > 0 && this.viewChangesCallbacks.size > 0) {
|
|
572
|
-
for (const callback of this.viewChangesCallbacks) {
|
|
573
|
-
try {
|
|
574
|
-
callback(committed);
|
|
575
|
-
} catch (error) {
|
|
576
|
-
console.error(`[EventPublisher] onViewChanges callback error:`, error);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
614
|
+
await this.dataStorage.commitChanges(allChanges);
|
|
580
615
|
await this.notifySubscribers(event);
|
|
581
616
|
if (this.syncCallback) {
|
|
582
617
|
this.syncCallback(event);
|
|
@@ -1262,9 +1297,8 @@ function object(element) {
|
|
|
1262
1297
|
|
|
1263
1298
|
// src/data-storage/data-storage.abstract.ts
|
|
1264
1299
|
class DataStorage {
|
|
1265
|
-
async commitChanges(changes
|
|
1300
|
+
async commitChanges(changes) {
|
|
1266
1301
|
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
1267
|
-
return [];
|
|
1268
1302
|
}
|
|
1269
1303
|
}
|
|
1270
1304
|
|
|
@@ -2229,11 +2263,6 @@ class ArcAggregateElement extends ArcContextElement {
|
|
|
2229
2263
|
}
|
|
2230
2264
|
return adapters.dataStorage.getStore(viewName).find(options);
|
|
2231
2265
|
}
|
|
2232
|
-
if (adapters.streamingCache) {
|
|
2233
|
-
const store = adapters.streamingCache.getStore(viewName, adapters.scope?.scopeName);
|
|
2234
|
-
if (store.hasData())
|
|
2235
|
-
return store.find(options);
|
|
2236
|
-
}
|
|
2237
2266
|
if (adapters.queryWire)
|
|
2238
2267
|
return adapters.queryWire.query(viewName, options);
|
|
2239
2268
|
return [];
|
|
@@ -2261,12 +2290,6 @@ class ArcAggregateElement extends ArcContextElement {
|
|
|
2261
2290
|
}
|
|
2262
2291
|
};
|
|
2263
2292
|
}
|
|
2264
|
-
getRestrictionsFor(adapters) {
|
|
2265
|
-
return {
|
|
2266
|
-
restrictions: this.getScopeRestrictions(adapters),
|
|
2267
|
-
denied: this.isScopeDenied(adapters)
|
|
2268
|
-
};
|
|
2269
|
-
}
|
|
2270
2293
|
isScopeDenied(adapters, protections = this._protections) {
|
|
2271
2294
|
if (protections.length === 0)
|
|
2272
2295
|
return false;
|
|
@@ -2912,13 +2935,6 @@ class ArcView extends ArcContextElement {
|
|
|
2912
2935
|
}
|
|
2913
2936
|
return adapters.dataStorage.getStore(viewName).find(options);
|
|
2914
2937
|
}
|
|
2915
|
-
if (adapters.streamingCache) {
|
|
2916
|
-
const store = adapters.streamingCache.getStore(viewName, adapters.scope?.scopeName);
|
|
2917
|
-
if (store.hasData()) {
|
|
2918
|
-
const where = restrictions ? { ...options?.where || {}, ...restrictions } : options?.where;
|
|
2919
|
-
return store.find({ ...options, where });
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
2938
|
if (adapters.queryWire) {
|
|
2923
2939
|
const where = restrictions ? { ...options?.where || {}, ...restrictions } : options?.where;
|
|
2924
2940
|
return adapters.queryWire.query(viewName, { ...options, where });
|
|
@@ -2937,13 +2953,6 @@ class ArcView extends ArcContextElement {
|
|
|
2937
2953
|
const results = await adapters.dataStorage.getStore(viewName).find({ where });
|
|
2938
2954
|
return results[0];
|
|
2939
2955
|
}
|
|
2940
|
-
if (adapters.streamingCache) {
|
|
2941
|
-
const store = adapters.streamingCache.getStore(viewName, adapters.scope?.scopeName);
|
|
2942
|
-
if (store.hasData()) {
|
|
2943
|
-
const mergedWhere = restrictions ? { ...where || {}, ...restrictions } : where;
|
|
2944
|
-
return store.findOne(mergedWhere);
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
2956
|
if (adapters.queryWire) {
|
|
2948
2957
|
const mergedWhere = restrictions ? { ...where, ...restrictions } : where;
|
|
2949
2958
|
const results = await adapters.queryWire.query(viewName, { where: mergedWhere });
|
|
@@ -2953,9 +2962,6 @@ class ArcView extends ArcContextElement {
|
|
|
2953
2962
|
}
|
|
2954
2963
|
};
|
|
2955
2964
|
}
|
|
2956
|
-
getRestrictionsFor(adapters) {
|
|
2957
|
-
return this.resolveProtection(this.data.protections || [], adapters);
|
|
2958
|
-
}
|
|
2959
2965
|
resolveProtection(protections, adapters) {
|
|
2960
2966
|
if (protections.length === 0)
|
|
2961
2967
|
return { restrictions: null, denied: false };
|
|
@@ -3403,12 +3409,8 @@ class MasterStoreState extends StoreState {
|
|
|
3403
3409
|
}
|
|
3404
3410
|
return transaction.find(this.storeName, { where: { _id: id2 } }).then((results) => results[0]);
|
|
3405
3411
|
}
|
|
3406
|
-
async applyChangeAndReturnEvent(transaction, change, transactionCache
|
|
3412
|
+
async applyChangeAndReturnEvent(transaction, change, transactionCache) {
|
|
3407
3413
|
if (change.type === "set") {
|
|
3408
|
-
let existing;
|
|
3409
|
-
if (options?.captureRows) {
|
|
3410
|
-
existing = await this.readExisting(transaction, change.data._id, transactionCache);
|
|
3411
|
-
}
|
|
3412
3414
|
await transaction.set(this.storeName, change.data);
|
|
3413
3415
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
3414
3416
|
if (transactionCache) {
|
|
@@ -3421,16 +3423,10 @@ class MasterStoreState extends StoreState {
|
|
|
3421
3423
|
type: "set",
|
|
3422
3424
|
item: change.data,
|
|
3423
3425
|
id: change.data._id
|
|
3424
|
-
}
|
|
3425
|
-
oldRow: existing ?? null,
|
|
3426
|
-
newRow: change.data
|
|
3426
|
+
}
|
|
3427
3427
|
};
|
|
3428
3428
|
}
|
|
3429
3429
|
if (change.type === "delete") {
|
|
3430
|
-
let existing;
|
|
3431
|
-
if (options?.captureRows) {
|
|
3432
|
-
existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
3433
|
-
}
|
|
3434
3430
|
await transaction.remove(this.storeName, change.id);
|
|
3435
3431
|
if (transactionCache) {
|
|
3436
3432
|
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
@@ -3442,9 +3438,7 @@ class MasterStoreState extends StoreState {
|
|
|
3442
3438
|
type: "delete",
|
|
3443
3439
|
item: null,
|
|
3444
3440
|
id: change.id
|
|
3445
|
-
}
|
|
3446
|
-
oldRow: existing ?? null,
|
|
3447
|
-
newRow: null
|
|
3441
|
+
}
|
|
3448
3442
|
};
|
|
3449
3443
|
}
|
|
3450
3444
|
if (change.type === "modify") {
|
|
@@ -3462,9 +3456,7 @@ class MasterStoreState extends StoreState {
|
|
|
3462
3456
|
type: "set",
|
|
3463
3457
|
item,
|
|
3464
3458
|
id: change.id
|
|
3465
|
-
}
|
|
3466
|
-
oldRow: existing ?? null,
|
|
3467
|
-
newRow: updated
|
|
3459
|
+
}
|
|
3468
3460
|
};
|
|
3469
3461
|
}
|
|
3470
3462
|
if (change.type === "mutate") {
|
|
@@ -3482,9 +3474,7 @@ class MasterStoreState extends StoreState {
|
|
|
3482
3474
|
type: "set",
|
|
3483
3475
|
item,
|
|
3484
3476
|
id: change.id
|
|
3485
|
-
}
|
|
3486
|
-
oldRow: existing ?? null,
|
|
3487
|
-
newRow: updated
|
|
3477
|
+
}
|
|
3488
3478
|
};
|
|
3489
3479
|
}
|
|
3490
3480
|
throw new Error("Unknown change type");
|
|
@@ -3563,22 +3553,17 @@ class MasterDataStorage extends DataStorage {
|
|
|
3563
3553
|
applySerializedChanges(changes) {
|
|
3564
3554
|
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
3565
3555
|
}
|
|
3566
|
-
async commitChanges(changes
|
|
3556
|
+
async commitChanges(changes) {
|
|
3567
3557
|
const transaction = await this.getReadWriteTransaction();
|
|
3568
3558
|
const transactionCache = new Map;
|
|
3569
3559
|
const eventsByStore = new Map;
|
|
3570
|
-
const committed = [];
|
|
3571
3560
|
for (const { store, changes: storeChanges } of changes) {
|
|
3572
3561
|
const storeState = this.getStore(store);
|
|
3573
3562
|
const storeEvents = [];
|
|
3574
|
-
const capture = options?.captureRowsFor?.has(store) ?? false;
|
|
3575
3563
|
for (const change of storeChanges) {
|
|
3576
|
-
const { event: event3
|
|
3564
|
+
const { event: event3 } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
3577
3565
|
if (event3)
|
|
3578
3566
|
storeEvents.push(event3);
|
|
3579
|
-
if (capture) {
|
|
3580
|
-
committed.push({ store, id: event3.id, oldRow, newRow });
|
|
3581
|
-
}
|
|
3582
3567
|
}
|
|
3583
3568
|
if (storeEvents.length > 0) {
|
|
3584
3569
|
eventsByStore.set(store, storeEvents);
|
|
@@ -3589,7 +3574,6 @@ class MasterDataStorage extends DataStorage {
|
|
|
3589
3574
|
const storeState = this.getStore(store);
|
|
3590
3575
|
storeState.notifyListenersPublic(events);
|
|
3591
3576
|
}
|
|
3592
|
-
return committed;
|
|
3593
3577
|
}
|
|
3594
3578
|
fork() {
|
|
3595
3579
|
return new ForkedDataStorage(this);
|
|
@@ -3706,8 +3690,8 @@ class ObservableDataStorage {
|
|
|
3706
3690
|
getReadWriteTransaction() {
|
|
3707
3691
|
return this.source.getReadWriteTransaction();
|
|
3708
3692
|
}
|
|
3709
|
-
commitChanges(changes
|
|
3710
|
-
return this.source.commitChanges(changes
|
|
3693
|
+
commitChanges(changes) {
|
|
3694
|
+
return this.source.commitChanges(changes);
|
|
3711
3695
|
}
|
|
3712
3696
|
trackQuery(storeName, options, result, listener4) {
|
|
3713
3697
|
const key = this.getQueryKey(storeName, options);
|
|
@@ -3720,7 +3704,8 @@ class ObservableDataStorage {
|
|
|
3720
3704
|
}
|
|
3721
3705
|
handleStoreChange(storeName, events) {
|
|
3722
3706
|
let hasChanges = false;
|
|
3723
|
-
|
|
3707
|
+
const staleKeys = [];
|
|
3708
|
+
for (const [key, query] of this.trackedQueries) {
|
|
3724
3709
|
if (query.storeName !== storeName)
|
|
3725
3710
|
continue;
|
|
3726
3711
|
let currentResult = query.result;
|
|
@@ -3732,10 +3717,20 @@ class ObservableDataStorage {
|
|
|
3732
3717
|
queryChanged = true;
|
|
3733
3718
|
}
|
|
3734
3719
|
}
|
|
3735
|
-
if (queryChanged)
|
|
3736
|
-
|
|
3720
|
+
if (!queryChanged)
|
|
3721
|
+
continue;
|
|
3722
|
+
if (query.options.limit !== undefined && query.result.length === query.options.limit && currentResult.length < query.options.limit) {
|
|
3723
|
+
staleKeys.push(key);
|
|
3737
3724
|
hasChanges = true;
|
|
3725
|
+
continue;
|
|
3738
3726
|
}
|
|
3727
|
+
query.result = currentResult;
|
|
3728
|
+
hasChanges = true;
|
|
3729
|
+
}
|
|
3730
|
+
for (const key of staleKeys) {
|
|
3731
|
+
const query = this.trackedQueries.get(key);
|
|
3732
|
+
this.source.getStore(query.storeName).unsubscribe(query.listener);
|
|
3733
|
+
this.trackedQueries.delete(key);
|
|
3739
3734
|
}
|
|
3740
3735
|
if (hasChanges) {
|
|
3741
3736
|
this.onChange();
|
|
@@ -4507,6 +4502,7 @@ class ScopedModel {
|
|
|
4507
4502
|
for (const listener4 of this.tokenListeners) {
|
|
4508
4503
|
listener4();
|
|
4509
4504
|
}
|
|
4505
|
+
return triggerModuleSync(this.scopeName);
|
|
4510
4506
|
}
|
|
4511
4507
|
getToken() {
|
|
4512
4508
|
return this.authAdapter.getToken();
|
|
@@ -4621,245 +4617,260 @@ function mutationExecutor(model) {
|
|
|
4621
4617
|
}
|
|
4622
4618
|
});
|
|
4623
4619
|
}
|
|
4624
|
-
// src/
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4620
|
+
// src/model/live-query/diff.ts
|
|
4621
|
+
function isIdList(value) {
|
|
4622
|
+
return Array.isArray(value) && value.every((it) => it && typeof it === "object" && typeof it._id === "string");
|
|
4623
|
+
}
|
|
4624
|
+
function diffResults(prev, next) {
|
|
4625
|
+
if (!isIdList(prev) || !isIdList(next)) {
|
|
4626
|
+
return JSON.stringify(prev) === JSON.stringify(next) ? { kind: "none" } : { kind: "snapshot", result: next };
|
|
4627
|
+
}
|
|
4628
|
+
const json = (o) => JSON.stringify(o);
|
|
4629
|
+
const nextIds = new Set(next.map((it) => it._id));
|
|
4630
|
+
const changes = [];
|
|
4631
|
+
for (const it of prev) {
|
|
4632
|
+
if (!nextIds.has(it._id)) {
|
|
4633
|
+
changes.push({ type: "delete", id: it._id });
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
const sim = prev.filter((it) => nextIds.has(it._id));
|
|
4637
|
+
for (let i = 0;i < next.length; i++) {
|
|
4638
|
+
const target = next[i];
|
|
4639
|
+
if (sim[i] && sim[i]._id === target._id && json(sim[i]) === json(target)) {
|
|
4640
|
+
continue;
|
|
4643
4641
|
}
|
|
4644
|
-
|
|
4642
|
+
changes.push({ type: "set", id: target._id, item: target, index: i });
|
|
4643
|
+
const oldIdx = sim.findIndex((it) => it._id === target._id);
|
|
4644
|
+
if (oldIdx !== -1)
|
|
4645
|
+
sim.splice(oldIdx, 1);
|
|
4646
|
+
sim.splice(i, 0, target);
|
|
4645
4647
|
}
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4648
|
+
if (changes.length === 0)
|
|
4649
|
+
return { kind: "none" };
|
|
4650
|
+
if (changes.length > next.length) {
|
|
4651
|
+
return { kind: "snapshot", result: next };
|
|
4649
4652
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
if (pending) {
|
|
4653
|
-
clearTimeout(pending);
|
|
4654
|
-
this.pendingUnsubscribes.delete(key);
|
|
4655
|
-
}
|
|
4656
|
-
const existing = this.activeStreams.get(key);
|
|
4657
|
-
if (existing) {
|
|
4658
|
-
existing.refCount++;
|
|
4659
|
-
return {
|
|
4660
|
-
unsubscribe: () => this.unregisterStream(key),
|
|
4661
|
-
wasReused: true
|
|
4662
|
-
};
|
|
4663
|
-
}
|
|
4664
|
-
const streamConn = createStream();
|
|
4665
|
-
this.activeStreams.set(key, {
|
|
4666
|
-
unsubscribe: streamConn.unsubscribe,
|
|
4667
|
-
refCount: 1
|
|
4668
|
-
});
|
|
4669
|
-
return {
|
|
4670
|
-
unsubscribe: () => this.unregisterStream(key),
|
|
4671
|
-
wasReused: false
|
|
4672
|
-
};
|
|
4653
|
+
if (sim.length !== next.length || json(sim) !== json(next)) {
|
|
4654
|
+
return { kind: "snapshot", result: next };
|
|
4673
4655
|
}
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
if (
|
|
4680
|
-
const
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
if (current && current.refCount <= 0) {
|
|
4684
|
-
current.unsubscribe();
|
|
4685
|
-
this.activeStreams.delete(key);
|
|
4686
|
-
}
|
|
4687
|
-
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
4688
|
-
this.pendingUnsubscribes.set(key, timeout);
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
subscribeView(viewName, eventWire, scope) {
|
|
4692
|
-
const key = this.storeKey(viewName, scope);
|
|
4693
|
-
const { unsubscribe } = this.registerStream(key, () => {
|
|
4694
|
-
const store = this.stores.get(key) ?? new StreamingStore;
|
|
4695
|
-
this.stores.set(key, store);
|
|
4696
|
-
eventWire.subscribeView(viewName, scope ?? DEFAULT_SCOPE, {
|
|
4697
|
-
onSnapshot: (items) => store.setAll(items),
|
|
4698
|
-
onChanges: (changes) => store.applyChanges(changes)
|
|
4699
|
-
});
|
|
4700
|
-
return {
|
|
4701
|
-
unsubscribe: () => eventWire.unsubscribeView(viewName, scope ?? DEFAULT_SCOPE)
|
|
4702
|
-
};
|
|
4703
|
-
});
|
|
4704
|
-
return unsubscribe;
|
|
4705
|
-
}
|
|
4706
|
-
invalidateScope(scope) {
|
|
4707
|
-
const prefix = `${scope}:`;
|
|
4708
|
-
for (const [key, timeout] of this.pendingUnsubscribes) {
|
|
4709
|
-
if (!key.startsWith(prefix))
|
|
4710
|
-
continue;
|
|
4711
|
-
clearTimeout(timeout);
|
|
4712
|
-
this.pendingUnsubscribes.delete(key);
|
|
4713
|
-
}
|
|
4714
|
-
for (const [key, stream] of this.activeStreams) {
|
|
4715
|
-
if (!key.startsWith(prefix))
|
|
4716
|
-
continue;
|
|
4717
|
-
try {
|
|
4718
|
-
stream.unsubscribe();
|
|
4719
|
-
} catch {}
|
|
4720
|
-
this.activeStreams.delete(key);
|
|
4721
|
-
}
|
|
4722
|
-
for (const [key, store] of this.stores) {
|
|
4723
|
-
if (!key.startsWith(prefix))
|
|
4724
|
-
continue;
|
|
4725
|
-
store.clear();
|
|
4726
|
-
}
|
|
4727
|
-
}
|
|
4728
|
-
async applyEvent(event3) {
|
|
4729
|
-
for (const view3 of this.views) {
|
|
4730
|
-
const handlers = view3.getHandlers();
|
|
4731
|
-
const handler = handlers[event3.type];
|
|
4732
|
-
if (!handler)
|
|
4733
|
-
continue;
|
|
4734
|
-
const suffix = `:${view3.name}`;
|
|
4735
|
-
for (const [key, store] of this.stores) {
|
|
4736
|
-
if (!key.endsWith(suffix))
|
|
4737
|
-
continue;
|
|
4738
|
-
const ctx = {
|
|
4739
|
-
set: async (id3, data) => {
|
|
4740
|
-
store.set(String(id3), { _id: String(id3), ...data });
|
|
4741
|
-
},
|
|
4742
|
-
modify: async (id3, data) => {
|
|
4743
|
-
store.modify(String(id3), data);
|
|
4744
|
-
},
|
|
4745
|
-
remove: async (id3) => {
|
|
4746
|
-
store.remove(String(id3));
|
|
4747
|
-
},
|
|
4748
|
-
find: async (options) => {
|
|
4749
|
-
return store.find(options);
|
|
4750
|
-
},
|
|
4751
|
-
findOne: async (where) => {
|
|
4752
|
-
return store.findOne(where);
|
|
4753
|
-
},
|
|
4754
|
-
$auth: {}
|
|
4755
|
-
};
|
|
4756
|
-
await handler(ctx, event3);
|
|
4757
|
-
}
|
|
4656
|
+
return { kind: "changes", changes };
|
|
4657
|
+
}
|
|
4658
|
+
function applyQueryChanges(result, changes) {
|
|
4659
|
+
const next = [...result];
|
|
4660
|
+
for (const change of changes) {
|
|
4661
|
+
if (change.type === "delete") {
|
|
4662
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
4663
|
+
if (idx !== -1)
|
|
4664
|
+
next.splice(idx, 1);
|
|
4758
4665
|
}
|
|
4759
4666
|
}
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
clearTimeout(timeout);
|
|
4767
|
-
}
|
|
4768
|
-
this.pendingUnsubscribes.clear();
|
|
4769
|
-
for (const store of this.stores.values()) {
|
|
4770
|
-
store.clear();
|
|
4667
|
+
for (const change of changes) {
|
|
4668
|
+
if (change.type === "set") {
|
|
4669
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
4670
|
+
if (idx !== -1)
|
|
4671
|
+
next.splice(idx, 1);
|
|
4672
|
+
next.splice(change.index, 0, change.item);
|
|
4771
4673
|
}
|
|
4772
4674
|
}
|
|
4675
|
+
return next;
|
|
4773
4676
|
}
|
|
4774
4677
|
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4678
|
+
// src/model/live-query/live-query-subscription.ts
|
|
4679
|
+
class LiveQuery {
|
|
4680
|
+
model;
|
|
4681
|
+
descriptor;
|
|
4682
|
+
scope;
|
|
4683
|
+
rawToken;
|
|
4684
|
+
onUpdate;
|
|
4685
|
+
observable = null;
|
|
4686
|
+
adapters = null;
|
|
4687
|
+
lastResult;
|
|
4688
|
+
scheduled = false;
|
|
4689
|
+
running = false;
|
|
4690
|
+
rerunRequested = false;
|
|
4691
|
+
stopped = false;
|
|
4692
|
+
constructor(model, descriptor, scope, rawToken, onUpdate) {
|
|
4693
|
+
this.model = model;
|
|
4694
|
+
this.descriptor = descriptor;
|
|
4695
|
+
this.scope = scope;
|
|
4696
|
+
this.rawToken = rawToken;
|
|
4697
|
+
this.onUpdate = onUpdate;
|
|
4698
|
+
}
|
|
4699
|
+
async start() {
|
|
4700
|
+
const scoped = new ScopedModel(this.model, this.scope);
|
|
4701
|
+
if (this.rawToken)
|
|
4702
|
+
scoped.setToken(this.rawToken);
|
|
4703
|
+
const baseAdapters = scoped.getAdapters();
|
|
4704
|
+
if (!baseAdapters.dataStorage) {
|
|
4705
|
+
throw new Error("LiveQuery requires a dataStorage adapter (server-side)");
|
|
4706
|
+
}
|
|
4707
|
+
this.observable = new ObservableDataStorage(baseAdapters.dataStorage, () => this.schedule());
|
|
4708
|
+
this.adapters = {
|
|
4709
|
+
...baseAdapters,
|
|
4710
|
+
dataStorage: this.observable
|
|
4711
|
+
};
|
|
4712
|
+
this.lastResult = await executeDescriptor(this.descriptor, this.model.context, this.adapters, "queryContext", { fromWire: true });
|
|
4713
|
+
return this.lastResult;
|
|
4781
4714
|
}
|
|
4782
|
-
|
|
4783
|
-
this.
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4715
|
+
flush() {
|
|
4716
|
+
this.schedule();
|
|
4717
|
+
}
|
|
4718
|
+
stop() {
|
|
4719
|
+
this.stopped = true;
|
|
4720
|
+
this.observable?.clear();
|
|
4721
|
+
}
|
|
4722
|
+
schedule() {
|
|
4723
|
+
if (this.stopped)
|
|
4724
|
+
return;
|
|
4725
|
+
if (this.running) {
|
|
4726
|
+
this.rerunRequested = true;
|
|
4727
|
+
return;
|
|
4787
4728
|
}
|
|
4788
|
-
this.
|
|
4729
|
+
if (this.scheduled)
|
|
4730
|
+
return;
|
|
4731
|
+
this.scheduled = true;
|
|
4732
|
+
queueMicrotask(() => {
|
|
4733
|
+
this.scheduled = false;
|
|
4734
|
+
this.run();
|
|
4735
|
+
});
|
|
4789
4736
|
}
|
|
4790
|
-
|
|
4791
|
-
if (
|
|
4737
|
+
async run() {
|
|
4738
|
+
if (this.stopped)
|
|
4792
4739
|
return;
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4740
|
+
this.running = true;
|
|
4741
|
+
try {
|
|
4742
|
+
const next = await executeDescriptor(this.descriptor, this.model.context, this.adapters, "queryContext", { fromWire: true });
|
|
4743
|
+
if (this.stopped)
|
|
4744
|
+
return;
|
|
4745
|
+
const diff = diffResults(this.lastResult, next);
|
|
4746
|
+
this.lastResult = next;
|
|
4747
|
+
if (diff.kind === "changes") {
|
|
4748
|
+
this.onUpdate({ type: "changes", changes: diff.changes });
|
|
4749
|
+
} else if (diff.kind === "snapshot") {
|
|
4750
|
+
this.onUpdate({ type: "snapshot", result: diff.result });
|
|
4751
|
+
}
|
|
4752
|
+
} catch (err) {
|
|
4753
|
+
console.error(`[Arc] LiveQuery re-execute error:`, err);
|
|
4754
|
+
} finally {
|
|
4755
|
+
this.running = false;
|
|
4756
|
+
if (this.rerunRequested) {
|
|
4757
|
+
this.rerunRequested = false;
|
|
4758
|
+
this.schedule();
|
|
4798
4759
|
}
|
|
4799
4760
|
}
|
|
4800
|
-
this.notifyListeners(events);
|
|
4801
|
-
}
|
|
4802
|
-
set(id3, item) {
|
|
4803
|
-
this.data.set(id3, item);
|
|
4804
|
-
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
4805
4761
|
}
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4762
|
+
}
|
|
4763
|
+
// src/streaming/streaming-query-cache.ts
|
|
4764
|
+
class StreamingQueryCache {
|
|
4765
|
+
entries = new Map;
|
|
4766
|
+
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
4767
|
+
entryKey(descriptor, scope) {
|
|
4768
|
+
return `${scope ?? "default"}:${murmurHash(JSON.stringify(descriptor))}`;
|
|
4769
|
+
}
|
|
4770
|
+
subscribe(descriptor, scope, eventWire, onChange) {
|
|
4771
|
+
const key = this.entryKey(descriptor, scope);
|
|
4772
|
+
let entry = this.entries.get(key);
|
|
4773
|
+
if (entry) {
|
|
4774
|
+
if (entry.pendingUnsub) {
|
|
4775
|
+
clearTimeout(entry.pendingUnsub);
|
|
4776
|
+
entry.pendingUnsub = undefined;
|
|
4777
|
+
}
|
|
4778
|
+
entry.refCount++;
|
|
4779
|
+
} else {
|
|
4780
|
+
const newEntry = {
|
|
4781
|
+
result: undefined,
|
|
4782
|
+
hasResult: false,
|
|
4783
|
+
listeners: new Set,
|
|
4784
|
+
refCount: 1,
|
|
4785
|
+
subscriptionId: ""
|
|
4786
|
+
};
|
|
4787
|
+
newEntry.subscriptionId = eventWire.subscribeQuery(descriptor, scope, {
|
|
4788
|
+
onSnapshot: (result) => {
|
|
4789
|
+
newEntry.result = result;
|
|
4790
|
+
newEntry.hasResult = true;
|
|
4791
|
+
this.notify(newEntry);
|
|
4792
|
+
},
|
|
4793
|
+
onChanges: (changes) => {
|
|
4794
|
+
if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
|
|
4795
|
+
return;
|
|
4796
|
+
}
|
|
4797
|
+
newEntry.result = applyQueryChanges(newEntry.result, changes);
|
|
4798
|
+
this.notify(newEntry);
|
|
4799
|
+
}
|
|
4800
|
+
});
|
|
4801
|
+
this.entries.set(key, newEntry);
|
|
4802
|
+
entry = newEntry;
|
|
4812
4803
|
}
|
|
4804
|
+
const subscribed = entry;
|
|
4805
|
+
subscribed.listeners.add(onChange);
|
|
4806
|
+
let active = true;
|
|
4807
|
+
return {
|
|
4808
|
+
read: () => ({
|
|
4809
|
+
result: subscribed.result,
|
|
4810
|
+
loading: !subscribed.hasResult
|
|
4811
|
+
}),
|
|
4812
|
+
unsubscribe: () => {
|
|
4813
|
+
if (!active)
|
|
4814
|
+
return;
|
|
4815
|
+
active = false;
|
|
4816
|
+
subscribed.listeners.delete(onChange);
|
|
4817
|
+
subscribed.refCount--;
|
|
4818
|
+
if (subscribed.refCount > 0)
|
|
4819
|
+
return;
|
|
4820
|
+
subscribed.pendingUnsub = setTimeout(() => {
|
|
4821
|
+
subscribed.pendingUnsub = undefined;
|
|
4822
|
+
if (subscribed.refCount > 0)
|
|
4823
|
+
return;
|
|
4824
|
+
eventWire.unsubscribeQuery(subscribed.subscriptionId);
|
|
4825
|
+
this.entries.delete(key);
|
|
4826
|
+
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
4827
|
+
}
|
|
4828
|
+
};
|
|
4813
4829
|
}
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4830
|
+
invalidateScope(scope, eventWire) {
|
|
4831
|
+
const prefix = `${scope}:`;
|
|
4832
|
+
for (const [key, entry] of this.entries) {
|
|
4833
|
+
if (!key.startsWith(prefix))
|
|
4834
|
+
continue;
|
|
4835
|
+
if (entry.pendingUnsub)
|
|
4836
|
+
clearTimeout(entry.pendingUnsub);
|
|
4837
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
4838
|
+
this.entries.delete(key);
|
|
4839
|
+
entry.result = undefined;
|
|
4840
|
+
entry.hasResult = false;
|
|
4841
|
+
this.notify(entry);
|
|
4817
4842
|
}
|
|
4818
4843
|
}
|
|
4819
|
-
clear() {
|
|
4820
|
-
this.
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
find(options = {}) {
|
|
4825
|
-
let results = Array.from(this.data.values());
|
|
4826
|
-
if (options.where) {
|
|
4827
|
-
results = results.filter((item) => checkItemMatchesWhere(item, options.where));
|
|
4844
|
+
clear(eventWire) {
|
|
4845
|
+
for (const entry of this.entries.values()) {
|
|
4846
|
+
if (entry.pendingUnsub)
|
|
4847
|
+
clearTimeout(entry.pendingUnsub);
|
|
4848
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
4828
4849
|
}
|
|
4829
|
-
|
|
4830
|
-
}
|
|
4831
|
-
findOne(where) {
|
|
4832
|
-
const results = this.find({ where });
|
|
4833
|
-
return results[0];
|
|
4850
|
+
this.entries.clear();
|
|
4834
4851
|
}
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
for (const listener4 of this.listeners) {
|
|
4843
|
-
listener4(events);
|
|
4852
|
+
notify(entry) {
|
|
4853
|
+
for (const listener4 of entry.listeners) {
|
|
4854
|
+
try {
|
|
4855
|
+
listener4();
|
|
4856
|
+
} catch (err) {
|
|
4857
|
+
console.error(`[Arc] Query cache listener error:`, err);
|
|
4858
|
+
}
|
|
4844
4859
|
}
|
|
4845
4860
|
}
|
|
4846
4861
|
}
|
|
4847
4862
|
// src/streaming/streaming-event-publisher.ts
|
|
4848
4863
|
class StreamingEventPublisher {
|
|
4849
|
-
cache;
|
|
4850
4864
|
eventWire;
|
|
4851
4865
|
views = [];
|
|
4852
4866
|
subscribers = new Map;
|
|
4853
|
-
constructor(
|
|
4854
|
-
this.cache = cache;
|
|
4867
|
+
constructor(eventWire) {
|
|
4855
4868
|
this.eventWire = eventWire;
|
|
4856
4869
|
}
|
|
4857
4870
|
registerViews(views) {
|
|
4858
4871
|
this.views = views;
|
|
4859
|
-
this.cache.registerViews(views);
|
|
4860
4872
|
}
|
|
4861
4873
|
async publish(event3) {
|
|
4862
|
-
await this.cache.applyEvent(event3);
|
|
4863
4874
|
await this.notifySubscribers(event3);
|
|
4864
4875
|
this.eventWire.syncEvents([
|
|
4865
4876
|
{
|
|
@@ -5290,6 +5301,7 @@ class TokenCache {
|
|
|
5290
5301
|
}
|
|
5291
5302
|
export {
|
|
5292
5303
|
view,
|
|
5304
|
+
triggerModuleSync,
|
|
5293
5305
|
token,
|
|
5294
5306
|
stringEnum,
|
|
5295
5307
|
string,
|
|
@@ -5297,6 +5309,7 @@ export {
|
|
|
5297
5309
|
secureDataStorage,
|
|
5298
5310
|
route,
|
|
5299
5311
|
resolveQueryChange,
|
|
5312
|
+
registerModuleSyncProvider,
|
|
5300
5313
|
record,
|
|
5301
5314
|
or,
|
|
5302
5315
|
observeQueries,
|
|
@@ -5307,10 +5320,12 @@ export {
|
|
|
5307
5320
|
mergeUnsafe,
|
|
5308
5321
|
listener,
|
|
5309
5322
|
id,
|
|
5323
|
+
hasModuleSyncProvider,
|
|
5310
5324
|
file,
|
|
5311
5325
|
extractDatabaseAgnosticSchema,
|
|
5312
5326
|
executeDescriptor,
|
|
5313
5327
|
event,
|
|
5328
|
+
diffResults,
|
|
5314
5329
|
defaultFunctionData,
|
|
5315
5330
|
deepMerge,
|
|
5316
5331
|
date,
|
|
@@ -5324,9 +5339,11 @@ export {
|
|
|
5324
5339
|
buildContextAccessor,
|
|
5325
5340
|
boolean,
|
|
5326
5341
|
blob,
|
|
5342
|
+
awaitModuleSync,
|
|
5327
5343
|
array,
|
|
5328
5344
|
arcFunctionWithCtx,
|
|
5329
5345
|
arcFunction,
|
|
5346
|
+
applyQueryChanges,
|
|
5330
5347
|
applyOrderByAndLimit,
|
|
5331
5348
|
any,
|
|
5332
5349
|
aggregate,
|
|
@@ -5347,6 +5364,7 @@ export {
|
|
|
5347
5364
|
MasterStoreState,
|
|
5348
5365
|
MasterDataStorage,
|
|
5349
5366
|
LocalEventPublisher,
|
|
5367
|
+
LiveQuery,
|
|
5350
5368
|
ForkedStoreState,
|
|
5351
5369
|
ForkedDataStorage,
|
|
5352
5370
|
EventWire,
|