@arcote.tech/arc-cli 0.7.14 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +436 -456
- package/package.json +9 -9
- package/src/platform/server.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -14018,11 +14018,11 @@ class EventWire {
|
|
|
14018
14018
|
reconnectTimeout;
|
|
14019
14019
|
syncRequested = false;
|
|
14020
14020
|
viewSubscriptions = new Map;
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
constructor(baseUrl) {
|
|
14021
|
+
enableEventSync;
|
|
14022
|
+
constructor(baseUrl, options) {
|
|
14024
14023
|
this.baseUrl = baseUrl;
|
|
14025
14024
|
this.instanceId = ++eventWireInstanceCounter;
|
|
14025
|
+
this.enableEventSync = options?.enableEventSync ?? true;
|
|
14026
14026
|
}
|
|
14027
14027
|
setScopeToken(scope, token) {
|
|
14028
14028
|
if (token === null) {
|
|
@@ -14068,9 +14068,11 @@ class EventWire {
|
|
|
14068
14068
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
14069
14069
|
this.state = "connected";
|
|
14070
14070
|
this.sendAllScopeTokens();
|
|
14071
|
-
this.
|
|
14071
|
+
if (this.enableEventSync) {
|
|
14072
|
+
this.requestSync();
|
|
14073
|
+
}
|
|
14072
14074
|
this.flushPendingEvents();
|
|
14073
|
-
this.
|
|
14075
|
+
this.sendAllViewSubscriptions();
|
|
14074
14076
|
} else {
|
|
14075
14077
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
14076
14078
|
}
|
|
@@ -14146,30 +14148,26 @@ class EventWire {
|
|
|
14146
14148
|
onSynced(callback) {
|
|
14147
14149
|
this.onSyncedCallback = callback;
|
|
14148
14150
|
}
|
|
14149
|
-
|
|
14150
|
-
const
|
|
14151
|
-
this.viewSubscriptions.set(
|
|
14151
|
+
subscribeView(element, scope, callbacks) {
|
|
14152
|
+
const key = `${scope}:${element}`;
|
|
14153
|
+
this.viewSubscriptions.set(key, callbacks);
|
|
14152
14154
|
if (this.state === "connected" && this.ws) {
|
|
14153
14155
|
this.ws.send(JSON.stringify({
|
|
14154
|
-
type: "subscribe-
|
|
14155
|
-
|
|
14156
|
-
descriptor,
|
|
14156
|
+
type: "subscribe-view",
|
|
14157
|
+
element,
|
|
14157
14158
|
scope
|
|
14158
14159
|
}));
|
|
14159
|
-
} else {
|
|
14160
|
-
this.pendingViewSubs.push({ subscriptionId, descriptor, scope });
|
|
14161
14160
|
}
|
|
14162
|
-
return subscriptionId;
|
|
14163
14161
|
}
|
|
14164
|
-
|
|
14165
|
-
this.viewSubscriptions.delete(
|
|
14162
|
+
unsubscribeView(element, scope) {
|
|
14163
|
+
this.viewSubscriptions.delete(`${scope}:${element}`);
|
|
14166
14164
|
if (this.state === "connected" && this.ws) {
|
|
14167
14165
|
this.ws.send(JSON.stringify({
|
|
14168
|
-
type: "unsubscribe-
|
|
14169
|
-
|
|
14166
|
+
type: "unsubscribe-view",
|
|
14167
|
+
element,
|
|
14168
|
+
scope
|
|
14170
14169
|
}));
|
|
14171
14170
|
}
|
|
14172
|
-
this.pendingViewSubs = this.pendingViewSubs.filter((s) => s.subscriptionId !== subscriptionId);
|
|
14173
14171
|
}
|
|
14174
14172
|
getState() {
|
|
14175
14173
|
return this.state;
|
|
@@ -14202,10 +14200,17 @@ class EventWire {
|
|
|
14202
14200
|
this.lastHostEventId = message.lastHostEventId;
|
|
14203
14201
|
}
|
|
14204
14202
|
break;
|
|
14205
|
-
case "
|
|
14206
|
-
const
|
|
14207
|
-
if (
|
|
14208
|
-
|
|
14203
|
+
case "view-snapshot": {
|
|
14204
|
+
const sub = this.viewSubscriptions.get(`${message.scope}:${message.element}`);
|
|
14205
|
+
if (sub) {
|
|
14206
|
+
sub.onSnapshot(message.items ?? []);
|
|
14207
|
+
}
|
|
14208
|
+
break;
|
|
14209
|
+
}
|
|
14210
|
+
case "view-changes": {
|
|
14211
|
+
const sub = this.viewSubscriptions.get(`${message.scope}:${message.element}`);
|
|
14212
|
+
if (sub && Array.isArray(message.changes)) {
|
|
14213
|
+
sub.onChanges(message.changes);
|
|
14209
14214
|
}
|
|
14210
14215
|
break;
|
|
14211
14216
|
}
|
|
@@ -14233,18 +14238,19 @@ class EventWire {
|
|
|
14233
14238
|
this.pendingEvents = [];
|
|
14234
14239
|
}
|
|
14235
14240
|
}
|
|
14236
|
-
|
|
14241
|
+
sendAllViewSubscriptions() {
|
|
14237
14242
|
if (!this.ws || this.state !== "connected")
|
|
14238
14243
|
return;
|
|
14239
|
-
for (const
|
|
14244
|
+
for (const key of this.viewSubscriptions.keys()) {
|
|
14245
|
+
const sepIdx = key.indexOf(":");
|
|
14246
|
+
const scope = key.slice(0, sepIdx);
|
|
14247
|
+
const element = key.slice(sepIdx + 1);
|
|
14240
14248
|
this.ws.send(JSON.stringify({
|
|
14241
|
-
type: "subscribe-
|
|
14242
|
-
|
|
14243
|
-
|
|
14244
|
-
scope: sub.scope
|
|
14249
|
+
type: "subscribe-view",
|
|
14250
|
+
element,
|
|
14251
|
+
scope
|
|
14245
14252
|
}));
|
|
14246
14253
|
}
|
|
14247
|
-
this.pendingViewSubs = [];
|
|
14248
14254
|
}
|
|
14249
14255
|
scheduleReconnect() {
|
|
14250
14256
|
if (this.reconnectTimeout)
|
|
@@ -14261,12 +14267,19 @@ class LocalEventPublisher2 {
|
|
|
14261
14267
|
views = [];
|
|
14262
14268
|
syncCallback;
|
|
14263
14269
|
subscribers = new Map;
|
|
14270
|
+
viewChangesCallbacks = new Set;
|
|
14264
14271
|
constructor(dataStorage) {
|
|
14265
14272
|
this.dataStorage = dataStorage;
|
|
14266
14273
|
}
|
|
14267
14274
|
onPublish(callback) {
|
|
14268
14275
|
this.syncCallback = callback;
|
|
14269
14276
|
}
|
|
14277
|
+
onViewChanges(callback) {
|
|
14278
|
+
this.viewChangesCallbacks.add(callback);
|
|
14279
|
+
return () => {
|
|
14280
|
+
this.viewChangesCallbacks.delete(callback);
|
|
14281
|
+
};
|
|
14282
|
+
}
|
|
14270
14283
|
registerViews(views) {
|
|
14271
14284
|
this.views = views;
|
|
14272
14285
|
}
|
|
@@ -14334,7 +14347,19 @@ class LocalEventPublisher2 {
|
|
|
14334
14347
|
});
|
|
14335
14348
|
const viewChanges = await this.collectViewChanges(event);
|
|
14336
14349
|
allChanges.push(...viewChanges);
|
|
14337
|
-
|
|
14350
|
+
const viewStoreNames = new Set(viewChanges.map((c2) => c2.store));
|
|
14351
|
+
const committed = await this.dataStorage.commitChanges(allChanges, {
|
|
14352
|
+
captureRowsFor: viewStoreNames
|
|
14353
|
+
});
|
|
14354
|
+
if (committed.length > 0 && this.viewChangesCallbacks.size > 0) {
|
|
14355
|
+
for (const callback of this.viewChangesCallbacks) {
|
|
14356
|
+
try {
|
|
14357
|
+
callback(committed);
|
|
14358
|
+
} catch (error) {
|
|
14359
|
+
console.error(`[EventPublisher] onViewChanges callback error:`, error);
|
|
14360
|
+
}
|
|
14361
|
+
}
|
|
14362
|
+
}
|
|
14338
14363
|
await this.notifySubscribers(event);
|
|
14339
14364
|
if (this.syncCallback) {
|
|
14340
14365
|
this.syncCallback(event);
|
|
@@ -14715,8 +14740,9 @@ function typeValidatorBuilder(typeName, comparatorStrategy) {
|
|
|
14715
14740
|
}
|
|
14716
14741
|
|
|
14717
14742
|
class DataStorage {
|
|
14718
|
-
async commitChanges(changes) {
|
|
14743
|
+
async commitChanges(changes, _options) {
|
|
14719
14744
|
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
14745
|
+
return [];
|
|
14720
14746
|
}
|
|
14721
14747
|
}
|
|
14722
14748
|
|
|
@@ -15174,8 +15200,8 @@ class ObservableDataStorage {
|
|
|
15174
15200
|
getReadWriteTransaction() {
|
|
15175
15201
|
return this.source.getReadWriteTransaction();
|
|
15176
15202
|
}
|
|
15177
|
-
commitChanges(changes) {
|
|
15178
|
-
return this.source.commitChanges(changes);
|
|
15203
|
+
commitChanges(changes, options) {
|
|
15204
|
+
return this.source.commitChanges(changes, options);
|
|
15179
15205
|
}
|
|
15180
15206
|
trackQuery(storeName, options, result, listener4) {
|
|
15181
15207
|
const key = this.getQueryKey(storeName, options);
|
|
@@ -15346,7 +15372,7 @@ function executeDescriptor(descriptor, context2, adapters, contextMethod, option
|
|
|
15346
15372
|
return method(...descriptor.args);
|
|
15347
15373
|
}
|
|
15348
15374
|
|
|
15349
|
-
class
|
|
15375
|
+
class ScopedModel3 {
|
|
15350
15376
|
context;
|
|
15351
15377
|
scopeName;
|
|
15352
15378
|
parent;
|
|
@@ -15418,13 +15444,6 @@ class ScopedModel4 {
|
|
|
15418
15444
|
}
|
|
15419
15445
|
return wire.query(viewName, options, this.getAuth());
|
|
15420
15446
|
}
|
|
15421
|
-
subscribeQuery(descriptor, callback) {
|
|
15422
|
-
const wire = this.parent.getAdapters().eventWire;
|
|
15423
|
-
if (!wire) {
|
|
15424
|
-
throw new Error(`Cannot subscribe to query: no eventWire available.`);
|
|
15425
|
-
}
|
|
15426
|
-
return wire.subscribeQuery(descriptor, callback, this.scopeName);
|
|
15427
|
-
}
|
|
15428
15447
|
get query() {
|
|
15429
15448
|
return buildContextAccessor(this.context, this.scopedAdapters, "queryContext", (descriptor) => descriptor);
|
|
15430
15449
|
}
|
|
@@ -15474,7 +15493,7 @@ class Model2 {
|
|
|
15474
15493
|
scope(name) {
|
|
15475
15494
|
let s = this.scopes.get(name);
|
|
15476
15495
|
if (!s) {
|
|
15477
|
-
s = new
|
|
15496
|
+
s = new ScopedModel3(this, name);
|
|
15478
15497
|
this.scopes.set(name, s);
|
|
15479
15498
|
}
|
|
15480
15499
|
return s;
|
|
@@ -15486,115 +15505,100 @@ class StreamingQueryCache {
|
|
|
15486
15505
|
views = [];
|
|
15487
15506
|
activeStreams = new Map;
|
|
15488
15507
|
pendingUnsubscribes = new Map;
|
|
15489
|
-
streamScopes = new Map;
|
|
15490
15508
|
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
15509
|
+
storeKey(viewName, scope) {
|
|
15510
|
+
return `${scope ?? DEFAULT_SCOPE}:${viewName}`;
|
|
15511
|
+
}
|
|
15491
15512
|
registerViews(views) {
|
|
15492
15513
|
this.views = views;
|
|
15493
|
-
for (const view3 of views) {
|
|
15494
|
-
if (!this.stores.has(view3.name)) {
|
|
15495
|
-
this.stores.set(view3.name, new StreamingStore);
|
|
15496
|
-
}
|
|
15497
|
-
}
|
|
15498
15514
|
}
|
|
15499
|
-
getStore(viewName) {
|
|
15500
|
-
|
|
15501
|
-
|
|
15515
|
+
getStore(viewName, scope) {
|
|
15516
|
+
const key = this.storeKey(viewName, scope);
|
|
15517
|
+
if (!this.stores.has(key)) {
|
|
15518
|
+
this.stores.set(key, new StreamingStore);
|
|
15502
15519
|
}
|
|
15503
|
-
return this.stores.get(
|
|
15520
|
+
return this.stores.get(key);
|
|
15504
15521
|
}
|
|
15505
|
-
hasData(viewName) {
|
|
15506
|
-
const store = this.stores.get(viewName);
|
|
15522
|
+
hasData(viewName, scope) {
|
|
15523
|
+
const store = this.stores.get(this.storeKey(viewName, scope));
|
|
15507
15524
|
return store ? store.hasData() : false;
|
|
15508
15525
|
}
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
}
|
|
15512
|
-
registerStream(viewName, createStream) {
|
|
15513
|
-
const pending = this.pendingUnsubscribes.get(viewName);
|
|
15526
|
+
registerStream(key, createStream) {
|
|
15527
|
+
const pending = this.pendingUnsubscribes.get(key);
|
|
15514
15528
|
if (pending) {
|
|
15515
15529
|
clearTimeout(pending);
|
|
15516
|
-
this.pendingUnsubscribes.delete(
|
|
15530
|
+
this.pendingUnsubscribes.delete(key);
|
|
15517
15531
|
}
|
|
15518
|
-
const existing = this.activeStreams.get(
|
|
15532
|
+
const existing = this.activeStreams.get(key);
|
|
15519
15533
|
if (existing) {
|
|
15520
15534
|
existing.refCount++;
|
|
15521
15535
|
return {
|
|
15522
|
-
unsubscribe: () => this.unregisterStream(
|
|
15536
|
+
unsubscribe: () => this.unregisterStream(key),
|
|
15523
15537
|
wasReused: true
|
|
15524
15538
|
};
|
|
15525
15539
|
}
|
|
15526
15540
|
const streamConn = createStream();
|
|
15527
|
-
this.activeStreams.set(
|
|
15541
|
+
this.activeStreams.set(key, {
|
|
15528
15542
|
unsubscribe: streamConn.unsubscribe,
|
|
15529
15543
|
refCount: 1
|
|
15530
15544
|
});
|
|
15531
15545
|
return {
|
|
15532
|
-
unsubscribe: () => this.unregisterStream(
|
|
15546
|
+
unsubscribe: () => this.unregisterStream(key),
|
|
15533
15547
|
wasReused: false
|
|
15534
15548
|
};
|
|
15535
15549
|
}
|
|
15536
|
-
unregisterStream(
|
|
15537
|
-
const stream2 = this.activeStreams.get(
|
|
15550
|
+
unregisterStream(key) {
|
|
15551
|
+
const stream2 = this.activeStreams.get(key);
|
|
15538
15552
|
if (!stream2)
|
|
15539
15553
|
return;
|
|
15540
15554
|
stream2.refCount--;
|
|
15541
15555
|
if (stream2.refCount <= 0) {
|
|
15542
15556
|
const timeout = setTimeout(() => {
|
|
15543
|
-
this.pendingUnsubscribes.delete(
|
|
15544
|
-
const current2 = this.activeStreams.get(
|
|
15557
|
+
this.pendingUnsubscribes.delete(key);
|
|
15558
|
+
const current2 = this.activeStreams.get(key);
|
|
15545
15559
|
if (current2 && current2.refCount <= 0) {
|
|
15546
15560
|
current2.unsubscribe();
|
|
15547
|
-
this.activeStreams.delete(
|
|
15548
|
-
this.streamScopes.delete(viewName);
|
|
15561
|
+
this.activeStreams.delete(key);
|
|
15549
15562
|
}
|
|
15550
15563
|
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
15551
|
-
this.pendingUnsubscribes.set(
|
|
15564
|
+
this.pendingUnsubscribes.set(key, timeout);
|
|
15552
15565
|
}
|
|
15553
15566
|
}
|
|
15554
|
-
|
|
15555
|
-
const key =
|
|
15556
|
-
if (scope)
|
|
15557
|
-
this.streamScopes.set(key, scope);
|
|
15567
|
+
subscribeView(viewName, eventWire, scope) {
|
|
15568
|
+
const key = this.storeKey(viewName, scope);
|
|
15558
15569
|
const { unsubscribe } = this.registerStream(key, () => {
|
|
15559
|
-
const
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15570
|
+
const store = this.stores.get(key) ?? new StreamingStore;
|
|
15571
|
+
this.stores.set(key, store);
|
|
15572
|
+
eventWire.subscribeView(viewName, scope ?? DEFAULT_SCOPE, {
|
|
15573
|
+
onSnapshot: (items) => store.setAll(items),
|
|
15574
|
+
onChanges: (changes) => store.applyChanges(changes)
|
|
15575
|
+
});
|
|
15576
|
+
return {
|
|
15577
|
+
unsubscribe: () => eventWire.unsubscribeView(viewName, scope ?? DEFAULT_SCOPE)
|
|
15578
|
+
};
|
|
15563
15579
|
});
|
|
15564
15580
|
return unsubscribe;
|
|
15565
15581
|
}
|
|
15566
15582
|
invalidateScope(scope) {
|
|
15567
|
-
|
|
15568
|
-
|
|
15583
|
+
const prefix = `${scope}:`;
|
|
15584
|
+
for (const [key, timeout] of this.pendingUnsubscribes) {
|
|
15585
|
+
if (!key.startsWith(prefix))
|
|
15569
15586
|
continue;
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
clearTimeout(pending);
|
|
15573
|
-
this.pendingUnsubscribes.delete(viewName);
|
|
15574
|
-
}
|
|
15575
|
-
const stream2 = this.activeStreams.get(viewName);
|
|
15576
|
-
if (stream2) {
|
|
15577
|
-
try {
|
|
15578
|
-
stream2.unsubscribe();
|
|
15579
|
-
} catch {}
|
|
15580
|
-
this.activeStreams.delete(viewName);
|
|
15581
|
-
}
|
|
15582
|
-
this.streamScopes.delete(viewName);
|
|
15583
|
-
const store = this.stores.get(viewName);
|
|
15584
|
-
if (store)
|
|
15585
|
-
store.clear();
|
|
15587
|
+
clearTimeout(timeout);
|
|
15588
|
+
this.pendingUnsubscribes.delete(key);
|
|
15586
15589
|
}
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
}
|
|
15595
|
-
|
|
15596
|
-
|
|
15597
|
-
|
|
15590
|
+
for (const [key, stream2] of this.activeStreams) {
|
|
15591
|
+
if (!key.startsWith(prefix))
|
|
15592
|
+
continue;
|
|
15593
|
+
try {
|
|
15594
|
+
stream2.unsubscribe();
|
|
15595
|
+
} catch {}
|
|
15596
|
+
this.activeStreams.delete(key);
|
|
15597
|
+
}
|
|
15598
|
+
for (const [key, store] of this.stores) {
|
|
15599
|
+
if (!key.startsWith(prefix))
|
|
15600
|
+
continue;
|
|
15601
|
+
store.clear();
|
|
15598
15602
|
}
|
|
15599
15603
|
}
|
|
15600
15604
|
async applyEvent(event3) {
|
|
@@ -15603,28 +15607,30 @@ class StreamingQueryCache {
|
|
|
15603
15607
|
const handler = handlers[event3.type];
|
|
15604
15608
|
if (!handler)
|
|
15605
15609
|
continue;
|
|
15606
|
-
const
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15610
|
+
const suffix = `:${view3.name}`;
|
|
15611
|
+
for (const [key, store] of this.stores) {
|
|
15612
|
+
if (!key.endsWith(suffix))
|
|
15613
|
+
continue;
|
|
15614
|
+
const ctx = {
|
|
15615
|
+
set: async (id3, data) => {
|
|
15616
|
+
store.set(String(id3), { _id: String(id3), ...data });
|
|
15617
|
+
},
|
|
15618
|
+
modify: async (id3, data) => {
|
|
15619
|
+
store.modify(String(id3), data);
|
|
15620
|
+
},
|
|
15621
|
+
remove: async (id3) => {
|
|
15622
|
+
store.remove(String(id3));
|
|
15623
|
+
},
|
|
15624
|
+
find: async (options) => {
|
|
15625
|
+
return store.find(options);
|
|
15626
|
+
},
|
|
15627
|
+
findOne: async (where) => {
|
|
15628
|
+
return store.findOne(where);
|
|
15629
|
+
},
|
|
15630
|
+
$auth: {}
|
|
15631
|
+
};
|
|
15632
|
+
await handler(ctx, event3);
|
|
15633
|
+
}
|
|
15628
15634
|
}
|
|
15629
15635
|
}
|
|
15630
15636
|
clear() {
|
|
@@ -15632,7 +15638,6 @@ class StreamingQueryCache {
|
|
|
15632
15638
|
stream2.unsubscribe();
|
|
15633
15639
|
}
|
|
15634
15640
|
this.activeStreams.clear();
|
|
15635
|
-
this.streamScopes.clear();
|
|
15636
15641
|
for (const timeout of this.pendingUnsubscribes.values()) {
|
|
15637
15642
|
clearTimeout(timeout);
|
|
15638
15643
|
}
|
|
@@ -15658,6 +15663,18 @@ class StreamingStore {
|
|
|
15658
15663
|
}
|
|
15659
15664
|
this.notifyListeners(null);
|
|
15660
15665
|
}
|
|
15666
|
+
applyChanges(events) {
|
|
15667
|
+
if (events.length === 0)
|
|
15668
|
+
return;
|
|
15669
|
+
for (const event3 of events) {
|
|
15670
|
+
if (event3.type === "set" && event3.item) {
|
|
15671
|
+
this.data.set(event3.id, event3.item);
|
|
15672
|
+
} else if (event3.type === "delete") {
|
|
15673
|
+
this.data.delete(event3.id);
|
|
15674
|
+
}
|
|
15675
|
+
}
|
|
15676
|
+
this.notifyListeners(events);
|
|
15677
|
+
}
|
|
15661
15678
|
set(id3, item) {
|
|
15662
15679
|
this.data.set(id3, item);
|
|
15663
15680
|
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
@@ -18125,7 +18142,7 @@ var Operation, PROXY_DRAFT, RAW_RETURN_SYMBOL, iteratorSymbol, dataTypes, intern
|
|
|
18125
18142
|
}
|
|
18126
18143
|
return returnValue(result);
|
|
18127
18144
|
};
|
|
18128
|
-
}, create, constructorString, TOKEN_PREFIX = "arc:token:", eventWireInstanceCounter = 0, EVENT_TABLES, arrayValidator, ArcArray, objectValidator, ArcObject, ScopedDataStorage, ArcPrimitive, stringValidator, ArcString, ArcContextElement, ArcBoolean, ArcId, numberValidator, ArcNumber, ArcEvent, ArcCommand, ArcListener, ArcRoute, ForkedStoreState, ForkedDataStorage, MasterStoreState, MasterDataStorage2, dateValidator, originCache, originStackCache, originError, CLOSE, Query, PostgresError, Errors, types2, Identifier, Parameter, Builder, defaultHandlers, builders, serializers, parsers, mergeUserTypes = function(types22) {
|
|
18145
|
+
}, create, constructorString, TOKEN_PREFIX = "arc:token:", eventWireInstanceCounter = 0, EVENT_TABLES, arrayValidator, ArcArray, objectValidator, ArcObject, ScopedDataStorage, ArcPrimitive, stringValidator, ArcString, ArcContextElement, ArcBoolean, ArcId, numberValidator, ArcNumber, ArcEvent, ArcCommand, ArcListener, ArcRoute, ForkedStoreState, ForkedDataStorage, MasterStoreState, MasterDataStorage2, dateValidator, DEFAULT_SCOPE = "default", originCache, originStackCache, originError, CLOSE, Query, PostgresError, Errors, types2, Identifier, Parameter, Builder, defaultHandlers, builders, serializers, parsers, mergeUserTypes = function(types22) {
|
|
18129
18146
|
const user = typeHandlers(types22 || {});
|
|
18130
18147
|
return {
|
|
18131
18148
|
serializers: Object.assign({}, serializers, user.serializers),
|
|
@@ -19724,12 +19741,23 @@ var init_dist = __esm(() => {
|
|
|
19724
19741
|
constructor(storeName, dataStorage, deserialize) {
|
|
19725
19742
|
super(storeName, dataStorage, deserialize);
|
|
19726
19743
|
}
|
|
19727
|
-
async
|
|
19744
|
+
async readExisting(transaction, id2, transactionCache) {
|
|
19745
|
+
const cacheKey = `${this.storeName}:${id2}`;
|
|
19746
|
+
if (transactionCache && transactionCache.has(cacheKey)) {
|
|
19747
|
+
return transactionCache.get(cacheKey);
|
|
19748
|
+
}
|
|
19749
|
+
return transaction.find(this.storeName, { where: { _id: id2 } }).then((results) => results[0]);
|
|
19750
|
+
}
|
|
19751
|
+
async applyChangeAndReturnEvent(transaction, change, transactionCache, options) {
|
|
19728
19752
|
if (change.type === "set") {
|
|
19753
|
+
let existing;
|
|
19754
|
+
if (options?.captureRows) {
|
|
19755
|
+
existing = await this.readExisting(transaction, change.data._id, transactionCache);
|
|
19756
|
+
}
|
|
19729
19757
|
await transaction.set(this.storeName, change.data);
|
|
19730
19758
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
19731
19759
|
if (transactionCache) {
|
|
19732
|
-
transactionCache.set(change.data._id
|
|
19760
|
+
transactionCache.set(`${this.storeName}:${change.data._id}`, item);
|
|
19733
19761
|
}
|
|
19734
19762
|
return {
|
|
19735
19763
|
from: null,
|
|
@@ -19738,11 +19766,20 @@ var init_dist = __esm(() => {
|
|
|
19738
19766
|
type: "set",
|
|
19739
19767
|
item: change.data,
|
|
19740
19768
|
id: change.data._id
|
|
19741
|
-
}
|
|
19769
|
+
},
|
|
19770
|
+
oldRow: existing ?? null,
|
|
19771
|
+
newRow: change.data
|
|
19742
19772
|
};
|
|
19743
19773
|
}
|
|
19744
19774
|
if (change.type === "delete") {
|
|
19775
|
+
let existing;
|
|
19776
|
+
if (options?.captureRows) {
|
|
19777
|
+
existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
19778
|
+
}
|
|
19745
19779
|
await transaction.remove(this.storeName, change.id);
|
|
19780
|
+
if (transactionCache) {
|
|
19781
|
+
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
19782
|
+
}
|
|
19746
19783
|
return {
|
|
19747
19784
|
from: null,
|
|
19748
19785
|
to: null,
|
|
@@ -19750,21 +19787,18 @@ var init_dist = __esm(() => {
|
|
|
19750
19787
|
type: "delete",
|
|
19751
19788
|
item: null,
|
|
19752
19789
|
id: change.id
|
|
19753
|
-
}
|
|
19790
|
+
},
|
|
19791
|
+
oldRow: existing ?? null,
|
|
19792
|
+
newRow: null
|
|
19754
19793
|
};
|
|
19755
19794
|
}
|
|
19756
19795
|
if (change.type === "modify") {
|
|
19757
|
-
|
|
19758
|
-
if (transactionCache && transactionCache.has(change.id)) {
|
|
19759
|
-
existing = transactionCache.get(change.id);
|
|
19760
|
-
} else {
|
|
19761
|
-
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
19762
|
-
}
|
|
19796
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
19763
19797
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
19764
19798
|
await transaction.set(this.storeName, updated);
|
|
19765
19799
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
19766
19800
|
if (transactionCache) {
|
|
19767
|
-
transactionCache.set(change.id
|
|
19801
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
19768
19802
|
}
|
|
19769
19803
|
return {
|
|
19770
19804
|
from: null,
|
|
@@ -19773,21 +19807,18 @@ var init_dist = __esm(() => {
|
|
|
19773
19807
|
type: "set",
|
|
19774
19808
|
item,
|
|
19775
19809
|
id: change.id
|
|
19776
|
-
}
|
|
19810
|
+
},
|
|
19811
|
+
oldRow: existing ?? null,
|
|
19812
|
+
newRow: updated
|
|
19777
19813
|
};
|
|
19778
19814
|
}
|
|
19779
19815
|
if (change.type === "mutate") {
|
|
19780
|
-
|
|
19781
|
-
if (transactionCache && transactionCache.has(change.id)) {
|
|
19782
|
-
existing = transactionCache.get(change.id);
|
|
19783
|
-
} else {
|
|
19784
|
-
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
19785
|
-
}
|
|
19816
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
19786
19817
|
const updated = apply(existing || {}, change.patches);
|
|
19787
19818
|
await transaction.set(this.storeName, updated);
|
|
19788
19819
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
19789
19820
|
if (transactionCache) {
|
|
19790
|
-
transactionCache.set(change.id
|
|
19821
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
19791
19822
|
}
|
|
19792
19823
|
return {
|
|
19793
19824
|
from: null,
|
|
@@ -19796,7 +19827,9 @@ var init_dist = __esm(() => {
|
|
|
19796
19827
|
type: "set",
|
|
19797
19828
|
item,
|
|
19798
19829
|
id: change.id
|
|
19799
|
-
}
|
|
19830
|
+
},
|
|
19831
|
+
oldRow: existing ?? null,
|
|
19832
|
+
newRow: updated
|
|
19800
19833
|
};
|
|
19801
19834
|
}
|
|
19802
19835
|
throw new Error("Unknown change type");
|
|
@@ -19873,17 +19906,22 @@ var init_dist = __esm(() => {
|
|
|
19873
19906
|
applySerializedChanges(changes) {
|
|
19874
19907
|
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
19875
19908
|
}
|
|
19876
|
-
async commitChanges(changes) {
|
|
19909
|
+
async commitChanges(changes, options) {
|
|
19877
19910
|
const transaction = await this.getReadWriteTransaction();
|
|
19878
19911
|
const transactionCache = new Map;
|
|
19879
19912
|
const eventsByStore = new Map;
|
|
19913
|
+
const committed = [];
|
|
19880
19914
|
for (const { store, changes: storeChanges } of changes) {
|
|
19881
19915
|
const storeState = this.getStore(store);
|
|
19882
19916
|
const storeEvents = [];
|
|
19917
|
+
const capture = options?.captureRowsFor?.has(store) ?? false;
|
|
19883
19918
|
for (const change of storeChanges) {
|
|
19884
|
-
const { event: event3 } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
19919
|
+
const { event: event3, oldRow, newRow } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache, { captureRows: capture });
|
|
19885
19920
|
if (event3)
|
|
19886
19921
|
storeEvents.push(event3);
|
|
19922
|
+
if (capture) {
|
|
19923
|
+
committed.push({ store, id: event3.id, oldRow, newRow });
|
|
19924
|
+
}
|
|
19887
19925
|
}
|
|
19888
19926
|
if (storeEvents.length > 0) {
|
|
19889
19927
|
eventsByStore.set(store, storeEvents);
|
|
@@ -19894,6 +19932,7 @@ var init_dist = __esm(() => {
|
|
|
19894
19932
|
const storeState = this.getStore(store);
|
|
19895
19933
|
storeState.notifyListenersPublic(events);
|
|
19896
19934
|
}
|
|
19935
|
+
return committed;
|
|
19897
19936
|
}
|
|
19898
19937
|
fork() {
|
|
19899
19938
|
return new ForkedDataStorage(this);
|
|
@@ -21410,11 +21449,11 @@ class EventWire2 {
|
|
|
21410
21449
|
reconnectTimeout;
|
|
21411
21450
|
syncRequested = false;
|
|
21412
21451
|
viewSubscriptions = new Map;
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
constructor(baseUrl) {
|
|
21452
|
+
enableEventSync;
|
|
21453
|
+
constructor(baseUrl, options) {
|
|
21416
21454
|
this.baseUrl = baseUrl;
|
|
21417
21455
|
this.instanceId = ++eventWireInstanceCounter2;
|
|
21456
|
+
this.enableEventSync = options?.enableEventSync ?? true;
|
|
21418
21457
|
}
|
|
21419
21458
|
setScopeToken(scope, token) {
|
|
21420
21459
|
if (token === null) {
|
|
@@ -21460,9 +21499,11 @@ class EventWire2 {
|
|
|
21460
21499
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
21461
21500
|
this.state = "connected";
|
|
21462
21501
|
this.sendAllScopeTokens();
|
|
21463
|
-
this.
|
|
21502
|
+
if (this.enableEventSync) {
|
|
21503
|
+
this.requestSync();
|
|
21504
|
+
}
|
|
21464
21505
|
this.flushPendingEvents();
|
|
21465
|
-
this.
|
|
21506
|
+
this.sendAllViewSubscriptions();
|
|
21466
21507
|
} else {
|
|
21467
21508
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
21468
21509
|
}
|
|
@@ -21538,30 +21579,26 @@ class EventWire2 {
|
|
|
21538
21579
|
onSynced(callback) {
|
|
21539
21580
|
this.onSyncedCallback = callback;
|
|
21540
21581
|
}
|
|
21541
|
-
|
|
21542
|
-
const
|
|
21543
|
-
this.viewSubscriptions.set(
|
|
21582
|
+
subscribeView(element, scope, callbacks) {
|
|
21583
|
+
const key = `${scope}:${element}`;
|
|
21584
|
+
this.viewSubscriptions.set(key, callbacks);
|
|
21544
21585
|
if (this.state === "connected" && this.ws) {
|
|
21545
21586
|
this.ws.send(JSON.stringify({
|
|
21546
|
-
type: "subscribe-
|
|
21547
|
-
|
|
21548
|
-
descriptor,
|
|
21587
|
+
type: "subscribe-view",
|
|
21588
|
+
element,
|
|
21549
21589
|
scope
|
|
21550
21590
|
}));
|
|
21551
|
-
} else {
|
|
21552
|
-
this.pendingViewSubs.push({ subscriptionId, descriptor, scope });
|
|
21553
21591
|
}
|
|
21554
|
-
return subscriptionId;
|
|
21555
21592
|
}
|
|
21556
|
-
|
|
21557
|
-
this.viewSubscriptions.delete(
|
|
21593
|
+
unsubscribeView(element, scope) {
|
|
21594
|
+
this.viewSubscriptions.delete(`${scope}:${element}`);
|
|
21558
21595
|
if (this.state === "connected" && this.ws) {
|
|
21559
21596
|
this.ws.send(JSON.stringify({
|
|
21560
|
-
type: "unsubscribe-
|
|
21561
|
-
|
|
21597
|
+
type: "unsubscribe-view",
|
|
21598
|
+
element,
|
|
21599
|
+
scope
|
|
21562
21600
|
}));
|
|
21563
21601
|
}
|
|
21564
|
-
this.pendingViewSubs = this.pendingViewSubs.filter((s) => s.subscriptionId !== subscriptionId);
|
|
21565
21602
|
}
|
|
21566
21603
|
getState() {
|
|
21567
21604
|
return this.state;
|
|
@@ -21594,10 +21631,17 @@ class EventWire2 {
|
|
|
21594
21631
|
this.lastHostEventId = message.lastHostEventId;
|
|
21595
21632
|
}
|
|
21596
21633
|
break;
|
|
21597
|
-
case "
|
|
21598
|
-
const
|
|
21599
|
-
if (
|
|
21600
|
-
|
|
21634
|
+
case "view-snapshot": {
|
|
21635
|
+
const sub = this.viewSubscriptions.get(`${message.scope}:${message.element}`);
|
|
21636
|
+
if (sub) {
|
|
21637
|
+
sub.onSnapshot(message.items ?? []);
|
|
21638
|
+
}
|
|
21639
|
+
break;
|
|
21640
|
+
}
|
|
21641
|
+
case "view-changes": {
|
|
21642
|
+
const sub = this.viewSubscriptions.get(`${message.scope}:${message.element}`);
|
|
21643
|
+
if (sub && Array.isArray(message.changes)) {
|
|
21644
|
+
sub.onChanges(message.changes);
|
|
21601
21645
|
}
|
|
21602
21646
|
break;
|
|
21603
21647
|
}
|
|
@@ -21625,18 +21669,19 @@ class EventWire2 {
|
|
|
21625
21669
|
this.pendingEvents = [];
|
|
21626
21670
|
}
|
|
21627
21671
|
}
|
|
21628
|
-
|
|
21672
|
+
sendAllViewSubscriptions() {
|
|
21629
21673
|
if (!this.ws || this.state !== "connected")
|
|
21630
21674
|
return;
|
|
21631
|
-
for (const
|
|
21675
|
+
for (const key of this.viewSubscriptions.keys()) {
|
|
21676
|
+
const sepIdx = key.indexOf(":");
|
|
21677
|
+
const scope = key.slice(0, sepIdx);
|
|
21678
|
+
const element = key.slice(sepIdx + 1);
|
|
21632
21679
|
this.ws.send(JSON.stringify({
|
|
21633
|
-
type: "subscribe-
|
|
21634
|
-
|
|
21635
|
-
|
|
21636
|
-
scope: sub.scope
|
|
21680
|
+
type: "subscribe-view",
|
|
21681
|
+
element,
|
|
21682
|
+
scope
|
|
21637
21683
|
}));
|
|
21638
21684
|
}
|
|
21639
|
-
this.pendingViewSubs = [];
|
|
21640
21685
|
}
|
|
21641
21686
|
scheduleReconnect() {
|
|
21642
21687
|
if (this.reconnectTimeout)
|
|
@@ -21653,12 +21698,19 @@ class LocalEventPublisher3 {
|
|
|
21653
21698
|
views = [];
|
|
21654
21699
|
syncCallback;
|
|
21655
21700
|
subscribers = new Map;
|
|
21701
|
+
viewChangesCallbacks = new Set;
|
|
21656
21702
|
constructor(dataStorage) {
|
|
21657
21703
|
this.dataStorage = dataStorage;
|
|
21658
21704
|
}
|
|
21659
21705
|
onPublish(callback) {
|
|
21660
21706
|
this.syncCallback = callback;
|
|
21661
21707
|
}
|
|
21708
|
+
onViewChanges(callback) {
|
|
21709
|
+
this.viewChangesCallbacks.add(callback);
|
|
21710
|
+
return () => {
|
|
21711
|
+
this.viewChangesCallbacks.delete(callback);
|
|
21712
|
+
};
|
|
21713
|
+
}
|
|
21662
21714
|
registerViews(views) {
|
|
21663
21715
|
this.views = views;
|
|
21664
21716
|
}
|
|
@@ -21726,7 +21778,19 @@ class LocalEventPublisher3 {
|
|
|
21726
21778
|
});
|
|
21727
21779
|
const viewChanges = await this.collectViewChanges(event);
|
|
21728
21780
|
allChanges.push(...viewChanges);
|
|
21729
|
-
|
|
21781
|
+
const viewStoreNames = new Set(viewChanges.map((c2) => c2.store));
|
|
21782
|
+
const committed = await this.dataStorage.commitChanges(allChanges, {
|
|
21783
|
+
captureRowsFor: viewStoreNames
|
|
21784
|
+
});
|
|
21785
|
+
if (committed.length > 0 && this.viewChangesCallbacks.size > 0) {
|
|
21786
|
+
for (const callback of this.viewChangesCallbacks) {
|
|
21787
|
+
try {
|
|
21788
|
+
callback(committed);
|
|
21789
|
+
} catch (error) {
|
|
21790
|
+
console.error(`[EventPublisher] onViewChanges callback error:`, error);
|
|
21791
|
+
}
|
|
21792
|
+
}
|
|
21793
|
+
}
|
|
21730
21794
|
await this.notifySubscribers(event);
|
|
21731
21795
|
if (this.syncCallback) {
|
|
21732
21796
|
this.syncCallback(event);
|
|
@@ -22107,8 +22171,9 @@ function typeValidatorBuilder2(typeName, comparatorStrategy) {
|
|
|
22107
22171
|
}
|
|
22108
22172
|
|
|
22109
22173
|
class DataStorage2 {
|
|
22110
|
-
async commitChanges(changes) {
|
|
22174
|
+
async commitChanges(changes, _options) {
|
|
22111
22175
|
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
22176
|
+
return [];
|
|
22112
22177
|
}
|
|
22113
22178
|
}
|
|
22114
22179
|
|
|
@@ -22566,8 +22631,8 @@ class ObservableDataStorage2 {
|
|
|
22566
22631
|
getReadWriteTransaction() {
|
|
22567
22632
|
return this.source.getReadWriteTransaction();
|
|
22568
22633
|
}
|
|
22569
|
-
commitChanges(changes) {
|
|
22570
|
-
return this.source.commitChanges(changes);
|
|
22634
|
+
commitChanges(changes, options) {
|
|
22635
|
+
return this.source.commitChanges(changes, options);
|
|
22571
22636
|
}
|
|
22572
22637
|
trackQuery(storeName, options, result, listener4) {
|
|
22573
22638
|
const key = this.getQueryKey(storeName, options);
|
|
@@ -22738,7 +22803,7 @@ function executeDescriptor2(descriptor, context2, adapters, contextMethod, optio
|
|
|
22738
22803
|
return method(...descriptor.args);
|
|
22739
22804
|
}
|
|
22740
22805
|
|
|
22741
|
-
class
|
|
22806
|
+
class ScopedModel4 {
|
|
22742
22807
|
context;
|
|
22743
22808
|
scopeName;
|
|
22744
22809
|
parent;
|
|
@@ -22810,13 +22875,6 @@ class ScopedModel5 {
|
|
|
22810
22875
|
}
|
|
22811
22876
|
return wire.query(viewName, options, this.getAuth());
|
|
22812
22877
|
}
|
|
22813
|
-
subscribeQuery(descriptor, callback) {
|
|
22814
|
-
const wire = this.parent.getAdapters().eventWire;
|
|
22815
|
-
if (!wire) {
|
|
22816
|
-
throw new Error(`Cannot subscribe to query: no eventWire available.`);
|
|
22817
|
-
}
|
|
22818
|
-
return wire.subscribeQuery(descriptor, callback, this.scopeName);
|
|
22819
|
-
}
|
|
22820
22878
|
get query() {
|
|
22821
22879
|
return buildContextAccessor2(this.context, this.scopedAdapters, "queryContext", (descriptor) => descriptor);
|
|
22822
22880
|
}
|
|
@@ -22866,7 +22924,7 @@ class Model3 {
|
|
|
22866
22924
|
scope(name) {
|
|
22867
22925
|
let s = this.scopes.get(name);
|
|
22868
22926
|
if (!s) {
|
|
22869
|
-
s = new
|
|
22927
|
+
s = new ScopedModel4(this, name);
|
|
22870
22928
|
this.scopes.set(name, s);
|
|
22871
22929
|
}
|
|
22872
22930
|
return s;
|
|
@@ -22878,115 +22936,100 @@ class StreamingQueryCache2 {
|
|
|
22878
22936
|
views = [];
|
|
22879
22937
|
activeStreams = new Map;
|
|
22880
22938
|
pendingUnsubscribes = new Map;
|
|
22881
|
-
streamScopes = new Map;
|
|
22882
22939
|
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
22940
|
+
storeKey(viewName, scope) {
|
|
22941
|
+
return `${scope ?? DEFAULT_SCOPE2}:${viewName}`;
|
|
22942
|
+
}
|
|
22883
22943
|
registerViews(views) {
|
|
22884
22944
|
this.views = views;
|
|
22885
|
-
for (const view3 of views) {
|
|
22886
|
-
if (!this.stores.has(view3.name)) {
|
|
22887
|
-
this.stores.set(view3.name, new StreamingStore2);
|
|
22888
|
-
}
|
|
22889
|
-
}
|
|
22890
22945
|
}
|
|
22891
|
-
getStore(viewName) {
|
|
22892
|
-
|
|
22893
|
-
|
|
22946
|
+
getStore(viewName, scope) {
|
|
22947
|
+
const key = this.storeKey(viewName, scope);
|
|
22948
|
+
if (!this.stores.has(key)) {
|
|
22949
|
+
this.stores.set(key, new StreamingStore2);
|
|
22894
22950
|
}
|
|
22895
|
-
return this.stores.get(
|
|
22951
|
+
return this.stores.get(key);
|
|
22896
22952
|
}
|
|
22897
|
-
hasData(viewName) {
|
|
22898
|
-
const store = this.stores.get(viewName);
|
|
22953
|
+
hasData(viewName, scope) {
|
|
22954
|
+
const store = this.stores.get(this.storeKey(viewName, scope));
|
|
22899
22955
|
return store ? store.hasData() : false;
|
|
22900
22956
|
}
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
}
|
|
22904
|
-
registerStream(viewName, createStream) {
|
|
22905
|
-
const pending = this.pendingUnsubscribes.get(viewName);
|
|
22957
|
+
registerStream(key, createStream) {
|
|
22958
|
+
const pending = this.pendingUnsubscribes.get(key);
|
|
22906
22959
|
if (pending) {
|
|
22907
22960
|
clearTimeout(pending);
|
|
22908
|
-
this.pendingUnsubscribes.delete(
|
|
22961
|
+
this.pendingUnsubscribes.delete(key);
|
|
22909
22962
|
}
|
|
22910
|
-
const existing = this.activeStreams.get(
|
|
22963
|
+
const existing = this.activeStreams.get(key);
|
|
22911
22964
|
if (existing) {
|
|
22912
22965
|
existing.refCount++;
|
|
22913
22966
|
return {
|
|
22914
|
-
unsubscribe: () => this.unregisterStream(
|
|
22967
|
+
unsubscribe: () => this.unregisterStream(key),
|
|
22915
22968
|
wasReused: true
|
|
22916
22969
|
};
|
|
22917
22970
|
}
|
|
22918
22971
|
const streamConn = createStream();
|
|
22919
|
-
this.activeStreams.set(
|
|
22972
|
+
this.activeStreams.set(key, {
|
|
22920
22973
|
unsubscribe: streamConn.unsubscribe,
|
|
22921
22974
|
refCount: 1
|
|
22922
22975
|
});
|
|
22923
22976
|
return {
|
|
22924
|
-
unsubscribe: () => this.unregisterStream(
|
|
22977
|
+
unsubscribe: () => this.unregisterStream(key),
|
|
22925
22978
|
wasReused: false
|
|
22926
22979
|
};
|
|
22927
22980
|
}
|
|
22928
|
-
unregisterStream(
|
|
22929
|
-
const stream2 = this.activeStreams.get(
|
|
22981
|
+
unregisterStream(key) {
|
|
22982
|
+
const stream2 = this.activeStreams.get(key);
|
|
22930
22983
|
if (!stream2)
|
|
22931
22984
|
return;
|
|
22932
22985
|
stream2.refCount--;
|
|
22933
22986
|
if (stream2.refCount <= 0) {
|
|
22934
22987
|
const timeout = setTimeout(() => {
|
|
22935
|
-
this.pendingUnsubscribes.delete(
|
|
22936
|
-
const current22 = this.activeStreams.get(
|
|
22988
|
+
this.pendingUnsubscribes.delete(key);
|
|
22989
|
+
const current22 = this.activeStreams.get(key);
|
|
22937
22990
|
if (current22 && current22.refCount <= 0) {
|
|
22938
22991
|
current22.unsubscribe();
|
|
22939
|
-
this.activeStreams.delete(
|
|
22940
|
-
this.streamScopes.delete(viewName);
|
|
22992
|
+
this.activeStreams.delete(key);
|
|
22941
22993
|
}
|
|
22942
22994
|
}, StreamingQueryCache2.UNSUBSCRIBE_DELAY_MS);
|
|
22943
|
-
this.pendingUnsubscribes.set(
|
|
22995
|
+
this.pendingUnsubscribes.set(key, timeout);
|
|
22944
22996
|
}
|
|
22945
22997
|
}
|
|
22946
|
-
|
|
22947
|
-
const key =
|
|
22948
|
-
if (scope)
|
|
22949
|
-
this.streamScopes.set(key, scope);
|
|
22998
|
+
subscribeView(viewName, eventWire, scope) {
|
|
22999
|
+
const key = this.storeKey(viewName, scope);
|
|
22950
23000
|
const { unsubscribe } = this.registerStream(key, () => {
|
|
22951
|
-
const
|
|
22952
|
-
|
|
22953
|
-
|
|
22954
|
-
|
|
23001
|
+
const store = this.stores.get(key) ?? new StreamingStore2;
|
|
23002
|
+
this.stores.set(key, store);
|
|
23003
|
+
eventWire.subscribeView(viewName, scope ?? DEFAULT_SCOPE2, {
|
|
23004
|
+
onSnapshot: (items) => store.setAll(items),
|
|
23005
|
+
onChanges: (changes) => store.applyChanges(changes)
|
|
23006
|
+
});
|
|
23007
|
+
return {
|
|
23008
|
+
unsubscribe: () => eventWire.unsubscribeView(viewName, scope ?? DEFAULT_SCOPE2)
|
|
23009
|
+
};
|
|
22955
23010
|
});
|
|
22956
23011
|
return unsubscribe;
|
|
22957
23012
|
}
|
|
22958
23013
|
invalidateScope(scope) {
|
|
22959
|
-
|
|
22960
|
-
|
|
23014
|
+
const prefix = `${scope}:`;
|
|
23015
|
+
for (const [key, timeout] of this.pendingUnsubscribes) {
|
|
23016
|
+
if (!key.startsWith(prefix))
|
|
22961
23017
|
continue;
|
|
22962
|
-
|
|
22963
|
-
|
|
22964
|
-
clearTimeout(pending);
|
|
22965
|
-
this.pendingUnsubscribes.delete(viewName);
|
|
22966
|
-
}
|
|
22967
|
-
const stream2 = this.activeStreams.get(viewName);
|
|
22968
|
-
if (stream2) {
|
|
22969
|
-
try {
|
|
22970
|
-
stream2.unsubscribe();
|
|
22971
|
-
} catch {}
|
|
22972
|
-
this.activeStreams.delete(viewName);
|
|
22973
|
-
}
|
|
22974
|
-
this.streamScopes.delete(viewName);
|
|
22975
|
-
const store = this.stores.get(viewName);
|
|
22976
|
-
if (store)
|
|
22977
|
-
store.clear();
|
|
23018
|
+
clearTimeout(timeout);
|
|
23019
|
+
this.pendingUnsubscribes.delete(key);
|
|
22978
23020
|
}
|
|
22979
|
-
|
|
22980
|
-
|
|
22981
|
-
|
|
22982
|
-
|
|
22983
|
-
|
|
22984
|
-
|
|
22985
|
-
|
|
22986
|
-
}
|
|
22987
|
-
|
|
22988
|
-
|
|
22989
|
-
|
|
23021
|
+
for (const [key, stream2] of this.activeStreams) {
|
|
23022
|
+
if (!key.startsWith(prefix))
|
|
23023
|
+
continue;
|
|
23024
|
+
try {
|
|
23025
|
+
stream2.unsubscribe();
|
|
23026
|
+
} catch {}
|
|
23027
|
+
this.activeStreams.delete(key);
|
|
23028
|
+
}
|
|
23029
|
+
for (const [key, store] of this.stores) {
|
|
23030
|
+
if (!key.startsWith(prefix))
|
|
23031
|
+
continue;
|
|
23032
|
+
store.clear();
|
|
22990
23033
|
}
|
|
22991
23034
|
}
|
|
22992
23035
|
async applyEvent(event3) {
|
|
@@ -22995,28 +23038,30 @@ class StreamingQueryCache2 {
|
|
|
22995
23038
|
const handler = handlers[event3.type];
|
|
22996
23039
|
if (!handler)
|
|
22997
23040
|
continue;
|
|
22998
|
-
const
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
|
|
23009
|
-
|
|
23010
|
-
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23016
|
-
|
|
23017
|
-
|
|
23018
|
-
|
|
23019
|
-
|
|
23041
|
+
const suffix = `:${view3.name}`;
|
|
23042
|
+
for (const [key, store] of this.stores) {
|
|
23043
|
+
if (!key.endsWith(suffix))
|
|
23044
|
+
continue;
|
|
23045
|
+
const ctx = {
|
|
23046
|
+
set: async (id3, data) => {
|
|
23047
|
+
store.set(String(id3), { _id: String(id3), ...data });
|
|
23048
|
+
},
|
|
23049
|
+
modify: async (id3, data) => {
|
|
23050
|
+
store.modify(String(id3), data);
|
|
23051
|
+
},
|
|
23052
|
+
remove: async (id3) => {
|
|
23053
|
+
store.remove(String(id3));
|
|
23054
|
+
},
|
|
23055
|
+
find: async (options) => {
|
|
23056
|
+
return store.find(options);
|
|
23057
|
+
},
|
|
23058
|
+
findOne: async (where) => {
|
|
23059
|
+
return store.findOne(where);
|
|
23060
|
+
},
|
|
23061
|
+
$auth: {}
|
|
23062
|
+
};
|
|
23063
|
+
await handler(ctx, event3);
|
|
23064
|
+
}
|
|
23020
23065
|
}
|
|
23021
23066
|
}
|
|
23022
23067
|
clear() {
|
|
@@ -23024,7 +23069,6 @@ class StreamingQueryCache2 {
|
|
|
23024
23069
|
stream2.unsubscribe();
|
|
23025
23070
|
}
|
|
23026
23071
|
this.activeStreams.clear();
|
|
23027
|
-
this.streamScopes.clear();
|
|
23028
23072
|
for (const timeout of this.pendingUnsubscribes.values()) {
|
|
23029
23073
|
clearTimeout(timeout);
|
|
23030
23074
|
}
|
|
@@ -23050,6 +23094,18 @@ class StreamingStore2 {
|
|
|
23050
23094
|
}
|
|
23051
23095
|
this.notifyListeners(null);
|
|
23052
23096
|
}
|
|
23097
|
+
applyChanges(events) {
|
|
23098
|
+
if (events.length === 0)
|
|
23099
|
+
return;
|
|
23100
|
+
for (const event3 of events) {
|
|
23101
|
+
if (event3.type === "set" && event3.item) {
|
|
23102
|
+
this.data.set(event3.id, event3.item);
|
|
23103
|
+
} else if (event3.type === "delete") {
|
|
23104
|
+
this.data.delete(event3.id);
|
|
23105
|
+
}
|
|
23106
|
+
}
|
|
23107
|
+
this.notifyListeners(events);
|
|
23108
|
+
}
|
|
23053
23109
|
set(id3, item) {
|
|
23054
23110
|
this.data.set(id3, item);
|
|
23055
23111
|
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
@@ -23993,7 +24049,7 @@ var Operation2, PROXY_DRAFT2, RAW_RETURN_SYMBOL2, iteratorSymbol2, dataTypes2, i
|
|
|
23993
24049
|
}
|
|
23994
24050
|
return returnValue(result);
|
|
23995
24051
|
};
|
|
23996
|
-
}, create2, constructorString2, TOKEN_PREFIX2 = "arc:token:", eventWireInstanceCounter2 = 0, EVENT_TABLES2, arrayValidator2, ArcArray2, objectValidator2, ArcObject2, ScopedDataStorage2, ArcPrimitive2, stringValidator2, ArcString2, ArcContextElement2, ArcBoolean2, ArcId2, numberValidator2, ArcNumber2, ArcEvent2, ArcCommand2, ArcListener2, ArcRoute2, ForkedStoreState2, ForkedDataStorage2, MasterStoreState2, MasterDataStorage3, dateValidator2, SQLiteReadWriteTransaction, createSQLiteAdapterFactory = (db) => {
|
|
24052
|
+
}, create2, constructorString2, TOKEN_PREFIX2 = "arc:token:", eventWireInstanceCounter2 = 0, EVENT_TABLES2, arrayValidator2, ArcArray2, objectValidator2, ArcObject2, ScopedDataStorage2, ArcPrimitive2, stringValidator2, ArcString2, ArcContextElement2, ArcBoolean2, ArcId2, numberValidator2, ArcNumber2, ArcEvent2, ArcCommand2, ArcListener2, ArcRoute2, ForkedStoreState2, ForkedDataStorage2, MasterStoreState2, MasterDataStorage3, dateValidator2, DEFAULT_SCOPE2 = "default", SQLiteReadWriteTransaction, createSQLiteAdapterFactory = (db) => {
|
|
23997
24053
|
return async (context) => {
|
|
23998
24054
|
const adapter = new SQLiteAdapter(db, context);
|
|
23999
24055
|
await adapter.initialize();
|
|
@@ -25551,12 +25607,23 @@ var init_dist2 = __esm(() => {
|
|
|
25551
25607
|
constructor(storeName, dataStorage, deserialize) {
|
|
25552
25608
|
super(storeName, dataStorage, deserialize);
|
|
25553
25609
|
}
|
|
25554
|
-
async
|
|
25610
|
+
async readExisting(transaction, id22, transactionCache) {
|
|
25611
|
+
const cacheKey = `${this.storeName}:${id22}`;
|
|
25612
|
+
if (transactionCache && transactionCache.has(cacheKey)) {
|
|
25613
|
+
return transactionCache.get(cacheKey);
|
|
25614
|
+
}
|
|
25615
|
+
return transaction.find(this.storeName, { where: { _id: id22 } }).then((results) => results[0]);
|
|
25616
|
+
}
|
|
25617
|
+
async applyChangeAndReturnEvent(transaction, change, transactionCache, options) {
|
|
25555
25618
|
if (change.type === "set") {
|
|
25619
|
+
let existing;
|
|
25620
|
+
if (options?.captureRows) {
|
|
25621
|
+
existing = await this.readExisting(transaction, change.data._id, transactionCache);
|
|
25622
|
+
}
|
|
25556
25623
|
await transaction.set(this.storeName, change.data);
|
|
25557
25624
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
25558
25625
|
if (transactionCache) {
|
|
25559
|
-
transactionCache.set(change.data._id
|
|
25626
|
+
transactionCache.set(`${this.storeName}:${change.data._id}`, item);
|
|
25560
25627
|
}
|
|
25561
25628
|
return {
|
|
25562
25629
|
from: null,
|
|
@@ -25565,11 +25632,20 @@ var init_dist2 = __esm(() => {
|
|
|
25565
25632
|
type: "set",
|
|
25566
25633
|
item: change.data,
|
|
25567
25634
|
id: change.data._id
|
|
25568
|
-
}
|
|
25635
|
+
},
|
|
25636
|
+
oldRow: existing ?? null,
|
|
25637
|
+
newRow: change.data
|
|
25569
25638
|
};
|
|
25570
25639
|
}
|
|
25571
25640
|
if (change.type === "delete") {
|
|
25641
|
+
let existing;
|
|
25642
|
+
if (options?.captureRows) {
|
|
25643
|
+
existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
25644
|
+
}
|
|
25572
25645
|
await transaction.remove(this.storeName, change.id);
|
|
25646
|
+
if (transactionCache) {
|
|
25647
|
+
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
25648
|
+
}
|
|
25573
25649
|
return {
|
|
25574
25650
|
from: null,
|
|
25575
25651
|
to: null,
|
|
@@ -25577,21 +25653,18 @@ var init_dist2 = __esm(() => {
|
|
|
25577
25653
|
type: "delete",
|
|
25578
25654
|
item: null,
|
|
25579
25655
|
id: change.id
|
|
25580
|
-
}
|
|
25656
|
+
},
|
|
25657
|
+
oldRow: existing ?? null,
|
|
25658
|
+
newRow: null
|
|
25581
25659
|
};
|
|
25582
25660
|
}
|
|
25583
25661
|
if (change.type === "modify") {
|
|
25584
|
-
|
|
25585
|
-
if (transactionCache && transactionCache.has(change.id)) {
|
|
25586
|
-
existing = transactionCache.get(change.id);
|
|
25587
|
-
} else {
|
|
25588
|
-
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
25589
|
-
}
|
|
25662
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
25590
25663
|
const updated = existing ? deepMerge2(existing, change.data) : { _id: change.id, ...change.data };
|
|
25591
25664
|
await transaction.set(this.storeName, updated);
|
|
25592
25665
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
25593
25666
|
if (transactionCache) {
|
|
25594
|
-
transactionCache.set(change.id
|
|
25667
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
25595
25668
|
}
|
|
25596
25669
|
return {
|
|
25597
25670
|
from: null,
|
|
@@ -25600,21 +25673,18 @@ var init_dist2 = __esm(() => {
|
|
|
25600
25673
|
type: "set",
|
|
25601
25674
|
item,
|
|
25602
25675
|
id: change.id
|
|
25603
|
-
}
|
|
25676
|
+
},
|
|
25677
|
+
oldRow: existing ?? null,
|
|
25678
|
+
newRow: updated
|
|
25604
25679
|
};
|
|
25605
25680
|
}
|
|
25606
25681
|
if (change.type === "mutate") {
|
|
25607
|
-
|
|
25608
|
-
if (transactionCache && transactionCache.has(change.id)) {
|
|
25609
|
-
existing = transactionCache.get(change.id);
|
|
25610
|
-
} else {
|
|
25611
|
-
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
25612
|
-
}
|
|
25682
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
25613
25683
|
const updated = apply2(existing || {}, change.patches);
|
|
25614
25684
|
await transaction.set(this.storeName, updated);
|
|
25615
25685
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
25616
25686
|
if (transactionCache) {
|
|
25617
|
-
transactionCache.set(change.id
|
|
25687
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
25618
25688
|
}
|
|
25619
25689
|
return {
|
|
25620
25690
|
from: null,
|
|
@@ -25623,7 +25693,9 @@ var init_dist2 = __esm(() => {
|
|
|
25623
25693
|
type: "set",
|
|
25624
25694
|
item,
|
|
25625
25695
|
id: change.id
|
|
25626
|
-
}
|
|
25696
|
+
},
|
|
25697
|
+
oldRow: existing ?? null,
|
|
25698
|
+
newRow: updated
|
|
25627
25699
|
};
|
|
25628
25700
|
}
|
|
25629
25701
|
throw new Error("Unknown change type");
|
|
@@ -25700,17 +25772,22 @@ var init_dist2 = __esm(() => {
|
|
|
25700
25772
|
applySerializedChanges(changes) {
|
|
25701
25773
|
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
25702
25774
|
}
|
|
25703
|
-
async commitChanges(changes) {
|
|
25775
|
+
async commitChanges(changes, options) {
|
|
25704
25776
|
const transaction = await this.getReadWriteTransaction();
|
|
25705
25777
|
const transactionCache = new Map;
|
|
25706
25778
|
const eventsByStore = new Map;
|
|
25779
|
+
const committed = [];
|
|
25707
25780
|
for (const { store, changes: storeChanges } of changes) {
|
|
25708
25781
|
const storeState = this.getStore(store);
|
|
25709
25782
|
const storeEvents = [];
|
|
25783
|
+
const capture = options?.captureRowsFor?.has(store) ?? false;
|
|
25710
25784
|
for (const change of storeChanges) {
|
|
25711
|
-
const { event: event3 } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
25785
|
+
const { event: event3, oldRow, newRow } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache, { captureRows: capture });
|
|
25712
25786
|
if (event3)
|
|
25713
25787
|
storeEvents.push(event3);
|
|
25788
|
+
if (capture) {
|
|
25789
|
+
committed.push({ store, id: event3.id, oldRow, newRow });
|
|
25790
|
+
}
|
|
25714
25791
|
}
|
|
25715
25792
|
if (storeEvents.length > 0) {
|
|
25716
25793
|
eventsByStore.set(store, storeEvents);
|
|
@@ -25721,6 +25798,7 @@ var init_dist2 = __esm(() => {
|
|
|
25721
25798
|
const storeState = this.getStore(store);
|
|
25722
25799
|
storeState.notifyListenersPublic(events);
|
|
25723
25800
|
}
|
|
25801
|
+
return committed;
|
|
25724
25802
|
}
|
|
25725
25803
|
fork() {
|
|
25726
25804
|
return new ForkedDataStorage2(this);
|
|
@@ -39950,88 +40028,6 @@ function queryHandler(ch) {
|
|
|
39950
40028
|
}
|
|
39951
40029
|
};
|
|
39952
40030
|
}
|
|
39953
|
-
var streamIdCounter = 0;
|
|
39954
|
-
var streamConnections = new Map;
|
|
39955
|
-
function streamHandler(ch) {
|
|
39956
|
-
return (_req, url, ctx) => {
|
|
39957
|
-
if (!url.pathname.startsWith("/stream/") || _req.method !== "GET")
|
|
39958
|
-
return null;
|
|
39959
|
-
const viewName = url.pathname.split("/stream/")[1];
|
|
39960
|
-
if (!viewName) {
|
|
39961
|
-
return new Response("Invalid stream path", {
|
|
39962
|
-
status: 400,
|
|
39963
|
-
headers: ctx.corsHeaders
|
|
39964
|
-
});
|
|
39965
|
-
}
|
|
39966
|
-
const findOptions = {};
|
|
39967
|
-
const whereParam = url.searchParams.get("where");
|
|
39968
|
-
if (whereParam) {
|
|
39969
|
-
try {
|
|
39970
|
-
findOptions.where = JSON.parse(whereParam);
|
|
39971
|
-
} catch {
|
|
39972
|
-
return new Response("Invalid 'where' parameter", {
|
|
39973
|
-
status: 400,
|
|
39974
|
-
headers: ctx.corsHeaders
|
|
39975
|
-
});
|
|
39976
|
-
}
|
|
39977
|
-
}
|
|
39978
|
-
const orderByParam = url.searchParams.get("orderBy");
|
|
39979
|
-
if (orderByParam) {
|
|
39980
|
-
try {
|
|
39981
|
-
findOptions.orderBy = JSON.parse(orderByParam);
|
|
39982
|
-
} catch {
|
|
39983
|
-
return new Response("Invalid 'orderBy' parameter", {
|
|
39984
|
-
status: 400,
|
|
39985
|
-
headers: ctx.corsHeaders
|
|
39986
|
-
});
|
|
39987
|
-
}
|
|
39988
|
-
}
|
|
39989
|
-
const limitParam = url.searchParams.get("limit");
|
|
39990
|
-
if (limitParam)
|
|
39991
|
-
findOptions.limit = parseInt(limitParam, 10);
|
|
39992
|
-
const streamId = `stream_${++streamIdCounter}_${Date.now()}`;
|
|
39993
|
-
const rawToken = ctx.rawToken;
|
|
39994
|
-
const stream2 = new ReadableStream({
|
|
39995
|
-
start(controller) {
|
|
39996
|
-
const scoped = new ScopedModel2(ch.getModel(), "stream");
|
|
39997
|
-
if (rawToken)
|
|
39998
|
-
scoped.setToken(rawToken);
|
|
39999
|
-
const descriptor = { element: viewName, method: "find", args: [findOptions] };
|
|
40000
|
-
const sendData = async () => {
|
|
40001
|
-
try {
|
|
40002
|
-
const data = await scoped.callQuery(descriptor);
|
|
40003
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({ type: "data", data })}
|
|
40004
|
-
|
|
40005
|
-
`));
|
|
40006
|
-
} catch {
|
|
40007
|
-
unsubscribe();
|
|
40008
|
-
}
|
|
40009
|
-
};
|
|
40010
|
-
sendData();
|
|
40011
|
-
const unsubscribe = ch.getEventPublisher().subscribe("*", () => sendData());
|
|
40012
|
-
streamConnections.set(streamId, { id: streamId, controller, unsubscribe });
|
|
40013
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({ type: "connected", streamId })}
|
|
40014
|
-
|
|
40015
|
-
`));
|
|
40016
|
-
},
|
|
40017
|
-
cancel() {
|
|
40018
|
-
const conn = streamConnections.get(streamId);
|
|
40019
|
-
if (conn) {
|
|
40020
|
-
conn.unsubscribe();
|
|
40021
|
-
streamConnections.delete(streamId);
|
|
40022
|
-
}
|
|
40023
|
-
}
|
|
40024
|
-
});
|
|
40025
|
-
return new Response(stream2, {
|
|
40026
|
-
headers: {
|
|
40027
|
-
...ctx.corsHeaders,
|
|
40028
|
-
"Content-Type": "text/event-stream",
|
|
40029
|
-
"Cache-Control": "no-cache",
|
|
40030
|
-
Connection: "keep-alive"
|
|
40031
|
-
}
|
|
40032
|
-
});
|
|
40033
|
-
};
|
|
40034
|
-
}
|
|
40035
40031
|
function eventSyncHandler(ch) {
|
|
40036
40032
|
return async (req, url, ctx) => {
|
|
40037
40033
|
if (url.pathname !== "/sync/events" || req.method !== "POST")
|
|
@@ -40132,25 +40128,19 @@ function arcHttpHandlers(ch, cm) {
|
|
|
40132
40128
|
healthHandler(cm),
|
|
40133
40129
|
commandHandler(ch),
|
|
40134
40130
|
queryHandler(ch),
|
|
40135
|
-
streamHandler(ch),
|
|
40136
40131
|
eventSyncHandler(ch),
|
|
40137
40132
|
routeHandler(ch)
|
|
40138
40133
|
];
|
|
40139
40134
|
}
|
|
40140
|
-
function cleanupStreams() {
|
|
40141
|
-
for (const conn of streamConnections.values())
|
|
40142
|
-
conn.unsubscribe();
|
|
40143
|
-
streamConnections.clear();
|
|
40144
|
-
}
|
|
40145
40135
|
// ../host/src/middleware/ws.ts
|
|
40146
|
-
import {
|
|
40147
|
-
var
|
|
40136
|
+
import { LiveQuery } from "@arcote.tech/arc";
|
|
40137
|
+
var clientQuerySubs = new Map;
|
|
40148
40138
|
function cleanupClientSubs(clientId) {
|
|
40149
|
-
const subs =
|
|
40139
|
+
const subs = clientQuerySubs.get(clientId);
|
|
40150
40140
|
if (subs) {
|
|
40151
|
-
for (const
|
|
40152
|
-
|
|
40153
|
-
|
|
40141
|
+
for (const live of subs.values())
|
|
40142
|
+
live.stop();
|
|
40143
|
+
clientQuerySubs.delete(clientId);
|
|
40154
40144
|
}
|
|
40155
40145
|
}
|
|
40156
40146
|
function scopeAuthHandler() {
|
|
@@ -40176,6 +40166,8 @@ function syncEventsHandler() {
|
|
|
40176
40166
|
for (const c2 of ctx.connectionManager.getAllClients()) {
|
|
40177
40167
|
if (c2.id === client.id)
|
|
40178
40168
|
continue;
|
|
40169
|
+
if (!c2.wantsEventSync)
|
|
40170
|
+
continue;
|
|
40179
40171
|
const clientTokens = ctx.connectionManager.getAllScopeTokens(c2.id);
|
|
40180
40172
|
const authorized = filterEventsForTokens(clientTokens, persisted, ctx.contextHandler.getEventDefinitions());
|
|
40181
40173
|
if (authorized.length > 0) {
|
|
@@ -40198,6 +40190,7 @@ function requestSyncHandler() {
|
|
|
40198
40190
|
return async (client, message, ctx) => {
|
|
40199
40191
|
if (message.type !== "request-sync")
|
|
40200
40192
|
return false;
|
|
40193
|
+
client.wantsEventSync = true;
|
|
40201
40194
|
const allTokens = ctx.connectionManager.getAllScopeTokens(client.id);
|
|
40202
40195
|
const token = allTokens.length > 0 ? allTokens[0] : null;
|
|
40203
40196
|
const events = await ctx.contextHandler.getEventsSince(message.lastHostEventId, token);
|
|
@@ -40249,70 +40242,58 @@ function executeCommandHandler() {
|
|
|
40249
40242
|
function querySubscriptionHandler() {
|
|
40250
40243
|
return async (client, message, ctx) => {
|
|
40251
40244
|
if (message.type === "subscribe-query") {
|
|
40252
|
-
const { subscriptionId, descriptor
|
|
40253
|
-
const
|
|
40245
|
+
const { subscriptionId, descriptor } = message;
|
|
40246
|
+
const scope = message.scope ?? "default";
|
|
40247
|
+
const scopeToken = client.scopeTokens.get(scope) ?? null;
|
|
40254
40248
|
let rawToken = scopeToken?.raw ?? null;
|
|
40255
40249
|
if (!rawToken && client.scopeTokens.size > 0) {
|
|
40256
40250
|
rawToken = client.scopeTokens.values().next().value.raw;
|
|
40257
40251
|
}
|
|
40258
|
-
|
|
40259
|
-
|
|
40260
|
-
|
|
40261
|
-
let lastResultJson = "";
|
|
40262
|
-
const sendData = async () => {
|
|
40263
|
-
try {
|
|
40264
|
-
const data = await scoped.callQuery(descriptor);
|
|
40265
|
-
const json = JSON.stringify(data ?? null);
|
|
40266
|
-
if (json === lastResultJson)
|
|
40267
|
-
return;
|
|
40268
|
-
lastResultJson = json;
|
|
40252
|
+
clientQuerySubs.get(client.id)?.get(subscriptionId)?.stop();
|
|
40253
|
+
const live = new LiveQuery(ctx.contextHandler.getModel(), descriptor, scope, rawToken, (update) => {
|
|
40254
|
+
if (update.type === "changes") {
|
|
40269
40255
|
ctx.connectionManager.sendToClient(client.id, {
|
|
40270
|
-
type: "query-
|
|
40256
|
+
type: "query-changes",
|
|
40271
40257
|
subscriptionId,
|
|
40272
|
-
|
|
40258
|
+
changes: update.changes
|
|
40259
|
+
});
|
|
40260
|
+
} else {
|
|
40261
|
+
ctx.connectionManager.sendToClient(client.id, {
|
|
40262
|
+
type: "query-snapshot",
|
|
40263
|
+
subscriptionId,
|
|
40264
|
+
result: update.result ?? null
|
|
40273
40265
|
});
|
|
40274
|
-
} catch (err3) {
|
|
40275
|
-
console.error(`[Arc] Query subscription error:`, err3);
|
|
40276
|
-
}
|
|
40277
|
-
};
|
|
40278
|
-
sendData();
|
|
40279
|
-
const element = ctx.contextHandler.getModel().context.get(descriptor.element);
|
|
40280
|
-
const handlers = element?.getHandlers?.();
|
|
40281
|
-
const eventTypes = handlers ? Object.keys(handlers) : [];
|
|
40282
|
-
let debounceTimer;
|
|
40283
|
-
const debouncedSend = () => {
|
|
40284
|
-
if (debounceTimer)
|
|
40285
|
-
clearTimeout(debounceTimer);
|
|
40286
|
-
debounceTimer = setTimeout(() => sendData(), 50);
|
|
40287
|
-
};
|
|
40288
|
-
const unsubscribes = [];
|
|
40289
|
-
if (eventTypes.length > 0) {
|
|
40290
|
-
for (const eventType of eventTypes) {
|
|
40291
|
-
unsubscribes.push(ctx.contextHandler.getEventPublisher().subscribe(eventType, debouncedSend));
|
|
40292
40266
|
}
|
|
40293
|
-
}
|
|
40294
|
-
|
|
40267
|
+
});
|
|
40268
|
+
if (!clientQuerySubs.has(client.id)) {
|
|
40269
|
+
clientQuerySubs.set(client.id, new Map);
|
|
40295
40270
|
}
|
|
40296
|
-
|
|
40297
|
-
|
|
40298
|
-
|
|
40299
|
-
|
|
40300
|
-
|
|
40301
|
-
|
|
40302
|
-
|
|
40303
|
-
|
|
40271
|
+
clientQuerySubs.get(client.id).set(subscriptionId, live);
|
|
40272
|
+
try {
|
|
40273
|
+
const result = await live.start();
|
|
40274
|
+
ctx.connectionManager.sendToClient(client.id, {
|
|
40275
|
+
type: "query-snapshot",
|
|
40276
|
+
subscriptionId,
|
|
40277
|
+
result: result ?? null
|
|
40278
|
+
});
|
|
40279
|
+
live.flush();
|
|
40280
|
+
} catch (err3) {
|
|
40281
|
+
live.stop();
|
|
40282
|
+
clientQuerySubs.get(client.id)?.delete(subscriptionId);
|
|
40283
|
+
console.error(`[Arc] Query subscription error (${descriptor?.element}.${descriptor?.method}):`, err3);
|
|
40284
|
+
ctx.connectionManager.sendToClient(client.id, {
|
|
40285
|
+
type: "error",
|
|
40286
|
+
message: `Failed to subscribe to query "${descriptor?.element}.${descriptor?.method}"`
|
|
40287
|
+
});
|
|
40304
40288
|
}
|
|
40305
|
-
clientViewSubs.get(client.id).set(subscriptionId, cleanup);
|
|
40306
40289
|
return true;
|
|
40307
40290
|
}
|
|
40308
40291
|
if (message.type === "unsubscribe-query") {
|
|
40309
|
-
const subs =
|
|
40310
|
-
|
|
40311
|
-
|
|
40312
|
-
|
|
40313
|
-
|
|
40314
|
-
subs.delete(message.subscriptionId);
|
|
40315
|
-
}
|
|
40292
|
+
const subs = clientQuerySubs.get(client.id);
|
|
40293
|
+
const live = subs?.get(message.subscriptionId);
|
|
40294
|
+
if (live) {
|
|
40295
|
+
live.stop();
|
|
40296
|
+
subs.delete(message.subscriptionId);
|
|
40316
40297
|
}
|
|
40317
40298
|
return true;
|
|
40318
40299
|
}
|
|
@@ -40508,7 +40489,6 @@ async function createArcServer(config) {
|
|
|
40508
40489
|
cronScheduler,
|
|
40509
40490
|
stop: () => {
|
|
40510
40491
|
cronScheduler.stop();
|
|
40511
|
-
cleanupStreams();
|
|
40512
40492
|
server.stop();
|
|
40513
40493
|
}
|
|
40514
40494
|
};
|