@happyvertical/smrt-web 0.51.28 → 0.51.29
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/AGENTS.md +2 -2
- package/README.md +48 -3
- package/agents/offline-outbox.md +75 -4
- package/agents/version-persistence.md +19 -0
- package/dist/chunks/{src-0IQGhPIM.js → src-B2uQWayW.js} +476 -81
- package/dist/chunks/src-B2uQWayW.js.map +1 -0
- package/dist/index.d.ts +291 -11
- package/dist/index.js +2 -2
- package/dist/webmcp.js +1 -1
- package/package.json +3 -3
- package/dist/chunks/src-0IQGhPIM.js.map +0 -1
|
@@ -372,6 +372,82 @@ async function executeSmrtWebDataSurfaceAction(transport, request, options) {
|
|
|
372
372
|
if (result.requestId !== request.requestId || result.actionId !== request.actionId || result.phase !== request.phase || !sameIdentity(result.identity, request.identity)) throw new TypeError("Data surface action result does not match its request");
|
|
373
373
|
return result;
|
|
374
374
|
}
|
|
375
|
+
var RETRYABLE_ACTION_REASONS = /* @__PURE__ */ new Set([
|
|
376
|
+
"idempotency_in_progress",
|
|
377
|
+
"background_unavailable",
|
|
378
|
+
"execution_failed"
|
|
379
|
+
]);
|
|
380
|
+
var AUTH_ACTION_REASONS = /* @__PURE__ */ new Set([
|
|
381
|
+
"auth_required",
|
|
382
|
+
"unauthenticated",
|
|
383
|
+
"unauthorized"
|
|
384
|
+
]);
|
|
385
|
+
var STALE_ACTION_REASONS = /* @__PURE__ */ new Set(["stale_revision", "stale_preview"]);
|
|
386
|
+
var UNUSABLE_CONFIRMATION_REASONS = /* @__PURE__ */ new Set([
|
|
387
|
+
"invalid_or_expired_confirmation",
|
|
388
|
+
"confirmation_mismatch",
|
|
389
|
+
"confirmation_replayed",
|
|
390
|
+
"confirmation_required",
|
|
391
|
+
"stale_preview"
|
|
392
|
+
]);
|
|
393
|
+
function classifySmrtWebDataSurfaceActionResult(result) {
|
|
394
|
+
if (result.ok) return { status: "applied" };
|
|
395
|
+
const reason = result.reason ?? "rejected";
|
|
396
|
+
if (RETRYABLE_ACTION_REASONS.has(reason)) return {
|
|
397
|
+
status: "rejected",
|
|
398
|
+
reason: "write_failed"
|
|
399
|
+
};
|
|
400
|
+
if (AUTH_ACTION_REASONS.has(reason)) return {
|
|
401
|
+
status: "rejected",
|
|
402
|
+
reason: "auth_required"
|
|
403
|
+
};
|
|
404
|
+
if (STALE_ACTION_REASONS.has(reason)) return {
|
|
405
|
+
status: "conflict",
|
|
406
|
+
reason: "stale_write"
|
|
407
|
+
};
|
|
408
|
+
return {
|
|
409
|
+
status: "rejected",
|
|
410
|
+
reason
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function dataSurfaceActionCommandTransport(options) {
|
|
414
|
+
const classify = options.classify ?? classifySmrtWebDataSurfaceActionResult;
|
|
415
|
+
return async (command) => {
|
|
416
|
+
let pinned = command.pinned;
|
|
417
|
+
if (pinned === void 0) {
|
|
418
|
+
pinned = { request: await options.request(command) };
|
|
419
|
+
await command.pin(pinned);
|
|
420
|
+
}
|
|
421
|
+
const base = pinned.request;
|
|
422
|
+
const requestId = (phase) => `${command.idempotencyKey}:${command.attempt}:${phase}`;
|
|
423
|
+
const apply = (confirmationToken) => executeSmrtWebDataSurfaceAction(options.transport, {
|
|
424
|
+
...base,
|
|
425
|
+
version: 1,
|
|
426
|
+
phase: "apply",
|
|
427
|
+
requestId: requestId("apply"),
|
|
428
|
+
idempotencyKey: command.idempotencyKey,
|
|
429
|
+
...confirmationToken === void 0 ? {} : { confirmationToken }
|
|
430
|
+
});
|
|
431
|
+
if (!options.preview) return classify(await apply(void 0));
|
|
432
|
+
if (pinned.confirmationToken !== void 0) {
|
|
433
|
+
const applied = await apply(pinned.confirmationToken);
|
|
434
|
+
if (applied.ok || !UNUSABLE_CONFIRMATION_REASONS.has(applied.reason ?? "")) return classify(applied);
|
|
435
|
+
}
|
|
436
|
+
const preview = await executeSmrtWebDataSurfaceAction(options.transport, {
|
|
437
|
+
...base,
|
|
438
|
+
version: 1,
|
|
439
|
+
phase: "preview",
|
|
440
|
+
requestId: requestId("preview")
|
|
441
|
+
});
|
|
442
|
+
if (!preview.ok) return classify(preview);
|
|
443
|
+
pinned = {
|
|
444
|
+
request: base,
|
|
445
|
+
...preview.confirmationToken === void 0 ? {} : { confirmationToken: preview.confirmationToken }
|
|
446
|
+
};
|
|
447
|
+
await command.pin(pinned);
|
|
448
|
+
return classify(await apply(preview.confirmationToken));
|
|
449
|
+
};
|
|
450
|
+
}
|
|
375
451
|
//#endregion
|
|
376
452
|
//#region src/durable-store.ts
|
|
377
453
|
function durableStoreNamespace(key) {
|
|
@@ -461,6 +537,7 @@ var DurableOutboxQueue = class {
|
|
|
461
537
|
id: input.id,
|
|
462
538
|
payload: input.payload,
|
|
463
539
|
baseUpdatedAt: input.baseUpdatedAt,
|
|
540
|
+
...input.transport === void 0 ? {} : { transport: input.transport },
|
|
464
541
|
state: "pending",
|
|
465
542
|
attempts: 0,
|
|
466
543
|
nextAttemptAt: 0,
|
|
@@ -626,6 +703,10 @@ function computeBackoffDelay(attempts, backoff, random = Math.random) {
|
|
|
626
703
|
function envelopeKindToOp(kind) {
|
|
627
704
|
return kind === "insert" ? "create" : kind;
|
|
628
705
|
}
|
|
706
|
+
var SYNC_APPLY_ROUTE = "sync-apply";
|
|
707
|
+
function routeOf(row) {
|
|
708
|
+
return row.transport === void 0 ? SYNC_APPLY_ROUTE : `transport:${row.transport}`;
|
|
709
|
+
}
|
|
629
710
|
function newItemId() {
|
|
630
711
|
const cryptoRef = globalThis.crypto;
|
|
631
712
|
if (cryptoRef?.randomUUID) return cryptoRef.randomUUID();
|
|
@@ -646,6 +727,12 @@ var OutboxEngine = class {
|
|
|
646
727
|
* collection per object, but the shared-engine model does not forbid more).
|
|
647
728
|
*/
|
|
648
729
|
listenersByObject = /* @__PURE__ */ new Map();
|
|
730
|
+
/**
|
|
731
|
+
* The sync-apply endpoint, adopted from the first binding that declares one.
|
|
732
|
+
* A tab without one never leads the sync-apply route, so its rows wait for a
|
|
733
|
+
* tab that does (never dropped) — see `drainOnce`.
|
|
734
|
+
*/
|
|
735
|
+
syncApply;
|
|
649
736
|
/** Ref count: number of collections currently attached to this engine. */
|
|
650
737
|
refCount = 0;
|
|
651
738
|
/** The durable queue, once opened. undefined while opening / if IDB absent. */
|
|
@@ -654,10 +741,12 @@ var OutboxEngine = class {
|
|
|
654
741
|
ready;
|
|
655
742
|
/** True when IndexedDB was unavailable and the engine is a durable no-op. */
|
|
656
743
|
degraded = false;
|
|
657
|
-
/**
|
|
658
|
-
leadership
|
|
659
|
-
|
|
660
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Per-route leadership, requested when the first binding serving a route
|
|
746
|
+
* attaches and released when the last one detaches. Exactly one tab replays a
|
|
747
|
+
* given route; a tab never leads a route it has no binding for.
|
|
748
|
+
*/
|
|
749
|
+
leaders = /* @__PURE__ */ new Map();
|
|
661
750
|
/** Unregister fn from the durable-store registry. */
|
|
662
751
|
unregisterResource;
|
|
663
752
|
/** True once dispose() ran — guards late async continuations. */
|
|
@@ -668,8 +757,14 @@ var OutboxEngine = class {
|
|
|
668
757
|
* explicit retry wakes it. Items stay queued.
|
|
669
758
|
*/
|
|
670
759
|
paused = false;
|
|
760
|
+
/** Wipes issued but not yet cleared; replay is suspended meanwhile. */
|
|
761
|
+
wipesPending = 0;
|
|
762
|
+
/** Those wipes, so `dispose()` can let them finish before closing. */
|
|
763
|
+
pendingWipes = /* @__PURE__ */ new Set();
|
|
671
764
|
/** True while a drain pass is running, to coalesce concurrent triggers. */
|
|
672
765
|
draining = false;
|
|
766
|
+
/** The running drain, while `draining`. */
|
|
767
|
+
activeDrain;
|
|
673
768
|
/** A drain requested while one was in flight — run one more pass after. */
|
|
674
769
|
drainQueued = false;
|
|
675
770
|
/** Timer for the next backoff-scheduled drain, if any. */
|
|
@@ -679,8 +774,8 @@ var OutboxEngine = class {
|
|
|
679
774
|
constructor(config) {
|
|
680
775
|
this.config = config;
|
|
681
776
|
this.ready = this.open();
|
|
777
|
+
this.registerForWipe();
|
|
682
778
|
this.wireOnlineListener();
|
|
683
|
-
this.requestLeadership();
|
|
684
779
|
this.ready.then(() => {
|
|
685
780
|
if (!this.disposed) this.drain();
|
|
686
781
|
});
|
|
@@ -694,21 +789,39 @@ var OutboxEngine = class {
|
|
|
694
789
|
}
|
|
695
790
|
try {
|
|
696
791
|
this.queue = await openDurableOutboxQueue(this.config.namespace);
|
|
697
|
-
this.unregisterResource = this.config.registerResource(async () => {
|
|
698
|
-
await this.queue?.clear();
|
|
699
|
-
});
|
|
700
|
-
if (this.disposed) {
|
|
701
|
-
this.queue.close();
|
|
702
|
-
this.queue = void 0;
|
|
703
|
-
this.unregisterResource?.();
|
|
704
|
-
this.unregisterResource = void 0;
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
792
|
} catch (error) {
|
|
708
793
|
this.degraded = true;
|
|
709
794
|
console.warn("[smrt-web] failed to open the offline outbox", error);
|
|
710
795
|
}
|
|
711
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Register the queue as a durable resource. A wipe drops the namespace's
|
|
799
|
+
* registrations, so the clear callback forgets ours and the next enqueue
|
|
800
|
+
* registers again — otherwise a second wipe would miss rows written after
|
|
801
|
+
* the first.
|
|
802
|
+
*/
|
|
803
|
+
registerForWipe() {
|
|
804
|
+
if (this.unregisterResource || this.disposed) return;
|
|
805
|
+
this.unregisterResource = this.config.registerResource(async () => {
|
|
806
|
+
this.unregisterResource = void 0;
|
|
807
|
+
this.wipesPending += 1;
|
|
808
|
+
const wipe = (async () => {
|
|
809
|
+
try {
|
|
810
|
+
await this.ready;
|
|
811
|
+
await this.queue?.clear();
|
|
812
|
+
} finally {
|
|
813
|
+
this.wipesPending -= 1;
|
|
814
|
+
if (!this.disposed) this.drain();
|
|
815
|
+
}
|
|
816
|
+
})();
|
|
817
|
+
this.pendingWipes.add(wipe);
|
|
818
|
+
try {
|
|
819
|
+
await wipe;
|
|
820
|
+
} finally {
|
|
821
|
+
this.pendingWipes.delete(wipe);
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
}
|
|
712
825
|
/** Wake the drain loop immediately when connectivity returns. */
|
|
713
826
|
wireOnlineListener() {
|
|
714
827
|
const target = globalThis;
|
|
@@ -719,16 +832,46 @@ var OutboxEngine = class {
|
|
|
719
832
|
target.addEventListener("online", listener);
|
|
720
833
|
this.onlineListener = listener;
|
|
721
834
|
}
|
|
722
|
-
/**
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
835
|
+
/**
|
|
836
|
+
* Request cross-tab leadership for `route` (ref-counted per tab). The
|
|
837
|
+
* sync-apply route keeps the pre-#3021 lock name, so it stays mutually
|
|
838
|
+
* exclusive with tabs running an older build; each transport route has its
|
|
839
|
+
* own lock.
|
|
840
|
+
*/
|
|
841
|
+
acquireRoute(route) {
|
|
842
|
+
const existing = this.leaders.get(route);
|
|
843
|
+
if (existing) {
|
|
844
|
+
existing.refs += 1;
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const root = `smrt-web-outbox-leader:${this.config.namespace}`;
|
|
848
|
+
const lockName = route === SYNC_APPLY_ROUTE ? root : `${root}:${route}`;
|
|
849
|
+
const entry = {
|
|
850
|
+
release: () => {},
|
|
851
|
+
isLeader: false,
|
|
852
|
+
refs: 1
|
|
853
|
+
};
|
|
854
|
+
this.leaders.set(route, entry);
|
|
855
|
+
entry.release = acquireLeadership(lockName, () => {
|
|
856
|
+
entry.isLeader = true;
|
|
727
857
|
this.drain();
|
|
728
858
|
}, () => {
|
|
729
|
-
|
|
859
|
+
entry.isLeader = false;
|
|
730
860
|
});
|
|
731
861
|
}
|
|
862
|
+
/** Drop one reference to `route`; the last one releases its lock. */
|
|
863
|
+
releaseRoute(route) {
|
|
864
|
+
const entry = this.leaders.get(route);
|
|
865
|
+
if (!entry) return;
|
|
866
|
+
entry.refs -= 1;
|
|
867
|
+
if (entry.refs > 0) return;
|
|
868
|
+
this.leaders.delete(route);
|
|
869
|
+
this.settled().then(() => entry.release());
|
|
870
|
+
}
|
|
871
|
+
/** Does this tab currently lead `route`? */
|
|
872
|
+
leads(route) {
|
|
873
|
+
return this.leaders.get(route)?.isLeader === true;
|
|
874
|
+
}
|
|
732
875
|
/**
|
|
733
876
|
* Attach a collection: register its per-object callbacks and bump the ref
|
|
734
877
|
* count. Returns the exact callback record registered so the caller can pass
|
|
@@ -739,9 +882,12 @@ var OutboxEngine = class {
|
|
|
739
882
|
*/
|
|
740
883
|
registerCollection(binding) {
|
|
741
884
|
this.refCount += 1;
|
|
885
|
+
const route = binding.transport ? routeOf({ transport: binding.object }) : binding.syncApply ? SYNC_APPLY_ROUTE : void 0;
|
|
742
886
|
const record = {
|
|
743
887
|
onSyncStateChange: binding.onSyncStateChange,
|
|
744
|
-
onConflict: binding.onConflict
|
|
888
|
+
onConflict: binding.onConflict,
|
|
889
|
+
transport: binding.transport,
|
|
890
|
+
route
|
|
745
891
|
};
|
|
746
892
|
let set = this.listenersByObject.get(binding.object);
|
|
747
893
|
if (!set) {
|
|
@@ -749,8 +895,16 @@ var OutboxEngine = class {
|
|
|
749
895
|
this.listenersByObject.set(binding.object, set);
|
|
750
896
|
}
|
|
751
897
|
set.add(record);
|
|
898
|
+
if (binding.syncApply && !this.syncApply) this.syncApply = binding.syncApply;
|
|
899
|
+
if (route && !this.disposed) this.acquireRoute(route);
|
|
752
900
|
return record;
|
|
753
901
|
}
|
|
902
|
+
/** The first live transport registered under `name`, if any. */
|
|
903
|
+
transportFor(name) {
|
|
904
|
+
const set = this.listenersByObject.get(name);
|
|
905
|
+
if (!set) return void 0;
|
|
906
|
+
for (const record of set) if (record.transport) return record.transport;
|
|
907
|
+
}
|
|
754
908
|
/**
|
|
755
909
|
* Detach a collection: remove its callback record and decrement the ref count;
|
|
756
910
|
* when it reaches zero, dispose the engine (release the lock, unregister from
|
|
@@ -760,9 +914,10 @@ var OutboxEngine = class {
|
|
|
760
914
|
*/
|
|
761
915
|
async unregisterCollection(object, record) {
|
|
762
916
|
const set = this.listenersByObject.get(object);
|
|
763
|
-
if (set) {
|
|
764
|
-
set.delete(record);
|
|
917
|
+
if (set?.delete(record)) {
|
|
765
918
|
if (set.size === 0) this.listenersByObject.delete(object);
|
|
919
|
+
const route = record.route;
|
|
920
|
+
if (route) this.releaseRoute(route);
|
|
766
921
|
}
|
|
767
922
|
this.refCount = Math.max(0, this.refCount - 1);
|
|
768
923
|
if (this.refCount > 0) return false;
|
|
@@ -788,20 +943,23 @@ var OutboxEngine = class {
|
|
|
788
943
|
async enqueue(request) {
|
|
789
944
|
await this.ready;
|
|
790
945
|
if (!this.queue || this.degraded) return void 0;
|
|
946
|
+
this.registerForWipe();
|
|
791
947
|
const itemId = newItemId();
|
|
792
948
|
const op = envelopeKindToOp(request.kind);
|
|
793
|
-
const
|
|
949
|
+
const rowId = request.rowId || itemId;
|
|
950
|
+
const payload = op === "delete" && request.transport === void 0 ? void 0 : request.data;
|
|
794
951
|
await this.queue.enqueue({
|
|
795
952
|
itemId,
|
|
796
953
|
object: request.object,
|
|
797
954
|
op,
|
|
798
|
-
id:
|
|
955
|
+
id: rowId,
|
|
799
956
|
payload,
|
|
800
|
-
baseUpdatedAt: request.baseUpdatedAt
|
|
957
|
+
baseUpdatedAt: request.baseUpdatedAt,
|
|
958
|
+
transport: request.transport
|
|
801
959
|
});
|
|
802
960
|
this.emit({
|
|
803
961
|
itemId,
|
|
804
|
-
rowId
|
|
962
|
+
rowId,
|
|
805
963
|
object: request.object,
|
|
806
964
|
state: "pending",
|
|
807
965
|
attempts: 0
|
|
@@ -842,7 +1000,8 @@ var OutboxEngine = class {
|
|
|
842
1000
|
state: row.state,
|
|
843
1001
|
attempts: row.attempts,
|
|
844
1002
|
nextAttemptAt: row.nextAttemptAt,
|
|
845
|
-
lastError: row.lastError
|
|
1003
|
+
lastError: row.lastError,
|
|
1004
|
+
...row.transport === void 0 ? {} : { transport: row.transport }
|
|
846
1005
|
}));
|
|
847
1006
|
}
|
|
848
1007
|
/**
|
|
@@ -871,53 +1030,124 @@ var OutboxEngine = class {
|
|
|
871
1030
|
}
|
|
872
1031
|
}
|
|
873
1032
|
/**
|
|
874
|
-
* The replay loop. Gated on: (a)
|
|
1033
|
+
* The replay loop. Gated on: (a) leading at least one route, (b) not paused by an
|
|
875
1034
|
* auth failure, (c) `navigator.onLine !== false`, (d) IndexedDB usable. Drains
|
|
876
|
-
*
|
|
877
|
-
* batches of ≤1000 per POST, one
|
|
878
|
-
*
|
|
1035
|
+
* every led route's rows due now (`nextAttemptAt <= now`), oldest-first per
|
|
1036
|
+
* route; sync-apply chunks into batches of ≤1000 per POST, one send at a time
|
|
1037
|
+
* to preserve each route's FIFO. Concurrency-coalesced: a drain requested while one runs sets a flag
|
|
879
1038
|
* to run exactly one more pass, so overlapping triggers never interleave.
|
|
880
1039
|
*/
|
|
881
|
-
|
|
1040
|
+
drain() {
|
|
882
1041
|
if (this.draining) {
|
|
883
1042
|
this.drainQueued = true;
|
|
884
|
-
return;
|
|
1043
|
+
return this.activeDrain ?? Promise.resolve();
|
|
885
1044
|
}
|
|
886
1045
|
this.draining = true;
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1046
|
+
this.activeDrain = (async () => {
|
|
1047
|
+
try {
|
|
1048
|
+
for (;;) {
|
|
1049
|
+
this.drainQueued = false;
|
|
1050
|
+
await this.drainOnce();
|
|
1051
|
+
if (!this.drainQueued) break;
|
|
1052
|
+
}
|
|
1053
|
+
} finally {
|
|
1054
|
+
this.draining = false;
|
|
1055
|
+
this.activeDrain = void 0;
|
|
892
1056
|
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
|
|
1057
|
+
})();
|
|
1058
|
+
return this.activeDrain;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Settles once the drain in flight (if any) has finished its request AND its
|
|
1062
|
+
* durable result transition. A route's lock is released only after this, so
|
|
1063
|
+
* another tab can never replay a row this tab is still settling.
|
|
1064
|
+
*/
|
|
1065
|
+
settled() {
|
|
1066
|
+
return (this.activeDrain ?? Promise.resolve()).catch(() => void 0);
|
|
896
1067
|
}
|
|
897
1068
|
/** One drain pass: send every currently-due batch, then schedule backoff. */
|
|
898
1069
|
async drainOnce() {
|
|
899
1070
|
if (this.disposed) return;
|
|
900
|
-
if (
|
|
1071
|
+
if (this.wipesPending > 0) return;
|
|
901
1072
|
if (this.paused) return;
|
|
902
1073
|
if (this.degraded || !this.queue) return;
|
|
903
1074
|
if (isDefinitelyOffline()) return;
|
|
904
|
-
const
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1075
|
+
const byRoute = /* @__PURE__ */ new Map();
|
|
1076
|
+
for (const row of await this.queue.all()) {
|
|
1077
|
+
if (row.state !== "pending") continue;
|
|
1078
|
+
const route = routeOf(row);
|
|
1079
|
+
if (!this.leads(route)) continue;
|
|
1080
|
+
const rows = byRoute.get(route);
|
|
1081
|
+
if (rows) rows.push(row);
|
|
1082
|
+
else byRoute.set(route, [row]);
|
|
912
1083
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const
|
|
916
|
-
|
|
1084
|
+
const now = Date.now();
|
|
1085
|
+
for (const [route, rows] of byRoute) {
|
|
1086
|
+
const firstBlocked = rows.findIndex((row) => row.nextAttemptAt > now);
|
|
1087
|
+
const due = firstBlocked === -1 ? rows : rows.slice(0, firstBlocked);
|
|
1088
|
+
if (route === SYNC_APPLY_ROUTE) {
|
|
1089
|
+
const syncApply = this.syncApply;
|
|
1090
|
+
if (!syncApply) continue;
|
|
1091
|
+
for (let i = 0; i < due.length; i += MAX_SYNC_APPLY_BATCH_SIZE) {
|
|
1092
|
+
if (this.disposed || this.paused || !this.leads(route)) break;
|
|
1093
|
+
const chunk = due.slice(i, i + MAX_SYNC_APPLY_BATCH_SIZE);
|
|
1094
|
+
if (!await this.sendBatch(chunk, syncApply)) break;
|
|
1095
|
+
}
|
|
1096
|
+
} else for (const row of due) {
|
|
1097
|
+
if (this.disposed || this.paused || !this.leads(route)) break;
|
|
1098
|
+
const transport = this.transportFor(row.transport ?? row.object);
|
|
1099
|
+
if (!transport) break;
|
|
1100
|
+
if (!await this.sendCommand(row, transport)) break;
|
|
1101
|
+
}
|
|
1102
|
+
if (this.disposed || this.paused) return;
|
|
917
1103
|
}
|
|
918
1104
|
await this.scheduleNextBackoff();
|
|
919
1105
|
}
|
|
920
1106
|
/**
|
|
1107
|
+
* Replay one transport row through its consumer-declared transport (#3021)
|
|
1108
|
+
* and map the outcome through the SAME transitions as a sync-apply result. A
|
|
1109
|
+
* throw is the ambiguous path (request may or may not have landed): the row
|
|
1110
|
+
* stays `pending` with backoff and is resent under the same idempotency key.
|
|
1111
|
+
* Returns false when the row remains pending, stopping this route's pass
|
|
1112
|
+
* (FIFO per route).
|
|
1113
|
+
*/
|
|
1114
|
+
async sendCommand(row, transport) {
|
|
1115
|
+
this.emit({
|
|
1116
|
+
itemId: row.itemId,
|
|
1117
|
+
rowId: row.id,
|
|
1118
|
+
object: row.object,
|
|
1119
|
+
state: "uploading",
|
|
1120
|
+
attempts: row.attempts
|
|
1121
|
+
});
|
|
1122
|
+
let outcome;
|
|
1123
|
+
try {
|
|
1124
|
+
outcome = await transport({
|
|
1125
|
+
idempotencyKey: row.itemId,
|
|
1126
|
+
name: row.transport ?? row.object,
|
|
1127
|
+
op: row.op,
|
|
1128
|
+
rowId: row.id,
|
|
1129
|
+
...row.payload === void 0 ? {} : { payload: row.payload },
|
|
1130
|
+
...row.baseUpdatedAt === void 0 ? {} : { baseUpdatedAt: row.baseUpdatedAt },
|
|
1131
|
+
attempt: row.attempts + 1,
|
|
1132
|
+
...row.pinned === void 0 ? {} : { pinned: row.pinned },
|
|
1133
|
+
pin: async (value) => {
|
|
1134
|
+
if (row.seq === void 0 || !this.queue) throw new Error("[smrt-web] outbox row is no longer queued");
|
|
1135
|
+
await this.queue.markState(row.seq, { pinned: value });
|
|
1136
|
+
row.pinned = value;
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
} catch {
|
|
1140
|
+
await this.requeueRow(row, "network error during command replay");
|
|
1141
|
+
return false;
|
|
1142
|
+
}
|
|
1143
|
+
const result = commandResultToApplyResult(row, outcome);
|
|
1144
|
+
if (!result) {
|
|
1145
|
+
await this.requeueRow(row, "unexpected command result shape");
|
|
1146
|
+
return false;
|
|
1147
|
+
}
|
|
1148
|
+
return this.applyResult(row, result);
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
921
1151
|
* Send one chunk through `POST {basePath}/sync/apply` and map results back
|
|
922
1152
|
* onto durable transitions. On a network reject / non-200 / lost/mismatched
|
|
923
1153
|
* response the WHOLE chunk stays `pending` (blind replay is safe) — every row
|
|
@@ -925,7 +1155,7 @@ var OutboxEngine = class {
|
|
|
925
1155
|
* doesn't hot-spin. Returns false when a retryable row remains pending, which
|
|
926
1156
|
* stops this drain pass so newer FIFO chunks do not overtake it.
|
|
927
1157
|
*/
|
|
928
|
-
async sendBatch(chunk) {
|
|
1158
|
+
async sendBatch(chunk, syncApply) {
|
|
929
1159
|
for (const row of chunk) this.emit({
|
|
930
1160
|
itemId: row.itemId,
|
|
931
1161
|
rowId: row.id,
|
|
@@ -943,7 +1173,7 @@ var OutboxEngine = class {
|
|
|
943
1173
|
}));
|
|
944
1174
|
let results;
|
|
945
1175
|
try {
|
|
946
|
-
results = await this.postBatch(items);
|
|
1176
|
+
results = await this.postBatch(items, syncApply);
|
|
947
1177
|
} catch {
|
|
948
1178
|
await this.requeueBatch(chunk, "network error during sync");
|
|
949
1179
|
return false;
|
|
@@ -971,9 +1201,9 @@ var OutboxEngine = class {
|
|
|
971
1201
|
* error (the caller treats a throw as "response lost → keep pending"). Returns
|
|
972
1202
|
* the positional `results` array, or `undefined` on a malformed 200 body.
|
|
973
1203
|
*/
|
|
974
|
-
async postBatch(items) {
|
|
975
|
-
const url = `${
|
|
976
|
-
const response = await
|
|
1204
|
+
async postBatch(items, syncApply) {
|
|
1205
|
+
const url = `${syncApply.basePath}/${SYNC_APPLY_ROUTE_SEGMENTS.join("/")}`;
|
|
1206
|
+
const response = await syncApply.fetchFn(url, {
|
|
977
1207
|
method: "POST",
|
|
978
1208
|
headers: { "Content-Type": "application/json" },
|
|
979
1209
|
body: JSON.stringify({ items })
|
|
@@ -1079,10 +1309,18 @@ var OutboxEngine = class {
|
|
|
1079
1309
|
*/
|
|
1080
1310
|
async scheduleNextBackoff() {
|
|
1081
1311
|
if (this.disposed || this.paused || !this.queue) return;
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1312
|
+
let nextAt;
|
|
1313
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1314
|
+
for (const row of await this.queue.all()) {
|
|
1315
|
+
if (row.state !== "pending") continue;
|
|
1316
|
+
const route = routeOf(row);
|
|
1317
|
+
if (seen.has(route)) continue;
|
|
1318
|
+
seen.add(route);
|
|
1319
|
+
if (!this.leads(route)) continue;
|
|
1320
|
+
nextAt = nextAt === void 0 ? row.nextAttemptAt : Math.min(nextAt, row.nextAttemptAt);
|
|
1321
|
+
}
|
|
1322
|
+
if (nextAt === void 0) return;
|
|
1323
|
+
const delay = Math.max(0, nextAt - Date.now());
|
|
1086
1324
|
if (this.backoffTimer) clearTimeout(this.backoffTimer);
|
|
1087
1325
|
const timers = globalThis;
|
|
1088
1326
|
if (typeof timers.setTimeout !== "function") return;
|
|
@@ -1110,11 +1348,14 @@ var OutboxEngine = class {
|
|
|
1110
1348
|
target.removeEventListener("online", this.onlineListener);
|
|
1111
1349
|
this.onlineListener = void 0;
|
|
1112
1350
|
}
|
|
1113
|
-
this.
|
|
1114
|
-
this.
|
|
1351
|
+
const leaders = [...this.leaders.values()];
|
|
1352
|
+
this.leaders.clear();
|
|
1353
|
+
await this.settled();
|
|
1354
|
+
for (const entry of leaders) entry.release();
|
|
1115
1355
|
this.unregisterResource?.();
|
|
1116
1356
|
this.unregisterResource = void 0;
|
|
1117
1357
|
await this.ready.catch(() => void 0);
|
|
1358
|
+
await Promise.allSettled([...this.pendingWipes]);
|
|
1118
1359
|
this.queue?.close();
|
|
1119
1360
|
this.queue = void 0;
|
|
1120
1361
|
this.listenersByObject.clear();
|
|
@@ -1129,6 +1370,29 @@ function getOrCreateOutboxEngine(config) {
|
|
|
1129
1370
|
}
|
|
1130
1371
|
return engine;
|
|
1131
1372
|
}
|
|
1373
|
+
function commandResultToApplyResult(row, outcome) {
|
|
1374
|
+
if (!outcome || typeof outcome !== "object") return void 0;
|
|
1375
|
+
const { status, reason, updatedAt } = outcome;
|
|
1376
|
+
const base = {
|
|
1377
|
+
itemId: row.itemId,
|
|
1378
|
+
id: row.id,
|
|
1379
|
+
...typeof updatedAt === "string" ? { updatedAt } : {}
|
|
1380
|
+
};
|
|
1381
|
+
if (status === "applied") return {
|
|
1382
|
+
...base,
|
|
1383
|
+
status
|
|
1384
|
+
};
|
|
1385
|
+
if (status === "conflict") return {
|
|
1386
|
+
...base,
|
|
1387
|
+
status,
|
|
1388
|
+
reason: reason === "create_conflict" ? "create_conflict" : "stale_write"
|
|
1389
|
+
};
|
|
1390
|
+
if (status === "rejected" && typeof reason === "string" && reason) return {
|
|
1391
|
+
...base,
|
|
1392
|
+
status,
|
|
1393
|
+
reason
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1132
1396
|
function acquireOutboxEngine(config, binding) {
|
|
1133
1397
|
const engine = getOrCreateOutboxEngine(config);
|
|
1134
1398
|
return {
|
|
@@ -1149,6 +1413,11 @@ function resolveBackoff(backoff) {
|
|
|
1149
1413
|
};
|
|
1150
1414
|
}
|
|
1151
1415
|
var handlesByNamespace = /* @__PURE__ */ new Map();
|
|
1416
|
+
async function releaseBinding(namespace, engine, object, record) {
|
|
1417
|
+
const before = engine.referenceCount;
|
|
1418
|
+
await releaseOutboxEngine(namespace, engine, object, record);
|
|
1419
|
+
if (before <= 1 && handlesByNamespace.get(namespace) === engine) handlesByNamespace.delete(namespace);
|
|
1420
|
+
}
|
|
1152
1421
|
function getPayloadUpdatedAt(data) {
|
|
1153
1422
|
const value = data.updatedAt ?? data.updated_at;
|
|
1154
1423
|
if (typeof value === "string") return value;
|
|
@@ -1159,6 +1428,7 @@ function offlineOutbox(config) {
|
|
|
1159
1428
|
const syncApplyBasePath = config.syncApplyBasePath ?? "/api/v1";
|
|
1160
1429
|
const fetchFn = config.fetchFn ?? ((...args) => globalThis.fetch(...args));
|
|
1161
1430
|
const backoff = resolveBackoff(config.backoff);
|
|
1431
|
+
const transport = config.transport;
|
|
1162
1432
|
const object = config.object.name;
|
|
1163
1433
|
let engine;
|
|
1164
1434
|
let record;
|
|
@@ -1167,8 +1437,6 @@ function offlineOutbox(config) {
|
|
|
1167
1437
|
onAttach() {
|
|
1168
1438
|
const acquired = acquireOutboxEngine({
|
|
1169
1439
|
namespace,
|
|
1170
|
-
syncApplyBasePath,
|
|
1171
|
-
fetchFn,
|
|
1172
1440
|
backoff,
|
|
1173
1441
|
random: config.random,
|
|
1174
1442
|
registerResource: (clear) => registerDurableResource(namespace, {
|
|
@@ -1178,7 +1446,11 @@ function offlineOutbox(config) {
|
|
|
1178
1446
|
}, {
|
|
1179
1447
|
object,
|
|
1180
1448
|
onSyncStateChange: config.onSyncStateChange,
|
|
1181
|
-
onConflict: config.onConflict
|
|
1449
|
+
onConflict: config.onConflict,
|
|
1450
|
+
...transport ? { transport } : { syncApply: {
|
|
1451
|
+
basePath: syncApplyBasePath,
|
|
1452
|
+
fetchFn
|
|
1453
|
+
} }
|
|
1182
1454
|
});
|
|
1183
1455
|
engine = acquired.engine;
|
|
1184
1456
|
record = acquired.record;
|
|
@@ -1191,7 +1463,8 @@ function offlineOutbox(config) {
|
|
|
1191
1463
|
object,
|
|
1192
1464
|
rowId: envelope.key,
|
|
1193
1465
|
data: envelope.data,
|
|
1194
|
-
baseUpdatedAt: envelope.baseUpdatedAt ?? getPayloadUpdatedAt(envelope.data)
|
|
1466
|
+
baseUpdatedAt: envelope.baseUpdatedAt ?? getPayloadUpdatedAt(envelope.data),
|
|
1467
|
+
...transport ? { transport: object } : {}
|
|
1195
1468
|
})) return { handled: false };
|
|
1196
1469
|
return {
|
|
1197
1470
|
handled: true,
|
|
@@ -1204,9 +1477,53 @@ function offlineOutbox(config) {
|
|
|
1204
1477
|
const currentRecord = record;
|
|
1205
1478
|
engine = void 0;
|
|
1206
1479
|
record = void 0;
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1480
|
+
await releaseBinding(namespace, current, object, currentRecord);
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
function offlineCommandQueue(config) {
|
|
1485
|
+
if (typeof config.name !== "string" || config.name.length === 0) throw new TypeError("offlineCommandQueue requires a non-empty name");
|
|
1486
|
+
if (typeof config.transport !== "function") throw new TypeError("offlineCommandQueue requires a transport function");
|
|
1487
|
+
const namespace = durableStoreNamespace(config.namespace);
|
|
1488
|
+
const name = config.name;
|
|
1489
|
+
const acquired = acquireOutboxEngine({
|
|
1490
|
+
namespace,
|
|
1491
|
+
backoff: resolveBackoff(config.backoff),
|
|
1492
|
+
random: config.random,
|
|
1493
|
+
registerResource: (clear) => registerDurableResource(namespace, {
|
|
1494
|
+
kind: "outbox",
|
|
1495
|
+
clear
|
|
1496
|
+
})
|
|
1497
|
+
}, {
|
|
1498
|
+
object: name,
|
|
1499
|
+
transport: config.transport,
|
|
1500
|
+
onSyncStateChange: config.onSyncStateChange,
|
|
1501
|
+
onConflict: config.onConflict
|
|
1502
|
+
});
|
|
1503
|
+
let engine = acquired.engine;
|
|
1504
|
+
const record = acquired.record;
|
|
1505
|
+
handlesByNamespace.set(namespace, engine);
|
|
1506
|
+
return {
|
|
1507
|
+
async enqueue(input) {
|
|
1508
|
+
if (!engine) return void 0;
|
|
1509
|
+
return engine.enqueue({
|
|
1510
|
+
kind: input.op === "create" || input.op === void 0 ? "insert" : input.op,
|
|
1511
|
+
object: name,
|
|
1512
|
+
rowId: input.rowId ?? "",
|
|
1513
|
+
data: input.payload ?? {},
|
|
1514
|
+
baseUpdatedAt: input.baseUpdatedAt,
|
|
1515
|
+
transport: name
|
|
1516
|
+
});
|
|
1517
|
+
},
|
|
1518
|
+
snapshot: async () => engine ? engine.snapshot() : [],
|
|
1519
|
+
retry: async (itemId) => {
|
|
1520
|
+
await engine?.retry(itemId);
|
|
1521
|
+
},
|
|
1522
|
+
async dispose() {
|
|
1523
|
+
const current = engine;
|
|
1524
|
+
if (!current) return;
|
|
1525
|
+
engine = void 0;
|
|
1526
|
+
await releaseBinding(namespace, current, name, record);
|
|
1210
1527
|
}
|
|
1211
1528
|
};
|
|
1212
1529
|
}
|
|
@@ -1331,6 +1648,17 @@ function openSnapshotStore(namespace) {
|
|
|
1331
1648
|
//#endregion
|
|
1332
1649
|
//#region src/persistence.ts
|
|
1333
1650
|
var DEFAULT_PERSIST_DEBOUNCE_MS = 250;
|
|
1651
|
+
function registerForWipe(engine, namespace) {
|
|
1652
|
+
if (engine.unregister) return;
|
|
1653
|
+
engine.unregister = registerDurableResource(namespace, {
|
|
1654
|
+
kind: "persisted-collection",
|
|
1655
|
+
clear: async () => {
|
|
1656
|
+
engine.wipeEpoch += 1;
|
|
1657
|
+
engine.unregister = void 0;
|
|
1658
|
+
await (await engine.ready)?.clear();
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1334
1662
|
var enginesByNamespace = /* @__PURE__ */ new Map();
|
|
1335
1663
|
var warnedNoIndexedDb = false;
|
|
1336
1664
|
function warnNoIndexedDbOnce() {
|
|
@@ -1342,13 +1670,15 @@ function acquireSnapshotEngine(namespace) {
|
|
|
1342
1670
|
const existing = enginesByNamespace.get(namespace);
|
|
1343
1671
|
if (existing) {
|
|
1344
1672
|
existing.refCount += 1;
|
|
1673
|
+
registerForWipe(existing, namespace);
|
|
1345
1674
|
return existing;
|
|
1346
1675
|
}
|
|
1347
1676
|
const engine = {
|
|
1348
1677
|
store: void 0,
|
|
1349
1678
|
refCount: 1,
|
|
1350
1679
|
unregister: void 0,
|
|
1351
|
-
ready: Promise.resolve(void 0)
|
|
1680
|
+
ready: Promise.resolve(void 0),
|
|
1681
|
+
wipeEpoch: 0
|
|
1352
1682
|
};
|
|
1353
1683
|
engine.ready = (async () => {
|
|
1354
1684
|
if (!await probeIndexedDb()) {
|
|
@@ -1358,10 +1688,6 @@ function acquireSnapshotEngine(namespace) {
|
|
|
1358
1688
|
try {
|
|
1359
1689
|
const store = await openSnapshotStore(namespace);
|
|
1360
1690
|
engine.store = store;
|
|
1361
|
-
engine.unregister = registerDurableResource(namespace, {
|
|
1362
|
-
kind: "persisted-collection",
|
|
1363
|
-
clear: () => store.clear()
|
|
1364
|
-
});
|
|
1365
1691
|
return store;
|
|
1366
1692
|
} catch {
|
|
1367
1693
|
warnNoIndexedDbOnce();
|
|
@@ -1369,6 +1695,7 @@ function acquireSnapshotEngine(namespace) {
|
|
|
1369
1695
|
}
|
|
1370
1696
|
})();
|
|
1371
1697
|
enginesByNamespace.set(namespace, engine);
|
|
1698
|
+
registerForWipe(engine, namespace);
|
|
1372
1699
|
return engine;
|
|
1373
1700
|
}
|
|
1374
1701
|
async function releaseSnapshotEngine(namespace) {
|
|
@@ -1458,6 +1785,74 @@ function persistCollection(config) {
|
|
|
1458
1785
|
}
|
|
1459
1786
|
};
|
|
1460
1787
|
}
|
|
1788
|
+
function dataSurfaceSnapshotKey(identity) {
|
|
1789
|
+
if (!identity || typeof identity.surfaceId !== "string" || identity.surfaceId.length === 0 || typeof identity.kind !== "string") throw new TypeError("persistDataSurface requires an identity with a surfaceId and kind");
|
|
1790
|
+
return `data-surface:${JSON.stringify([
|
|
1791
|
+
identity.kind,
|
|
1792
|
+
identity.surfaceId,
|
|
1793
|
+
identity.subject?.type ?? null,
|
|
1794
|
+
identity.subject?.id ?? null
|
|
1795
|
+
])}`;
|
|
1796
|
+
}
|
|
1797
|
+
function persistDataSurface(config) {
|
|
1798
|
+
const namespace = durableStoreNamespace(config.namespace);
|
|
1799
|
+
const key = dataSurfaceSnapshotKey(config.identity);
|
|
1800
|
+
const debounceMs = config.debounceMs ?? 250;
|
|
1801
|
+
let engine = acquireSnapshotEngine(namespace);
|
|
1802
|
+
const bornEpoch = engine.wipeEpoch;
|
|
1803
|
+
const wiped = (current) => current.wipeEpoch !== bornEpoch;
|
|
1804
|
+
let pending;
|
|
1805
|
+
let timer;
|
|
1806
|
+
let writing = Promise.resolve();
|
|
1807
|
+
const writePending = () => {
|
|
1808
|
+
if (timer) {
|
|
1809
|
+
clearTimeout(timer);
|
|
1810
|
+
timer = void 0;
|
|
1811
|
+
}
|
|
1812
|
+
const rows = pending;
|
|
1813
|
+
pending = void 0;
|
|
1814
|
+
const current = engine;
|
|
1815
|
+
if (!rows || !current) return writing;
|
|
1816
|
+
writing = writing.then(async () => {
|
|
1817
|
+
const store = await current.ready;
|
|
1818
|
+
if (!store || wiped(current)) return;
|
|
1819
|
+
try {
|
|
1820
|
+
await store.save(key, rows);
|
|
1821
|
+
} catch {}
|
|
1822
|
+
});
|
|
1823
|
+
return writing;
|
|
1824
|
+
};
|
|
1825
|
+
return {
|
|
1826
|
+
async load() {
|
|
1827
|
+
const current = engine;
|
|
1828
|
+
if (!current) return void 0;
|
|
1829
|
+
const store = await current.ready;
|
|
1830
|
+
if (!store || wiped(current)) return void 0;
|
|
1831
|
+
const rows = await store.load(key);
|
|
1832
|
+
if (!rows || rows.length === 0) return void 0;
|
|
1833
|
+
return rows;
|
|
1834
|
+
},
|
|
1835
|
+
save(rows) {
|
|
1836
|
+
if (!engine) return;
|
|
1837
|
+
if (!Array.isArray(rows)) throw new TypeError("persistDataSurface.save requires an array of rows");
|
|
1838
|
+
if (wiped(engine)) return;
|
|
1839
|
+
pending = rows.map((row) => ({ ...row }));
|
|
1840
|
+
if (timer) clearTimeout(timer);
|
|
1841
|
+
timer = setTimeout(() => {
|
|
1842
|
+
timer = void 0;
|
|
1843
|
+
writePending();
|
|
1844
|
+
}, Math.max(0, debounceMs));
|
|
1845
|
+
timer.unref?.();
|
|
1846
|
+
},
|
|
1847
|
+
flush: () => writePending(),
|
|
1848
|
+
async dispose() {
|
|
1849
|
+
if (!engine) return;
|
|
1850
|
+
await writePending();
|
|
1851
|
+
engine = void 0;
|
|
1852
|
+
await releaseSnapshotEngine(namespace);
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1461
1856
|
//#endregion
|
|
1462
1857
|
//#region src/remote-query.ts
|
|
1463
1858
|
function canonicalize(value) {
|
|
@@ -3151,6 +3546,6 @@ function createSmrtCollection(definition, options) {
|
|
|
3151
3546
|
return handle;
|
|
3152
3547
|
}
|
|
3153
3548
|
//#endregion
|
|
3154
|
-
export {
|
|
3549
|
+
export { MAX_SMRT_WEB_DATA_SURFACE_ACTION_IDENTIFIER_LENGTH as A, MAX_SMRT_WEB_DATA_QUERY_OFFSET as B, getOutboxHandle as C, registerDurableResource as D, durableStoreNamespace as E, executeSmrtWebDataSurfaceAction as F, MAX_SMRT_WEB_DATA_QUERY_WARNINGS as G, MAX_SMRT_WEB_DATA_QUERY_RESULT_BYTES as H, normalizeSmrtWebDataSurfaceActionResult as I, runWrapMutation as J, executeSmrtWebDataQuery as K, MAX_SMRT_WEB_DATA_QUERY_CONTAINER_ITEMS as L, MAX_SMRT_WEB_DATA_SURFACE_ACTION_RESULT_BYTES as M, classifySmrtWebDataSurfaceActionResult as N, wipeDurableStore as O, dataSurfaceActionCommandTransport as P, MAX_SMRT_WEB_DATA_QUERY_FACETS as R, persistDataSurface as S, offlineOutbox as T, MAX_SMRT_WEB_DATA_QUERY_ROWS as U, MAX_SMRT_WEB_DATA_QUERY_PAGE_LIMIT as V, MAX_SMRT_WEB_DATA_QUERY_STRING_LENGTH as W, createSmrtWebEventSubscriber as _, createSmrtWebClient as a, DEFAULT_PERSIST_DEBOUNCE_MS as b, newLocalId as c, unwrapListResult as d, validateSmrtWebClient as f, createUpdateState as g, registerWebMcpTools as h, createSmrtCollection as i, MAX_SMRT_WEB_DATA_SURFACE_ACTION_JSON_DEPTH as j, MAX_SMRT_WEB_DATA_SURFACE_ACTION_CONTAINER_ITEMS as k, throwIfSmrtWebError as l, registerWebMcpBespokeTool as m, buildListQuery as n, getEngineCollection as o, registerViewIntent as p, normalizeSmrtWebDataQueryResult as q, createDefinitionFetchers as r, invalidateSmrtWebCollections as s, SmrtWebRequestError as t, unwrapItemResult as u, liveInvalidation as v, offlineCommandQueue as w, persistCollection as x, createSmrtWebQuery as y, MAX_SMRT_WEB_DATA_QUERY_FACET_VALUES as z };
|
|
3155
3550
|
|
|
3156
|
-
//# sourceMappingURL=src-
|
|
3551
|
+
//# sourceMappingURL=src-B2uQWayW.js.map
|