@graphrefly/ts 0.0.0 → 0.0.1
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/README.md +135 -0
- package/dist/adapters/index.cjs +105 -66
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +11 -136
- package/dist/adapters/index.d.ts +11 -136
- package/dist/adapters/index.js +27 -225
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/observe-storage.d.cts +4 -4
- package/dist/adapters/observe-storage.d.ts +4 -4
- package/dist/adapters/observe-storage.js +1 -0
- package/dist/adapters/observe-storage.js.map +1 -1
- package/dist/adapters/react.cjs +166 -0
- package/dist/adapters/react.cjs.map +1 -0
- package/dist/adapters/react.d.cts +31 -0
- package/dist/adapters/react.d.ts +31 -0
- package/dist/adapters/react.js +51 -0
- package/dist/adapters/react.js.map +1 -0
- package/dist/adapters/solid.cjs +157 -0
- package/dist/adapters/solid.cjs.map +1 -0
- package/dist/adapters/solid.d.cts +26 -0
- package/dist/adapters/solid.d.ts +26 -0
- package/dist/adapters/solid.js +48 -0
- package/dist/adapters/solid.js.map +1 -0
- package/dist/adapters/svelte.cjs +148 -0
- package/dist/adapters/svelte.cjs.map +1 -0
- package/dist/adapters/svelte.d.cts +31 -0
- package/dist/adapters/svelte.d.ts +31 -0
- package/dist/adapters/svelte.js +46 -0
- package/dist/adapters/svelte.js.map +1 -0
- package/dist/adapters/vue.cjs +155 -0
- package/dist/adapters/vue.cjs.map +1 -0
- package/dist/adapters/vue.d.cts +26 -0
- package/dist/adapters/vue.d.ts +26 -0
- package/dist/adapters/vue.js +46 -0
- package/dist/adapters/vue.js.map +1 -0
- package/dist/agent-runtime-BesGDZcB.d.ts +555 -0
- package/dist/agent-runtime-DZTJ3C-p.d.cts +555 -0
- package/dist/agentic-memory-BmRCvh0d.d.ts +517 -0
- package/dist/agentic-memory-DfuVZ-lY.d.cts +517 -0
- package/dist/capability-admission-B0BzqAm5.d.cts +102 -0
- package/dist/capability-admission-g0mn9hpq.d.ts +102 -0
- package/dist/{chunk-JR4IKCR2.js → chunk-2747WN4Y.js} +5 -5
- package/dist/{chunk-2ILL7HQI.js → chunk-3JR3EDB2.js} +17 -17
- package/dist/{chunk-2ILL7HQI.js.map → chunk-3JR3EDB2.js.map} +1 -1
- package/dist/{chunk-GDL26N2T.js → chunk-3KN7NJDR.js} +2 -2
- package/dist/{chunk-DJC4CHLQ.js → chunk-43BKLFZU.js} +9 -9
- package/dist/chunk-6XAIOL3X.js +2407 -0
- package/dist/chunk-6XAIOL3X.js.map +1 -0
- package/dist/{chunk-SCRFRCGR.js → chunk-BSQGE5PS.js} +1 -1
- package/dist/{chunk-Y3ZLRGHC.js → chunk-CZNV3VXM.js} +16 -16
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/{chunk-JIMUDQN3.js → chunk-FBUU43PM.js} +2 -2
- package/dist/chunk-H75VXTGT.js +219 -0
- package/dist/chunk-H75VXTGT.js.map +1 -0
- package/dist/{chunk-RIIOUTGM.js → chunk-HKK2JD7Z.js} +12 -9
- package/dist/chunk-HKK2JD7Z.js.map +1 -0
- package/dist/{chunk-D6BG7U6A.js → chunk-IXZBUWSP.js} +3 -3
- package/dist/chunk-PC3BTTBE.js +3217 -0
- package/dist/chunk-PC3BTTBE.js.map +1 -0
- package/dist/chunk-QFY4AFY4.js +879 -0
- package/dist/chunk-QFY4AFY4.js.map +1 -0
- package/dist/{chunk-DK554R7P.js → chunk-S6HXABGL.js} +2 -1
- package/dist/chunk-S6HXABGL.js.map +1 -0
- package/dist/{chunk-PKVBHGBM.js → chunk-SS2A464I.js} +7 -7
- package/dist/chunk-UT3MHJCW.js +49 -0
- package/dist/chunk-UT3MHJCW.js.map +1 -0
- package/dist/{chunk-G4M3NHRJ.js → chunk-V7CIJE4B.js} +3 -3
- package/dist/{chunk-TEW47IVR.js → chunk-VPEFSDME.js} +2 -2
- package/dist/chunk-VTHLCFN4.js +368 -0
- package/dist/chunk-VTHLCFN4.js.map +1 -0
- package/dist/{chunk-SWTLEQL6.js → chunk-WQIA3GFB.js} +2 -2
- package/dist/chunk-WQIA3GFB.js.map +1 -0
- package/dist/{chunk-I3IHOHCJ.js → chunk-XKXWK2KP.js} +108 -64
- package/dist/chunk-XKXWK2KP.js.map +1 -0
- package/dist/{chunk-COHO25CZ.js → chunk-YDSIVHRR.js} +553 -7
- package/dist/chunk-YDSIVHRR.js.map +1 -0
- package/dist/chunk-YXMZ2N6J.js +1884 -0
- package/dist/chunk-YXMZ2N6J.js.map +1 -0
- package/dist/composition/index.cjs +102 -57
- package/dist/composition/index.cjs.map +1 -1
- package/dist/composition/index.d.cts +4 -4
- package/dist/composition/index.d.ts +4 -4
- package/dist/composition/index.js +6 -5
- package/dist/core/index.cjs +102 -57
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +5 -4
- package/dist/cqrs/index.cjs.map +1 -1
- package/dist/cqrs/index.d.cts +4 -4
- package/dist/cqrs/index.d.ts +4 -4
- package/dist/cqrs/index.js +3 -2
- package/dist/cqrs/index.js.map +1 -1
- package/dist/cqrs/messaging.cjs +312 -0
- package/dist/cqrs/messaging.cjs.map +1 -0
- package/dist/cqrs/messaging.d.cts +57 -0
- package/dist/cqrs/messaging.d.ts +57 -0
- package/dist/cqrs/messaging.js +274 -0
- package/dist/cqrs/messaging.js.map +1 -0
- package/dist/cqrs/work-queue.cjs +329 -0
- package/dist/cqrs/work-queue.cjs.map +1 -0
- package/dist/cqrs/work-queue.d.cts +90 -0
- package/dist/cqrs/work-queue.d.ts +90 -0
- package/dist/cqrs/work-queue.js +292 -0
- package/dist/cqrs/work-queue.js.map +1 -0
- package/dist/data/index.cjs +19 -0
- package/dist/data/index.cjs.map +1 -0
- package/dist/data/index.d.cts +36 -0
- package/dist/data/index.d.ts +36 -0
- package/dist/data/index.js +1 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data-structures/index.cjs +105 -57
- package/dist/data-structures/index.cjs.map +1 -1
- package/dist/data-structures/index.d.cts +8 -8
- package/dist/data-structures/index.d.ts +8 -8
- package/dist/data-structures/index.js +7 -6
- package/dist/{describe-BZb7AS2y.d.ts → describe-BpyJuWkw.d.ts} +1 -1
- package/dist/{describe-Ciu8SUnN.d.cts → describe-DYMw_l1C.d.cts} +1 -1
- package/dist/executors/work-queue.cjs +342 -0
- package/dist/executors/work-queue.cjs.map +1 -0
- package/dist/executors/work-queue.d.cts +90 -0
- package/dist/executors/work-queue.d.ts +90 -0
- package/dist/executors/work-queue.js +306 -0
- package/dist/executors/work-queue.js.map +1 -0
- package/dist/graph/index.cjs +112 -58
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +9 -9
- package/dist/graph/index.d.ts +9 -9
- package/dist/graph/index.js +18 -17
- package/dist/{graph-DfdF9zy4.d.ts → graph-D-1-SQeC.d.ts} +3 -3
- package/dist/{graph-s23RbbFO.d.cts → graph-DCEWh4T2.d.cts} +3 -3
- package/dist/{index-CAEmLCYF.d.cts → index-72VZC0-E.d.cts} +2 -2
- package/dist/{index-SnzZu3x7.d.ts → index-Bmxeu0nK.d.ts} +2 -2
- package/dist/index.cjs +112 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -10
- package/dist/index.d.ts +11 -10
- package/dist/index.js +118 -113
- package/dist/inspection/boundary.cjs +98 -0
- package/dist/inspection/boundary.cjs.map +1 -0
- package/dist/inspection/boundary.d.cts +61 -0
- package/dist/inspection/boundary.d.ts +61 -0
- package/dist/inspection/boundary.js +75 -0
- package/dist/inspection/boundary.js.map +1 -0
- package/dist/{messages-BZqrGGm1.d.ts → messages-CAzUPPJM.d.cts} +10 -5
- package/dist/{messages-BZqrGGm1.d.cts → messages-CAzUPPJM.d.ts} +10 -5
- package/dist/messaging/index.cjs +744 -613
- package/dist/messaging/index.cjs.map +1 -1
- package/dist/messaging/index.d.cts +216 -160
- package/dist/messaging/index.d.ts +216 -160
- package/dist/messaging/index.js +19 -749
- package/dist/messaging/index.js.map +1 -1
- package/dist/operators/index.cjs +102 -57
- package/dist/operators/index.cjs.map +1 -1
- package/dist/operators/index.d.cts +6 -6
- package/dist/operators/index.d.ts +6 -6
- package/dist/operators/index.js +8 -7
- package/dist/orchestration/index.cjs +3333 -59
- package/dist/orchestration/index.cjs.map +1 -1
- package/dist/orchestration/index.d.cts +7 -4
- package/dist/orchestration/index.d.ts +7 -4
- package/dist/orchestration/index.js +49 -8
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/messaging.cjs +314 -0
- package/dist/orchestration/messaging.cjs.map +1 -0
- package/dist/orchestration/messaging.d.cts +59 -0
- package/dist/orchestration/messaging.d.ts +59 -0
- package/dist/orchestration/messaging.js +276 -0
- package/dist/orchestration/messaging.js.map +1 -0
- package/dist/orchestration/work-queue.cjs +262 -0
- package/dist/orchestration/work-queue.cjs.map +1 -0
- package/dist/orchestration/work-queue.d.cts +85 -0
- package/dist/orchestration/work-queue.d.ts +85 -0
- package/dist/orchestration/work-queue.js +226 -0
- package/dist/orchestration/work-queue.js.map +1 -0
- package/dist/patterns/event-flow.cjs +447 -0
- package/dist/patterns/event-flow.cjs.map +1 -0
- package/dist/patterns/event-flow.d.cts +115 -0
- package/dist/patterns/event-flow.d.ts +115 -0
- package/dist/patterns/event-flow.js +14 -0
- package/dist/patterns/event-flow.js.map +1 -0
- package/dist/patterns/index.cjs +423 -18
- package/dist/patterns/index.cjs.map +1 -1
- package/dist/patterns/index.d.cts +8 -5
- package/dist/patterns/index.d.ts +8 -5
- package/dist/patterns/index.js +13 -4
- package/dist/{reactive-collection-Cb1II1Fi.d.ts → reactive-collection-BAmfrGjd.d.ts} +9 -9
- package/dist/{reactive-collection-BxBqtIOE.d.cts → reactive-collection-DNV0-gFi.d.cts} +9 -9
- package/dist/{reactive-collection-storage-D-KmjE0J.d.cts → reactive-collection-storage-BNEldDyi.d.cts} +5 -5
- package/dist/{reactive-collection-storage-BNiEc-Gd.d.ts → reactive-collection-storage-YZsAQsWK.d.ts} +5 -5
- package/dist/{reactive-map-D-g3dv8I.d.cts → reactive-map-DRD1LJ2H.d.cts} +6 -6
- package/dist/{reactive-map-BcB4KsQR.d.ts → reactive-map-EsDiCmJk.d.ts} +6 -6
- package/dist/render/index.d.cts +3 -3
- package/dist/render/index.d.ts +3 -3
- package/dist/render/index.js +1 -0
- package/dist/{semantic-memory-graph-BYoIlP35.d.cts → semantic-memory-graph-DKuNzuWw.d.cts} +2 -2
- package/dist/{semantic-memory-graph-CxyXLms3.d.ts → semantic-memory-graph-uwbwJdhe.d.ts} +2 -2
- package/dist/solutions/index.cjs +4478 -2
- package/dist/solutions/index.cjs.map +1 -1
- package/dist/solutions/index.d.cts +13 -519
- package/dist/solutions/index.d.ts +13 -519
- package/dist/solutions/index.js +90 -6
- package/dist/solutions/reactive-layout/browser/index.cjs +449 -0
- package/dist/solutions/reactive-layout/browser/index.cjs.map +1 -0
- package/dist/solutions/reactive-layout/browser/index.d.cts +44 -0
- package/dist/solutions/reactive-layout/browser/index.d.ts +44 -0
- package/dist/solutions/reactive-layout/browser/index.js +69 -0
- package/dist/solutions/reactive-layout/browser/index.js.map +1 -0
- package/dist/solutions/reactive-layout/index.cjs +4266 -0
- package/dist/solutions/reactive-layout/index.cjs.map +1 -0
- package/dist/solutions/reactive-layout/index.d.cts +581 -0
- package/dist/solutions/reactive-layout/index.d.ts +581 -0
- package/dist/solutions/reactive-layout/index.js +86 -0
- package/dist/solutions/reactive-layout/index.js.map +1 -0
- package/dist/solutions/reactive-layout/node-canvas/index.cjs +545 -0
- package/dist/solutions/reactive-layout/node-canvas/index.cjs.map +1 -0
- package/dist/solutions/reactive-layout/node-canvas/index.d.cts +54 -0
- package/dist/solutions/reactive-layout/node-canvas/index.d.ts +54 -0
- package/dist/solutions/reactive-layout/node-canvas/index.js +131 -0
- package/dist/solutions/reactive-layout/node-canvas/index.js.map +1 -0
- package/dist/solutions/reactive-layout/react-native/index.cjs +494 -0
- package/dist/solutions/reactive-layout/react-native/index.cjs.map +1 -0
- package/dist/solutions/reactive-layout/react-native/index.d.cts +51 -0
- package/dist/solutions/reactive-layout/react-native/index.d.ts +51 -0
- package/dist/solutions/reactive-layout/react-native/index.js +77 -0
- package/dist/solutions/reactive-layout/react-native/index.js.map +1 -0
- package/dist/solutions/reactive-layout/skia/index.cjs +547 -0
- package/dist/solutions/reactive-layout/skia/index.cjs.map +1 -0
- package/dist/solutions/reactive-layout/skia/index.d.cts +64 -0
- package/dist/solutions/reactive-layout/skia/index.d.ts +64 -0
- package/dist/solutions/reactive-layout/skia/index.js +70 -0
- package/dist/solutions/reactive-layout/skia/index.js.map +1 -0
- package/dist/solutions/work-item/actions.cjs +2164 -0
- package/dist/solutions/work-item/actions.cjs.map +1 -0
- package/dist/solutions/work-item/actions.d.cts +201 -0
- package/dist/solutions/work-item/actions.d.ts +201 -0
- package/dist/solutions/work-item/actions.js +1454 -0
- package/dist/solutions/work-item/actions.js.map +1 -0
- package/dist/solutions/work-item/scheduling.cjs +2454 -0
- package/dist/solutions/work-item/scheduling.cjs.map +1 -0
- package/dist/solutions/work-item/scheduling.d.cts +433 -0
- package/dist/solutions/work-item/scheduling.d.ts +433 -0
- package/dist/solutions/work-item/scheduling.js +32 -0
- package/dist/solutions/work-item/scheduling.js.map +1 -0
- package/dist/solutions/work-item/work-queue.cjs +306 -0
- package/dist/solutions/work-item/work-queue.cjs.map +1 -0
- package/dist/solutions/work-item/work-queue.d.cts +71 -0
- package/dist/solutions/work-item/work-queue.d.ts +71 -0
- package/dist/solutions/work-item/work-queue.js +270 -0
- package/dist/solutions/work-item/work-queue.js.map +1 -0
- package/dist/sources/browser.cjs +169 -57
- package/dist/sources/browser.cjs.map +1 -1
- package/dist/sources/browser.d.cts +33 -6
- package/dist/sources/browser.d.ts +33 -6
- package/dist/sources/browser.js +73 -6
- package/dist/sources/browser.js.map +1 -1
- package/dist/sources/index.cjs +102 -57
- package/dist/sources/index.cjs.map +1 -1
- package/dist/sources/index.d.cts +5 -5
- package/dist/sources/index.d.ts +5 -5
- package/dist/sources/index.js +6 -5
- package/dist/sources/node.cjs.map +1 -1
- package/dist/sources/node.d.cts +4 -4
- package/dist/sources/node.d.ts +4 -4
- package/dist/sources/node.js +2 -1
- package/dist/sources/node.js.map +1 -1
- package/dist/storage/browser.js +1 -0
- package/dist/storage/browser.js.map +1 -1
- package/dist/storage/index.d.cts +5 -5
- package/dist/storage/index.d.ts +5 -5
- package/dist/storage/index.js +5 -4
- package/dist/storage/node.js +1 -0
- package/dist/storage/node.js.map +1 -1
- package/dist/store-C-joM4gh.d.ts +123 -0
- package/dist/store-CNJwmT-h.d.cts +123 -0
- package/dist/testing/index.cjs +1 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +3 -2
- package/dist/{types-h12mNYbq.d.ts → types-BsJvElYZ.d.ts} +31 -31
- package/dist/{types-bNMSWKqF.d.cts → types-CcPIn7sy.d.cts} +31 -31
- package/dist/work-item-runtime-C3M79vE9.d.ts +228 -0
- package/dist/work-item-runtime-qaVgsL0T.d.cts +228 -0
- package/dist/work-queue/index.cjs +1132 -0
- package/dist/work-queue/index.cjs.map +1 -0
- package/dist/work-queue/index.d.cts +369 -0
- package/dist/work-queue/index.d.ts +369 -0
- package/dist/work-queue/index.js +1078 -0
- package/dist/work-queue/index.js.map +1 -0
- package/package.json +266 -2
- package/dist/chunk-COHO25CZ.js.map +0 -1
- package/dist/chunk-DK554R7P.js.map +0 -1
- package/dist/chunk-I3IHOHCJ.js.map +0 -1
- package/dist/chunk-RIIOUTGM.js.map +0 -1
- package/dist/chunk-SWTLEQL6.js.map +0 -1
- /package/dist/{chunk-JR4IKCR2.js.map → chunk-2747WN4Y.js.map} +0 -0
- /package/dist/{chunk-GDL26N2T.js.map → chunk-3KN7NJDR.js.map} +0 -0
- /package/dist/{chunk-DJC4CHLQ.js.map → chunk-43BKLFZU.js.map} +0 -0
- /package/dist/{chunk-SCRFRCGR.js.map → chunk-BSQGE5PS.js.map} +0 -0
- /package/dist/{chunk-Y3ZLRGHC.js.map → chunk-CZNV3VXM.js.map} +0 -0
- /package/dist/{chunk-JIMUDQN3.js.map → chunk-FBUU43PM.js.map} +0 -0
- /package/dist/{chunk-D6BG7U6A.js.map → chunk-IXZBUWSP.js.map} +0 -0
- /package/dist/{chunk-PKVBHGBM.js.map → chunk-SS2A464I.js.map} +0 -0
- /package/dist/{chunk-G4M3NHRJ.js.map → chunk-V7CIJE4B.js.map} +0 -0
- /package/dist/{chunk-TEW47IVR.js.map → chunk-VPEFSDME.js.map} +0 -0
package/dist/messaging/index.cjs
CHANGED
|
@@ -28,11 +28,11 @@ __export(messaging_exports, {
|
|
|
28
28
|
SPAWNS_TOPIC: () => SPAWNS_TOPIC,
|
|
29
29
|
STANDARD_TOPICS: () => STANDARD_TOPICS,
|
|
30
30
|
TODOS_TOPIC: () => TODOS_TOPIC,
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
eventMessage: () => eventMessage,
|
|
32
|
+
eventMessageIssue: () => eventMessageIssue,
|
|
33
33
|
fromTopic: () => fromTopic,
|
|
34
|
+
isEventMessage: () => isEventMessage,
|
|
34
35
|
messageBus: () => messageBus,
|
|
35
|
-
toHubTopic: () => toHubTopic,
|
|
36
36
|
toTopic: () => toTopic
|
|
37
37
|
});
|
|
38
38
|
module.exports = __toCommonJS(messaging_exports);
|
|
@@ -51,15 +51,78 @@ function depBatch(ctx, depIndex) {
|
|
|
51
51
|
return flattened.length === 0 ? [] : flattened;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
// src/
|
|
55
|
-
var
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
// src/messaging/internal.ts
|
|
55
|
+
var busStates = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
function registerMessageBusState(bus, state) {
|
|
57
|
+
busStates.set(bus, state);
|
|
58
|
+
}
|
|
59
|
+
function getMessageBusState(bus) {
|
|
60
|
+
return busStates.get(bus);
|
|
61
|
+
}
|
|
62
|
+
function attachMessageBusCommandSource(graph, bus, commands) {
|
|
63
|
+
const state = getMessageBusState(bus);
|
|
64
|
+
if (state === void 0) throw new Error("messageBus: unknown implementation");
|
|
65
|
+
if (state.graph !== graph) throw new Error("messageBus: command source graph must match");
|
|
66
|
+
state.commandSources.push(commands);
|
|
67
|
+
const busCommands = bus.commands;
|
|
68
|
+
if (busCommands === void 0) throw new Error("messageBus: missing commands node");
|
|
69
|
+
busCommands.replaceDeps([...state.commandSources], state.commandBody);
|
|
70
|
+
let released = false;
|
|
71
|
+
return () => {
|
|
72
|
+
if (released) return;
|
|
73
|
+
released = true;
|
|
74
|
+
const index = state.commandSources.indexOf(commands);
|
|
75
|
+
if (index >= 0) state.commandSources.splice(index, 1);
|
|
76
|
+
busCommands.replaceDeps([...state.commandSources], state.commandBody);
|
|
77
|
+
};
|
|
60
78
|
}
|
|
61
79
|
|
|
62
80
|
// src/messaging/index.ts
|
|
81
|
+
function eventMessage(type, payload, opts) {
|
|
82
|
+
assertNonEmpty(type, "eventMessage.type");
|
|
83
|
+
assertNonEmpty(opts.id, "eventMessage.id");
|
|
84
|
+
return {
|
|
85
|
+
id: opts.id,
|
|
86
|
+
type,
|
|
87
|
+
payload,
|
|
88
|
+
...opts.key === void 0 ? {} : { key: opts.key },
|
|
89
|
+
...opts.subjectId === void 0 ? {} : { subjectId: opts.subjectId },
|
|
90
|
+
...opts.correlationId === void 0 ? {} : { correlationId: opts.correlationId },
|
|
91
|
+
...opts.causationId === void 0 ? {} : { causationId: opts.causationId },
|
|
92
|
+
...opts.occurredAtMs === void 0 ? {} : { occurredAtMs: opts.occurredAtMs },
|
|
93
|
+
...opts.actor === void 0 ? {} : { actor: opts.actor },
|
|
94
|
+
...opts.evidenceRefs === void 0 ? {} : { evidenceRefs: opts.evidenceRefs },
|
|
95
|
+
...opts.schema === void 0 ? {} : { schema: opts.schema },
|
|
96
|
+
...opts.metadata === void 0 ? {} : { metadata: opts.metadata }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function isEventMessage(value) {
|
|
100
|
+
return eventMessageIssue(value) === void 0;
|
|
101
|
+
}
|
|
102
|
+
function eventMessageIssue(value) {
|
|
103
|
+
if (!isObjectRecord(value)) {
|
|
104
|
+
return eventIssue("event message must be an object");
|
|
105
|
+
}
|
|
106
|
+
if (typeof value.id !== "string" || value.id.length === 0) {
|
|
107
|
+
return eventIssue("event message id must be a non-empty string");
|
|
108
|
+
}
|
|
109
|
+
if (typeof value.type !== "string" || value.type.length === 0) {
|
|
110
|
+
return eventIssue("event message type must be a non-empty string");
|
|
111
|
+
}
|
|
112
|
+
if (!("payload" in value)) {
|
|
113
|
+
return eventIssue("event message payload field is required");
|
|
114
|
+
}
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
function eventIssue(message) {
|
|
118
|
+
return {
|
|
119
|
+
kind: "issue",
|
|
120
|
+
code: "malformed-event-message",
|
|
121
|
+
message,
|
|
122
|
+
severity: "error",
|
|
123
|
+
source: "messageBus.eventMessage"
|
|
124
|
+
};
|
|
125
|
+
}
|
|
63
126
|
var PROMPTS_TOPIC = "prompts";
|
|
64
127
|
var RESPONSES_TOPIC = "responses";
|
|
65
128
|
var INJECTIONS_TOPIC = "injections";
|
|
@@ -76,727 +139,795 @@ var STANDARD_TOPICS = Object.freeze([
|
|
|
76
139
|
CONTEXT_TOPIC,
|
|
77
140
|
TODOS_TOPIC
|
|
78
141
|
]);
|
|
79
|
-
|
|
80
|
-
var dynamicHubStates = /* @__PURE__ */ new WeakMap();
|
|
81
|
-
var DEFAULT_DYNAMIC_HUB_MAX_TOPICS = 1024;
|
|
82
|
-
var DEFAULT_DYNAMIC_HUB_MAX_TOPIC_LENGTH = 256;
|
|
83
|
-
function messageBus(graph, opts) {
|
|
84
|
-
const unique = [...new Set(opts.topics)];
|
|
85
|
-
if (unique.length !== opts.topics.length) throw new Error("messageBus: duplicate topic");
|
|
86
|
-
if (unique.length === 0) throw new Error("messageBus: at least one topic is required");
|
|
142
|
+
function messageBus(graph, opts = {}) {
|
|
87
143
|
const name = opts.name ?? "messageBus";
|
|
88
|
-
const
|
|
89
|
-
graph,
|
|
90
|
-
name,
|
|
91
|
-
now: opts.now ?? Date.now,
|
|
92
|
-
topics: Object.freeze([...unique]),
|
|
93
|
-
records: /* @__PURE__ */ new Map(),
|
|
94
|
-
seq: 0
|
|
95
|
-
};
|
|
96
|
-
for (const topic of unique) {
|
|
97
|
-
const producers = [];
|
|
98
|
-
const body = (ctx) => {
|
|
99
|
-
for (let i = 0; i < producers.length; i++) {
|
|
100
|
-
for (const produced of depBatch(ctx, i) ?? []) {
|
|
101
|
-
if (produced.kind === "publish") {
|
|
102
|
-
ctx.down([
|
|
103
|
-
[
|
|
104
|
-
"DATA",
|
|
105
|
-
produced.envelope
|
|
106
|
-
]
|
|
107
|
-
]);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
const node = graph.node([], body, {
|
|
113
|
-
name: `${name}/${topic}`,
|
|
114
|
-
factory: "messageTopic",
|
|
115
|
-
meta: { topic },
|
|
116
|
-
completeWhenDepsComplete: false,
|
|
117
|
-
errorWhenDepsError: false
|
|
118
|
-
});
|
|
119
|
-
state.records.set(topic, { node, producers, body });
|
|
120
|
-
}
|
|
121
|
-
const bus = {
|
|
122
|
-
topics: state.topics,
|
|
123
|
-
topic(topic) {
|
|
124
|
-
return requireTopic(state, topic);
|
|
125
|
-
},
|
|
126
|
-
publish(topic, payload, publishOpts = {}) {
|
|
127
|
-
const node = requireTopic(state, topic);
|
|
128
|
-
const envelope = {
|
|
129
|
-
topic,
|
|
130
|
-
seq: ++state.seq,
|
|
131
|
-
payload,
|
|
132
|
-
timestampMs: state.now(),
|
|
133
|
-
...publishOpts.key === void 0 ? {} : { key: publishOpts.key }
|
|
134
|
-
};
|
|
135
|
-
node.down([["DATA", envelope]]);
|
|
136
|
-
return envelope;
|
|
137
|
-
},
|
|
138
|
-
has(topic) {
|
|
139
|
-
return state.records.has(topic);
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
busStates.set(bus, state);
|
|
143
|
-
return bus;
|
|
144
|
-
}
|
|
145
|
-
function dynamicHub(graph, opts = {}) {
|
|
146
|
-
const name = opts.name ?? "dynamicHub";
|
|
147
|
-
const unknownTopic = normalizeUnknownTopicPolicy(opts.unknownTopic);
|
|
148
|
-
const maxTopics = normalizeHubLimit(
|
|
149
|
-
opts.maxTopics,
|
|
150
|
-
DEFAULT_DYNAMIC_HUB_MAX_TOPICS,
|
|
151
|
-
"dynamicHub: maxTopics"
|
|
152
|
-
);
|
|
153
|
-
const maxTopicLength = normalizeHubLimit(
|
|
154
|
-
opts.maxTopicLength,
|
|
155
|
-
DEFAULT_DYNAMIC_HUB_MAX_TOPIC_LENGTH,
|
|
156
|
-
"dynamicHub: maxTopicLength"
|
|
157
|
-
);
|
|
158
|
-
const now = opts.now ?? Date.now;
|
|
159
|
-
const initialTopics = uniqueTopics(opts.topics ?? [], maxTopicLength);
|
|
160
|
-
if (initialTopics.length > maxTopics) {
|
|
161
|
-
throw new Error("dynamicHub: topics exceed maxTopics");
|
|
162
|
-
}
|
|
144
|
+
const initialTopics = uniqueTopics(opts.topics ?? []);
|
|
163
145
|
const commandSources = [];
|
|
164
146
|
const commandBody = (ctx) => {
|
|
165
147
|
for (let i = 0; i < commandSources.length; i++) {
|
|
166
|
-
for (const
|
|
148
|
+
for (const command of depBatch(ctx, i) ?? []) ctx.down([["DATA", command]]);
|
|
167
149
|
}
|
|
168
150
|
};
|
|
169
|
-
const
|
|
170
|
-
name: `${name}/
|
|
171
|
-
factory: "
|
|
151
|
+
const commands = graph.node([], commandBody, {
|
|
152
|
+
name: `${name}/commands`,
|
|
153
|
+
factory: "messageBusCommands",
|
|
172
154
|
completeWhenDepsComplete: false,
|
|
173
155
|
errorWhenDepsError: false
|
|
174
156
|
});
|
|
175
|
-
const
|
|
176
|
-
|
|
157
|
+
const state = {
|
|
158
|
+
graph,
|
|
159
|
+
name,
|
|
160
|
+
now: opts.now ?? Date.now,
|
|
161
|
+
topicPolicy: opts.topicPolicy ?? "strict",
|
|
162
|
+
retention: opts.retention ?? {},
|
|
163
|
+
dedupe: opts.dedupe ?? { commandId: "status" },
|
|
164
|
+
topics: new Map(initialTopics.map((topic) => [topic, makeTopicState()])),
|
|
165
|
+
subscriptions: /* @__PURE__ */ new Map(),
|
|
166
|
+
seenCommandIds: /* @__PURE__ */ new Set(),
|
|
167
|
+
seenIdempotencyKeys: /* @__PURE__ */ new Set(),
|
|
168
|
+
deadLetters: [],
|
|
169
|
+
deadLetterSeq: 0,
|
|
170
|
+
commandSources,
|
|
171
|
+
commandBody
|
|
172
|
+
};
|
|
173
|
+
const runtime = graph.node(
|
|
174
|
+
[commands],
|
|
177
175
|
(ctx) => {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
seq: 0,
|
|
182
|
-
cursor: 0
|
|
183
|
-
};
|
|
184
|
-
ctx.state.persist(true);
|
|
185
|
-
for (const commandFact of depBatch(ctx, 0) ?? []) {
|
|
186
|
-
const parsed = parseDynamicHubCommand(commandFact);
|
|
187
|
-
if (typeof parsed === "string") {
|
|
188
|
-
ctx.down([["DATA", hubError(runtime, commandFact, parsed, timestampOrFallback(now))]]);
|
|
189
|
-
continue;
|
|
176
|
+
for (const command of depBatch(ctx, 0) ?? []) {
|
|
177
|
+
for (const event of reduceMessageBusCommand(state, command)) {
|
|
178
|
+
ctx.down([["DATA", event]]);
|
|
190
179
|
}
|
|
191
|
-
const reduced = reduceDynamicHubCommand(
|
|
192
|
-
runtime,
|
|
193
|
-
parsed,
|
|
194
|
-
unknownTopic,
|
|
195
|
-
maxTopics,
|
|
196
|
-
maxTopicLength,
|
|
197
|
-
now
|
|
198
|
-
);
|
|
199
|
-
for (const event of reduced) ctx.down([["DATA", event]]);
|
|
200
180
|
}
|
|
201
|
-
ctx.state.set(runtime);
|
|
202
181
|
},
|
|
203
182
|
{
|
|
204
|
-
name: `${name}/
|
|
205
|
-
factory: "
|
|
206
|
-
meta: { unknownTopic },
|
|
183
|
+
name: `${name}/runtime`,
|
|
184
|
+
factory: "messageBusRuntime",
|
|
207
185
|
completeWhenDepsComplete: false,
|
|
208
186
|
errorWhenDepsError: false
|
|
209
187
|
}
|
|
210
188
|
);
|
|
211
|
-
|
|
212
|
-
|
|
189
|
+
graph.retain(runtime, { reason: `${name}.messageBus.runtime` });
|
|
190
|
+
const messages = graph.node(
|
|
191
|
+
[runtime],
|
|
213
192
|
(ctx) => {
|
|
214
193
|
for (const event of depBatch(ctx, 0) ?? []) {
|
|
215
194
|
const typed = event;
|
|
216
|
-
ctx.down([["DATA", typed.
|
|
195
|
+
if (typed.kind === "message") ctx.down([["DATA", typed.message]]);
|
|
217
196
|
}
|
|
218
197
|
},
|
|
219
198
|
{
|
|
220
|
-
name: `${name}/
|
|
221
|
-
factory: "
|
|
199
|
+
name: `${name}/messages`,
|
|
200
|
+
factory: "messageBusMessages",
|
|
222
201
|
completeWhenDepsComplete: false,
|
|
223
202
|
errorWhenDepsError: false
|
|
224
203
|
}
|
|
225
204
|
);
|
|
226
|
-
const
|
|
227
|
-
[
|
|
205
|
+
const status = graph.node(
|
|
206
|
+
[runtime],
|
|
228
207
|
(ctx) => {
|
|
229
208
|
for (const event of depBatch(ctx, 0) ?? []) {
|
|
230
209
|
const typed = event;
|
|
231
|
-
if (typed.kind
|
|
232
|
-
ctx.down([
|
|
233
|
-
[
|
|
234
|
-
"DATA",
|
|
235
|
-
{
|
|
236
|
-
...typed.topic === void 0 ? {} : { topic: typed.topic },
|
|
237
|
-
error: typed.error,
|
|
238
|
-
command: typed.command,
|
|
239
|
-
meta: typed.meta
|
|
240
|
-
}
|
|
241
|
-
]
|
|
242
|
-
]);
|
|
210
|
+
if (typed.kind === "status") ctx.down([["DATA", typed.status]]);
|
|
243
211
|
}
|
|
244
212
|
},
|
|
245
213
|
{
|
|
246
|
-
name: `${name}/
|
|
247
|
-
factory: "
|
|
214
|
+
name: `${name}/status`,
|
|
215
|
+
factory: "messageBusStatus",
|
|
248
216
|
completeWhenDepsComplete: false,
|
|
249
217
|
errorWhenDepsError: false
|
|
250
218
|
}
|
|
251
219
|
);
|
|
252
|
-
const
|
|
253
|
-
[
|
|
220
|
+
const issues = graph.node(
|
|
221
|
+
[runtime],
|
|
254
222
|
(ctx) => {
|
|
255
223
|
for (const event of depBatch(ctx, 0) ?? []) {
|
|
256
224
|
const typed = event;
|
|
257
|
-
if (typed.kind
|
|
258
|
-
ctx.down([
|
|
259
|
-
[
|
|
260
|
-
"DATA",
|
|
261
|
-
{
|
|
262
|
-
...typed.topic === void 0 ? {} : { topic: typed.topic },
|
|
263
|
-
reason: typed.reason,
|
|
264
|
-
command: typed.command,
|
|
265
|
-
meta: typed.meta
|
|
266
|
-
}
|
|
267
|
-
]
|
|
268
|
-
]);
|
|
225
|
+
if (typed.kind === "issue") ctx.down([["DATA", typed.issue]]);
|
|
269
226
|
}
|
|
270
227
|
},
|
|
271
228
|
{
|
|
272
|
-
name: `${name}/
|
|
273
|
-
factory: "
|
|
229
|
+
name: `${name}/issues`,
|
|
230
|
+
factory: "messageBusIssues",
|
|
274
231
|
completeWhenDepsComplete: false,
|
|
275
232
|
errorWhenDepsError: false
|
|
276
233
|
}
|
|
277
|
-
)
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
events,
|
|
234
|
+
);
|
|
235
|
+
const bus = {
|
|
236
|
+
commands,
|
|
237
|
+
messages,
|
|
282
238
|
status,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
kind: "create",
|
|
239
|
+
issues,
|
|
240
|
+
ensureTopic(topic, commandOpts = {}) {
|
|
241
|
+
return publishCommand(commands, {
|
|
242
|
+
kind: "ensure-topic",
|
|
288
243
|
topic,
|
|
289
|
-
...commandOpts.
|
|
290
|
-
...commandOpts.payload === void 0 ? {} : { payload: commandOpts.payload }
|
|
244
|
+
...commandOpts.commandId === void 0 ? {} : { commandId: commandOpts.commandId }
|
|
291
245
|
});
|
|
292
246
|
},
|
|
293
|
-
|
|
294
|
-
return
|
|
295
|
-
kind: "
|
|
247
|
+
closeTopic(topic, commandOpts = {}) {
|
|
248
|
+
return publishCommand(commands, {
|
|
249
|
+
kind: "close-topic",
|
|
296
250
|
topic,
|
|
297
|
-
...commandOpts.
|
|
298
|
-
...commandOpts.payload === void 0 ? {} : { payload: commandOpts.payload }
|
|
251
|
+
...commandOpts.commandId === void 0 ? {} : { commandId: commandOpts.commandId }
|
|
299
252
|
});
|
|
300
253
|
},
|
|
301
|
-
publish(topic, payload,
|
|
302
|
-
return
|
|
254
|
+
publish(topic, payload, publishOpts = {}) {
|
|
255
|
+
return publishCommand(commands, {
|
|
303
256
|
kind: "publish",
|
|
304
257
|
topic,
|
|
305
258
|
payload,
|
|
306
|
-
...
|
|
259
|
+
...publishOpts.key === void 0 ? {} : { key: publishOpts.key },
|
|
260
|
+
...publishOpts.commandId === void 0 ? {} : { commandId: publishOpts.commandId },
|
|
261
|
+
...publishOpts.idempotencyKey === void 0 ? {} : { idempotencyKey: publishOpts.idempotencyKey }
|
|
307
262
|
});
|
|
308
263
|
},
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
264
|
+
topic(topic, projectionOpts = {}) {
|
|
265
|
+
assertTopicKey(topic, "messageBus.topic");
|
|
266
|
+
const snapshotPullId = /* @__PURE__ */ Symbol(`${name}/${topic}/topicSnapshot`);
|
|
267
|
+
const snapshot = graph.node(
|
|
268
|
+
[messages],
|
|
269
|
+
(ctx) => {
|
|
270
|
+
const params = pullParams(ctx.pull);
|
|
271
|
+
ctx.down([["DATA", topicPage(state, topic, params)]]);
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: projectionOpts.name ?? `${name}/${topic}/topic`,
|
|
275
|
+
factory: "messageBusTopicProjection",
|
|
276
|
+
meta: { topic },
|
|
277
|
+
pullId: snapshotPullId,
|
|
278
|
+
partial: true,
|
|
279
|
+
completeWhenDepsComplete: false,
|
|
280
|
+
errorWhenDepsError: false
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
return { snapshot, snapshotPullId, status, issues };
|
|
284
|
+
},
|
|
285
|
+
catalog(projectionOpts = {}) {
|
|
286
|
+
const snapshotPullId = /* @__PURE__ */ Symbol(`${name}/catalogSnapshot`);
|
|
287
|
+
const snapshot = graph.node(
|
|
288
|
+
[runtime],
|
|
289
|
+
(ctx) => {
|
|
290
|
+
const params = pullParams(ctx.pull);
|
|
291
|
+
ctx.down([["DATA", catalogPage(state, params)]]);
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: projectionOpts.name ?? `${name}/catalog`,
|
|
295
|
+
factory: "messageBusCatalog",
|
|
296
|
+
pullId: snapshotPullId,
|
|
297
|
+
partial: true,
|
|
298
|
+
completeWhenDepsComplete: false,
|
|
299
|
+
errorWhenDepsError: false
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
return { snapshot, snapshotPullId, status, issues };
|
|
303
|
+
},
|
|
304
|
+
deadLetter(projectionOpts = {}) {
|
|
305
|
+
const snapshotPullId = /* @__PURE__ */ Symbol(`${name}/deadLetterSnapshot`);
|
|
306
|
+
const snapshot = graph.node(
|
|
307
|
+
[runtime],
|
|
308
|
+
(ctx) => {
|
|
309
|
+
const params = pullParams(ctx.pull);
|
|
310
|
+
ctx.down([["DATA", deadLetterPage(state, params)]]);
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: projectionOpts.name ?? `${name}/deadLetter`,
|
|
314
|
+
factory: "messageBusDeadLetter",
|
|
315
|
+
pullId: snapshotPullId,
|
|
316
|
+
partial: true,
|
|
317
|
+
completeWhenDepsComplete: false,
|
|
318
|
+
errorWhenDepsError: false
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
return { snapshot, snapshotPullId, status, issues };
|
|
316
322
|
},
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
323
|
+
subscription(subscriptionOpts) {
|
|
324
|
+
assertTopicKey(subscriptionOpts.topic, "messageBus.subscription");
|
|
325
|
+
assertNonEmpty(subscriptionOpts.subscriptionId, "messageBus.subscriptionId");
|
|
326
|
+
const sub = ensureSubscription(state, {
|
|
327
|
+
topic: subscriptionOpts.topic,
|
|
328
|
+
subscriptionId: subscriptionOpts.subscriptionId,
|
|
329
|
+
from: subscriptionOpts.from ?? "earliest"
|
|
322
330
|
});
|
|
331
|
+
const availablePullId = /* @__PURE__ */ Symbol(
|
|
332
|
+
`${name}/${subscriptionOpts.topic}/${subscriptionOpts.subscriptionId}/available`
|
|
333
|
+
);
|
|
334
|
+
const projectionName = subscriptionOpts.name ?? `${name}/${subscriptionOpts.topic}/${subscriptionOpts.subscriptionId}`;
|
|
335
|
+
const available = graph.node(
|
|
336
|
+
[messages, status],
|
|
337
|
+
(ctx) => {
|
|
338
|
+
const params = pullParams(ctx.pull);
|
|
339
|
+
ctx.down([["DATA", availablePage(state, sub, params)]]);
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: `${projectionName}/available`,
|
|
343
|
+
factory: "messageBusSubscriptionAvailable",
|
|
344
|
+
meta: { topic: subscriptionOpts.topic, subscriptionId: subscriptionOpts.subscriptionId },
|
|
345
|
+
pullId: availablePullId,
|
|
346
|
+
partial: true,
|
|
347
|
+
completeWhenDepsComplete: false,
|
|
348
|
+
errorWhenDepsError: false
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
const cursor = graph.node(
|
|
352
|
+
[status],
|
|
353
|
+
(ctx) => {
|
|
354
|
+
for (const fact of depBatch(ctx, 0) ?? []) {
|
|
355
|
+
const typed = fact;
|
|
356
|
+
const subscriptionMoved = typed.topic === sub.topic && typed.subscriptionId === sub.subscriptionId && (typed.kind === "subscription-acked" || typed.kind === "subscription-sought" || typed.kind === "subscription-closed");
|
|
357
|
+
const retentionMoved = typed.topic === sub.topic && typed.kind === "retention-trimmed";
|
|
358
|
+
if (subscriptionMoved || retentionMoved) {
|
|
359
|
+
ctx.down([["DATA", cursorSnapshot(state, sub)]]);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: `${projectionName}/cursor`,
|
|
365
|
+
factory: "messageBusSubscriptionCursor",
|
|
366
|
+
completeWhenDepsComplete: false,
|
|
367
|
+
errorWhenDepsError: false
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
return {
|
|
371
|
+
available,
|
|
372
|
+
availablePullId,
|
|
373
|
+
cursor,
|
|
374
|
+
status,
|
|
375
|
+
issues,
|
|
376
|
+
ack(seq, commandOpts = {}) {
|
|
377
|
+
return publishCommand(commands, {
|
|
378
|
+
kind: "ack",
|
|
379
|
+
topic: sub.topic,
|
|
380
|
+
subscriptionId: sub.subscriptionId,
|
|
381
|
+
seq,
|
|
382
|
+
...commandOpts.commandId === void 0 ? {} : { commandId: commandOpts.commandId }
|
|
383
|
+
});
|
|
384
|
+
},
|
|
385
|
+
seek(nextSeq, commandOpts = {}) {
|
|
386
|
+
return publishCommand(commands, {
|
|
387
|
+
kind: "seek",
|
|
388
|
+
topic: sub.topic,
|
|
389
|
+
subscriptionId: sub.subscriptionId,
|
|
390
|
+
nextSeq,
|
|
391
|
+
...commandOpts.commandId === void 0 ? {} : { commandId: commandOpts.commandId }
|
|
392
|
+
});
|
|
393
|
+
},
|
|
394
|
+
close(commandOpts = {}) {
|
|
395
|
+
return publishCommand(commands, {
|
|
396
|
+
kind: "close-subscription",
|
|
397
|
+
topic: sub.topic,
|
|
398
|
+
subscriptionId: sub.subscriptionId,
|
|
399
|
+
...commandOpts.commandId === void 0 ? {} : { commandId: commandOpts.commandId }
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
};
|
|
323
403
|
}
|
|
324
404
|
};
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
commandBody,
|
|
331
|
-
releaseEventsRetain
|
|
332
|
-
});
|
|
333
|
-
return hub;
|
|
405
|
+
registerMessageBusState(bus, state);
|
|
406
|
+
return bus;
|
|
407
|
+
}
|
|
408
|
+
function fromTopic(bus, topic) {
|
|
409
|
+
return bus.topic(topic).snapshot;
|
|
334
410
|
}
|
|
335
|
-
function
|
|
336
|
-
const state =
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
411
|
+
function toTopic(graph, source, bus, topic, opts = {}) {
|
|
412
|
+
const state = getMessageBusState(bus);
|
|
413
|
+
if (state === void 0) throw new Error("toTopic: unknown message bus implementation");
|
|
414
|
+
if (state.graph !== graph) throw new Error("toTopic: bus and source graph must match");
|
|
415
|
+
assertTopicKey(topic, "toTopic");
|
|
416
|
+
const commands = graph.node(
|
|
417
|
+
[source],
|
|
340
418
|
(ctx) => {
|
|
341
|
-
for (const
|
|
342
|
-
const
|
|
343
|
-
|
|
419
|
+
for (const value of depBatch(ctx, 0) ?? []) {
|
|
420
|
+
const key = opts.keyOf?.(value);
|
|
421
|
+
const commandId = opts.commandIdOf?.(value);
|
|
344
422
|
ctx.down([
|
|
345
423
|
[
|
|
346
424
|
"DATA",
|
|
347
425
|
{
|
|
426
|
+
kind: "publish",
|
|
348
427
|
topic,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
...typed.key === void 0 ? {} : { key: typed.key }
|
|
428
|
+
payload: value,
|
|
429
|
+
...key === void 0 ? {} : { key },
|
|
430
|
+
...commandId === void 0 ? {} : { commandId }
|
|
353
431
|
}
|
|
354
432
|
]
|
|
355
433
|
]);
|
|
356
434
|
}
|
|
357
435
|
},
|
|
358
436
|
{
|
|
359
|
-
name: opts.name ?? `${state.name}/${topic}/
|
|
360
|
-
factory: "
|
|
437
|
+
name: opts.name ?? `${state.name}/${topic}/toTopic`,
|
|
438
|
+
factory: "toTopic",
|
|
361
439
|
meta: { topic },
|
|
362
440
|
completeWhenDepsComplete: false,
|
|
363
441
|
errorWhenDepsError: false
|
|
364
442
|
}
|
|
365
443
|
);
|
|
444
|
+
const releaseSource = attachMessageBusCommandSource(
|
|
445
|
+
graph,
|
|
446
|
+
bus,
|
|
447
|
+
commands
|
|
448
|
+
);
|
|
449
|
+
let released = false;
|
|
450
|
+
return {
|
|
451
|
+
commands,
|
|
452
|
+
release() {
|
|
453
|
+
if (released) return;
|
|
454
|
+
released = true;
|
|
455
|
+
releaseSource();
|
|
456
|
+
}
|
|
457
|
+
};
|
|
366
458
|
}
|
|
367
|
-
function
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
|
|
459
|
+
function reduceMessageBusCommand(state, command) {
|
|
460
|
+
const malformed = validateCommand(command);
|
|
461
|
+
if (malformed !== void 0) return rejectCommand(state, command, malformed);
|
|
462
|
+
if (command.commandId !== void 0) {
|
|
463
|
+
if (state.seenCommandIds.has(command.commandId)) {
|
|
464
|
+
return duplicateCommandEvents(state, command, "duplicate commandId");
|
|
465
|
+
}
|
|
466
|
+
state.seenCommandIds.add(command.commandId);
|
|
373
467
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
kind: "publish",
|
|
385
|
-
topic,
|
|
386
|
-
payload: value,
|
|
387
|
-
...key === void 0 ? {} : { key }
|
|
388
|
-
}
|
|
389
|
-
]
|
|
390
|
-
]);
|
|
391
|
-
} catch (error) {
|
|
392
|
-
ctx.down([
|
|
393
|
-
[
|
|
394
|
-
"DATA",
|
|
395
|
-
{
|
|
396
|
-
kind: "invalid",
|
|
397
|
-
topic,
|
|
398
|
-
error: error instanceof Error ? error.message : String(error)
|
|
399
|
-
}
|
|
400
|
-
]
|
|
401
|
-
]);
|
|
402
|
-
}
|
|
468
|
+
if (command.kind === "publish" && command.idempotencyKey !== void 0 && state.seenIdempotencyKeys.has(idempotencyKey(command.topic, command.idempotencyKey))) {
|
|
469
|
+
return duplicateCommandEvents(state, command, "duplicate idempotencyKey");
|
|
470
|
+
}
|
|
471
|
+
switch (command.kind) {
|
|
472
|
+
case "ensure-topic":
|
|
473
|
+
return ensureTopic(state, command.topic, command.commandId);
|
|
474
|
+
case "close-topic": {
|
|
475
|
+
const topic = state.topics.get(command.topic);
|
|
476
|
+
if (topic === void 0) {
|
|
477
|
+
return issueEvents(state, command, "unknown-topic", `unknown topic '${command.topic}'`);
|
|
403
478
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
479
|
+
topic.closed = true;
|
|
480
|
+
return [
|
|
481
|
+
{
|
|
482
|
+
kind: "status",
|
|
483
|
+
status: statusFact(state, "topic-closed", {
|
|
484
|
+
topic: command.topic,
|
|
485
|
+
commandId: command.commandId
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
];
|
|
411
489
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
490
|
+
case "topic-policy":
|
|
491
|
+
state.topicPolicy = command.topicPolicy;
|
|
492
|
+
return [
|
|
493
|
+
{
|
|
494
|
+
kind: "status",
|
|
495
|
+
status: statusFact(state, "projection-ready", {
|
|
496
|
+
commandId: command.commandId,
|
|
497
|
+
details: { topicPolicy: command.topicPolicy }
|
|
498
|
+
})
|
|
499
|
+
}
|
|
500
|
+
];
|
|
501
|
+
case "publish":
|
|
502
|
+
return publishMessage(state, command);
|
|
503
|
+
case "ack":
|
|
504
|
+
return ackSubscription(state, command);
|
|
505
|
+
case "seek":
|
|
506
|
+
return seekSubscription(state, command);
|
|
507
|
+
case "close-subscription":
|
|
508
|
+
return closeSubscription(state, command);
|
|
423
509
|
}
|
|
424
|
-
return { commands };
|
|
425
510
|
}
|
|
426
|
-
function
|
|
427
|
-
|
|
511
|
+
function publishMessage(state, command) {
|
|
512
|
+
let topic = state.topics.get(command.topic);
|
|
513
|
+
const events = [];
|
|
514
|
+
if (topic === void 0) {
|
|
515
|
+
if (state.topicPolicy !== "create-as-fact") {
|
|
516
|
+
return issueEvents(state, command, "unknown-topic", `unknown topic '${command.topic}'`);
|
|
517
|
+
}
|
|
518
|
+
events.push(...ensureTopic(state, command.topic, command.commandId));
|
|
519
|
+
topic = state.topics.get(command.topic);
|
|
520
|
+
}
|
|
521
|
+
if (topic === void 0)
|
|
522
|
+
return issueEvents(state, command, "unknown-topic", `unknown topic '${command.topic}'`);
|
|
523
|
+
if (topic.closed)
|
|
524
|
+
return issueEvents(state, command, "closed-topic", `closed topic '${command.topic}'`);
|
|
525
|
+
const message = {
|
|
526
|
+
topic: command.topic,
|
|
527
|
+
seq: topic.nextSeq++,
|
|
528
|
+
payload: command.payload,
|
|
529
|
+
timestampMs: state.now(),
|
|
530
|
+
...command.key === void 0 ? {} : { key: command.key },
|
|
531
|
+
...command.idempotencyKey === void 0 ? {} : { idempotencyKey: command.idempotencyKey },
|
|
532
|
+
...command.commandId === void 0 ? {} : { commandId: command.commandId }
|
|
533
|
+
};
|
|
534
|
+
if (command.idempotencyKey !== void 0) {
|
|
535
|
+
state.seenIdempotencyKeys.add(idempotencyKey(command.topic, command.idempotencyKey));
|
|
536
|
+
}
|
|
537
|
+
topic.messages.push(message);
|
|
538
|
+
events.push({ kind: "message", message });
|
|
539
|
+
events.push({
|
|
540
|
+
kind: "status",
|
|
541
|
+
status: statusFact(state, "message-published", {
|
|
542
|
+
topic: command.topic,
|
|
543
|
+
seq: message.seq,
|
|
544
|
+
commandId: command.commandId
|
|
545
|
+
})
|
|
546
|
+
});
|
|
547
|
+
events.push(...trimRetention(state, command.topic, topic));
|
|
548
|
+
return events;
|
|
428
549
|
}
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
if (state
|
|
432
|
-
|
|
433
|
-
const record = requireTopicRecord(state, topic);
|
|
434
|
-
const name = opts.name ?? `toTopic/${topic}`;
|
|
435
|
-
const producer = graph.node(
|
|
436
|
-
[source],
|
|
437
|
-
(ctx) => {
|
|
438
|
-
for (const value of depBatch(ctx, 0) ?? []) {
|
|
439
|
-
try {
|
|
440
|
-
const key = opts.keyOf?.(value);
|
|
441
|
-
const envelope = {
|
|
442
|
-
topic,
|
|
443
|
-
seq: ++state.seq,
|
|
444
|
-
payload: value,
|
|
445
|
-
timestampMs: state.now(),
|
|
446
|
-
...key === void 0 ? {} : { key }
|
|
447
|
-
};
|
|
448
|
-
ctx.down([
|
|
449
|
-
[
|
|
450
|
-
"DATA",
|
|
451
|
-
{
|
|
452
|
-
kind: "publish",
|
|
453
|
-
envelope
|
|
454
|
-
}
|
|
455
|
-
]
|
|
456
|
-
]);
|
|
457
|
-
} catch (error) {
|
|
458
|
-
ctx.down([["DATA", { kind: "error", topic, error }]]);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
},
|
|
550
|
+
function ensureTopic(state, topic, commandId) {
|
|
551
|
+
assertTopicKey(topic, "messageBus");
|
|
552
|
+
if (!state.topics.has(topic)) state.topics.set(topic, makeTopicState());
|
|
553
|
+
return [
|
|
462
554
|
{
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
meta: { topic },
|
|
466
|
-
completeWhenDepsComplete: false,
|
|
467
|
-
errorWhenDepsError: false
|
|
555
|
+
kind: "status",
|
|
556
|
+
status: statusFact(state, "topic-created", { topic, commandId })
|
|
468
557
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
]
|
|
488
|
-
]);
|
|
489
|
-
} else {
|
|
490
|
-
ctx.down([
|
|
491
|
-
["DATA", { kind: "error", topic, error: typed.error }]
|
|
492
|
-
]);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
const terminal = ctx.terminal[0];
|
|
496
|
-
if (terminal !== false && terminal !== void 0 && terminal !== true) {
|
|
497
|
-
ctx.down([
|
|
498
|
-
["DATA", { kind: "error", topic, error: terminal }]
|
|
499
|
-
]);
|
|
500
|
-
}
|
|
501
|
-
const sourceTerminal = ctx.terminal[1];
|
|
502
|
-
if (sourceTerminal === true) {
|
|
503
|
-
ctx.down([["DATA", { kind: "complete", topic }]]);
|
|
504
|
-
} else if (sourceTerminal !== false && sourceTerminal !== void 0) {
|
|
505
|
-
ctx.down([
|
|
506
|
-
["DATA", { kind: "error", topic, error: sourceTerminal }]
|
|
507
|
-
]);
|
|
508
|
-
}
|
|
509
|
-
},
|
|
558
|
+
];
|
|
559
|
+
}
|
|
560
|
+
function trimRetention(state, topicName, topic) {
|
|
561
|
+
const max = state.retention.maxMessages;
|
|
562
|
+
if (max === void 0 || topic.messages.length <= max) return [];
|
|
563
|
+
if (!Number.isInteger(max) || max < 1) {
|
|
564
|
+
return issueEvents(
|
|
565
|
+
state,
|
|
566
|
+
{ kind: "publish", topic: topicName, payload: void 0 },
|
|
567
|
+
"policy-rejected",
|
|
568
|
+
"retention.maxMessages must be a positive integer"
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
const trimCount = topic.messages.length - max;
|
|
572
|
+
topic.messages.splice(0, trimCount);
|
|
573
|
+
topic.headSeq = topic.messages[0]?.seq ?? topic.nextSeq;
|
|
574
|
+
const events = [
|
|
510
575
|
{
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
576
|
+
kind: "status",
|
|
577
|
+
status: statusFact(state, "retention-trimmed", {
|
|
578
|
+
topic: topicName,
|
|
579
|
+
headSeq: topic.headSeq,
|
|
580
|
+
details: { trimCount }
|
|
581
|
+
})
|
|
517
582
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
function normalizeUnknownTopicPolicy(policy) {
|
|
536
|
-
if (policy === void 0) return "error";
|
|
537
|
-
if (policy === "drop" || policy === "error" || policy === "dead-letter" || policy === "create-as-fact") {
|
|
538
|
-
return policy;
|
|
539
|
-
}
|
|
540
|
-
throw new Error("dynamicHub: unknownTopic policy is not recognized");
|
|
541
|
-
}
|
|
542
|
-
function normalizeHubLimit(value, fallback, owner) {
|
|
543
|
-
if (value === void 0) return fallback;
|
|
544
|
-
if (!Number.isInteger(value) || value < 1) {
|
|
545
|
-
throw new Error(`${owner} must be a positive integer`);
|
|
583
|
+
];
|
|
584
|
+
for (const sub of state.subscriptions.values()) {
|
|
585
|
+
if (sub.closed || sub.topic !== topicName || sub.nextSeq >= topic.headSeq) continue;
|
|
586
|
+
sub.retentionGap = true;
|
|
587
|
+
events.push(
|
|
588
|
+
...issueEvents(
|
|
589
|
+
state,
|
|
590
|
+
{
|
|
591
|
+
kind: "seek",
|
|
592
|
+
topic: topicName,
|
|
593
|
+
subscriptionId: sub.subscriptionId,
|
|
594
|
+
nextSeq: sub.nextSeq
|
|
595
|
+
},
|
|
596
|
+
"retention-gap",
|
|
597
|
+
`subscription '${sub.subscriptionId}' is before retained headSeq`
|
|
598
|
+
)
|
|
599
|
+
);
|
|
546
600
|
}
|
|
547
|
-
return
|
|
601
|
+
return events;
|
|
602
|
+
}
|
|
603
|
+
function ackSubscription(state, command) {
|
|
604
|
+
const topic = state.topics.get(command.topic);
|
|
605
|
+
if (topic === void 0)
|
|
606
|
+
return issueEvents(state, command, "unknown-topic", `unknown topic '${command.topic}'`);
|
|
607
|
+
const sub = getSubscription(state, command.topic, command.subscriptionId);
|
|
608
|
+
if (sub === void 0)
|
|
609
|
+
return issueEvents(
|
|
610
|
+
state,
|
|
611
|
+
command,
|
|
612
|
+
"unknown-subscription",
|
|
613
|
+
`unknown subscription '${command.subscriptionId}'`
|
|
614
|
+
);
|
|
615
|
+
if (sub.closed)
|
|
616
|
+
return issueEvents(state, command, "subscription-closed", "subscription is closed");
|
|
617
|
+
if (sub.retentionGap)
|
|
618
|
+
return issueEvents(state, command, "retention-gap", "subscription must seek before ack");
|
|
619
|
+
if (command.seq < sub.nextSeq)
|
|
620
|
+
return issueEvents(state, command, "source-cursor-stale", "ack is behind subscription cursor");
|
|
621
|
+
if (command.seq >= topic.nextSeq)
|
|
622
|
+
return issueEvents(state, command, "cursor-out-of-range", "ack is beyond topic tail");
|
|
623
|
+
sub.nextSeq = Math.max(sub.nextSeq, command.seq + 1);
|
|
624
|
+
return [
|
|
625
|
+
{
|
|
626
|
+
kind: "status",
|
|
627
|
+
status: statusFact(state, "subscription-acked", {
|
|
628
|
+
topic: command.topic,
|
|
629
|
+
subscriptionId: command.subscriptionId,
|
|
630
|
+
nextSeq: sub.nextSeq,
|
|
631
|
+
commandId: command.commandId
|
|
632
|
+
})
|
|
633
|
+
}
|
|
634
|
+
];
|
|
635
|
+
}
|
|
636
|
+
function seekSubscription(state, command) {
|
|
637
|
+
const topic = state.topics.get(command.topic);
|
|
638
|
+
if (topic === void 0)
|
|
639
|
+
return issueEvents(state, command, "unknown-topic", `unknown topic '${command.topic}'`);
|
|
640
|
+
const sub = getSubscription(state, command.topic, command.subscriptionId);
|
|
641
|
+
if (sub === void 0)
|
|
642
|
+
return issueEvents(
|
|
643
|
+
state,
|
|
644
|
+
command,
|
|
645
|
+
"unknown-subscription",
|
|
646
|
+
`unknown subscription '${command.subscriptionId}'`
|
|
647
|
+
);
|
|
648
|
+
if (sub.closed)
|
|
649
|
+
return issueEvents(state, command, "subscription-closed", "subscription is closed");
|
|
650
|
+
if (command.nextSeq < topic.headSeq)
|
|
651
|
+
return issueEvents(state, command, "retention-gap", "seek is before retained headSeq");
|
|
652
|
+
if (command.nextSeq > topic.nextSeq)
|
|
653
|
+
return issueEvents(state, command, "cursor-out-of-range", "seek is beyond topic tail");
|
|
654
|
+
sub.nextSeq = command.nextSeq;
|
|
655
|
+
sub.retentionGap = false;
|
|
656
|
+
return [
|
|
657
|
+
{
|
|
658
|
+
kind: "status",
|
|
659
|
+
status: statusFact(state, "subscription-sought", {
|
|
660
|
+
topic: command.topic,
|
|
661
|
+
subscriptionId: command.subscriptionId,
|
|
662
|
+
nextSeq: sub.nextSeq,
|
|
663
|
+
commandId: command.commandId
|
|
664
|
+
})
|
|
665
|
+
}
|
|
666
|
+
];
|
|
667
|
+
}
|
|
668
|
+
function closeSubscription(state, command) {
|
|
669
|
+
if (!state.topics.has(command.topic))
|
|
670
|
+
return issueEvents(state, command, "unknown-topic", `unknown topic '${command.topic}'`);
|
|
671
|
+
const sub = getSubscription(state, command.topic, command.subscriptionId);
|
|
672
|
+
if (sub === void 0)
|
|
673
|
+
return issueEvents(
|
|
674
|
+
state,
|
|
675
|
+
command,
|
|
676
|
+
"unknown-subscription",
|
|
677
|
+
`unknown subscription '${command.subscriptionId}'`
|
|
678
|
+
);
|
|
679
|
+
sub.closed = true;
|
|
680
|
+
return [
|
|
681
|
+
{
|
|
682
|
+
kind: "status",
|
|
683
|
+
status: statusFact(state, "subscription-closed", {
|
|
684
|
+
topic: command.topic,
|
|
685
|
+
subscriptionId: command.subscriptionId,
|
|
686
|
+
nextSeq: sub.nextSeq,
|
|
687
|
+
commandId: command.commandId
|
|
688
|
+
})
|
|
689
|
+
}
|
|
690
|
+
];
|
|
691
|
+
}
|
|
692
|
+
function issueEvents(state, command, code, message) {
|
|
693
|
+
const topic = commandTopic(command);
|
|
694
|
+
const commandId = commandIdOf(command);
|
|
695
|
+
const issue = {
|
|
696
|
+
kind: "issue",
|
|
697
|
+
code,
|
|
698
|
+
message,
|
|
699
|
+
severity: "error",
|
|
700
|
+
source: "messageBus",
|
|
701
|
+
details: command,
|
|
702
|
+
metadata: topic === void 0 ? void 0 : { topic }
|
|
703
|
+
};
|
|
704
|
+
const entry = {
|
|
705
|
+
entrySeq: ++state.deadLetterSeq,
|
|
706
|
+
...topic === void 0 ? {} : { topic },
|
|
707
|
+
...isObjectRecord(command) ? { command } : {},
|
|
708
|
+
issue,
|
|
709
|
+
timestampMs: state.now()
|
|
710
|
+
};
|
|
711
|
+
state.deadLetters.push(entry);
|
|
712
|
+
return [
|
|
713
|
+
{ kind: "issue", issue },
|
|
714
|
+
{ kind: "dead-letter", entry },
|
|
715
|
+
{
|
|
716
|
+
kind: "status",
|
|
717
|
+
status: statusFact(state, "command-rejected", {
|
|
718
|
+
topic,
|
|
719
|
+
commandId,
|
|
720
|
+
issueCode: code
|
|
721
|
+
})
|
|
722
|
+
}
|
|
723
|
+
];
|
|
548
724
|
}
|
|
549
|
-
function
|
|
550
|
-
|
|
551
|
-
return command;
|
|
725
|
+
function rejectCommand(state, command, reason) {
|
|
726
|
+
return issueEvents(state, command, "malformed-command", reason);
|
|
552
727
|
}
|
|
553
|
-
function
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
...value.payload === void 0 ? {} : { payload: value.payload }
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
const topic = value.topic;
|
|
567
|
-
if (typeof topic !== "string" || topic.length === 0) {
|
|
568
|
-
return "dynamicHub: command topic must be a non-empty string";
|
|
569
|
-
}
|
|
570
|
-
if (kind === "publish") {
|
|
571
|
-
if (value.payload === void 0) return "dynamicHub: publish command payload is required";
|
|
572
|
-
return {
|
|
573
|
-
kind,
|
|
574
|
-
topic,
|
|
575
|
-
payload: value.payload,
|
|
576
|
-
...typeof value.key === "string" ? { key: value.key } : {}
|
|
577
|
-
};
|
|
728
|
+
function duplicateCommandEvents(state, command, message) {
|
|
729
|
+
const status = statusFact(state, "duplicate-command", {
|
|
730
|
+
topic: "topic" in command ? command.topic : void 0,
|
|
731
|
+
commandId: command.commandId
|
|
732
|
+
});
|
|
733
|
+
if (state.dedupe.commandId === "issue") {
|
|
734
|
+
return [
|
|
735
|
+
{ kind: "status", status },
|
|
736
|
+
...issueEvents(state, command, "duplicate-command", message)
|
|
737
|
+
];
|
|
578
738
|
}
|
|
739
|
+
return [{ kind: "status", status }];
|
|
740
|
+
}
|
|
741
|
+
function statusFact(state, kind, fields) {
|
|
579
742
|
return {
|
|
580
743
|
kind,
|
|
581
|
-
|
|
582
|
-
...
|
|
583
|
-
...
|
|
744
|
+
timestampMs: state.now(),
|
|
745
|
+
...fields.topic === void 0 ? {} : { topic: fields.topic },
|
|
746
|
+
...fields.seq === void 0 ? {} : { seq: fields.seq },
|
|
747
|
+
...fields.headSeq === void 0 ? {} : { headSeq: fields.headSeq },
|
|
748
|
+
...fields.subscriptionId === void 0 ? {} : { subscriptionId: fields.subscriptionId },
|
|
749
|
+
...fields.nextSeq === void 0 ? {} : { nextSeq: fields.nextSeq },
|
|
750
|
+
...fields.commandId === void 0 ? {} : { commandId: fields.commandId },
|
|
751
|
+
...fields.issueCode === void 0 ? {} : { issueCode: fields.issueCode },
|
|
752
|
+
...fields.details === void 0 ? {} : { details: fields.details }
|
|
584
753
|
};
|
|
585
754
|
}
|
|
586
|
-
function
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
return `dynamicHub: now() threw: ${error instanceof Error ? error.message : String(error)}`;
|
|
755
|
+
function validateCommand(command) {
|
|
756
|
+
if (!isObjectRecord(command)) return "command must be an object";
|
|
757
|
+
if (typeof command.kind !== "string") return "command kind is required";
|
|
758
|
+
if (!isMessageBusCommandKind(command.kind)) return `unknown command kind '${command.kind}'`;
|
|
759
|
+
if ("topic" in command) {
|
|
760
|
+
if (typeof command.topic !== "string") return "messageBus: topic must be a non-empty string";
|
|
761
|
+
const topicError = validateTopicKey(command.topic, "messageBus");
|
|
762
|
+
if (topicError !== void 0) return topicError;
|
|
595
763
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const timestampMs = readHubTimestamp(now);
|
|
599
|
-
return typeof timestampMs === "number" ? timestampMs : Date.now();
|
|
600
|
-
}
|
|
601
|
-
function reduceDynamicHubCommand(runtime, command, unknownTopic, maxTopics, maxTopicLength, now) {
|
|
602
|
-
const topic = "topic" in command ? command.topic : void 0;
|
|
603
|
-
if (topic !== void 0) {
|
|
604
|
-
const topicError = validateTopicKey(topic, "dynamicHub", maxTopicLength);
|
|
605
|
-
if (topicError !== void 0) {
|
|
606
|
-
return [hubError(runtime, command, topicError, timestampOrFallback(now))];
|
|
607
|
-
}
|
|
764
|
+
if ("subscriptionId" in command && typeof command.subscriptionId !== "string") {
|
|
765
|
+
return "subscriptionId must be a string";
|
|
608
766
|
}
|
|
609
|
-
if (
|
|
610
|
-
return
|
|
767
|
+
if (command.kind === "publish" && command.payload === void 0)
|
|
768
|
+
return "publish payload is required";
|
|
769
|
+
if (command.kind === "ack" && !isPositiveSeq(command.seq))
|
|
770
|
+
return "ack seq must be a positive integer";
|
|
771
|
+
if (command.kind === "seek" && !isPositiveSeq(command.nextSeq)) {
|
|
772
|
+
return "seek nextSeq must be a positive integer";
|
|
611
773
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const capacityError = canAddHubTopic(runtime, command.topic, maxTopics);
|
|
615
|
-
if (capacityError !== void 0) {
|
|
616
|
-
return [hubError(runtime, command, capacityError, timestampOrFallback(now))];
|
|
617
|
-
}
|
|
618
|
-
const timestampMs = readHubTimestamp(now);
|
|
619
|
-
if (typeof timestampMs === "string")
|
|
620
|
-
return [hubError(runtime, command, timestampMs, Date.now())];
|
|
621
|
-
addHubTopic(runtime, command.topic);
|
|
622
|
-
return [hubEvent(runtime, timestampMs, command)];
|
|
623
|
-
}
|
|
624
|
-
case "delete": {
|
|
625
|
-
if (!hasHubTopic(runtime, command.topic)) {
|
|
626
|
-
return unknownHubTopic(runtime, command, unknownTopic, now);
|
|
627
|
-
}
|
|
628
|
-
const timestampMs = readHubTimestamp(now);
|
|
629
|
-
if (typeof timestampMs === "string")
|
|
630
|
-
return [hubError(runtime, command, timestampMs, Date.now())];
|
|
631
|
-
deleteHubTopic(runtime, command.topic);
|
|
632
|
-
return [hubEvent(runtime, timestampMs, command)];
|
|
633
|
-
}
|
|
634
|
-
case "publish": {
|
|
635
|
-
if (!hasHubTopic(runtime, command.topic)) {
|
|
636
|
-
if (unknownTopic === "create-as-fact") {
|
|
637
|
-
const capacityError = canAddHubTopic(runtime, command.topic, maxTopics);
|
|
638
|
-
if (capacityError !== void 0) {
|
|
639
|
-
return [hubError(runtime, command, capacityError, timestampOrFallback(now))];
|
|
640
|
-
}
|
|
641
|
-
const createTimestampMs = readHubTimestamp(now);
|
|
642
|
-
if (typeof createTimestampMs === "string") {
|
|
643
|
-
return [hubError(runtime, command, createTimestampMs, Date.now())];
|
|
644
|
-
}
|
|
645
|
-
const messageTimestampMs = readHubTimestamp(now);
|
|
646
|
-
if (typeof messageTimestampMs === "string") {
|
|
647
|
-
return [hubError(runtime, command, messageTimestampMs, Date.now())];
|
|
648
|
-
}
|
|
649
|
-
addHubTopic(runtime, command.topic);
|
|
650
|
-
return [
|
|
651
|
-
hubEvent(runtime, createTimestampMs, { kind: "create", topic: command.topic }),
|
|
652
|
-
hubEvent(runtime, messageTimestampMs, {
|
|
653
|
-
kind: "message",
|
|
654
|
-
topic: command.topic,
|
|
655
|
-
payload: command.payload,
|
|
656
|
-
...command.key === void 0 ? {} : { key: command.key }
|
|
657
|
-
})
|
|
658
|
-
];
|
|
659
|
-
}
|
|
660
|
-
return unknownHubTopic(runtime, command, unknownTopic, now);
|
|
661
|
-
}
|
|
662
|
-
const timestampMs = readHubTimestamp(now);
|
|
663
|
-
if (typeof timestampMs === "string")
|
|
664
|
-
return [hubError(runtime, command, timestampMs, Date.now())];
|
|
665
|
-
return [
|
|
666
|
-
hubEvent(runtime, timestampMs, {
|
|
667
|
-
kind: "message",
|
|
668
|
-
topic: command.topic,
|
|
669
|
-
payload: command.payload,
|
|
670
|
-
...command.key === void 0 ? {} : { key: command.key }
|
|
671
|
-
})
|
|
672
|
-
];
|
|
673
|
-
}
|
|
674
|
-
case "subscribe": {
|
|
675
|
-
if (!hasHubTopic(runtime, command.topic)) {
|
|
676
|
-
if (unknownTopic === "create-as-fact") {
|
|
677
|
-
const capacityError = canAddHubTopic(runtime, command.topic, maxTopics);
|
|
678
|
-
if (capacityError !== void 0) {
|
|
679
|
-
return [hubError(runtime, command, capacityError, timestampOrFallback(now))];
|
|
680
|
-
}
|
|
681
|
-
const createTimestampMs = readHubTimestamp(now);
|
|
682
|
-
if (typeof createTimestampMs === "string") {
|
|
683
|
-
return [hubError(runtime, command, createTimestampMs, Date.now())];
|
|
684
|
-
}
|
|
685
|
-
const subscribeTimestampMs = readHubTimestamp(now);
|
|
686
|
-
if (typeof subscribeTimestampMs === "string") {
|
|
687
|
-
return [hubError(runtime, command, subscribeTimestampMs, Date.now())];
|
|
688
|
-
}
|
|
689
|
-
addHubTopic(runtime, command.topic);
|
|
690
|
-
return [
|
|
691
|
-
hubEvent(runtime, createTimestampMs, { kind: "create", topic: command.topic }),
|
|
692
|
-
hubEvent(runtime, subscribeTimestampMs, command)
|
|
693
|
-
];
|
|
694
|
-
}
|
|
695
|
-
return unknownHubTopic(runtime, command, unknownTopic, now);
|
|
696
|
-
}
|
|
697
|
-
const timestampMs = readHubTimestamp(now);
|
|
698
|
-
if (typeof timestampMs === "string")
|
|
699
|
-
return [hubError(runtime, command, timestampMs, Date.now())];
|
|
700
|
-
return [hubEvent(runtime, timestampMs, command)];
|
|
701
|
-
}
|
|
702
|
-
case "close": {
|
|
703
|
-
const timestampMs = readHubTimestamp(now);
|
|
704
|
-
if (typeof timestampMs === "string")
|
|
705
|
-
return [hubError(runtime, command, timestampMs, Date.now())];
|
|
706
|
-
runtime.open = false;
|
|
707
|
-
return [hubEvent(runtime, timestampMs, command)];
|
|
708
|
-
}
|
|
774
|
+
if (command.kind === "topic-policy" && command.topicPolicy !== "strict" && command.topicPolicy !== "create-as-fact") {
|
|
775
|
+
return "topicPolicy is not recognized";
|
|
709
776
|
}
|
|
777
|
+
return void 0;
|
|
778
|
+
}
|
|
779
|
+
function isMessageBusCommandKind(kind) {
|
|
780
|
+
return kind === "ensure-topic" || kind === "close-topic" || kind === "publish" || kind === "topic-policy" || kind === "ack" || kind === "seek" || kind === "close-subscription";
|
|
710
781
|
}
|
|
711
|
-
function
|
|
712
|
-
const
|
|
782
|
+
function catalogPage(state, params) {
|
|
783
|
+
const limit = positiveLimit(params.limit);
|
|
784
|
+
const topics = [...state.topics.entries()].filter(
|
|
785
|
+
([topic, value]) => (params.includeClosed ? true : !value.closed) && (params.afterTopic === void 0 || topic > params.afterTopic)
|
|
786
|
+
).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
787
|
+
const page = topics.slice(0, limit);
|
|
788
|
+
const hasMore = topics.length > limit;
|
|
713
789
|
return {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
790
|
+
topics: page.map(([topic, value]) => ({
|
|
791
|
+
topic,
|
|
792
|
+
closed: value.closed,
|
|
793
|
+
headSeq: value.headSeq,
|
|
794
|
+
nextSeq: value.nextSeq,
|
|
795
|
+
messageCount: value.messages.length
|
|
796
|
+
})),
|
|
797
|
+
...hasMore && page.length > 0 ? { nextAfterTopic: page[page.length - 1]?.[0] } : {},
|
|
798
|
+
hasMore
|
|
717
799
|
};
|
|
718
800
|
}
|
|
719
|
-
function
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
801
|
+
function topicPage(state, topicName, params) {
|
|
802
|
+
const topic = state.topics.get(topicName);
|
|
803
|
+
const start = params.afterSeq === void 0 ? topic?.headSeq ?? 1 : params.afterSeq + 1;
|
|
804
|
+
const all = (topic?.messages ?? []).filter((message) => message.seq >= start);
|
|
805
|
+
const limit = positiveLimit(params.limit);
|
|
806
|
+
const messages = all.slice(0, limit);
|
|
807
|
+
const hasMore = all.length > limit;
|
|
808
|
+
return {
|
|
809
|
+
topic: topicName,
|
|
810
|
+
messages,
|
|
811
|
+
fromSeq: start,
|
|
812
|
+
...messages.length > 0 ? { throughSeq: messages[messages.length - 1]?.seq } : {},
|
|
813
|
+
...hasMore && messages.length > 0 ? { nextAfterSeq: messages[messages.length - 1]?.seq } : {},
|
|
814
|
+
hasMore
|
|
815
|
+
};
|
|
726
816
|
}
|
|
727
|
-
function
|
|
728
|
-
const
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
817
|
+
function availablePage(state, sub, params) {
|
|
818
|
+
const cursor = cursorSnapshot(state, sub);
|
|
819
|
+
const start = params.afterSeq === void 0 ? sub.nextSeq : params.afterSeq + 1;
|
|
820
|
+
const topic = state.topics.get(sub.topic);
|
|
821
|
+
const all = sub.retentionGap ? [] : (topic?.messages ?? []).filter((message) => message.seq >= start);
|
|
822
|
+
const limit = positiveLimit(params.limit);
|
|
823
|
+
const messages = all.slice(0, limit);
|
|
824
|
+
const hasMore = all.length > limit;
|
|
825
|
+
return {
|
|
826
|
+
topic: sub.topic,
|
|
827
|
+
subscriptionId: sub.subscriptionId,
|
|
828
|
+
cursor,
|
|
829
|
+
messages,
|
|
830
|
+
fromSeq: start,
|
|
831
|
+
...messages.length > 0 ? { throughSeq: messages[messages.length - 1]?.seq } : {},
|
|
832
|
+
...hasMore && messages.length > 0 ? { nextAfterSeq: messages[messages.length - 1]?.seq } : {},
|
|
833
|
+
hasMore
|
|
834
|
+
};
|
|
742
835
|
}
|
|
743
|
-
function
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
836
|
+
function deadLetterPage(state, params) {
|
|
837
|
+
const limit = positiveLimit(params.limit);
|
|
838
|
+
const entries = state.deadLetters.filter((entry) => {
|
|
839
|
+
if (params.afterEntrySeq !== void 0 && entry.entrySeq <= params.afterEntrySeq) return false;
|
|
840
|
+
if (params.topic !== void 0 && entry.topic !== params.topic) return false;
|
|
841
|
+
if (params.code !== void 0 && entry.issue.code !== params.code) return false;
|
|
842
|
+
return true;
|
|
843
|
+
});
|
|
844
|
+
const page = entries.slice(0, limit);
|
|
845
|
+
const hasMore = entries.length > limit;
|
|
846
|
+
return {
|
|
847
|
+
entries: page,
|
|
848
|
+
...hasMore && page.length > 0 ? { nextAfterEntrySeq: page[page.length - 1]?.entrySeq } : {},
|
|
849
|
+
hasMore
|
|
850
|
+
};
|
|
747
851
|
}
|
|
748
|
-
function
|
|
852
|
+
function cursorSnapshot(state, sub) {
|
|
853
|
+
const topic = state.topics.get(sub.topic);
|
|
749
854
|
return {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
855
|
+
topic: sub.topic,
|
|
856
|
+
subscriptionId: sub.subscriptionId,
|
|
857
|
+
nextSeq: sub.nextSeq,
|
|
858
|
+
closed: sub.closed,
|
|
859
|
+
retentionGap: sub.retentionGap,
|
|
860
|
+
headSeq: topic?.headSeq ?? 1
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function ensureSubscription(state, opts) {
|
|
864
|
+
const key = `${opts.topic}\0${opts.subscriptionId}`;
|
|
865
|
+
const existing = state.subscriptions.get(key);
|
|
866
|
+
if (existing !== void 0) return existing;
|
|
867
|
+
const topic = state.topics.get(opts.topic);
|
|
868
|
+
const nextSeq = opts.from === "earliest" ? topic?.headSeq ?? 1 : opts.from === "latest" ? topic?.nextSeq ?? 1 : opts.from;
|
|
869
|
+
const sub = {
|
|
870
|
+
topic: opts.topic,
|
|
871
|
+
subscriptionId: opts.subscriptionId,
|
|
872
|
+
nextSeq,
|
|
873
|
+
closed: false,
|
|
874
|
+
retentionGap: topic !== void 0 && nextSeq < topic.headSeq
|
|
755
875
|
};
|
|
876
|
+
state.subscriptions.set(key, sub);
|
|
877
|
+
return sub;
|
|
878
|
+
}
|
|
879
|
+
function getSubscription(state, topic, subscriptionId) {
|
|
880
|
+
return state.subscriptions.get(`${topic}\0${subscriptionId}`);
|
|
881
|
+
}
|
|
882
|
+
function pullParams(pull) {
|
|
883
|
+
return isObjectRecord(pull?.params) ? pull.params : {};
|
|
884
|
+
}
|
|
885
|
+
function publishCommand(commandNode, command) {
|
|
886
|
+
commandNode.down([["DATA", command]]);
|
|
887
|
+
return command;
|
|
888
|
+
}
|
|
889
|
+
function makeTopicState() {
|
|
890
|
+
return { closed: false, headSeq: 1, nextSeq: 1, messages: [] };
|
|
891
|
+
}
|
|
892
|
+
function uniqueTopics(topics) {
|
|
893
|
+
const unique = [...new Set(topics)];
|
|
894
|
+
if (unique.length !== topics.length) throw new Error("messageBus: duplicate topic");
|
|
895
|
+
for (const topic of unique) assertTopicKey(topic, "messageBus");
|
|
896
|
+
return Object.freeze(unique.sort());
|
|
756
897
|
}
|
|
757
|
-
function assertTopicKey(topic, owner
|
|
758
|
-
const error = validateTopicKey(topic, owner
|
|
898
|
+
function assertTopicKey(topic, owner) {
|
|
899
|
+
const error = validateTopicKey(topic, owner);
|
|
759
900
|
if (error !== void 0) throw new Error(error);
|
|
760
901
|
}
|
|
761
|
-
function validateTopicKey(topic, owner
|
|
762
|
-
if (typeof topic !== "string" || topic.length === 0)
|
|
902
|
+
function validateTopicKey(topic, owner) {
|
|
903
|
+
if (typeof topic !== "string" || topic.length === 0)
|
|
763
904
|
return `${owner}: topic must be a non-empty string`;
|
|
764
|
-
}
|
|
765
|
-
if (topic.length > maxTopicLength) {
|
|
766
|
-
return `${owner}: topic exceeds maxTopicLength`;
|
|
767
|
-
}
|
|
768
905
|
return void 0;
|
|
769
906
|
}
|
|
770
|
-
function
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
return
|
|
782
|
-
}
|
|
783
|
-
function
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
seen.add(node);
|
|
792
|
-
for (const dep of node.deps) stack.push(dep);
|
|
793
|
-
}
|
|
794
|
-
return false;
|
|
907
|
+
function assertNonEmpty(value, owner) {
|
|
908
|
+
if (typeof value !== "string" || value.length === 0)
|
|
909
|
+
throw new Error(`${owner}: must be a non-empty string`);
|
|
910
|
+
}
|
|
911
|
+
function positiveLimit(limit) {
|
|
912
|
+
if (limit === void 0) return 100;
|
|
913
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
914
|
+
throw new Error("messageBus: limit must be a positive integer");
|
|
915
|
+
return limit;
|
|
916
|
+
}
|
|
917
|
+
function isPositiveSeq(value) {
|
|
918
|
+
return Number.isInteger(value) && value >= 1;
|
|
919
|
+
}
|
|
920
|
+
function isObjectRecord(value) {
|
|
921
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
922
|
+
}
|
|
923
|
+
function commandTopic(command) {
|
|
924
|
+
return isObjectRecord(command) && typeof command.topic === "string" ? command.topic : void 0;
|
|
925
|
+
}
|
|
926
|
+
function commandIdOf(command) {
|
|
927
|
+
return isObjectRecord(command) && typeof command.commandId === "string" ? command.commandId : void 0;
|
|
795
928
|
}
|
|
796
|
-
function
|
|
797
|
-
|
|
798
|
-
if (state === void 0) throw new Error("dynamicHub: unknown hub implementation");
|
|
799
|
-
return state;
|
|
929
|
+
function idempotencyKey(topic, key) {
|
|
930
|
+
return `${topic}\0${key}`;
|
|
800
931
|
}
|
|
801
932
|
// Annotate the CommonJS export names for ESM import in node:
|
|
802
933
|
0 && (module.exports = {
|
|
@@ -808,11 +939,11 @@ function requireDynamicHubState(hub) {
|
|
|
808
939
|
SPAWNS_TOPIC,
|
|
809
940
|
STANDARD_TOPICS,
|
|
810
941
|
TODOS_TOPIC,
|
|
811
|
-
|
|
812
|
-
|
|
942
|
+
eventMessage,
|
|
943
|
+
eventMessageIssue,
|
|
813
944
|
fromTopic,
|
|
945
|
+
isEventMessage,
|
|
814
946
|
messageBus,
|
|
815
|
-
toHubTopic,
|
|
816
947
|
toTopic
|
|
817
948
|
});
|
|
818
949
|
//# sourceMappingURL=index.cjs.map
|