@arcote.tech/arc-cli 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/index.js +642 -776
- package/package.json +9 -9
- package/src/builder/chunk-planner.ts +92 -8
- package/src/platform/server.ts +14 -11
- package/src/platform/shared.ts +5 -1
package/dist/index.js
CHANGED
|
@@ -14017,12 +14017,13 @@ class EventWire {
|
|
|
14017
14017
|
onSyncedCallback;
|
|
14018
14018
|
reconnectTimeout;
|
|
14019
14019
|
syncRequested = false;
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
constructor(baseUrl) {
|
|
14020
|
+
querySubscriptions = new Map;
|
|
14021
|
+
querySubCounter = 0;
|
|
14022
|
+
enableEventSync;
|
|
14023
|
+
constructor(baseUrl, options) {
|
|
14024
14024
|
this.baseUrl = baseUrl;
|
|
14025
14025
|
this.instanceId = ++eventWireInstanceCounter;
|
|
14026
|
+
this.enableEventSync = options?.enableEventSync ?? true;
|
|
14026
14027
|
}
|
|
14027
14028
|
setScopeToken(scope, token) {
|
|
14028
14029
|
if (token === null) {
|
|
@@ -14068,9 +14069,11 @@ class EventWire {
|
|
|
14068
14069
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
14069
14070
|
this.state = "connected";
|
|
14070
14071
|
this.sendAllScopeTokens();
|
|
14071
|
-
this.
|
|
14072
|
+
if (this.enableEventSync) {
|
|
14073
|
+
this.requestSync();
|
|
14074
|
+
}
|
|
14072
14075
|
this.flushPendingEvents();
|
|
14073
|
-
this.
|
|
14076
|
+
this.sendAllQuerySubscriptions();
|
|
14074
14077
|
} else {
|
|
14075
14078
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
14076
14079
|
}
|
|
@@ -14146,9 +14149,13 @@ class EventWire {
|
|
|
14146
14149
|
onSynced(callback) {
|
|
14147
14150
|
this.onSyncedCallback = callback;
|
|
14148
14151
|
}
|
|
14149
|
-
subscribeQuery(descriptor,
|
|
14150
|
-
const subscriptionId = `qs_${
|
|
14151
|
-
this.
|
|
14152
|
+
subscribeQuery(descriptor, scope, callbacks) {
|
|
14153
|
+
const subscriptionId = `qs_${this.instanceId}_${++this.querySubCounter}`;
|
|
14154
|
+
this.querySubscriptions.set(subscriptionId, {
|
|
14155
|
+
descriptor,
|
|
14156
|
+
scope,
|
|
14157
|
+
callbacks
|
|
14158
|
+
});
|
|
14152
14159
|
if (this.state === "connected" && this.ws) {
|
|
14153
14160
|
this.ws.send(JSON.stringify({
|
|
14154
14161
|
type: "subscribe-query",
|
|
@@ -14156,20 +14163,17 @@ class EventWire {
|
|
|
14156
14163
|
descriptor,
|
|
14157
14164
|
scope
|
|
14158
14165
|
}));
|
|
14159
|
-
} else {
|
|
14160
|
-
this.pendingViewSubs.push({ subscriptionId, descriptor, scope });
|
|
14161
14166
|
}
|
|
14162
14167
|
return subscriptionId;
|
|
14163
14168
|
}
|
|
14164
14169
|
unsubscribeQuery(subscriptionId) {
|
|
14165
|
-
this.
|
|
14170
|
+
this.querySubscriptions.delete(subscriptionId);
|
|
14166
14171
|
if (this.state === "connected" && this.ws) {
|
|
14167
14172
|
this.ws.send(JSON.stringify({
|
|
14168
14173
|
type: "unsubscribe-query",
|
|
14169
14174
|
subscriptionId
|
|
14170
14175
|
}));
|
|
14171
14176
|
}
|
|
14172
|
-
this.pendingViewSubs = this.pendingViewSubs.filter((s) => s.subscriptionId !== subscriptionId);
|
|
14173
14177
|
}
|
|
14174
14178
|
getState() {
|
|
14175
14179
|
return this.state;
|
|
@@ -14202,10 +14206,17 @@ class EventWire {
|
|
|
14202
14206
|
this.lastHostEventId = message.lastHostEventId;
|
|
14203
14207
|
}
|
|
14204
14208
|
break;
|
|
14205
|
-
case "query-
|
|
14206
|
-
const
|
|
14207
|
-
if (
|
|
14208
|
-
|
|
14209
|
+
case "query-snapshot": {
|
|
14210
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
14211
|
+
if (sub) {
|
|
14212
|
+
sub.callbacks.onSnapshot(message.result ?? null);
|
|
14213
|
+
}
|
|
14214
|
+
break;
|
|
14215
|
+
}
|
|
14216
|
+
case "query-changes": {
|
|
14217
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
14218
|
+
if (sub && Array.isArray(message.changes)) {
|
|
14219
|
+
sub.callbacks.onChanges(message.changes);
|
|
14209
14220
|
}
|
|
14210
14221
|
break;
|
|
14211
14222
|
}
|
|
@@ -14233,18 +14244,17 @@ class EventWire {
|
|
|
14233
14244
|
this.pendingEvents = [];
|
|
14234
14245
|
}
|
|
14235
14246
|
}
|
|
14236
|
-
|
|
14247
|
+
sendAllQuerySubscriptions() {
|
|
14237
14248
|
if (!this.ws || this.state !== "connected")
|
|
14238
14249
|
return;
|
|
14239
|
-
for (const sub of this.
|
|
14250
|
+
for (const [subscriptionId, sub] of this.querySubscriptions) {
|
|
14240
14251
|
this.ws.send(JSON.stringify({
|
|
14241
14252
|
type: "subscribe-query",
|
|
14242
|
-
subscriptionId
|
|
14253
|
+
subscriptionId,
|
|
14243
14254
|
descriptor: sub.descriptor,
|
|
14244
14255
|
scope: sub.scope
|
|
14245
14256
|
}));
|
|
14246
14257
|
}
|
|
14247
|
-
this.pendingViewSubs = [];
|
|
14248
14258
|
}
|
|
14249
14259
|
scheduleReconnect() {
|
|
14250
14260
|
if (this.reconnectTimeout)
|
|
@@ -14720,7 +14730,7 @@ class DataStorage {
|
|
|
14720
14730
|
}
|
|
14721
14731
|
}
|
|
14722
14732
|
|
|
14723
|
-
class
|
|
14733
|
+
class ScopedStore {
|
|
14724
14734
|
#inner;
|
|
14725
14735
|
#restrictions;
|
|
14726
14736
|
#canWrite;
|
|
@@ -15073,6 +15083,47 @@ function deepMerge(target, source) {
|
|
|
15073
15083
|
function isPlainObject(item) {
|
|
15074
15084
|
return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
|
|
15075
15085
|
}
|
|
15086
|
+
function murmurHash(key, seed = 0) {
|
|
15087
|
+
let remainder, bytes, h1, h1b, c1, c2, k1, i;
|
|
15088
|
+
remainder = key.length & 3;
|
|
15089
|
+
bytes = key.length - remainder;
|
|
15090
|
+
h1 = seed;
|
|
15091
|
+
c1 = 3432918353;
|
|
15092
|
+
c2 = 461845907;
|
|
15093
|
+
i = 0;
|
|
15094
|
+
while (i < bytes) {
|
|
15095
|
+
k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
|
|
15096
|
+
++i;
|
|
15097
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
15098
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
15099
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
15100
|
+
h1 ^= k1;
|
|
15101
|
+
h1 = h1 << 13 | h1 >>> 19;
|
|
15102
|
+
h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
|
|
15103
|
+
h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
|
|
15104
|
+
}
|
|
15105
|
+
k1 = 0;
|
|
15106
|
+
if (remainder >= 3) {
|
|
15107
|
+
k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
|
|
15108
|
+
}
|
|
15109
|
+
if (remainder >= 2) {
|
|
15110
|
+
k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
|
|
15111
|
+
}
|
|
15112
|
+
if (remainder >= 1) {
|
|
15113
|
+
k1 ^= key.charCodeAt(i) & 255;
|
|
15114
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
15115
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
15116
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
15117
|
+
h1 ^= k1;
|
|
15118
|
+
}
|
|
15119
|
+
h1 ^= key.length;
|
|
15120
|
+
h1 ^= h1 >>> 16;
|
|
15121
|
+
h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
|
|
15122
|
+
h1 ^= h1 >>> 13;
|
|
15123
|
+
h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
|
|
15124
|
+
h1 ^= h1 >>> 16;
|
|
15125
|
+
return h1 >>> 0;
|
|
15126
|
+
}
|
|
15076
15127
|
function resolveQueryChange(currentResult, event3, options) {
|
|
15077
15128
|
const index = currentResult.findIndex((e2) => e2._id === event3.id);
|
|
15078
15129
|
const isInCurrentResult = index !== -1;
|
|
@@ -15082,7 +15133,7 @@ function resolveQueryChange(currentResult, event3, options) {
|
|
|
15082
15133
|
}
|
|
15083
15134
|
return false;
|
|
15084
15135
|
}
|
|
15085
|
-
const shouldBeInResult =
|
|
15136
|
+
const shouldBeInResult = checkItemMatchesWhere(event3.item, options.where);
|
|
15086
15137
|
if (isInCurrentResult && shouldBeInResult) {
|
|
15087
15138
|
const newResult = currentResult.toSpliced(index, 1, event3.item);
|
|
15088
15139
|
return applyOrderByAndLimit(newResult, options);
|
|
@@ -15094,7 +15145,7 @@ function resolveQueryChange(currentResult, event3, options) {
|
|
|
15094
15145
|
}
|
|
15095
15146
|
return false;
|
|
15096
15147
|
}
|
|
15097
|
-
function
|
|
15148
|
+
function checkItemMatchesWhere(item, where) {
|
|
15098
15149
|
if (!where) {
|
|
15099
15150
|
return true;
|
|
15100
15151
|
}
|
|
@@ -15188,7 +15239,8 @@ class ObservableDataStorage {
|
|
|
15188
15239
|
}
|
|
15189
15240
|
handleStoreChange(storeName, events) {
|
|
15190
15241
|
let hasChanges = false;
|
|
15191
|
-
|
|
15242
|
+
const staleKeys = [];
|
|
15243
|
+
for (const [key, query] of this.trackedQueries) {
|
|
15192
15244
|
if (query.storeName !== storeName)
|
|
15193
15245
|
continue;
|
|
15194
15246
|
let currentResult = query.result;
|
|
@@ -15200,10 +15252,20 @@ class ObservableDataStorage {
|
|
|
15200
15252
|
queryChanged = true;
|
|
15201
15253
|
}
|
|
15202
15254
|
}
|
|
15203
|
-
if (queryChanged)
|
|
15204
|
-
|
|
15255
|
+
if (!queryChanged)
|
|
15256
|
+
continue;
|
|
15257
|
+
if (query.options.limit !== undefined && query.result.length === query.options.limit && currentResult.length < query.options.limit) {
|
|
15258
|
+
staleKeys.push(key);
|
|
15205
15259
|
hasChanges = true;
|
|
15260
|
+
continue;
|
|
15206
15261
|
}
|
|
15262
|
+
query.result = currentResult;
|
|
15263
|
+
hasChanges = true;
|
|
15264
|
+
}
|
|
15265
|
+
for (const key of staleKeys) {
|
|
15266
|
+
const query = this.trackedQueries.get(key);
|
|
15267
|
+
this.source.getStore(query.storeName).unsubscribe(query.listener);
|
|
15268
|
+
this.trackedQueries.delete(key);
|
|
15207
15269
|
}
|
|
15208
15270
|
if (hasChanges) {
|
|
15209
15271
|
this.onChange();
|
|
@@ -15346,7 +15408,7 @@ function executeDescriptor(descriptor, context2, adapters, contextMethod, option
|
|
|
15346
15408
|
return method(...descriptor.args);
|
|
15347
15409
|
}
|
|
15348
15410
|
|
|
15349
|
-
class
|
|
15411
|
+
class ScopedModel3 {
|
|
15350
15412
|
context;
|
|
15351
15413
|
scopeName;
|
|
15352
15414
|
parent;
|
|
@@ -15418,13 +15480,6 @@ class ScopedModel4 {
|
|
|
15418
15480
|
}
|
|
15419
15481
|
return wire.query(viewName, options, this.getAuth());
|
|
15420
15482
|
}
|
|
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
15483
|
get query() {
|
|
15429
15484
|
return buildContextAccessor(this.context, this.scopedAdapters, "queryContext", (descriptor) => descriptor);
|
|
15430
15485
|
}
|
|
@@ -15474,251 +15529,142 @@ class Model2 {
|
|
|
15474
15529
|
scope(name) {
|
|
15475
15530
|
let s = this.scopes.get(name);
|
|
15476
15531
|
if (!s) {
|
|
15477
|
-
s = new
|
|
15532
|
+
s = new ScopedModel3(this, name);
|
|
15478
15533
|
this.scopes.set(name, s);
|
|
15479
15534
|
}
|
|
15480
15535
|
return s;
|
|
15481
15536
|
}
|
|
15482
15537
|
}
|
|
15483
|
-
|
|
15484
|
-
|
|
15485
|
-
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
15491
|
-
registerViews(views) {
|
|
15492
|
-
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
|
-
}
|
|
15538
|
+
function applyQueryChanges(result, changes) {
|
|
15539
|
+
const next = [...result];
|
|
15540
|
+
for (const change of changes) {
|
|
15541
|
+
if (change.type === "delete") {
|
|
15542
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
15543
|
+
if (idx !== -1)
|
|
15544
|
+
next.splice(idx, 1);
|
|
15497
15545
|
}
|
|
15498
15546
|
}
|
|
15499
|
-
|
|
15500
|
-
if (
|
|
15501
|
-
|
|
15547
|
+
for (const change of changes) {
|
|
15548
|
+
if (change.type === "set") {
|
|
15549
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
15550
|
+
if (idx !== -1)
|
|
15551
|
+
next.splice(idx, 1);
|
|
15552
|
+
next.splice(change.index, 0, change.item);
|
|
15502
15553
|
}
|
|
15503
|
-
return this.stores.get(viewName);
|
|
15504
|
-
}
|
|
15505
|
-
hasData(viewName) {
|
|
15506
|
-
const store = this.stores.get(viewName);
|
|
15507
|
-
return store ? store.hasData() : false;
|
|
15508
15554
|
}
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15555
|
+
return next;
|
|
15556
|
+
}
|
|
15557
|
+
|
|
15558
|
+
class StreamingQueryCache {
|
|
15559
|
+
entries = new Map;
|
|
15560
|
+
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
15561
|
+
entryKey(descriptor, scope) {
|
|
15562
|
+
return `${scope ?? "default"}:${murmurHash(JSON.stringify(descriptor))}`;
|
|
15563
|
+
}
|
|
15564
|
+
subscribe(descriptor, scope, eventWire, onChange) {
|
|
15565
|
+
const key = this.entryKey(descriptor, scope);
|
|
15566
|
+
let entry = this.entries.get(key);
|
|
15567
|
+
if (entry) {
|
|
15568
|
+
if (entry.pendingUnsub) {
|
|
15569
|
+
clearTimeout(entry.pendingUnsub);
|
|
15570
|
+
entry.pendingUnsub = undefined;
|
|
15571
|
+
}
|
|
15572
|
+
entry.refCount++;
|
|
15573
|
+
} else {
|
|
15574
|
+
const newEntry = {
|
|
15575
|
+
result: undefined,
|
|
15576
|
+
hasResult: false,
|
|
15577
|
+
listeners: new Set,
|
|
15578
|
+
refCount: 1,
|
|
15579
|
+
subscriptionId: ""
|
|
15524
15580
|
};
|
|
15581
|
+
newEntry.subscriptionId = eventWire.subscribeQuery(descriptor, scope, {
|
|
15582
|
+
onSnapshot: (result) => {
|
|
15583
|
+
newEntry.result = result;
|
|
15584
|
+
newEntry.hasResult = true;
|
|
15585
|
+
this.notify(newEntry);
|
|
15586
|
+
},
|
|
15587
|
+
onChanges: (changes) => {
|
|
15588
|
+
if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
|
|
15589
|
+
return;
|
|
15590
|
+
}
|
|
15591
|
+
newEntry.result = applyQueryChanges(newEntry.result, changes);
|
|
15592
|
+
this.notify(newEntry);
|
|
15593
|
+
}
|
|
15594
|
+
});
|
|
15595
|
+
this.entries.set(key, newEntry);
|
|
15596
|
+
entry = newEntry;
|
|
15525
15597
|
}
|
|
15526
|
-
const
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
refCount: 1
|
|
15530
|
-
});
|
|
15598
|
+
const subscribed = entry;
|
|
15599
|
+
subscribed.listeners.add(onChange);
|
|
15600
|
+
let active = true;
|
|
15531
15601
|
return {
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
this.pendingUnsubscribes.set(viewName, timeout);
|
|
15552
|
-
}
|
|
15553
|
-
}
|
|
15554
|
-
subscribeQuery(descriptor, eventWire, scope) {
|
|
15555
|
-
const key = descriptor.element;
|
|
15556
|
-
if (scope)
|
|
15557
|
-
this.streamScopes.set(key, scope);
|
|
15558
|
-
const { unsubscribe } = this.registerStream(key, () => {
|
|
15559
|
-
const subId = eventWire.subscribeQuery(descriptor, (data) => {
|
|
15560
|
-
this.setViewData(descriptor.element, data);
|
|
15561
|
-
}, scope);
|
|
15562
|
-
return { unsubscribe: () => eventWire.unsubscribeQuery(subId) };
|
|
15563
|
-
});
|
|
15564
|
-
return unsubscribe;
|
|
15565
|
-
}
|
|
15566
|
-
invalidateScope(scope) {
|
|
15567
|
-
for (const [viewName, viewScope] of this.streamScopes) {
|
|
15568
|
-
if (viewScope !== scope)
|
|
15569
|
-
continue;
|
|
15570
|
-
const pending = this.pendingUnsubscribes.get(viewName);
|
|
15571
|
-
if (pending) {
|
|
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);
|
|
15602
|
+
read: () => ({
|
|
15603
|
+
result: subscribed.result,
|
|
15604
|
+
loading: !subscribed.hasResult
|
|
15605
|
+
}),
|
|
15606
|
+
unsubscribe: () => {
|
|
15607
|
+
if (!active)
|
|
15608
|
+
return;
|
|
15609
|
+
active = false;
|
|
15610
|
+
subscribed.listeners.delete(onChange);
|
|
15611
|
+
subscribed.refCount--;
|
|
15612
|
+
if (subscribed.refCount > 0)
|
|
15613
|
+
return;
|
|
15614
|
+
subscribed.pendingUnsub = setTimeout(() => {
|
|
15615
|
+
subscribed.pendingUnsub = undefined;
|
|
15616
|
+
if (subscribed.refCount > 0)
|
|
15617
|
+
return;
|
|
15618
|
+
eventWire.unsubscribeQuery(subscribed.subscriptionId);
|
|
15619
|
+
this.entries.delete(key);
|
|
15620
|
+
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
15581
15621
|
}
|
|
15582
|
-
|
|
15583
|
-
const store = this.stores.get(viewName);
|
|
15584
|
-
if (store)
|
|
15585
|
-
store.clear();
|
|
15586
|
-
}
|
|
15587
|
-
}
|
|
15588
|
-
setViewData(viewName, data) {
|
|
15589
|
-
const store = this.stores.get(viewName);
|
|
15590
|
-
if (!store)
|
|
15591
|
-
return;
|
|
15592
|
-
if (Array.isArray(data)) {
|
|
15593
|
-
store.setAll(data);
|
|
15594
|
-
} else if (data && typeof data === "object" && "_id" in data) {
|
|
15595
|
-
store.setAll([data]);
|
|
15596
|
-
} else {
|
|
15597
|
-
store.setAll([]);
|
|
15598
|
-
}
|
|
15622
|
+
};
|
|
15599
15623
|
}
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
if (!handler)
|
|
15624
|
+
invalidateScope(scope, eventWire) {
|
|
15625
|
+
const prefix = `${scope}:`;
|
|
15626
|
+
for (const [key, entry] of this.entries) {
|
|
15627
|
+
if (!key.startsWith(prefix))
|
|
15605
15628
|
continue;
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
modify: async (id3, data) => {
|
|
15614
|
-
store.modify(String(id3), data);
|
|
15615
|
-
},
|
|
15616
|
-
remove: async (id3) => {
|
|
15617
|
-
store.remove(String(id3));
|
|
15618
|
-
},
|
|
15619
|
-
find: async (options) => {
|
|
15620
|
-
return store.find(options);
|
|
15621
|
-
},
|
|
15622
|
-
findOne: async (where) => {
|
|
15623
|
-
return store.findOne(where);
|
|
15624
|
-
},
|
|
15625
|
-
$auth: {}
|
|
15626
|
-
};
|
|
15627
|
-
await handler(ctx, event3);
|
|
15628
|
-
}
|
|
15629
|
-
}
|
|
15630
|
-
clear() {
|
|
15631
|
-
for (const stream2 of this.activeStreams.values()) {
|
|
15632
|
-
stream2.unsubscribe();
|
|
15633
|
-
}
|
|
15634
|
-
this.activeStreams.clear();
|
|
15635
|
-
this.streamScopes.clear();
|
|
15636
|
-
for (const timeout of this.pendingUnsubscribes.values()) {
|
|
15637
|
-
clearTimeout(timeout);
|
|
15638
|
-
}
|
|
15639
|
-
this.pendingUnsubscribes.clear();
|
|
15640
|
-
for (const store of this.stores.values()) {
|
|
15641
|
-
store.clear();
|
|
15629
|
+
if (entry.pendingUnsub)
|
|
15630
|
+
clearTimeout(entry.pendingUnsub);
|
|
15631
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
15632
|
+
this.entries.delete(key);
|
|
15633
|
+
entry.result = undefined;
|
|
15634
|
+
entry.hasResult = false;
|
|
15635
|
+
this.notify(entry);
|
|
15642
15636
|
}
|
|
15643
15637
|
}
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
initialized = false;
|
|
15650
|
-
hasData() {
|
|
15651
|
-
return this.initialized;
|
|
15652
|
-
}
|
|
15653
|
-
setAll(items) {
|
|
15654
|
-
this.initialized = true;
|
|
15655
|
-
this.data.clear();
|
|
15656
|
-
for (const item of items) {
|
|
15657
|
-
this.data.set(item._id, item);
|
|
15638
|
+
clear(eventWire) {
|
|
15639
|
+
for (const entry of this.entries.values()) {
|
|
15640
|
+
if (entry.pendingUnsub)
|
|
15641
|
+
clearTimeout(entry.pendingUnsub);
|
|
15642
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
15658
15643
|
}
|
|
15659
|
-
this.
|
|
15660
|
-
}
|
|
15661
|
-
set(id3, item) {
|
|
15662
|
-
this.data.set(id3, item);
|
|
15663
|
-
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
15664
|
-
}
|
|
15665
|
-
modify(id3, updates) {
|
|
15666
|
-
const existing = this.data.get(id3);
|
|
15667
|
-
if (existing) {
|
|
15668
|
-
const updated = { ...existing, ...updates };
|
|
15669
|
-
this.data.set(id3, updated);
|
|
15670
|
-
this.notifyListeners([{ type: "set", id: id3, item: updated }]);
|
|
15671
|
-
}
|
|
15672
|
-
}
|
|
15673
|
-
remove(id3) {
|
|
15674
|
-
if (this.data.delete(id3)) {
|
|
15675
|
-
this.notifyListeners([{ type: "delete", id: id3, item: null }]);
|
|
15676
|
-
}
|
|
15677
|
-
}
|
|
15678
|
-
clear() {
|
|
15679
|
-
this.initialized = false;
|
|
15680
|
-
this.data.clear();
|
|
15681
|
-
this.notifyListeners(null);
|
|
15682
|
-
}
|
|
15683
|
-
find(options = {}) {
|
|
15684
|
-
let results = Array.from(this.data.values());
|
|
15685
|
-
if (options.where) {
|
|
15686
|
-
results = results.filter((item) => checkItemMatchesWhere2(item, options.where));
|
|
15687
|
-
}
|
|
15688
|
-
return applyOrderByAndLimit(results, options);
|
|
15689
|
-
}
|
|
15690
|
-
findOne(where) {
|
|
15691
|
-
const results = this.find({ where });
|
|
15692
|
-
return results[0];
|
|
15693
|
-
}
|
|
15694
|
-
subscribe(listener4) {
|
|
15695
|
-
this.listeners.add(listener4);
|
|
15696
|
-
return () => {
|
|
15697
|
-
this.listeners.delete(listener4);
|
|
15698
|
-
};
|
|
15644
|
+
this.entries.clear();
|
|
15699
15645
|
}
|
|
15700
|
-
|
|
15701
|
-
for (const listener4 of
|
|
15702
|
-
|
|
15646
|
+
notify(entry) {
|
|
15647
|
+
for (const listener4 of entry.listeners) {
|
|
15648
|
+
try {
|
|
15649
|
+
listener4();
|
|
15650
|
+
} catch (err3) {
|
|
15651
|
+
console.error(`[Arc] Query cache listener error:`, err3);
|
|
15652
|
+
}
|
|
15703
15653
|
}
|
|
15704
15654
|
}
|
|
15705
15655
|
}
|
|
15706
15656
|
|
|
15707
15657
|
class StreamingEventPublisher {
|
|
15708
|
-
cache;
|
|
15709
15658
|
eventWire;
|
|
15710
15659
|
views = [];
|
|
15711
15660
|
subscribers = new Map;
|
|
15712
|
-
constructor(
|
|
15713
|
-
this.cache = cache;
|
|
15661
|
+
constructor(eventWire) {
|
|
15714
15662
|
this.eventWire = eventWire;
|
|
15715
15663
|
}
|
|
15716
15664
|
registerViews(views) {
|
|
15717
15665
|
this.views = views;
|
|
15718
|
-
this.cache.registerViews(views);
|
|
15719
15666
|
}
|
|
15720
15667
|
async publish(event3) {
|
|
15721
|
-
await this.cache.applyEvent(event3);
|
|
15722
15668
|
await this.notifySubscribers(event3);
|
|
15723
15669
|
this.eventWire.syncEvents([
|
|
15724
15670
|
{
|
|
@@ -18862,7 +18808,7 @@ var init_dist = __esm(() => {
|
|
|
18862
18808
|
throw new Error(`Scope violation: access denied to store "${storeName}" (not declared in query/mutate)`);
|
|
18863
18809
|
}
|
|
18864
18810
|
const inner = this.#inner.getStore(storeName);
|
|
18865
|
-
return new
|
|
18811
|
+
return new ScopedStore(inner, this.#restrictions, permission === "read-write");
|
|
18866
18812
|
}
|
|
18867
18813
|
fork() {
|
|
18868
18814
|
return this.#inner.fork();
|
|
@@ -19724,12 +19670,19 @@ var init_dist = __esm(() => {
|
|
|
19724
19670
|
constructor(storeName, dataStorage, deserialize) {
|
|
19725
19671
|
super(storeName, dataStorage, deserialize);
|
|
19726
19672
|
}
|
|
19673
|
+
async readExisting(transaction, id2, transactionCache) {
|
|
19674
|
+
const cacheKey = `${this.storeName}:${id2}`;
|
|
19675
|
+
if (transactionCache && transactionCache.has(cacheKey)) {
|
|
19676
|
+
return transactionCache.get(cacheKey);
|
|
19677
|
+
}
|
|
19678
|
+
return transaction.find(this.storeName, { where: { _id: id2 } }).then((results) => results[0]);
|
|
19679
|
+
}
|
|
19727
19680
|
async applyChangeAndReturnEvent(transaction, change, transactionCache) {
|
|
19728
19681
|
if (change.type === "set") {
|
|
19729
19682
|
await transaction.set(this.storeName, change.data);
|
|
19730
19683
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
19731
19684
|
if (transactionCache) {
|
|
19732
|
-
transactionCache.set(change.data._id
|
|
19685
|
+
transactionCache.set(`${this.storeName}:${change.data._id}`, item);
|
|
19733
19686
|
}
|
|
19734
19687
|
return {
|
|
19735
19688
|
from: null,
|
|
@@ -19743,6 +19696,9 @@ var init_dist = __esm(() => {
|
|
|
19743
19696
|
}
|
|
19744
19697
|
if (change.type === "delete") {
|
|
19745
19698
|
await transaction.remove(this.storeName, change.id);
|
|
19699
|
+
if (transactionCache) {
|
|
19700
|
+
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
19701
|
+
}
|
|
19746
19702
|
return {
|
|
19747
19703
|
from: null,
|
|
19748
19704
|
to: null,
|
|
@@ -19754,17 +19710,12 @@ var init_dist = __esm(() => {
|
|
|
19754
19710
|
};
|
|
19755
19711
|
}
|
|
19756
19712
|
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
|
-
}
|
|
19713
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
19763
19714
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
19764
19715
|
await transaction.set(this.storeName, updated);
|
|
19765
19716
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
19766
19717
|
if (transactionCache) {
|
|
19767
|
-
transactionCache.set(change.id
|
|
19718
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
19768
19719
|
}
|
|
19769
19720
|
return {
|
|
19770
19721
|
from: null,
|
|
@@ -19777,17 +19728,12 @@ var init_dist = __esm(() => {
|
|
|
19777
19728
|
};
|
|
19778
19729
|
}
|
|
19779
19730
|
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
|
-
}
|
|
19731
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
19786
19732
|
const updated = apply(existing || {}, change.patches);
|
|
19787
19733
|
await transaction.set(this.storeName, updated);
|
|
19788
19734
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
19789
19735
|
if (transactionCache) {
|
|
19790
|
-
transactionCache.set(change.id
|
|
19736
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
19791
19737
|
}
|
|
19792
19738
|
return {
|
|
19793
19739
|
from: null,
|
|
@@ -21409,12 +21355,13 @@ class EventWire2 {
|
|
|
21409
21355
|
onSyncedCallback;
|
|
21410
21356
|
reconnectTimeout;
|
|
21411
21357
|
syncRequested = false;
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
constructor(baseUrl) {
|
|
21358
|
+
querySubscriptions = new Map;
|
|
21359
|
+
querySubCounter = 0;
|
|
21360
|
+
enableEventSync;
|
|
21361
|
+
constructor(baseUrl, options) {
|
|
21416
21362
|
this.baseUrl = baseUrl;
|
|
21417
21363
|
this.instanceId = ++eventWireInstanceCounter2;
|
|
21364
|
+
this.enableEventSync = options?.enableEventSync ?? true;
|
|
21418
21365
|
}
|
|
21419
21366
|
setScopeToken(scope, token) {
|
|
21420
21367
|
if (token === null) {
|
|
@@ -21460,9 +21407,11 @@ class EventWire2 {
|
|
|
21460
21407
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
21461
21408
|
this.state = "connected";
|
|
21462
21409
|
this.sendAllScopeTokens();
|
|
21463
|
-
this.
|
|
21410
|
+
if (this.enableEventSync) {
|
|
21411
|
+
this.requestSync();
|
|
21412
|
+
}
|
|
21464
21413
|
this.flushPendingEvents();
|
|
21465
|
-
this.
|
|
21414
|
+
this.sendAllQuerySubscriptions();
|
|
21466
21415
|
} else {
|
|
21467
21416
|
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
21468
21417
|
}
|
|
@@ -21538,9 +21487,13 @@ class EventWire2 {
|
|
|
21538
21487
|
onSynced(callback) {
|
|
21539
21488
|
this.onSyncedCallback = callback;
|
|
21540
21489
|
}
|
|
21541
|
-
subscribeQuery(descriptor,
|
|
21542
|
-
const subscriptionId = `qs_${
|
|
21543
|
-
this.
|
|
21490
|
+
subscribeQuery(descriptor, scope, callbacks) {
|
|
21491
|
+
const subscriptionId = `qs_${this.instanceId}_${++this.querySubCounter}`;
|
|
21492
|
+
this.querySubscriptions.set(subscriptionId, {
|
|
21493
|
+
descriptor,
|
|
21494
|
+
scope,
|
|
21495
|
+
callbacks
|
|
21496
|
+
});
|
|
21544
21497
|
if (this.state === "connected" && this.ws) {
|
|
21545
21498
|
this.ws.send(JSON.stringify({
|
|
21546
21499
|
type: "subscribe-query",
|
|
@@ -21548,20 +21501,17 @@ class EventWire2 {
|
|
|
21548
21501
|
descriptor,
|
|
21549
21502
|
scope
|
|
21550
21503
|
}));
|
|
21551
|
-
} else {
|
|
21552
|
-
this.pendingViewSubs.push({ subscriptionId, descriptor, scope });
|
|
21553
21504
|
}
|
|
21554
21505
|
return subscriptionId;
|
|
21555
21506
|
}
|
|
21556
21507
|
unsubscribeQuery(subscriptionId) {
|
|
21557
|
-
this.
|
|
21508
|
+
this.querySubscriptions.delete(subscriptionId);
|
|
21558
21509
|
if (this.state === "connected" && this.ws) {
|
|
21559
21510
|
this.ws.send(JSON.stringify({
|
|
21560
21511
|
type: "unsubscribe-query",
|
|
21561
21512
|
subscriptionId
|
|
21562
21513
|
}));
|
|
21563
21514
|
}
|
|
21564
|
-
this.pendingViewSubs = this.pendingViewSubs.filter((s) => s.subscriptionId !== subscriptionId);
|
|
21565
21515
|
}
|
|
21566
21516
|
getState() {
|
|
21567
21517
|
return this.state;
|
|
@@ -21594,10 +21544,17 @@ class EventWire2 {
|
|
|
21594
21544
|
this.lastHostEventId = message.lastHostEventId;
|
|
21595
21545
|
}
|
|
21596
21546
|
break;
|
|
21597
|
-
case "query-
|
|
21598
|
-
const
|
|
21599
|
-
if (
|
|
21600
|
-
|
|
21547
|
+
case "query-snapshot": {
|
|
21548
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
21549
|
+
if (sub) {
|
|
21550
|
+
sub.callbacks.onSnapshot(message.result ?? null);
|
|
21551
|
+
}
|
|
21552
|
+
break;
|
|
21553
|
+
}
|
|
21554
|
+
case "query-changes": {
|
|
21555
|
+
const sub = this.querySubscriptions.get(message.subscriptionId);
|
|
21556
|
+
if (sub && Array.isArray(message.changes)) {
|
|
21557
|
+
sub.callbacks.onChanges(message.changes);
|
|
21601
21558
|
}
|
|
21602
21559
|
break;
|
|
21603
21560
|
}
|
|
@@ -21625,18 +21582,17 @@ class EventWire2 {
|
|
|
21625
21582
|
this.pendingEvents = [];
|
|
21626
21583
|
}
|
|
21627
21584
|
}
|
|
21628
|
-
|
|
21585
|
+
sendAllQuerySubscriptions() {
|
|
21629
21586
|
if (!this.ws || this.state !== "connected")
|
|
21630
21587
|
return;
|
|
21631
|
-
for (const sub of this.
|
|
21588
|
+
for (const [subscriptionId, sub] of this.querySubscriptions) {
|
|
21632
21589
|
this.ws.send(JSON.stringify({
|
|
21633
21590
|
type: "subscribe-query",
|
|
21634
|
-
subscriptionId
|
|
21591
|
+
subscriptionId,
|
|
21635
21592
|
descriptor: sub.descriptor,
|
|
21636
21593
|
scope: sub.scope
|
|
21637
21594
|
}));
|
|
21638
21595
|
}
|
|
21639
|
-
this.pendingViewSubs = [];
|
|
21640
21596
|
}
|
|
21641
21597
|
scheduleReconnect() {
|
|
21642
21598
|
if (this.reconnectTimeout)
|
|
@@ -22112,7 +22068,7 @@ class DataStorage2 {
|
|
|
22112
22068
|
}
|
|
22113
22069
|
}
|
|
22114
22070
|
|
|
22115
|
-
class
|
|
22071
|
+
class ScopedStore2 {
|
|
22116
22072
|
#inner;
|
|
22117
22073
|
#restrictions;
|
|
22118
22074
|
#canWrite;
|
|
@@ -22465,6 +22421,47 @@ function deepMerge2(target, source) {
|
|
|
22465
22421
|
function isPlainObject2(item) {
|
|
22466
22422
|
return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
|
|
22467
22423
|
}
|
|
22424
|
+
function murmurHash2(key, seed = 0) {
|
|
22425
|
+
let remainder, bytes, h1, h1b, c1, c2, k1, i;
|
|
22426
|
+
remainder = key.length & 3;
|
|
22427
|
+
bytes = key.length - remainder;
|
|
22428
|
+
h1 = seed;
|
|
22429
|
+
c1 = 3432918353;
|
|
22430
|
+
c2 = 461845907;
|
|
22431
|
+
i = 0;
|
|
22432
|
+
while (i < bytes) {
|
|
22433
|
+
k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
|
|
22434
|
+
++i;
|
|
22435
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
22436
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
22437
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
22438
|
+
h1 ^= k1;
|
|
22439
|
+
h1 = h1 << 13 | h1 >>> 19;
|
|
22440
|
+
h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
|
|
22441
|
+
h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
|
|
22442
|
+
}
|
|
22443
|
+
k1 = 0;
|
|
22444
|
+
if (remainder >= 3) {
|
|
22445
|
+
k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
|
|
22446
|
+
}
|
|
22447
|
+
if (remainder >= 2) {
|
|
22448
|
+
k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
|
|
22449
|
+
}
|
|
22450
|
+
if (remainder >= 1) {
|
|
22451
|
+
k1 ^= key.charCodeAt(i) & 255;
|
|
22452
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
22453
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
22454
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
22455
|
+
h1 ^= k1;
|
|
22456
|
+
}
|
|
22457
|
+
h1 ^= key.length;
|
|
22458
|
+
h1 ^= h1 >>> 16;
|
|
22459
|
+
h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
|
|
22460
|
+
h1 ^= h1 >>> 13;
|
|
22461
|
+
h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
|
|
22462
|
+
h1 ^= h1 >>> 16;
|
|
22463
|
+
return h1 >>> 0;
|
|
22464
|
+
}
|
|
22468
22465
|
function resolveQueryChange2(currentResult, event3, options) {
|
|
22469
22466
|
const index = currentResult.findIndex((e2) => e2._id === event3.id);
|
|
22470
22467
|
const isInCurrentResult = index !== -1;
|
|
@@ -22474,7 +22471,7 @@ function resolveQueryChange2(currentResult, event3, options) {
|
|
|
22474
22471
|
}
|
|
22475
22472
|
return false;
|
|
22476
22473
|
}
|
|
22477
|
-
const shouldBeInResult =
|
|
22474
|
+
const shouldBeInResult = checkItemMatchesWhere2(event3.item, options.where);
|
|
22478
22475
|
if (isInCurrentResult && shouldBeInResult) {
|
|
22479
22476
|
const newResult = currentResult.toSpliced(index, 1, event3.item);
|
|
22480
22477
|
return applyOrderByAndLimit2(newResult, options);
|
|
@@ -22486,7 +22483,7 @@ function resolveQueryChange2(currentResult, event3, options) {
|
|
|
22486
22483
|
}
|
|
22487
22484
|
return false;
|
|
22488
22485
|
}
|
|
22489
|
-
function
|
|
22486
|
+
function checkItemMatchesWhere2(item, where) {
|
|
22490
22487
|
if (!where) {
|
|
22491
22488
|
return true;
|
|
22492
22489
|
}
|
|
@@ -22580,7 +22577,8 @@ class ObservableDataStorage2 {
|
|
|
22580
22577
|
}
|
|
22581
22578
|
handleStoreChange(storeName, events) {
|
|
22582
22579
|
let hasChanges = false;
|
|
22583
|
-
|
|
22580
|
+
const staleKeys = [];
|
|
22581
|
+
for (const [key, query] of this.trackedQueries) {
|
|
22584
22582
|
if (query.storeName !== storeName)
|
|
22585
22583
|
continue;
|
|
22586
22584
|
let currentResult = query.result;
|
|
@@ -22592,10 +22590,20 @@ class ObservableDataStorage2 {
|
|
|
22592
22590
|
queryChanged = true;
|
|
22593
22591
|
}
|
|
22594
22592
|
}
|
|
22595
|
-
if (queryChanged)
|
|
22596
|
-
|
|
22593
|
+
if (!queryChanged)
|
|
22594
|
+
continue;
|
|
22595
|
+
if (query.options.limit !== undefined && query.result.length === query.options.limit && currentResult.length < query.options.limit) {
|
|
22596
|
+
staleKeys.push(key);
|
|
22597
22597
|
hasChanges = true;
|
|
22598
|
+
continue;
|
|
22598
22599
|
}
|
|
22600
|
+
query.result = currentResult;
|
|
22601
|
+
hasChanges = true;
|
|
22602
|
+
}
|
|
22603
|
+
for (const key of staleKeys) {
|
|
22604
|
+
const query = this.trackedQueries.get(key);
|
|
22605
|
+
this.source.getStore(query.storeName).unsubscribe(query.listener);
|
|
22606
|
+
this.trackedQueries.delete(key);
|
|
22599
22607
|
}
|
|
22600
22608
|
if (hasChanges) {
|
|
22601
22609
|
this.onChange();
|
|
@@ -22738,7 +22746,7 @@ function executeDescriptor2(descriptor, context2, adapters, contextMethod, optio
|
|
|
22738
22746
|
return method(...descriptor.args);
|
|
22739
22747
|
}
|
|
22740
22748
|
|
|
22741
|
-
class
|
|
22749
|
+
class ScopedModel4 {
|
|
22742
22750
|
context;
|
|
22743
22751
|
scopeName;
|
|
22744
22752
|
parent;
|
|
@@ -22810,13 +22818,6 @@ class ScopedModel5 {
|
|
|
22810
22818
|
}
|
|
22811
22819
|
return wire.query(viewName, options, this.getAuth());
|
|
22812
22820
|
}
|
|
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
22821
|
get query() {
|
|
22821
22822
|
return buildContextAccessor2(this.context, this.scopedAdapters, "queryContext", (descriptor) => descriptor);
|
|
22822
22823
|
}
|
|
@@ -22866,251 +22867,142 @@ class Model3 {
|
|
|
22866
22867
|
scope(name) {
|
|
22867
22868
|
let s = this.scopes.get(name);
|
|
22868
22869
|
if (!s) {
|
|
22869
|
-
s = new
|
|
22870
|
+
s = new ScopedModel4(this, name);
|
|
22870
22871
|
this.scopes.set(name, s);
|
|
22871
22872
|
}
|
|
22872
22873
|
return s;
|
|
22873
22874
|
}
|
|
22874
22875
|
}
|
|
22875
|
-
|
|
22876
|
-
|
|
22877
|
-
|
|
22878
|
-
|
|
22879
|
-
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
22883
|
-
registerViews(views) {
|
|
22884
|
-
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
|
-
}
|
|
22876
|
+
function applyQueryChanges2(result, changes) {
|
|
22877
|
+
const next = [...result];
|
|
22878
|
+
for (const change of changes) {
|
|
22879
|
+
if (change.type === "delete") {
|
|
22880
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
22881
|
+
if (idx !== -1)
|
|
22882
|
+
next.splice(idx, 1);
|
|
22889
22883
|
}
|
|
22890
22884
|
}
|
|
22891
|
-
|
|
22892
|
-
if (
|
|
22893
|
-
|
|
22885
|
+
for (const change of changes) {
|
|
22886
|
+
if (change.type === "set") {
|
|
22887
|
+
const idx = next.findIndex((it) => it._id === change.id);
|
|
22888
|
+
if (idx !== -1)
|
|
22889
|
+
next.splice(idx, 1);
|
|
22890
|
+
next.splice(change.index, 0, change.item);
|
|
22894
22891
|
}
|
|
22895
|
-
return this.stores.get(viewName);
|
|
22896
22892
|
}
|
|
22897
|
-
|
|
22898
|
-
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
|
|
22907
|
-
|
|
22908
|
-
|
|
22909
|
-
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22893
|
+
return next;
|
|
22894
|
+
}
|
|
22895
|
+
|
|
22896
|
+
class StreamingQueryCache2 {
|
|
22897
|
+
entries = new Map;
|
|
22898
|
+
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
22899
|
+
entryKey(descriptor, scope) {
|
|
22900
|
+
return `${scope ?? "default"}:${murmurHash2(JSON.stringify(descriptor))}`;
|
|
22901
|
+
}
|
|
22902
|
+
subscribe(descriptor, scope, eventWire, onChange) {
|
|
22903
|
+
const key = this.entryKey(descriptor, scope);
|
|
22904
|
+
let entry = this.entries.get(key);
|
|
22905
|
+
if (entry) {
|
|
22906
|
+
if (entry.pendingUnsub) {
|
|
22907
|
+
clearTimeout(entry.pendingUnsub);
|
|
22908
|
+
entry.pendingUnsub = undefined;
|
|
22909
|
+
}
|
|
22910
|
+
entry.refCount++;
|
|
22911
|
+
} else {
|
|
22912
|
+
const newEntry = {
|
|
22913
|
+
result: undefined,
|
|
22914
|
+
hasResult: false,
|
|
22915
|
+
listeners: new Set,
|
|
22916
|
+
refCount: 1,
|
|
22917
|
+
subscriptionId: ""
|
|
22916
22918
|
};
|
|
22919
|
+
newEntry.subscriptionId = eventWire.subscribeQuery(descriptor, scope, {
|
|
22920
|
+
onSnapshot: (result) => {
|
|
22921
|
+
newEntry.result = result;
|
|
22922
|
+
newEntry.hasResult = true;
|
|
22923
|
+
this.notify(newEntry);
|
|
22924
|
+
},
|
|
22925
|
+
onChanges: (changes) => {
|
|
22926
|
+
if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
|
|
22927
|
+
return;
|
|
22928
|
+
}
|
|
22929
|
+
newEntry.result = applyQueryChanges2(newEntry.result, changes);
|
|
22930
|
+
this.notify(newEntry);
|
|
22931
|
+
}
|
|
22932
|
+
});
|
|
22933
|
+
this.entries.set(key, newEntry);
|
|
22934
|
+
entry = newEntry;
|
|
22917
22935
|
}
|
|
22918
|
-
const
|
|
22919
|
-
|
|
22920
|
-
|
|
22921
|
-
refCount: 1
|
|
22922
|
-
});
|
|
22936
|
+
const subscribed = entry;
|
|
22937
|
+
subscribed.listeners.add(onChange);
|
|
22938
|
+
let active = true;
|
|
22923
22939
|
return {
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
|
|
22936
|
-
|
|
22937
|
-
|
|
22938
|
-
|
|
22939
|
-
|
|
22940
|
-
|
|
22941
|
-
|
|
22942
|
-
|
|
22943
|
-
this.pendingUnsubscribes.set(viewName, timeout);
|
|
22944
|
-
}
|
|
22945
|
-
}
|
|
22946
|
-
subscribeQuery(descriptor, eventWire, scope) {
|
|
22947
|
-
const key = descriptor.element;
|
|
22948
|
-
if (scope)
|
|
22949
|
-
this.streamScopes.set(key, scope);
|
|
22950
|
-
const { unsubscribe } = this.registerStream(key, () => {
|
|
22951
|
-
const subId = eventWire.subscribeQuery(descriptor, (data) => {
|
|
22952
|
-
this.setViewData(descriptor.element, data);
|
|
22953
|
-
}, scope);
|
|
22954
|
-
return { unsubscribe: () => eventWire.unsubscribeQuery(subId) };
|
|
22955
|
-
});
|
|
22956
|
-
return unsubscribe;
|
|
22957
|
-
}
|
|
22958
|
-
invalidateScope(scope) {
|
|
22959
|
-
for (const [viewName, viewScope] of this.streamScopes) {
|
|
22960
|
-
if (viewScope !== scope)
|
|
22961
|
-
continue;
|
|
22962
|
-
const pending = this.pendingUnsubscribes.get(viewName);
|
|
22963
|
-
if (pending) {
|
|
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);
|
|
22940
|
+
read: () => ({
|
|
22941
|
+
result: subscribed.result,
|
|
22942
|
+
loading: !subscribed.hasResult
|
|
22943
|
+
}),
|
|
22944
|
+
unsubscribe: () => {
|
|
22945
|
+
if (!active)
|
|
22946
|
+
return;
|
|
22947
|
+
active = false;
|
|
22948
|
+
subscribed.listeners.delete(onChange);
|
|
22949
|
+
subscribed.refCount--;
|
|
22950
|
+
if (subscribed.refCount > 0)
|
|
22951
|
+
return;
|
|
22952
|
+
subscribed.pendingUnsub = setTimeout(() => {
|
|
22953
|
+
subscribed.pendingUnsub = undefined;
|
|
22954
|
+
if (subscribed.refCount > 0)
|
|
22955
|
+
return;
|
|
22956
|
+
eventWire.unsubscribeQuery(subscribed.subscriptionId);
|
|
22957
|
+
this.entries.delete(key);
|
|
22958
|
+
}, StreamingQueryCache2.UNSUBSCRIBE_DELAY_MS);
|
|
22973
22959
|
}
|
|
22974
|
-
|
|
22975
|
-
const store = this.stores.get(viewName);
|
|
22976
|
-
if (store)
|
|
22977
|
-
store.clear();
|
|
22978
|
-
}
|
|
22979
|
-
}
|
|
22980
|
-
setViewData(viewName, data) {
|
|
22981
|
-
const store = this.stores.get(viewName);
|
|
22982
|
-
if (!store)
|
|
22983
|
-
return;
|
|
22984
|
-
if (Array.isArray(data)) {
|
|
22985
|
-
store.setAll(data);
|
|
22986
|
-
} else if (data && typeof data === "object" && "_id" in data) {
|
|
22987
|
-
store.setAll([data]);
|
|
22988
|
-
} else {
|
|
22989
|
-
store.setAll([]);
|
|
22990
|
-
}
|
|
22960
|
+
};
|
|
22991
22961
|
}
|
|
22992
|
-
|
|
22993
|
-
|
|
22994
|
-
|
|
22995
|
-
|
|
22996
|
-
if (!handler)
|
|
22997
|
-
continue;
|
|
22998
|
-
const store = this.stores.get(view3.name);
|
|
22999
|
-
if (!store)
|
|
22962
|
+
invalidateScope(scope, eventWire) {
|
|
22963
|
+
const prefix = `${scope}:`;
|
|
22964
|
+
for (const [key, entry] of this.entries) {
|
|
22965
|
+
if (!key.startsWith(prefix))
|
|
23000
22966
|
continue;
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
remove: async (id3) => {
|
|
23009
|
-
store.remove(String(id3));
|
|
23010
|
-
},
|
|
23011
|
-
find: async (options) => {
|
|
23012
|
-
return store.find(options);
|
|
23013
|
-
},
|
|
23014
|
-
findOne: async (where) => {
|
|
23015
|
-
return store.findOne(where);
|
|
23016
|
-
},
|
|
23017
|
-
$auth: {}
|
|
23018
|
-
};
|
|
23019
|
-
await handler(ctx, event3);
|
|
22967
|
+
if (entry.pendingUnsub)
|
|
22968
|
+
clearTimeout(entry.pendingUnsub);
|
|
22969
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
22970
|
+
this.entries.delete(key);
|
|
22971
|
+
entry.result = undefined;
|
|
22972
|
+
entry.hasResult = false;
|
|
22973
|
+
this.notify(entry);
|
|
23020
22974
|
}
|
|
23021
22975
|
}
|
|
23022
|
-
clear() {
|
|
23023
|
-
for (const
|
|
23024
|
-
|
|
23025
|
-
|
|
23026
|
-
|
|
23027
|
-
this.streamScopes.clear();
|
|
23028
|
-
for (const timeout of this.pendingUnsubscribes.values()) {
|
|
23029
|
-
clearTimeout(timeout);
|
|
23030
|
-
}
|
|
23031
|
-
this.pendingUnsubscribes.clear();
|
|
23032
|
-
for (const store of this.stores.values()) {
|
|
23033
|
-
store.clear();
|
|
22976
|
+
clear(eventWire) {
|
|
22977
|
+
for (const entry of this.entries.values()) {
|
|
22978
|
+
if (entry.pendingUnsub)
|
|
22979
|
+
clearTimeout(entry.pendingUnsub);
|
|
22980
|
+
eventWire?.unsubscribeQuery(entry.subscriptionId);
|
|
23034
22981
|
}
|
|
22982
|
+
this.entries.clear();
|
|
23035
22983
|
}
|
|
23036
|
-
|
|
23037
|
-
|
|
23038
|
-
|
|
23039
|
-
|
|
23040
|
-
|
|
23041
|
-
|
|
23042
|
-
|
|
23043
|
-
return this.initialized;
|
|
23044
|
-
}
|
|
23045
|
-
setAll(items) {
|
|
23046
|
-
this.initialized = true;
|
|
23047
|
-
this.data.clear();
|
|
23048
|
-
for (const item of items) {
|
|
23049
|
-
this.data.set(item._id, item);
|
|
23050
|
-
}
|
|
23051
|
-
this.notifyListeners(null);
|
|
23052
|
-
}
|
|
23053
|
-
set(id3, item) {
|
|
23054
|
-
this.data.set(id3, item);
|
|
23055
|
-
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
23056
|
-
}
|
|
23057
|
-
modify(id3, updates) {
|
|
23058
|
-
const existing = this.data.get(id3);
|
|
23059
|
-
if (existing) {
|
|
23060
|
-
const updated = { ...existing, ...updates };
|
|
23061
|
-
this.data.set(id3, updated);
|
|
23062
|
-
this.notifyListeners([{ type: "set", id: id3, item: updated }]);
|
|
23063
|
-
}
|
|
23064
|
-
}
|
|
23065
|
-
remove(id3) {
|
|
23066
|
-
if (this.data.delete(id3)) {
|
|
23067
|
-
this.notifyListeners([{ type: "delete", id: id3, item: null }]);
|
|
23068
|
-
}
|
|
23069
|
-
}
|
|
23070
|
-
clear() {
|
|
23071
|
-
this.initialized = false;
|
|
23072
|
-
this.data.clear();
|
|
23073
|
-
this.notifyListeners(null);
|
|
23074
|
-
}
|
|
23075
|
-
find(options = {}) {
|
|
23076
|
-
let results = Array.from(this.data.values());
|
|
23077
|
-
if (options.where) {
|
|
23078
|
-
results = results.filter((item) => checkItemMatchesWhere3(item, options.where));
|
|
23079
|
-
}
|
|
23080
|
-
return applyOrderByAndLimit2(results, options);
|
|
23081
|
-
}
|
|
23082
|
-
findOne(where) {
|
|
23083
|
-
const results = this.find({ where });
|
|
23084
|
-
return results[0];
|
|
23085
|
-
}
|
|
23086
|
-
subscribe(listener4) {
|
|
23087
|
-
this.listeners.add(listener4);
|
|
23088
|
-
return () => {
|
|
23089
|
-
this.listeners.delete(listener4);
|
|
23090
|
-
};
|
|
23091
|
-
}
|
|
23092
|
-
notifyListeners(events) {
|
|
23093
|
-
for (const listener4 of this.listeners) {
|
|
23094
|
-
listener4(events);
|
|
22984
|
+
notify(entry) {
|
|
22985
|
+
for (const listener4 of entry.listeners) {
|
|
22986
|
+
try {
|
|
22987
|
+
listener4();
|
|
22988
|
+
} catch (err3) {
|
|
22989
|
+
console.error(`[Arc] Query cache listener error:`, err3);
|
|
22990
|
+
}
|
|
23095
22991
|
}
|
|
23096
22992
|
}
|
|
23097
22993
|
}
|
|
23098
22994
|
|
|
23099
22995
|
class StreamingEventPublisher2 {
|
|
23100
|
-
cache;
|
|
23101
22996
|
eventWire;
|
|
23102
22997
|
views = [];
|
|
23103
22998
|
subscribers = new Map;
|
|
23104
|
-
constructor(
|
|
23105
|
-
this.cache = cache;
|
|
22999
|
+
constructor(eventWire) {
|
|
23106
23000
|
this.eventWire = eventWire;
|
|
23107
23001
|
}
|
|
23108
23002
|
registerViews(views) {
|
|
23109
23003
|
this.views = views;
|
|
23110
|
-
this.cache.registerViews(views);
|
|
23111
23004
|
}
|
|
23112
23005
|
async publish(event3) {
|
|
23113
|
-
await this.cache.applyEvent(event3);
|
|
23114
23006
|
await this.notifySubscribers(event3);
|
|
23115
23007
|
this.eventWire.syncEvents([
|
|
23116
23008
|
{
|
|
@@ -24689,7 +24581,7 @@ var init_dist2 = __esm(() => {
|
|
|
24689
24581
|
throw new Error(`Scope violation: access denied to store "${storeName}" (not declared in query/mutate)`);
|
|
24690
24582
|
}
|
|
24691
24583
|
const inner = this.#inner.getStore(storeName);
|
|
24692
|
-
return new
|
|
24584
|
+
return new ScopedStore2(inner, this.#restrictions, permission === "read-write");
|
|
24693
24585
|
}
|
|
24694
24586
|
fork() {
|
|
24695
24587
|
return this.#inner.fork();
|
|
@@ -25551,12 +25443,19 @@ var init_dist2 = __esm(() => {
|
|
|
25551
25443
|
constructor(storeName, dataStorage, deserialize) {
|
|
25552
25444
|
super(storeName, dataStorage, deserialize);
|
|
25553
25445
|
}
|
|
25446
|
+
async readExisting(transaction, id22, transactionCache) {
|
|
25447
|
+
const cacheKey = `${this.storeName}:${id22}`;
|
|
25448
|
+
if (transactionCache && transactionCache.has(cacheKey)) {
|
|
25449
|
+
return transactionCache.get(cacheKey);
|
|
25450
|
+
}
|
|
25451
|
+
return transaction.find(this.storeName, { where: { _id: id22 } }).then((results) => results[0]);
|
|
25452
|
+
}
|
|
25554
25453
|
async applyChangeAndReturnEvent(transaction, change, transactionCache) {
|
|
25555
25454
|
if (change.type === "set") {
|
|
25556
25455
|
await transaction.set(this.storeName, change.data);
|
|
25557
25456
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
25558
25457
|
if (transactionCache) {
|
|
25559
|
-
transactionCache.set(change.data._id
|
|
25458
|
+
transactionCache.set(`${this.storeName}:${change.data._id}`, item);
|
|
25560
25459
|
}
|
|
25561
25460
|
return {
|
|
25562
25461
|
from: null,
|
|
@@ -25570,6 +25469,9 @@ var init_dist2 = __esm(() => {
|
|
|
25570
25469
|
}
|
|
25571
25470
|
if (change.type === "delete") {
|
|
25572
25471
|
await transaction.remove(this.storeName, change.id);
|
|
25472
|
+
if (transactionCache) {
|
|
25473
|
+
transactionCache.delete(`${this.storeName}:${change.id}`);
|
|
25474
|
+
}
|
|
25573
25475
|
return {
|
|
25574
25476
|
from: null,
|
|
25575
25477
|
to: null,
|
|
@@ -25581,17 +25483,12 @@ var init_dist2 = __esm(() => {
|
|
|
25581
25483
|
};
|
|
25582
25484
|
}
|
|
25583
25485
|
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
|
-
}
|
|
25486
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
25590
25487
|
const updated = existing ? deepMerge2(existing, change.data) : { _id: change.id, ...change.data };
|
|
25591
25488
|
await transaction.set(this.storeName, updated);
|
|
25592
25489
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
25593
25490
|
if (transactionCache) {
|
|
25594
|
-
transactionCache.set(change.id
|
|
25491
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
25595
25492
|
}
|
|
25596
25493
|
return {
|
|
25597
25494
|
from: null,
|
|
@@ -25604,17 +25501,12 @@ var init_dist2 = __esm(() => {
|
|
|
25604
25501
|
};
|
|
25605
25502
|
}
|
|
25606
25503
|
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
|
-
}
|
|
25504
|
+
const existing = await this.readExisting(transaction, change.id, transactionCache);
|
|
25613
25505
|
const updated = apply2(existing || {}, change.patches);
|
|
25614
25506
|
await transaction.set(this.storeName, updated);
|
|
25615
25507
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
25616
25508
|
if (transactionCache) {
|
|
25617
|
-
transactionCache.set(change.id
|
|
25509
|
+
transactionCache.set(`${this.storeName}:${change.id}`, item);
|
|
25618
25510
|
}
|
|
25619
25511
|
return {
|
|
25620
25512
|
from: null,
|
|
@@ -34504,8 +34396,8 @@ ${colors3.yellow}Type declaration errors:${colors3.reset}`);
|
|
|
34504
34396
|
}
|
|
34505
34397
|
|
|
34506
34398
|
// src/platform/shared.ts
|
|
34507
|
-
import { copyFileSync, existsSync as existsSync10, mkdirSync as mkdirSync9, readdirSync as
|
|
34508
|
-
import { dirname as dirname8, join as
|
|
34399
|
+
import { copyFileSync, existsSync as existsSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
34400
|
+
import { dirname as dirname8, join as join12 } from "path";
|
|
34509
34401
|
|
|
34510
34402
|
// src/builder/module-builder.ts
|
|
34511
34403
|
import { execSync } from "child_process";
|
|
@@ -35300,7 +35192,8 @@ writeFileSync(out, JSON.stringify(result, null, 2) + "\\n");
|
|
|
35300
35192
|
`.trim();
|
|
35301
35193
|
|
|
35302
35194
|
// src/builder/chunk-planner.ts
|
|
35303
|
-
import {
|
|
35195
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
35196
|
+
import { basename as basename3, join as join10 } from "path";
|
|
35304
35197
|
var PUBLIC_CHUNK = "public";
|
|
35305
35198
|
function planChunks(packages, accessMap) {
|
|
35306
35199
|
const assignments = new Map;
|
|
@@ -35332,6 +35225,53 @@ function planChunks(packages, accessMap) {
|
|
|
35332
35225
|
});
|
|
35333
35226
|
return { assignments, groups, chunks };
|
|
35334
35227
|
}
|
|
35228
|
+
var MODULE_CALL = /\bmodule\(\s*["'`]([a-zA-Z0-9_-]+)["'`]/g;
|
|
35229
|
+
function collectModuleNames(dir, acc) {
|
|
35230
|
+
let entries;
|
|
35231
|
+
try {
|
|
35232
|
+
entries = readdirSync5(dir);
|
|
35233
|
+
} catch {
|
|
35234
|
+
return;
|
|
35235
|
+
}
|
|
35236
|
+
for (const e of entries) {
|
|
35237
|
+
if (e === "node_modules" || e === "dist")
|
|
35238
|
+
continue;
|
|
35239
|
+
const full = join10(dir, e);
|
|
35240
|
+
let isDir = false;
|
|
35241
|
+
try {
|
|
35242
|
+
isDir = statSync2(full).isDirectory();
|
|
35243
|
+
} catch {
|
|
35244
|
+
continue;
|
|
35245
|
+
}
|
|
35246
|
+
if (isDir) {
|
|
35247
|
+
collectModuleNames(full, acc);
|
|
35248
|
+
} else if (e.endsWith(".ts") || e.endsWith(".tsx")) {
|
|
35249
|
+
const src = readFileSync9(full, "utf-8");
|
|
35250
|
+
MODULE_CALL.lastIndex = 0;
|
|
35251
|
+
let m;
|
|
35252
|
+
while (m = MODULE_CALL.exec(src))
|
|
35253
|
+
acc.add(m[1]);
|
|
35254
|
+
}
|
|
35255
|
+
}
|
|
35256
|
+
}
|
|
35257
|
+
function assertOneModulePerPackage(packages) {
|
|
35258
|
+
const offenders = [];
|
|
35259
|
+
for (const pkg of packages) {
|
|
35260
|
+
const names = new Set;
|
|
35261
|
+
collectModuleNames(join10(pkg.path, "src"), names);
|
|
35262
|
+
if (names.size > 1) {
|
|
35263
|
+
offenders.push({ pkg: pkg.name, modules: [...names].sort() });
|
|
35264
|
+
}
|
|
35265
|
+
}
|
|
35266
|
+
if (offenders.length === 0)
|
|
35267
|
+
return;
|
|
35268
|
+
const detail = offenders.map((o) => ` \u2022 ${o.pkg} declares ${o.modules.length}: ${o.modules.join(", ")}`).join(`
|
|
35269
|
+
`);
|
|
35270
|
+
throw new Error(`Arc build: a workspace package must declare at most ONE module().
|
|
35271
|
+
` + `Chunk grouping is per-package \u2014 a second module() silently inherits the ` + `first module's token protection and chunk, so e.g. a public onboarding ` + `page sharing a package with a workspaceToken-gated module becomes ` + `unreachable for users without that token.
|
|
35272
|
+
${detail}
|
|
35273
|
+
` + `Fix: move each extra module() into its own workspace package ` + `(declare its own protectedBy there).`);
|
|
35274
|
+
}
|
|
35335
35275
|
function moduleNameOf(pkgName) {
|
|
35336
35276
|
return pkgName.includes("/") ? pkgName.split("/").pop() : pkgName;
|
|
35337
35277
|
}
|
|
@@ -35339,17 +35279,19 @@ function resolveChunk(moduleName, access) {
|
|
|
35339
35279
|
if (!access || access.rules.length === 0)
|
|
35340
35280
|
return PUBLIC_CHUNK;
|
|
35341
35281
|
const tokenNames = new Set(access.rules.map((r) => r.token.name));
|
|
35342
|
-
if (tokenNames.size
|
|
35343
|
-
|
|
35282
|
+
if (tokenNames.size > 1) {
|
|
35283
|
+
const list = [...tokenNames].sort().join(", ");
|
|
35284
|
+
throw new Error(`Module "${moduleName}" has access rules for multiple tokens [${list}]. ` + `Multi-token modules are not supported \u2014 split the module or unify on a single token type.`);
|
|
35344
35285
|
}
|
|
35345
|
-
const
|
|
35346
|
-
|
|
35286
|
+
const tokenName = [...tokenNames][0];
|
|
35287
|
+
const hasCheck = access.rules.some((r) => r.hasCheck);
|
|
35288
|
+
return hasCheck ? `${tokenName}__${moduleName}` : tokenName;
|
|
35347
35289
|
}
|
|
35348
35290
|
|
|
35349
35291
|
// src/builder/dependency-collector.ts
|
|
35350
35292
|
import { createHash } from "crypto";
|
|
35351
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as
|
|
35352
|
-
import { basename as basename4, dirname as dirname7, join as
|
|
35293
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
35294
|
+
import { basename as basename4, dirname as dirname7, join as join11 } from "path";
|
|
35353
35295
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
35354
35296
|
function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = []) {
|
|
35355
35297
|
mkdirSync8(arcDir, { recursive: true });
|
|
@@ -35360,7 +35302,7 @@ function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = []) {
|
|
|
35360
35302
|
try {
|
|
35361
35303
|
const cliDir = dirname7(fileURLToPath6(import.meta.url));
|
|
35362
35304
|
const arcOtelPkgPath = Bun.resolveSync("@arcote.tech/arc-otel/package.json", cliDir);
|
|
35363
|
-
const arcOtelPkg = JSON.parse(
|
|
35305
|
+
const arcOtelPkg = JSON.parse(readFileSync10(arcOtelPkgPath, "utf-8"));
|
|
35364
35306
|
for (const [name, spec] of Object.entries(arcOtelPkg.dependencies ?? {})) {
|
|
35365
35307
|
if (name.startsWith("@opentelemetry/")) {
|
|
35366
35308
|
versions[name] = spec;
|
|
@@ -35371,10 +35313,10 @@ function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = []) {
|
|
|
35371
35313
|
console.warn(`[arc-otel] could not resolve @arcote.tech/arc-otel \u2014 image will run without telemetry deps: ${e.message}`);
|
|
35372
35314
|
}
|
|
35373
35315
|
let rootArc;
|
|
35374
|
-
const rootPkgPath =
|
|
35316
|
+
const rootPkgPath = join11(rootDir, "package.json");
|
|
35375
35317
|
if (existsSync9(rootPkgPath)) {
|
|
35376
35318
|
try {
|
|
35377
|
-
const rootPkg = JSON.parse(
|
|
35319
|
+
const rootPkg = JSON.parse(readFileSync10(rootPkgPath, "utf-8"));
|
|
35378
35320
|
if (rootPkg.arc && typeof rootPkg.arc === "object")
|
|
35379
35321
|
rootArc = rootPkg.arc;
|
|
35380
35322
|
} catch {}
|
|
@@ -35386,11 +35328,11 @@ function collectFrameworkDeps(arcDir, rootDir, packages, sharedDeps = []) {
|
|
|
35386
35328
|
dependencies: versions,
|
|
35387
35329
|
...rootArc ? { arc: rootArc } : {}
|
|
35388
35330
|
};
|
|
35389
|
-
const manifestPath =
|
|
35331
|
+
const manifestPath = join11(arcDir, "package.json");
|
|
35390
35332
|
writeFileSync8(manifestPath, JSON.stringify(manifest, null, 2) + `
|
|
35391
35333
|
`);
|
|
35392
|
-
const hash = sha256Hex2(
|
|
35393
|
-
writeFileSync8(
|
|
35334
|
+
const hash = sha256Hex2(readFileSync10(manifestPath));
|
|
35335
|
+
writeFileSync8(join11(arcDir, ".deps-hash"), hash + `
|
|
35394
35336
|
`);
|
|
35395
35337
|
return { hash, manifestPath };
|
|
35396
35338
|
}
|
|
@@ -35398,7 +35340,7 @@ function sha256Hex2(bytes) {
|
|
|
35398
35340
|
return createHash("sha256").update(bytes).digest("hex");
|
|
35399
35341
|
}
|
|
35400
35342
|
function resolveFrameworkVersions(rootDir, packages) {
|
|
35401
|
-
const rootPkg = JSON.parse(
|
|
35343
|
+
const rootPkg = JSON.parse(readFileSync10(join11(rootDir, "package.json"), "utf-8"));
|
|
35402
35344
|
const rootDeps = rootPkg.dependencies ?? {};
|
|
35403
35345
|
const rootDevDeps = rootPkg.devDependencies ?? {};
|
|
35404
35346
|
const required = new Set(FRAMEWORK_PEERS);
|
|
@@ -35457,9 +35399,9 @@ function resolveWorkspace() {
|
|
|
35457
35399
|
process.exit(1);
|
|
35458
35400
|
}
|
|
35459
35401
|
const rootDir = dirname8(packageJsonPath);
|
|
35460
|
-
const rootPkg = JSON.parse(
|
|
35402
|
+
const rootPkg = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
|
|
35461
35403
|
const appName = rootPkg.name ?? "Arc App";
|
|
35462
|
-
const arcDir =
|
|
35404
|
+
const arcDir = join12(rootDir, ".arc", "platform");
|
|
35463
35405
|
log2("Scanning workspaces...");
|
|
35464
35406
|
const packages = discoverPackages(rootDir);
|
|
35465
35407
|
if (packages.length > 0) {
|
|
@@ -35469,10 +35411,10 @@ function resolveWorkspace() {
|
|
|
35469
35411
|
}
|
|
35470
35412
|
let manifest;
|
|
35471
35413
|
for (const name of ["manifest.json", "manifest.webmanifest"]) {
|
|
35472
|
-
const manifestPath =
|
|
35414
|
+
const manifestPath = join12(rootDir, name);
|
|
35473
35415
|
if (existsSync10(manifestPath)) {
|
|
35474
35416
|
try {
|
|
35475
|
-
const data = JSON.parse(
|
|
35417
|
+
const data = JSON.parse(readFileSync11(manifestPath, "utf-8"));
|
|
35476
35418
|
const icons = data.icons;
|
|
35477
35419
|
manifest = {
|
|
35478
35420
|
path: manifestPath,
|
|
@@ -35491,9 +35433,9 @@ function resolveWorkspace() {
|
|
|
35491
35433
|
rootPkg,
|
|
35492
35434
|
appName,
|
|
35493
35435
|
arcDir,
|
|
35494
|
-
browserDir:
|
|
35495
|
-
assetsDir:
|
|
35496
|
-
publicDir:
|
|
35436
|
+
browserDir: join12(arcDir, "browser"),
|
|
35437
|
+
assetsDir: join12(arcDir, "assets"),
|
|
35438
|
+
publicDir: join12(rootDir, "public"),
|
|
35497
35439
|
packages,
|
|
35498
35440
|
manifest
|
|
35499
35441
|
};
|
|
@@ -35503,11 +35445,12 @@ async function buildAll(ws, opts = {}) {
|
|
|
35503
35445
|
const noCache = opts.noCache ?? false;
|
|
35504
35446
|
const themePath = ws.rootPkg.arc?.theme;
|
|
35505
35447
|
log2(`Building (concurrency parallel${noCache ? ", no-cache" : ""})...`);
|
|
35448
|
+
assertOneModulePerPackage(ws.packages);
|
|
35506
35449
|
await buildContextPackages(ws.rootDir, ws.packages, cache, noCache);
|
|
35507
35450
|
copyContextServerBundles(ws);
|
|
35508
35451
|
const accessMap = await extractAccessMap(ws.rootDir, ws.packages);
|
|
35509
35452
|
mkdirSync9(ws.arcDir, { recursive: true });
|
|
35510
|
-
writeFileSync9(
|
|
35453
|
+
writeFileSync9(join12(ws.arcDir, "access.json"), JSON.stringify(accessMap, null, 2) + `
|
|
35511
35454
|
`);
|
|
35512
35455
|
const plan = planChunks(ws.packages, accessMap);
|
|
35513
35456
|
ok(`Chunks: ${plan.chunks.map((c) => `${c}(${plan.groups.get(c)?.length ?? 0})`).join(", ")}`);
|
|
@@ -35523,7 +35466,7 @@ async function buildAll(ws, opts = {}) {
|
|
|
35523
35466
|
collectFrameworkDeps(ws.arcDir, ws.rootDir, ws.packages);
|
|
35524
35467
|
saveBuildCache(ws.arcDir, cache);
|
|
35525
35468
|
const finalManifest = assembleManifest(ws, browserResult, cache);
|
|
35526
|
-
writeFileSync9(
|
|
35469
|
+
writeFileSync9(join12(ws.arcDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
|
|
35527
35470
|
return finalManifest;
|
|
35528
35471
|
}
|
|
35529
35472
|
function assembleManifest(ws, browser, cache) {
|
|
@@ -35537,40 +35480,40 @@ function assembleManifest(ws, browser, cache) {
|
|
|
35537
35480
|
};
|
|
35538
35481
|
}
|
|
35539
35482
|
function copyContextServerBundles(ws) {
|
|
35540
|
-
const outDir =
|
|
35483
|
+
const outDir = join12(ws.arcDir, "server");
|
|
35541
35484
|
mkdirSync9(outDir, { recursive: true });
|
|
35542
35485
|
for (const pkg of ws.packages) {
|
|
35543
35486
|
if (!isContextPackage(pkg.packageJson))
|
|
35544
35487
|
continue;
|
|
35545
|
-
const src =
|
|
35488
|
+
const src = join12(pkg.path, "dist", "server", "main", "index.js");
|
|
35546
35489
|
if (!existsSync10(src)) {
|
|
35547
35490
|
err(`Server bundle missing for ${pkg.name}: ${src}`);
|
|
35548
35491
|
continue;
|
|
35549
35492
|
}
|
|
35550
35493
|
const safeName = pkg.path.split("/").pop();
|
|
35551
|
-
const dst =
|
|
35494
|
+
const dst = join12(outDir, `${safeName}.js`);
|
|
35552
35495
|
copyFileSync(src, dst);
|
|
35553
35496
|
}
|
|
35554
35497
|
}
|
|
35555
35498
|
function resolveAssetSource(from, pkgDir, rootDir) {
|
|
35556
35499
|
if (from.startsWith("./") || from.startsWith("../")) {
|
|
35557
|
-
const resolved =
|
|
35500
|
+
const resolved = join12(pkgDir, from);
|
|
35558
35501
|
return existsSync10(resolved) ? resolved : null;
|
|
35559
35502
|
}
|
|
35560
35503
|
const candidates = [
|
|
35561
|
-
|
|
35562
|
-
|
|
35504
|
+
join12(rootDir, "node_modules", from),
|
|
35505
|
+
join12(pkgDir, "node_modules", from)
|
|
35563
35506
|
];
|
|
35564
35507
|
for (const c of candidates) {
|
|
35565
35508
|
if (existsSync10(c))
|
|
35566
35509
|
return c;
|
|
35567
35510
|
}
|
|
35568
|
-
const bunCacheDir =
|
|
35511
|
+
const bunCacheDir = join12(rootDir, "node_modules", ".bun");
|
|
35569
35512
|
if (existsSync10(bunCacheDir)) {
|
|
35570
|
-
for (const entry of
|
|
35513
|
+
for (const entry of readdirSync6(bunCacheDir, { withFileTypes: true })) {
|
|
35571
35514
|
if (!entry.isDirectory())
|
|
35572
35515
|
continue;
|
|
35573
|
-
const candidate =
|
|
35516
|
+
const candidate = join12(bunCacheDir, entry.name, "node_modules", from);
|
|
35574
35517
|
if (existsSync10(candidate))
|
|
35575
35518
|
return candidate;
|
|
35576
35519
|
}
|
|
@@ -35578,11 +35521,11 @@ function resolveAssetSource(from, pkgDir, rootDir) {
|
|
|
35578
35521
|
return null;
|
|
35579
35522
|
}
|
|
35580
35523
|
function readBrowserAssets(pkgDir) {
|
|
35581
|
-
const pkgJsonPath =
|
|
35524
|
+
const pkgJsonPath = join12(pkgDir, "package.json");
|
|
35582
35525
|
if (!existsSync10(pkgJsonPath))
|
|
35583
35526
|
return [];
|
|
35584
35527
|
try {
|
|
35585
|
-
const pkg = JSON.parse(
|
|
35528
|
+
const pkg = JSON.parse(readFileSync11(pkgJsonPath, "utf-8"));
|
|
35586
35529
|
const assets = pkg.arc?.browserAssets;
|
|
35587
35530
|
if (!Array.isArray(assets))
|
|
35588
35531
|
return [];
|
|
@@ -35592,14 +35535,14 @@ function readBrowserAssets(pkgDir) {
|
|
|
35592
35535
|
}
|
|
35593
35536
|
}
|
|
35594
35537
|
function discoverBrowserAssets(ws) {
|
|
35595
|
-
const arcDir =
|
|
35538
|
+
const arcDir = join12(ws.rootDir, "node_modules", "@arcote.tech");
|
|
35596
35539
|
if (!existsSync10(arcDir))
|
|
35597
35540
|
return [];
|
|
35598
35541
|
const out = [];
|
|
35599
|
-
for (const entry of
|
|
35542
|
+
for (const entry of readdirSync6(arcDir, { withFileTypes: true })) {
|
|
35600
35543
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
35601
35544
|
continue;
|
|
35602
|
-
const pkgDir =
|
|
35545
|
+
const pkgDir = join12(arcDir, entry.name);
|
|
35603
35546
|
const assets = readBrowserAssets(pkgDir);
|
|
35604
35547
|
for (const asset of assets) {
|
|
35605
35548
|
const src = resolveAssetSource(asset.from, pkgDir, ws.rootDir);
|
|
@@ -35624,7 +35567,7 @@ async function copyBrowserAssets(ws, cache, noCache) {
|
|
|
35624
35567
|
to: a.to,
|
|
35625
35568
|
mtime: mtimeOf(a.src)
|
|
35626
35569
|
})));
|
|
35627
|
-
const requiredOutputs = assets.map((a) =>
|
|
35570
|
+
const requiredOutputs = assets.map((a) => join12(ws.assetsDir, a.to));
|
|
35628
35571
|
if (!noCache && isCacheHit(cache, unitId, inputHash, requiredOutputs)) {
|
|
35629
35572
|
console.log(` \u2713 cached: browser-assets (${assets.length})`);
|
|
35630
35573
|
return;
|
|
@@ -35632,10 +35575,10 @@ async function copyBrowserAssets(ws, cache, noCache) {
|
|
|
35632
35575
|
console.log(` building: browser-assets (${assets.length})`);
|
|
35633
35576
|
const outputHashes = {};
|
|
35634
35577
|
for (const asset of assets) {
|
|
35635
|
-
const dest =
|
|
35578
|
+
const dest = join12(ws.assetsDir, asset.to);
|
|
35636
35579
|
mkdirSync9(dirname8(dest), { recursive: true });
|
|
35637
35580
|
copyFileSync(asset.src, dest);
|
|
35638
|
-
outputHashes[asset.to] = sha256Hex(
|
|
35581
|
+
outputHashes[asset.to] = sha256Hex(readFileSync11(dest));
|
|
35639
35582
|
}
|
|
35640
35583
|
updateCache(cache, unitId, inputHash, { outputHashes });
|
|
35641
35584
|
}
|
|
@@ -35643,15 +35586,15 @@ async function loadServerContext(ws) {
|
|
|
35643
35586
|
globalThis.ONLY_SERVER = true;
|
|
35644
35587
|
globalThis.ONLY_BROWSER = false;
|
|
35645
35588
|
globalThis.ONLY_CLIENT = false;
|
|
35646
|
-
const platformDir =
|
|
35647
|
-
const platformPkg = JSON.parse(
|
|
35648
|
-
const platformEntry =
|
|
35589
|
+
const platformDir = join12(process.cwd(), "node_modules", "@arcote.tech", "platform");
|
|
35590
|
+
const platformPkg = JSON.parse(readFileSync11(join12(platformDir, "package.json"), "utf-8"));
|
|
35591
|
+
const platformEntry = join12(platformDir, platformPkg.main ?? "src/index.ts");
|
|
35649
35592
|
await import(platformEntry);
|
|
35650
|
-
const serverDir =
|
|
35651
|
-
const bundles = existsSync10(serverDir) ?
|
|
35593
|
+
const serverDir = join12(ws.arcDir, "server");
|
|
35594
|
+
const bundles = existsSync10(serverDir) ? readdirSync6(serverDir).filter((f) => f.endsWith(".js")) : [];
|
|
35652
35595
|
if (bundles.length > 0) {
|
|
35653
35596
|
for (const file of bundles) {
|
|
35654
|
-
const bundlePath =
|
|
35597
|
+
const bundlePath = join12(serverDir, file);
|
|
35655
35598
|
try {
|
|
35656
35599
|
await import(bundlePath);
|
|
35657
35600
|
} catch (e) {
|
|
@@ -35661,7 +35604,7 @@ async function loadServerContext(ws) {
|
|
|
35661
35604
|
} else if (ws.packages.length > 0) {
|
|
35662
35605
|
const ctxPackages = ws.packages.filter((p) => isContextPackage(p.packageJson));
|
|
35663
35606
|
for (const ctx of ctxPackages) {
|
|
35664
|
-
const serverDist =
|
|
35607
|
+
const serverDist = join12(ctx.path, "dist", "server", "main", "index.js");
|
|
35665
35608
|
if (!existsSync10(serverDist)) {
|
|
35666
35609
|
err(`Context server dist not found: ${serverDist}`);
|
|
35667
35610
|
continue;
|
|
@@ -35691,21 +35634,21 @@ async function platformBuild(opts = {}) {
|
|
|
35691
35634
|
}
|
|
35692
35635
|
|
|
35693
35636
|
// src/commands/platform-deploy.ts
|
|
35694
|
-
import { existsSync as existsSync17, readFileSync as
|
|
35695
|
-
import { dirname as dirname11, join as
|
|
35637
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
35638
|
+
import { dirname as dirname11, join as join20 } from "path";
|
|
35696
35639
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
35697
35640
|
|
|
35698
35641
|
// src/deploy/bootstrap.ts
|
|
35699
35642
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
35700
35643
|
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync13 } from "fs";
|
|
35701
35644
|
import { tmpdir as tmpdir2 } from "os";
|
|
35702
|
-
import { dirname as dirname9, join as
|
|
35645
|
+
import { dirname as dirname9, join as join18 } from "path";
|
|
35703
35646
|
|
|
35704
35647
|
// src/deploy/ansible.ts
|
|
35705
35648
|
import { spawn as nodeSpawn } from "child_process";
|
|
35706
35649
|
import { existsSync as existsSync11, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
35707
35650
|
import { homedir, tmpdir } from "os";
|
|
35708
|
-
import { join as
|
|
35651
|
+
import { join as join13 } from "path";
|
|
35709
35652
|
|
|
35710
35653
|
// src/deploy/assets.ts
|
|
35711
35654
|
var TERRAFORM_MAIN_TF = `terraform {
|
|
@@ -35979,28 +35922,28 @@ var ASSETS = {
|
|
|
35979
35922
|
};
|
|
35980
35923
|
async function materializeAssets(targetDir, files) {
|
|
35981
35924
|
const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync10 } = await import("fs");
|
|
35982
|
-
const { join:
|
|
35925
|
+
const { join: join13 } = await import("path");
|
|
35983
35926
|
mkdirSync10(targetDir, { recursive: true });
|
|
35984
35927
|
for (const [name, content] of Object.entries(files)) {
|
|
35985
|
-
writeFileSync10(
|
|
35928
|
+
writeFileSync10(join13(targetDir, name), content);
|
|
35986
35929
|
}
|
|
35987
35930
|
}
|
|
35988
35931
|
|
|
35989
35932
|
// src/deploy/ansible.ts
|
|
35990
35933
|
function pickSshKeyForAnsible(configured) {
|
|
35991
35934
|
if (configured) {
|
|
35992
|
-
const expanded = configured.startsWith("~") ?
|
|
35935
|
+
const expanded = configured.startsWith("~") ? join13(homedir(), configured.slice(1)) : configured;
|
|
35993
35936
|
return existsSync11(expanded) ? expanded : null;
|
|
35994
35937
|
}
|
|
35995
35938
|
for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
|
|
35996
|
-
const path4 =
|
|
35939
|
+
const path4 = join13(homedir(), ".ssh", name);
|
|
35997
35940
|
if (existsSync11(path4))
|
|
35998
35941
|
return path4;
|
|
35999
35942
|
}
|
|
36000
35943
|
return null;
|
|
36001
35944
|
}
|
|
36002
35945
|
async function runAnsible(inputs) {
|
|
36003
|
-
const workDir =
|
|
35946
|
+
const workDir = join13(tmpdir(), "arc-deploy", `ansible-${Date.now()}`);
|
|
36004
35947
|
mkdirSync10(workDir, { recursive: true });
|
|
36005
35948
|
await materializeAssets(workDir, ASSETS.ansible);
|
|
36006
35949
|
const user = inputs.asRoot ? "root" : inputs.target.user;
|
|
@@ -36017,7 +35960,7 @@ async function runAnsible(inputs) {
|
|
|
36017
35960
|
""
|
|
36018
35961
|
].join(`
|
|
36019
35962
|
`);
|
|
36020
|
-
writeFileSync10(
|
|
35963
|
+
writeFileSync10(join13(workDir, "inventory.ini"), inventory);
|
|
36021
35964
|
const extraVarsJson = JSON.stringify({
|
|
36022
35965
|
username: inputs.target.user,
|
|
36023
35966
|
ssh_port: port,
|
|
@@ -37065,10 +37008,10 @@ import { spawn as nodeSpawn2 } from "child_process";
|
|
|
37065
37008
|
import { createHash as createHash2 } from "crypto";
|
|
37066
37009
|
import { existsSync as existsSync12, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
37067
37010
|
import { homedir as homedir2 } from "os";
|
|
37068
|
-
import { join as
|
|
37011
|
+
import { join as join14 } from "path";
|
|
37069
37012
|
async function runTerraform(inputs) {
|
|
37070
37013
|
const wsHash = createHash2("sha256").update(inputs.workspaceDir).digest("hex").slice(0, 16);
|
|
37071
|
-
const workDir =
|
|
37014
|
+
const workDir = join14(homedir2(), ".arc-deploy", wsHash, "tf");
|
|
37072
37015
|
mkdirSync11(workDir, { recursive: true });
|
|
37073
37016
|
await materializeAssets(workDir, ASSETS.terraform);
|
|
37074
37017
|
const sshPubKey = inputs.tf.sshPublicKey ?? expandHome("~/.ssh/id_ed25519.pub");
|
|
@@ -37085,7 +37028,7 @@ async function runTerraform(inputs) {
|
|
|
37085
37028
|
].join(`
|
|
37086
37029
|
`) + `
|
|
37087
37030
|
`;
|
|
37088
|
-
writeFileSync11(
|
|
37031
|
+
writeFileSync11(join14(workDir, "terraform.tfvars"), tfvars);
|
|
37089
37032
|
await runTf(workDir, ["init", "-input=false", "-no-color"]);
|
|
37090
37033
|
await runTf(workDir, [
|
|
37091
37034
|
"apply",
|
|
@@ -37143,20 +37086,20 @@ function expandHome(p) {
|
|
|
37143
37086
|
}
|
|
37144
37087
|
|
|
37145
37088
|
// src/deploy/config.ts
|
|
37146
|
-
import { existsSync as existsSync14, readFileSync as
|
|
37147
|
-
import { join as
|
|
37089
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
37090
|
+
import { join as join16 } from "path";
|
|
37148
37091
|
|
|
37149
37092
|
// src/deploy/env-file.ts
|
|
37150
|
-
import { appendFileSync, existsSync as existsSync13, readFileSync as
|
|
37151
|
-
import { join as
|
|
37093
|
+
import { appendFileSync, existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
37094
|
+
import { join as join15 } from "path";
|
|
37152
37095
|
function loadDeployEnvFiles(rootDir, envNames) {
|
|
37153
|
-
const globalsPath =
|
|
37154
|
-
const globals = existsSync13(globalsPath) ? parseEnvFile(
|
|
37096
|
+
const globalsPath = join15(rootDir, "deploy.arc.env");
|
|
37097
|
+
const globals = existsSync13(globalsPath) ? parseEnvFile(readFileSync12(globalsPath, "utf-8"), globalsPath) : {};
|
|
37155
37098
|
const perEnv = {};
|
|
37156
37099
|
for (const name of envNames) {
|
|
37157
|
-
const envPath =
|
|
37100
|
+
const envPath = join15(rootDir, `deploy.arc.${name}.env`);
|
|
37158
37101
|
if (existsSync13(envPath)) {
|
|
37159
|
-
perEnv[name] = parseEnvFile(
|
|
37102
|
+
perEnv[name] = parseEnvFile(readFileSync12(envPath, "utf-8"), envPath);
|
|
37160
37103
|
}
|
|
37161
37104
|
}
|
|
37162
37105
|
return { globals, perEnv };
|
|
@@ -37172,16 +37115,16 @@ function ensurePersistedSecret(rootDir, scope, key, generate) {
|
|
|
37172
37115
|
if (process.env[key])
|
|
37173
37116
|
return process.env[key];
|
|
37174
37117
|
const fileName = scope === "globals" ? "deploy.arc.env" : `deploy.arc.${scope}.env`;
|
|
37175
|
-
const path4 =
|
|
37118
|
+
const path4 = join15(rootDir, fileName);
|
|
37176
37119
|
if (existsSync13(path4)) {
|
|
37177
|
-
const existing = parseEnvFile(
|
|
37120
|
+
const existing = parseEnvFile(readFileSync12(path4, "utf-8"), path4);
|
|
37178
37121
|
if (existing[key]) {
|
|
37179
37122
|
process.env[key] = existing[key];
|
|
37180
37123
|
return existing[key];
|
|
37181
37124
|
}
|
|
37182
37125
|
}
|
|
37183
37126
|
const value = generate();
|
|
37184
|
-
const prefix = existsSync13(path4) && !
|
|
37127
|
+
const prefix = existsSync13(path4) && !readFileSync12(path4, "utf-8").endsWith(`
|
|
37185
37128
|
`) ? `
|
|
37186
37129
|
` : "";
|
|
37187
37130
|
appendFileSync(path4, `${prefix}${key}=${value}
|
|
@@ -37217,7 +37160,7 @@ function parseEnvFile(content, pathForErrors) {
|
|
|
37217
37160
|
// src/deploy/config.ts
|
|
37218
37161
|
var DEPLOY_CONFIG_FILE = "deploy.arc.json";
|
|
37219
37162
|
function deployConfigPath(rootDir) {
|
|
37220
|
-
return
|
|
37163
|
+
return join16(rootDir, DEPLOY_CONFIG_FILE);
|
|
37221
37164
|
}
|
|
37222
37165
|
function deployConfigExists(rootDir) {
|
|
37223
37166
|
return existsSync14(deployConfigPath(rootDir));
|
|
@@ -37227,7 +37170,7 @@ function loadDeployConfig(rootDir) {
|
|
|
37227
37170
|
if (!existsSync14(path4)) {
|
|
37228
37171
|
throw new Error(`Missing ${DEPLOY_CONFIG_FILE} at ${path4}`);
|
|
37229
37172
|
}
|
|
37230
|
-
const raw =
|
|
37173
|
+
const raw = readFileSync13(path4, "utf-8");
|
|
37231
37174
|
let parsed;
|
|
37232
37175
|
try {
|
|
37233
37176
|
parsed = JSON.parse(raw);
|
|
@@ -37250,7 +37193,7 @@ function loadDeployConfig(rootDir) {
|
|
|
37250
37193
|
}
|
|
37251
37194
|
function saveDeployConfig(rootDir, cfg) {
|
|
37252
37195
|
const path4 = deployConfigPath(rootDir);
|
|
37253
|
-
const raw = existsSync14(path4) ? JSON.parse(
|
|
37196
|
+
const raw = existsSync14(path4) ? JSON.parse(readFileSync13(path4, "utf-8")) : {};
|
|
37254
37197
|
raw.target = { ...raw.target, ...cfg.target };
|
|
37255
37198
|
writeFileSync12(path4, JSON.stringify(raw, null, 2) + `
|
|
37256
37199
|
`);
|
|
@@ -37463,14 +37406,14 @@ function cfgErr(path4, expected) {
|
|
|
37463
37406
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
37464
37407
|
import { existsSync as existsSync15 } from "fs";
|
|
37465
37408
|
import { homedir as homedir3 } from "os";
|
|
37466
|
-
import { join as
|
|
37409
|
+
import { join as join17 } from "path";
|
|
37467
37410
|
function pickSshKey(target) {
|
|
37468
37411
|
if (target.sshKey) {
|
|
37469
|
-
const expanded = target.sshKey.startsWith("~") ?
|
|
37412
|
+
const expanded = target.sshKey.startsWith("~") ? join17(homedir3(), target.sshKey.slice(1)) : target.sshKey;
|
|
37470
37413
|
return existsSync15(expanded) ? expanded : null;
|
|
37471
37414
|
}
|
|
37472
37415
|
for (const name of ["id_ed25519", "id_ecdsa", "id_rsa"]) {
|
|
37473
|
-
const path4 =
|
|
37416
|
+
const path4 = join17(homedir3(), ".ssh", name);
|
|
37474
37417
|
if (existsSync15(path4))
|
|
37475
37418
|
return path4;
|
|
37476
37419
|
}
|
|
@@ -37706,7 +37649,7 @@ async function isRegistryRunning(cfg) {
|
|
|
37706
37649
|
}
|
|
37707
37650
|
async function upStack(inputs) {
|
|
37708
37651
|
const { cfg } = inputs;
|
|
37709
|
-
const workDir =
|
|
37652
|
+
const workDir = join18(tmpdir2(), "arc-deploy", `stack-${Date.now()}`);
|
|
37710
37653
|
mkdirSync12(workDir, { recursive: true });
|
|
37711
37654
|
await assertRegistryDnsResolves(cfg);
|
|
37712
37655
|
const password = process.env[cfg.registry.passwordEnv];
|
|
@@ -37714,9 +37657,9 @@ async function upStack(inputs) {
|
|
|
37714
37657
|
throw new Error(`Registry password env var ${cfg.registry.passwordEnv} is not set. ` + `Set it (e.g. \`export ${cfg.registry.passwordEnv}=...\`) before bootstrap.`);
|
|
37715
37658
|
}
|
|
37716
37659
|
const htpasswdLine = await generateHtpasswd(cfg.registry.username, password);
|
|
37717
|
-
writeFileSync13(
|
|
37718
|
-
writeFileSync13(
|
|
37719
|
-
writeFileSync13(
|
|
37660
|
+
writeFileSync13(join18(workDir, "htpasswd"), htpasswdLine);
|
|
37661
|
+
writeFileSync13(join18(workDir, "Caddyfile"), generateCaddyfile(cfg));
|
|
37662
|
+
writeFileSync13(join18(workDir, "docker-compose.yml"), generateCompose({ cfg }));
|
|
37720
37663
|
let observabilityFiles = null;
|
|
37721
37664
|
let observabilityHtpasswd = null;
|
|
37722
37665
|
if (cfg.observability?.enabled) {
|
|
@@ -37728,30 +37671,30 @@ async function upStack(inputs) {
|
|
|
37728
37671
|
}
|
|
37729
37672
|
observabilityHtpasswd = await generateCaddyBasicAuthLine("admin", adminPassword);
|
|
37730
37673
|
for (const [relPath, contents] of Object.entries(observabilityFiles)) {
|
|
37731
|
-
const fullPath =
|
|
37674
|
+
const fullPath = join18(workDir, relPath);
|
|
37732
37675
|
mkdirSync12(dirname9(fullPath), { recursive: true });
|
|
37733
37676
|
writeFileSync13(fullPath, contents);
|
|
37734
37677
|
}
|
|
37735
|
-
writeFileSync13(
|
|
37678
|
+
writeFileSync13(join18(workDir, "observability-htpasswd"), observabilityHtpasswd);
|
|
37736
37679
|
}
|
|
37737
37680
|
await assertExec(cfg.target, `sudo mkdir -p ${cfg.target.remoteDir} && sudo chown ${cfg.target.user}:${cfg.target.user} ${cfg.target.remoteDir}`);
|
|
37738
37681
|
for (const name of Object.keys(cfg.envs)) {
|
|
37739
37682
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/${name}`);
|
|
37740
37683
|
}
|
|
37741
37684
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/registry-auth`);
|
|
37742
|
-
await scpUpload(cfg.target,
|
|
37743
|
-
await scpUpload(cfg.target,
|
|
37744
|
-
await scpUpload(cfg.target,
|
|
37685
|
+
await scpUpload(cfg.target, join18(workDir, "Caddyfile"), `${cfg.target.remoteDir}/Caddyfile`);
|
|
37686
|
+
await scpUpload(cfg.target, join18(workDir, "docker-compose.yml"), `${cfg.target.remoteDir}/docker-compose.yml`);
|
|
37687
|
+
await scpUpload(cfg.target, join18(workDir, "htpasswd"), `${cfg.target.remoteDir}/registry-auth/htpasswd`);
|
|
37745
37688
|
if (observabilityFiles && observabilityHtpasswd) {
|
|
37746
37689
|
await assertExec(cfg.target, `mkdir -p ${cfg.target.remoteDir}/observability/grafana-dashboards`);
|
|
37747
37690
|
for (const relPath of Object.keys(observabilityFiles)) {
|
|
37748
|
-
const localDir = dirname9(
|
|
37691
|
+
const localDir = dirname9(join18(workDir, relPath));
|
|
37749
37692
|
mkdirSync12(localDir, { recursive: true });
|
|
37750
37693
|
}
|
|
37751
37694
|
for (const relPath of Object.keys(observabilityFiles)) {
|
|
37752
|
-
await scpUpload(cfg.target,
|
|
37695
|
+
await scpUpload(cfg.target, join18(workDir, relPath), `${cfg.target.remoteDir}/${relPath}`);
|
|
37753
37696
|
}
|
|
37754
|
-
await scpUpload(cfg.target,
|
|
37697
|
+
await scpUpload(cfg.target, join18(workDir, "observability-htpasswd"), `${cfg.target.remoteDir}/observability-htpasswd`);
|
|
37755
37698
|
}
|
|
37756
37699
|
await assertExec(cfg.target, `touch ${cfg.target.remoteDir}/.env`);
|
|
37757
37700
|
if (cfg.observability?.enabled) {
|
|
@@ -37919,12 +37862,12 @@ import {
|
|
|
37919
37862
|
copyFileSync as copyFileSync2,
|
|
37920
37863
|
existsSync as existsSync16,
|
|
37921
37864
|
mkdirSync as mkdirSync13,
|
|
37922
|
-
readFileSync as
|
|
37865
|
+
readFileSync as readFileSync14,
|
|
37923
37866
|
realpathSync as realpathSync2,
|
|
37924
37867
|
writeFileSync as writeFileSync14
|
|
37925
37868
|
} from "fs";
|
|
37926
37869
|
import { tmpdir as tmpdir3 } from "os";
|
|
37927
|
-
import { dirname as dirname10, join as
|
|
37870
|
+
import { dirname as dirname10, join as join19 } from "path";
|
|
37928
37871
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
37929
37872
|
|
|
37930
37873
|
// src/deploy/image-template.ts
|
|
@@ -37974,7 +37917,7 @@ function generateDockerfile(inputs) {
|
|
|
37974
37917
|
// src/deploy/image.ts
|
|
37975
37918
|
async function buildImage(ws, opts) {
|
|
37976
37919
|
await ensureDocker();
|
|
37977
|
-
const manifestPath =
|
|
37920
|
+
const manifestPath = join19(ws.arcDir, "manifest.json");
|
|
37978
37921
|
if (!existsSync16(manifestPath)) {
|
|
37979
37922
|
throw new Error(`No build manifest at ${manifestPath}. Run \`arc platform build\` first or omit --skip-build.`);
|
|
37980
37923
|
}
|
|
@@ -37985,9 +37928,9 @@ async function buildImage(ws, opts) {
|
|
|
37985
37928
|
const dockerfileInputs = collectDockerfileInputs(ws);
|
|
37986
37929
|
const dockerfile = generateDockerfile(dockerfileInputs);
|
|
37987
37930
|
const buildContextDir = ws.rootDir;
|
|
37988
|
-
const dockerfileDir =
|
|
37931
|
+
const dockerfileDir = join19(tmpdir3(), `arc-image-${Date.now()}`);
|
|
37989
37932
|
mkdirSync13(dockerfileDir, { recursive: true });
|
|
37990
|
-
const dockerfilePath =
|
|
37933
|
+
const dockerfilePath = join19(dockerfileDir, "Dockerfile");
|
|
37991
37934
|
writeFileSync14(dockerfilePath, dockerfile);
|
|
37992
37935
|
const buildArgs = [
|
|
37993
37936
|
"build",
|
|
@@ -38013,19 +37956,19 @@ async function buildImage(ws, opts) {
|
|
|
38013
37956
|
}
|
|
38014
37957
|
function embedCliBundle(ws) {
|
|
38015
37958
|
const source = locateCliBundle();
|
|
38016
|
-
const target =
|
|
37959
|
+
const target = join19(ws.arcDir, "host.js");
|
|
38017
37960
|
copyFileSync2(source, target);
|
|
38018
37961
|
}
|
|
38019
37962
|
function locateCliBundle() {
|
|
38020
37963
|
const here = fileURLToPath7(import.meta.url);
|
|
38021
37964
|
let cur = dirname10(here);
|
|
38022
37965
|
while (cur !== "/" && cur !== "") {
|
|
38023
|
-
const candidate =
|
|
37966
|
+
const candidate = join19(cur, "package.json");
|
|
38024
37967
|
if (existsSync16(candidate)) {
|
|
38025
37968
|
try {
|
|
38026
|
-
const pkg = JSON.parse(
|
|
37969
|
+
const pkg = JSON.parse(readFileSync14(candidate, "utf-8"));
|
|
38027
37970
|
if (pkg.name === "@arcote.tech/arc-cli") {
|
|
38028
|
-
const distIndex =
|
|
37971
|
+
const distIndex = join19(realpathSync2(cur), "dist", "index.js");
|
|
38029
37972
|
if (!existsSync16(distIndex)) {
|
|
38030
37973
|
throw new Error(`arc-cli bundle missing at ${distIndex}. Run \`bun run build\` in packages/cli/.`);
|
|
38031
37974
|
}
|
|
@@ -38058,17 +38001,17 @@ async function ensureDocker() {
|
|
|
38058
38001
|
}
|
|
38059
38002
|
}
|
|
38060
38003
|
function computeContentHash(manifestPath) {
|
|
38061
|
-
const raw = JSON.parse(
|
|
38004
|
+
const raw = JSON.parse(readFileSync14(manifestPath, "utf-8"));
|
|
38062
38005
|
delete raw.buildTime;
|
|
38063
38006
|
const canonical = JSON.stringify(raw);
|
|
38064
38007
|
return createHash3("sha256").update(canonical).digest("hex").slice(0, 12);
|
|
38065
38008
|
}
|
|
38066
38009
|
function collectDockerfileInputs(ws) {
|
|
38067
|
-
const hasPublicDir = existsSync16(
|
|
38068
|
-
const hasLocales = existsSync16(
|
|
38010
|
+
const hasPublicDir = existsSync16(join19(ws.rootDir, "public"));
|
|
38011
|
+
const hasLocales = existsSync16(join19(ws.rootDir, "locales"));
|
|
38069
38012
|
let manifestPath;
|
|
38070
38013
|
for (const name of ["manifest.webmanifest", "manifest.json"]) {
|
|
38071
|
-
if (existsSync16(
|
|
38014
|
+
if (existsSync16(join19(ws.rootDir, name))) {
|
|
38072
38015
|
manifestPath = name;
|
|
38073
38016
|
break;
|
|
38074
38017
|
}
|
|
@@ -38897,7 +38840,7 @@ async function platformDeploy(envArg, options = {}) {
|
|
|
38897
38840
|
err(`Unknown env "${envArg}". Known: ${Object.keys(cfg.envs).join(", ")}`);
|
|
38898
38841
|
process.exit(1);
|
|
38899
38842
|
})() : Object.keys(cfg.envs);
|
|
38900
|
-
const manifestPath =
|
|
38843
|
+
const manifestPath = join20(ws.arcDir, "manifest.json");
|
|
38901
38844
|
if (!options.imageTag) {
|
|
38902
38845
|
const needBuild = options.rebuild || !existsSync17(manifestPath);
|
|
38903
38846
|
if (needBuild && !options.skipBuild) {
|
|
@@ -38970,9 +38913,9 @@ function readCliVersion2() {
|
|
|
38970
38913
|
let cur = dirname11(fileURLToPath8(import.meta.url));
|
|
38971
38914
|
const root = dirname11(cur).startsWith("/") ? "/" : ".";
|
|
38972
38915
|
while (cur !== root && cur !== "") {
|
|
38973
|
-
const candidate =
|
|
38916
|
+
const candidate = join20(cur, "package.json");
|
|
38974
38917
|
if (existsSync17(candidate)) {
|
|
38975
|
-
const pkg = JSON.parse(
|
|
38918
|
+
const pkg = JSON.parse(readFileSync15(candidate, "utf-8"));
|
|
38976
38919
|
if (pkg.name === "@arcote.tech/arc-cli") {
|
|
38977
38920
|
return pkg.version ?? "unknown";
|
|
38978
38921
|
}
|
|
@@ -38988,8 +38931,8 @@ function readCliVersion2() {
|
|
|
38988
38931
|
}
|
|
38989
38932
|
}
|
|
38990
38933
|
async function hashDeployConfig(rootDir) {
|
|
38991
|
-
const p2 =
|
|
38992
|
-
const content =
|
|
38934
|
+
const p2 = join20(rootDir, "deploy.arc.json");
|
|
38935
|
+
const content = readFileSync15(p2);
|
|
38993
38936
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
38994
38937
|
hasher.update(content);
|
|
38995
38938
|
hasher.update(readCliVersion2());
|
|
@@ -38997,8 +38940,8 @@ async function hashDeployConfig(rootDir) {
|
|
|
38997
38940
|
}
|
|
38998
38941
|
|
|
38999
38942
|
// src/platform/startup.ts
|
|
39000
|
-
import { existsSync as existsSync19, readFileSync as
|
|
39001
|
-
import { join as
|
|
38943
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16, watch } from "fs";
|
|
38944
|
+
import { join as join22 } from "path";
|
|
39002
38945
|
|
|
39003
38946
|
// ../host/src/create-server.ts
|
|
39004
38947
|
var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
|
|
@@ -40055,66 +39998,14 @@ function arcHttpHandlers(ch, cm) {
|
|
|
40055
39998
|
];
|
|
40056
39999
|
}
|
|
40057
40000
|
// ../host/src/middleware/ws.ts
|
|
40058
|
-
import {
|
|
40059
|
-
|
|
40060
|
-
ScopedStore,
|
|
40061
|
-
checkItemMatchesWhere
|
|
40062
|
-
} from "@arcote.tech/arc";
|
|
40063
|
-
var viewSubscribers = new Map;
|
|
40001
|
+
import { LiveQuery } from "@arcote.tech/arc";
|
|
40002
|
+
var clientQuerySubs = new Map;
|
|
40064
40003
|
function cleanupClientSubs(clientId) {
|
|
40065
|
-
const
|
|
40066
|
-
|
|
40067
|
-
for (const
|
|
40068
|
-
|
|
40069
|
-
|
|
40070
|
-
}
|
|
40071
|
-
}
|
|
40072
|
-
}
|
|
40073
|
-
function filterChangeForSubscriber(change, restrictions) {
|
|
40074
|
-
const newMatches = change.newRow !== null && (restrictions === null || checkItemMatchesWhere(change.newRow, restrictions));
|
|
40075
|
-
if (newMatches) {
|
|
40076
|
-
return { type: "set", id: change.id, item: change.newRow };
|
|
40077
|
-
}
|
|
40078
|
-
const oldMatches = change.oldRow !== null && (restrictions === null || checkItemMatchesWhere(change.oldRow, restrictions));
|
|
40079
|
-
if (oldMatches) {
|
|
40080
|
-
return { type: "delete", id: change.id, item: null };
|
|
40081
|
-
}
|
|
40082
|
-
return null;
|
|
40083
|
-
}
|
|
40084
|
-
function broadcastViewChanges(committed, connectionManager) {
|
|
40085
|
-
const byStore = new Map;
|
|
40086
|
-
for (const change of committed) {
|
|
40087
|
-
let list = byStore.get(change.store);
|
|
40088
|
-
if (!list) {
|
|
40089
|
-
list = [];
|
|
40090
|
-
byStore.set(change.store, list);
|
|
40091
|
-
}
|
|
40092
|
-
list.push(change);
|
|
40093
|
-
}
|
|
40094
|
-
for (const [viewName, changes] of byStore) {
|
|
40095
|
-
const subs = viewSubscribers.get(viewName);
|
|
40096
|
-
if (!subs || subs.size === 0)
|
|
40097
|
-
continue;
|
|
40098
|
-
for (const sub of subs.values()) {
|
|
40099
|
-
const filtered = [];
|
|
40100
|
-
for (const change of changes) {
|
|
40101
|
-
const wireChange = filterChangeForSubscriber(change, sub.restrictions);
|
|
40102
|
-
if (wireChange)
|
|
40103
|
-
filtered.push(wireChange);
|
|
40104
|
-
}
|
|
40105
|
-
if (filtered.length === 0)
|
|
40106
|
-
continue;
|
|
40107
|
-
if (sub.buffer) {
|
|
40108
|
-
sub.buffer.push(...filtered);
|
|
40109
|
-
continue;
|
|
40110
|
-
}
|
|
40111
|
-
connectionManager.sendToClient(sub.clientId, {
|
|
40112
|
-
type: "view-changes",
|
|
40113
|
-
element: viewName,
|
|
40114
|
-
scope: sub.scope,
|
|
40115
|
-
changes: filtered
|
|
40116
|
-
});
|
|
40117
|
-
}
|
|
40004
|
+
const subs = clientQuerySubs.get(clientId);
|
|
40005
|
+
if (subs) {
|
|
40006
|
+
for (const live of subs.values())
|
|
40007
|
+
live.stop();
|
|
40008
|
+
clientQuerySubs.delete(clientId);
|
|
40118
40009
|
}
|
|
40119
40010
|
}
|
|
40120
40011
|
function scopeAuthHandler() {
|
|
@@ -40213,82 +40104,62 @@ function executeCommandHandler() {
|
|
|
40213
40104
|
return true;
|
|
40214
40105
|
};
|
|
40215
40106
|
}
|
|
40216
|
-
function
|
|
40107
|
+
function querySubscriptionHandler() {
|
|
40217
40108
|
return async (client, message, ctx) => {
|
|
40218
|
-
if (message.type === "subscribe-
|
|
40219
|
-
const {
|
|
40109
|
+
if (message.type === "subscribe-query") {
|
|
40110
|
+
const { subscriptionId, descriptor } = message;
|
|
40220
40111
|
const scope = message.scope ?? "default";
|
|
40221
40112
|
const scopeToken = client.scopeTokens.get(scope) ?? null;
|
|
40222
40113
|
let rawToken = scopeToken?.raw ?? null;
|
|
40223
40114
|
if (!rawToken && client.scopeTokens.size > 0) {
|
|
40224
40115
|
rawToken = client.scopeTokens.values().next().value.raw;
|
|
40225
40116
|
}
|
|
40226
|
-
|
|
40227
|
-
|
|
40228
|
-
|
|
40229
|
-
|
|
40230
|
-
|
|
40231
|
-
|
|
40232
|
-
|
|
40233
|
-
|
|
40234
|
-
}
|
|
40235
|
-
|
|
40236
|
-
|
|
40237
|
-
|
|
40238
|
-
|
|
40239
|
-
|
|
40240
|
-
|
|
40241
|
-
|
|
40242
|
-
|
|
40243
|
-
|
|
40244
|
-
scope,
|
|
40245
|
-
items: []
|
|
40246
|
-
});
|
|
40247
|
-
return true;
|
|
40248
|
-
}
|
|
40249
|
-
const subscriber = {
|
|
40250
|
-
clientId: client.id,
|
|
40251
|
-
scope,
|
|
40252
|
-
restrictions,
|
|
40253
|
-
buffer: []
|
|
40254
|
-
};
|
|
40255
|
-
if (!viewSubscribers.has(elementName)) {
|
|
40256
|
-
viewSubscribers.set(elementName, new Map);
|
|
40117
|
+
clientQuerySubs.get(client.id)?.get(subscriptionId)?.stop();
|
|
40118
|
+
const live = new LiveQuery(ctx.contextHandler.getModel(), descriptor, scope, rawToken, (update) => {
|
|
40119
|
+
if (update.type === "changes") {
|
|
40120
|
+
ctx.connectionManager.sendToClient(client.id, {
|
|
40121
|
+
type: "query-changes",
|
|
40122
|
+
subscriptionId,
|
|
40123
|
+
changes: update.changes
|
|
40124
|
+
});
|
|
40125
|
+
} else {
|
|
40126
|
+
ctx.connectionManager.sendToClient(client.id, {
|
|
40127
|
+
type: "query-snapshot",
|
|
40128
|
+
subscriptionId,
|
|
40129
|
+
result: update.result ?? null
|
|
40130
|
+
});
|
|
40131
|
+
}
|
|
40132
|
+
});
|
|
40133
|
+
if (!clientQuerySubs.has(client.id)) {
|
|
40134
|
+
clientQuerySubs.set(client.id, new Map);
|
|
40257
40135
|
}
|
|
40258
|
-
|
|
40136
|
+
clientQuerySubs.get(client.id).set(subscriptionId, live);
|
|
40259
40137
|
try {
|
|
40260
|
-
const
|
|
40261
|
-
const items = restrictions ? await new ScopedStore(store, restrictions, false).find({}) : await store.find({});
|
|
40138
|
+
const result = await live.start();
|
|
40262
40139
|
ctx.connectionManager.sendToClient(client.id, {
|
|
40263
|
-
type: "
|
|
40264
|
-
|
|
40265
|
-
|
|
40266
|
-
items
|
|
40140
|
+
type: "query-snapshot",
|
|
40141
|
+
subscriptionId,
|
|
40142
|
+
result: result ?? null
|
|
40267
40143
|
});
|
|
40144
|
+
live.flush();
|
|
40268
40145
|
} catch (err3) {
|
|
40269
|
-
|
|
40270
|
-
|
|
40146
|
+
live.stop();
|
|
40147
|
+
clientQuerySubs.get(client.id)?.delete(subscriptionId);
|
|
40148
|
+
console.error(`[Arc] Query subscription error (${descriptor?.element}.${descriptor?.method}):`, err3);
|
|
40271
40149
|
ctx.connectionManager.sendToClient(client.id, {
|
|
40272
40150
|
type: "error",
|
|
40273
|
-
message: `Failed to subscribe to
|
|
40274
|
-
});
|
|
40275
|
-
return true;
|
|
40276
|
-
}
|
|
40277
|
-
const buffered = subscriber.buffer;
|
|
40278
|
-
subscriber.buffer = null;
|
|
40279
|
-
if (buffered.length > 0) {
|
|
40280
|
-
ctx.connectionManager.sendToClient(client.id, {
|
|
40281
|
-
type: "view-changes",
|
|
40282
|
-
element: elementName,
|
|
40283
|
-
scope,
|
|
40284
|
-
changes: buffered
|
|
40151
|
+
message: `Failed to subscribe to query "${descriptor?.element}.${descriptor?.method}"`
|
|
40285
40152
|
});
|
|
40286
40153
|
}
|
|
40287
40154
|
return true;
|
|
40288
40155
|
}
|
|
40289
|
-
if (message.type === "unsubscribe-
|
|
40290
|
-
const
|
|
40291
|
-
|
|
40156
|
+
if (message.type === "unsubscribe-query") {
|
|
40157
|
+
const subs = clientQuerySubs.get(client.id);
|
|
40158
|
+
const live = subs?.get(message.subscriptionId);
|
|
40159
|
+
if (live) {
|
|
40160
|
+
live.stop();
|
|
40161
|
+
subs.delete(message.subscriptionId);
|
|
40162
|
+
}
|
|
40292
40163
|
return true;
|
|
40293
40164
|
}
|
|
40294
40165
|
return false;
|
|
@@ -40300,7 +40171,7 @@ function arcWsHandlers() {
|
|
|
40300
40171
|
syncEventsHandler(),
|
|
40301
40172
|
requestSyncHandler(),
|
|
40302
40173
|
executeCommandHandler(),
|
|
40303
|
-
|
|
40174
|
+
querySubscriptionHandler()
|
|
40304
40175
|
];
|
|
40305
40176
|
}
|
|
40306
40177
|
// ../host/src/create-server.ts
|
|
@@ -40313,7 +40184,6 @@ async function createArcServer(config) {
|
|
|
40313
40184
|
const cronScheduler = new CronScheduler(contextHandler);
|
|
40314
40185
|
cronScheduler.start();
|
|
40315
40186
|
const connectionManager = new ConnectionManager;
|
|
40316
|
-
contextHandler.getEventPublisher().onViewChanges((changes) => broadcastViewChanges(changes, connectionManager));
|
|
40317
40187
|
function buildCorsHeaders(req) {
|
|
40318
40188
|
const origin = req?.headers.get("Origin") || "*";
|
|
40319
40189
|
return {
|
|
@@ -40491,7 +40361,7 @@ async function createArcServer(config) {
|
|
|
40491
40361
|
// src/platform/server.ts
|
|
40492
40362
|
init_i18n();
|
|
40493
40363
|
import { existsSync as existsSync18, mkdirSync as mkdirSync14 } from "fs";
|
|
40494
|
-
import { join as
|
|
40364
|
+
import { join as join21 } from "path";
|
|
40495
40365
|
async function resolveDbAdapterFactory(dbPath) {
|
|
40496
40366
|
const databaseUrl = process.env.DATABASE_URL;
|
|
40497
40367
|
if (databaseUrl && databaseUrl.length > 0) {
|
|
@@ -40623,15 +40493,13 @@ function parseArcTokensHeader(header) {
|
|
|
40623
40493
|
return payloads;
|
|
40624
40494
|
}
|
|
40625
40495
|
async function filterManifestForTokens(manifest, moduleAccessMap, tokenPayloads) {
|
|
40626
|
-
const
|
|
40496
|
+
const tokensByName = new Map;
|
|
40627
40497
|
for (const t of tokenPayloads) {
|
|
40628
40498
|
if (t?.tokenType)
|
|
40629
|
-
|
|
40499
|
+
tokensByName.set(t.tokenType, t);
|
|
40630
40500
|
}
|
|
40631
40501
|
const filteredGroups = {};
|
|
40632
40502
|
for (const [name, group] of Object.entries(manifest.groups)) {
|
|
40633
|
-
if (!allowedGroups.has(name))
|
|
40634
|
-
continue;
|
|
40635
40503
|
let allGranted = true;
|
|
40636
40504
|
for (const moduleName of group.modules) {
|
|
40637
40505
|
const access = moduleAccessMap.get(moduleName);
|
|
@@ -40639,9 +40507,7 @@ async function filterManifestForTokens(manifest, moduleAccessMap, tokenPayloads)
|
|
|
40639
40507
|
continue;
|
|
40640
40508
|
let granted = false;
|
|
40641
40509
|
for (const rule of access.rules) {
|
|
40642
|
-
|
|
40643
|
-
continue;
|
|
40644
|
-
const matching = tokenPayloads.find((t) => t.tokenType === name);
|
|
40510
|
+
const matching = tokensByName.get(rule.token.name);
|
|
40645
40511
|
if (!matching)
|
|
40646
40512
|
continue;
|
|
40647
40513
|
granted = rule.check ? await rule.check(matching) : true;
|
|
@@ -40682,28 +40548,28 @@ function staticFilesHandler(ws, devMode, getManifest) {
|
|
|
40682
40548
|
return new Response("Forbidden", { status: 403, headers: ctx.corsHeaders });
|
|
40683
40549
|
}
|
|
40684
40550
|
}
|
|
40685
|
-
return serveFile(
|
|
40551
|
+
return serveFile(join21(ws.browserDir, file), {
|
|
40686
40552
|
...ctx.corsHeaders,
|
|
40687
40553
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
40688
40554
|
});
|
|
40689
40555
|
}
|
|
40690
40556
|
if (path4.startsWith("/locales/"))
|
|
40691
|
-
return serveFile(
|
|
40557
|
+
return serveFile(join21(ws.arcDir, path4.slice(1)), {
|
|
40692
40558
|
...ctx.corsHeaders,
|
|
40693
40559
|
"Cache-Control": devMode ? "no-cache" : "max-age=300,stale-while-revalidate=3600"
|
|
40694
40560
|
});
|
|
40695
40561
|
if (path4.startsWith("/assets/"))
|
|
40696
|
-
return serveFile(
|
|
40562
|
+
return serveFile(join21(ws.assetsDir, path4.slice(8)), {
|
|
40697
40563
|
...ctx.corsHeaders,
|
|
40698
40564
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
40699
40565
|
});
|
|
40700
40566
|
if (path4 === "/styles.css")
|
|
40701
|
-
return serveFile(
|
|
40567
|
+
return serveFile(join21(ws.arcDir, "styles.css"), {
|
|
40702
40568
|
...ctx.corsHeaders,
|
|
40703
40569
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
40704
40570
|
});
|
|
40705
40571
|
if (path4 === "/theme.css")
|
|
40706
|
-
return serveFile(
|
|
40572
|
+
return serveFile(join21(ws.arcDir, "theme.css"), {
|
|
40707
40573
|
...ctx.corsHeaders,
|
|
40708
40574
|
"Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
|
|
40709
40575
|
});
|
|
@@ -40711,7 +40577,7 @@ function staticFilesHandler(ws, devMode, getManifest) {
|
|
|
40711
40577
|
return serveFile(ws.manifest.path, ctx.corsHeaders);
|
|
40712
40578
|
}
|
|
40713
40579
|
if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
|
|
40714
|
-
const publicFile =
|
|
40580
|
+
const publicFile = join21(ws.publicDir, path4.slice(1));
|
|
40715
40581
|
if (existsSync18(publicFile))
|
|
40716
40582
|
return serveFile(publicFile, ctx.corsHeaders);
|
|
40717
40583
|
}
|
|
@@ -40866,7 +40732,7 @@ async function startPlatformServer(opts) {
|
|
|
40866
40732
|
stop: () => server.stop()
|
|
40867
40733
|
};
|
|
40868
40734
|
}
|
|
40869
|
-
const dbPath = opts.dbPath ||
|
|
40735
|
+
const dbPath = opts.dbPath || join21(ws.arcDir, "data", "arc.db");
|
|
40870
40736
|
const baseDbFactory = await resolveDbAdapterFactory(dbPath);
|
|
40871
40737
|
let dbAdapterFactory = baseDbFactory;
|
|
40872
40738
|
if (telemetry) {
|
|
@@ -40907,17 +40773,17 @@ async function startPlatformServer(opts) {
|
|
|
40907
40773
|
async function startPlatform(opts) {
|
|
40908
40774
|
const { ws, devMode } = opts;
|
|
40909
40775
|
const port = opts.port ?? parseInt(process.env.PORT || "5005", 10);
|
|
40910
|
-
const dbPath = opts.dbPath ??
|
|
40776
|
+
const dbPath = opts.dbPath ?? join22(ws.rootDir, ".arc", "data", devMode ? "dev.db" : "prod.db");
|
|
40911
40777
|
let manifest;
|
|
40912
40778
|
if (devMode) {
|
|
40913
40779
|
manifest = await buildAll(ws);
|
|
40914
40780
|
} else {
|
|
40915
|
-
const manifestPath =
|
|
40781
|
+
const manifestPath = join22(ws.arcDir, "manifest.json");
|
|
40916
40782
|
if (!existsSync19(manifestPath)) {
|
|
40917
40783
|
err("No build found. Run `arc platform build` first.");
|
|
40918
40784
|
process.exit(1);
|
|
40919
40785
|
}
|
|
40920
|
-
manifest = JSON.parse(
|
|
40786
|
+
manifest = JSON.parse(readFileSync16(manifestPath, "utf-8"));
|
|
40921
40787
|
}
|
|
40922
40788
|
log2("Loading server context...");
|
|
40923
40789
|
const { context: context2, moduleAccess } = await loadServerContext(ws);
|
|
@@ -40993,7 +40859,7 @@ function attachDevWatcher(ws, platform3) {
|
|
|
40993
40859
|
}, 300);
|
|
40994
40860
|
};
|
|
40995
40861
|
for (const pkg of ws.packages) {
|
|
40996
|
-
const srcDir =
|
|
40862
|
+
const srcDir = join22(pkg.path, "src");
|
|
40997
40863
|
if (!existsSync19(srcDir))
|
|
40998
40864
|
continue;
|
|
40999
40865
|
watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
@@ -41004,7 +40870,7 @@ function attachDevWatcher(ws, platform3) {
|
|
|
41004
40870
|
triggerRebuild();
|
|
41005
40871
|
});
|
|
41006
40872
|
}
|
|
41007
|
-
const localesDir =
|
|
40873
|
+
const localesDir = join22(ws.rootDir, "locales");
|
|
41008
40874
|
if (existsSync19(localesDir)) {
|
|
41009
40875
|
watch(localesDir, { recursive: false }, (_event, filename) => {
|
|
41010
40876
|
if (!filename?.endsWith(".po"))
|