yaml-flow 3.1.1 → 5.0.0

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.
Files changed (194) hide show
  1. package/README.md +81 -20
  2. package/board-live-cards-cli.js +37 -0
  3. package/browser/board-livegraph-runtime.js +1453 -0
  4. package/browser/board-livegraph-runtime.js.map +1 -0
  5. package/browser/card-compute.js +153 -433
  6. package/browser/live-cards.js +868 -115
  7. package/browser/live-cards.schema.json +90 -83
  8. package/dist/board-livegraph-runtime/index.cjs +1448 -0
  9. package/dist/board-livegraph-runtime/index.cjs.map +1 -0
  10. package/dist/board-livegraph-runtime/index.d.cts +101 -0
  11. package/dist/board-livegraph-runtime/index.d.ts +101 -0
  12. package/dist/board-livegraph-runtime/index.js +1441 -0
  13. package/dist/board-livegraph-runtime/index.js.map +1 -0
  14. package/dist/card-compute/index.cjs +266 -431
  15. package/dist/card-compute/index.cjs.map +1 -1
  16. package/dist/card-compute/index.d.cts +77 -49
  17. package/dist/card-compute/index.d.ts +77 -49
  18. package/dist/card-compute/index.js +263 -432
  19. package/dist/card-compute/index.js.map +1 -1
  20. package/dist/cli/board-live-cards-cli.cjs +2750 -0
  21. package/dist/cli/board-live-cards-cli.cjs.map +1 -0
  22. package/dist/cli/board-live-cards-cli.d.cts +205 -0
  23. package/dist/cli/board-live-cards-cli.d.ts +205 -0
  24. package/dist/cli/board-live-cards-cli.js +2702 -0
  25. package/dist/cli/board-live-cards-cli.js.map +1 -0
  26. package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
  27. package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
  28. package/dist/continuous-event-graph/index.cjs +258 -464
  29. package/dist/continuous-event-graph/index.cjs.map +1 -1
  30. package/dist/continuous-event-graph/index.d.cts +18 -358
  31. package/dist/continuous-event-graph/index.d.ts +18 -358
  32. package/dist/continuous-event-graph/index.js +255 -464
  33. package/dist/continuous-event-graph/index.js.map +1 -1
  34. package/dist/event-graph/index.cjs +4 -4
  35. package/dist/event-graph/index.cjs.map +1 -1
  36. package/dist/event-graph/index.d.cts +5 -5
  37. package/dist/event-graph/index.d.ts +5 -5
  38. package/dist/event-graph/index.js +4 -4
  39. package/dist/event-graph/index.js.map +1 -1
  40. package/dist/index.cjs +1684 -555
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +26 -7
  43. package/dist/index.d.ts +26 -7
  44. package/dist/index.js +1678 -555
  45. package/dist/index.js.map +1 -1
  46. package/dist/inference/index.cjs +138 -19
  47. package/dist/inference/index.cjs.map +1 -1
  48. package/dist/inference/index.d.cts +2 -2
  49. package/dist/inference/index.d.ts +2 -2
  50. package/dist/inference/index.js +138 -19
  51. package/dist/inference/index.js.map +1 -1
  52. package/dist/journal-DRfJiheM.d.cts +28 -0
  53. package/dist/journal-NLYuqege.d.ts +28 -0
  54. package/dist/live-cards-bridge-Or7fdEJV.d.ts +316 -0
  55. package/dist/live-cards-bridge-vGJ6tMzN.d.cts +316 -0
  56. package/dist/schedule-CMcZe5Ny.d.ts +21 -0
  57. package/dist/schedule-CiucyCan.d.cts +21 -0
  58. package/dist/step-machine/index.cjs +18 -1
  59. package/dist/step-machine/index.cjs.map +1 -1
  60. package/dist/step-machine/index.d.cts +2 -2
  61. package/dist/step-machine/index.d.ts +2 -2
  62. package/dist/step-machine/index.js +18 -1
  63. package/dist/step-machine/index.js.map +1 -1
  64. package/dist/stores/file.d.cts +1 -1
  65. package/dist/stores/file.d.ts +1 -1
  66. package/dist/stores/index.d.cts +1 -1
  67. package/dist/stores/index.d.ts +1 -1
  68. package/dist/stores/localStorage.d.cts +1 -1
  69. package/dist/stores/localStorage.d.ts +1 -1
  70. package/dist/stores/memory.d.cts +1 -1
  71. package/dist/stores/memory.d.ts +1 -1
  72. package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
  73. package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
  74. package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
  75. package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
  76. package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
  77. package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
  78. package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
  79. package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
  80. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
  81. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
  82. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
  83. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
  84. package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
  85. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  86. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
  87. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +217 -0
  88. package/examples/browser/livecards-browser/index.html +41 -0
  89. package/examples/browser/{index.html → step-machine-browser/index.html} +53 -53
  90. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  91. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  92. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  93. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  94. package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  95. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  96. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  97. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  98. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  99. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  100. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  101. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  102. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  103. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  104. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  105. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  106. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  107. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
  108. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
  109. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
  110. package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
  111. package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
  112. package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
  113. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
  114. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
  115. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
  116. package/examples/example-board/board.yaml +23 -0
  117. package/examples/example-board/bootstrap_payload.json +1 -0
  118. package/examples/example-board/cards/card-chain-region-alert.json +39 -0
  119. package/examples/example-board/cards/card-chain-region-totals.json +26 -0
  120. package/examples/example-board/cards/card-chain-top-region.json +24 -0
  121. package/examples/example-board/cards/card-ex-actions.json +32 -0
  122. package/examples/example-board/cards/card-ex-chart.json +30 -0
  123. package/examples/example-board/cards/card-ex-filter.json +36 -0
  124. package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
  125. package/examples/example-board/cards/card-ex-form.json +91 -0
  126. package/examples/example-board/cards/card-ex-list.json +22 -0
  127. package/examples/example-board/cards/card-ex-markdown.json +17 -0
  128. package/examples/example-board/cards/card-ex-metric.json +19 -0
  129. package/examples/example-board/cards/card-ex-narrative.json +36 -0
  130. package/examples/example-board/cards/card-ex-source-http.json +28 -0
  131. package/examples/example-board/cards/card-ex-source.json +21 -0
  132. package/examples/example-board/cards/card-ex-status.json +35 -0
  133. package/examples/example-board/cards/card-ex-table.json +30 -0
  134. package/examples/example-board/cards/card-ex-todo.json +29 -0
  135. package/examples/example-board/demo-chat-handler.js +69 -0
  136. package/examples/example-board/demo-server.js +87 -0
  137. package/examples/example-board/demo-shell-browser.html +806 -0
  138. package/examples/example-board/demo-shell-with-server.html +280 -0
  139. package/examples/example-board/demo-shell.html +62 -0
  140. package/examples/example-board/demo-task-executor.js +255 -0
  141. package/examples/example-board/mock.db +15 -0
  142. package/examples/example-board/reusable-board-runtime-client.js +265 -0
  143. package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
  144. package/examples/example-board/reusable-server-runtime.js +1284 -0
  145. package/examples/index.html +799 -0
  146. package/examples/{batch → npm-libs/batch}/batch-step-machine.ts +1 -1
  147. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-cards-board.ts +18 -18
  148. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-portfolio-dashboard.ts +24 -24
  149. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/portfolio-tracker.ts +1 -1
  150. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-monitoring.ts +1 -1
  151. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-pipeline.ts +1 -1
  152. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/soc-incident-board.ts +1 -1
  153. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/stock-dashboard.ts +1 -1
  154. package/examples/{event-graph → npm-libs/event-graph}/ci-cd-pipeline.ts +1 -1
  155. package/examples/{event-graph → npm-libs/event-graph}/executor-diamond.ts +1 -1
  156. package/examples/{event-graph → npm-libs/event-graph}/executor-pipeline.ts +1 -1
  157. package/examples/{event-graph → npm-libs/event-graph}/research-pipeline.ts +1 -1
  158. package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/multi-stage-etl.ts +1 -1
  159. package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/url-processing-pipeline.ts +1 -1
  160. package/examples/{inference → npm-libs/inference}/azure-deployment.ts +1 -1
  161. package/examples/{inference → npm-libs/inference}/copilot-cli.ts +1 -1
  162. package/examples/{inference → npm-libs/inference}/data-pipeline.ts +1 -1
  163. package/examples/{inference → npm-libs/inference}/pluggable-adapters.ts +1 -1
  164. package/examples/{node → npm-libs/node}/ai-conversation.ts +1 -1
  165. package/examples/{node → npm-libs/node}/simple-greeting.ts +2 -2
  166. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  167. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  168. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  169. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  170. package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  171. package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  172. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  173. package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  174. package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  175. package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  176. package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  177. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  178. package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  179. package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  180. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  181. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  182. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  183. package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  184. package/package.json +27 -2
  185. package/schema/board-status.schema.json +118 -0
  186. package/schema/card-runtime.schema.json +25 -0
  187. package/schema/flow.schema.json +5 -0
  188. package/schema/live-cards.schema.json +90 -83
  189. package/step-machine-cli.js +674 -0
  190. package/browser/ingest-board.js +0 -296
  191. package/examples/ingest.js +0 -733
  192. /package/examples/{flows → npm-libs/flows}/ai-conversation.yaml +0 -0
  193. /package/examples/{flows → npm-libs/flows}/order-processing.yaml +0 -0
  194. /package/examples/{flows → npm-libs/flows}/simple-greeting.yaml +0 -0
@@ -0,0 +1,1441 @@
1
+ import jsonata2 from 'jsonata';
2
+ import 'ajv-formats';
3
+ import 'child_process';
4
+
5
+ // src/card-compute/index.ts
6
+
7
+ // src/card-compute/index.ts
8
+ function deepGet(obj, path) {
9
+ if (!path || !obj) return void 0;
10
+ const parts = path.split(".");
11
+ let cur = obj;
12
+ for (let i = 0; i < parts.length; i++) {
13
+ if (cur == null) return void 0;
14
+ cur = cur[parts[i]];
15
+ }
16
+ return cur;
17
+ }
18
+ function deepSet(obj, path, value) {
19
+ const parts = path.split(".");
20
+ let cur = obj;
21
+ for (let i = 0; i < parts.length - 1; i++) {
22
+ if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") cur[parts[i]] = {};
23
+ cur = cur[parts[i]];
24
+ }
25
+ cur[parts[parts.length - 1]] = value;
26
+ }
27
+ async function run(node, options) {
28
+ if (!node?.compute?.length) return node;
29
+ if (!node.card_data) node.card_data = {};
30
+ node.computed_values = {};
31
+ node._sourcesData = options?.sourcesData ?? {};
32
+ const ctx = {
33
+ card_data: node.card_data,
34
+ requires: node.requires ?? {},
35
+ fetched_sources: node._sourcesData,
36
+ computed_values: node.computed_values
37
+ };
38
+ for (const step of node.compute) {
39
+ try {
40
+ const val = await jsonata2(step.expr).evaluate(ctx);
41
+ deepSet(node.computed_values, step.bindTo, val);
42
+ ctx.computed_values = node.computed_values;
43
+ } catch (err) {
44
+ console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
45
+ }
46
+ }
47
+ return node;
48
+ }
49
+ async function evalExpr(expr, node) {
50
+ const ctx = {
51
+ card_data: node.card_data ?? {},
52
+ requires: node.requires ?? {},
53
+ fetched_sources: node._sourcesData ?? {},
54
+ computed_values: node.computed_values ?? {}
55
+ };
56
+ return jsonata2(expr).evaluate(ctx);
57
+ }
58
+ function resolve(node, path) {
59
+ if (path.startsWith("fetched_sources.")) {
60
+ return deepGet(node._sourcesData ?? {}, path.slice("fetched_sources.".length));
61
+ }
62
+ return deepGet(node, path);
63
+ }
64
+ var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
65
+ "metric",
66
+ "table",
67
+ "chart",
68
+ "form",
69
+ "filter",
70
+ "list",
71
+ "notes",
72
+ "todo",
73
+ "alert",
74
+ "narrative",
75
+ "badge",
76
+ "text",
77
+ "markdown",
78
+ "custom"
79
+ ]);
80
+ var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "card_data", "compute", "sources"]);
81
+ function validateNode(node) {
82
+ const errors = [];
83
+ if (!node || typeof node !== "object" || Array.isArray(node)) {
84
+ return { ok: false, errors: ["Node must be a non-null object"] };
85
+ }
86
+ const n = node;
87
+ if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
88
+ for (const key of Object.keys(n)) {
89
+ if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
90
+ }
91
+ if (n.card_data == null || typeof n.card_data !== "object" || Array.isArray(n.card_data)) {
92
+ errors.push("card_data: required, must be an object");
93
+ }
94
+ if (n.meta != null) {
95
+ if (typeof n.meta !== "object" || Array.isArray(n.meta)) {
96
+ errors.push("meta: must be an object");
97
+ } else {
98
+ const meta = n.meta;
99
+ if (meta.title != null && typeof meta.title !== "string") errors.push("meta.title: must be a string");
100
+ if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
101
+ }
102
+ }
103
+ if (n.requires != null && !Array.isArray(n.requires)) errors.push("requires: must be an array of strings");
104
+ if (n.provides != null) {
105
+ if (!Array.isArray(n.provides)) {
106
+ errors.push("provides: must be an array of { bindTo, src } bindings");
107
+ } else {
108
+ n.provides.forEach((p, i) => {
109
+ if (!p || typeof p !== "object" || Array.isArray(p)) {
110
+ errors.push(`provides[${i}]: must be an object with bindTo and src`);
111
+ } else {
112
+ const b = p;
113
+ if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
114
+ if (typeof b.src !== "string" || !b.src) errors.push(`provides[${i}]: missing required "src" string`);
115
+ }
116
+ });
117
+ }
118
+ }
119
+ if (n.compute != null) {
120
+ if (!Array.isArray(n.compute)) {
121
+ errors.push("compute: must be an array of compute steps");
122
+ } else {
123
+ n.compute.forEach((step, i) => {
124
+ if (!step || typeof step !== "object" || Array.isArray(step)) {
125
+ errors.push(`compute[${i}]: must be a compute step object`);
126
+ } else {
127
+ const s = step;
128
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
129
+ if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
130
+ }
131
+ });
132
+ }
133
+ }
134
+ if (n.sources != null) {
135
+ if (!Array.isArray(n.sources)) {
136
+ errors.push("sources: must be an array");
137
+ } else {
138
+ n.sources.forEach((src, i) => {
139
+ if (!src || typeof src !== "object" || Array.isArray(src)) {
140
+ errors.push(`sources[${i}]: must be an object`);
141
+ } else {
142
+ const s = src;
143
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`sources[${i}]: missing required "bindTo" property`);
144
+ if (s.outputFile != null && typeof s.outputFile !== "string") errors.push(`sources[${i}]: outputFile must be a string`);
145
+ if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
146
+ errors.push(`sources[${i}]: optionalForCompletionGating must be a boolean`);
147
+ }
148
+ }
149
+ });
150
+ }
151
+ }
152
+ if (n.view != null) {
153
+ if (typeof n.view !== "object" || Array.isArray(n.view)) {
154
+ errors.push("view: must be an object");
155
+ } else {
156
+ const view = n.view;
157
+ if (!Array.isArray(view.elements) || view.elements.length === 0) {
158
+ errors.push("view.elements: required, must be a non-empty array");
159
+ } else {
160
+ view.elements.forEach((elem, i) => {
161
+ if (!elem || typeof elem !== "object") {
162
+ errors.push(`view.elements[${i}]: must be an object`);
163
+ return;
164
+ }
165
+ if (!elem.kind || typeof elem.kind !== "string") {
166
+ errors.push(`view.elements[${i}].kind: required, must be a string`);
167
+ } else if (!VALID_ELEMENT_KINDS.has(elem.kind)) {
168
+ errors.push(`view.elements[${i}].kind: unknown kind "${elem.kind}". Valid: ${[...VALID_ELEMENT_KINDS].join(", ")}`);
169
+ }
170
+ if (elem.data != null && (typeof elem.data !== "object" || Array.isArray(elem.data))) {
171
+ errors.push(`view.elements[${i}].data: must be an object`);
172
+ }
173
+ });
174
+ }
175
+ if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
176
+ if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
177
+ }
178
+ }
179
+ return { ok: errors.length === 0, errors };
180
+ }
181
+ function enrichSources(sources, context) {
182
+ if (!sources || sources.length === 0) return [];
183
+ return sources.map((src) => ({
184
+ ...src,
185
+ _requires: context.requires ?? {},
186
+ _sourcesData: context.sourcesData ?? {},
187
+ _computed_values: context.computed_values ?? {}
188
+ }));
189
+ }
190
+ var CardCompute = {
191
+ run,
192
+ eval: evalExpr,
193
+ resolve,
194
+ validate: validateNode,
195
+ enrichSources
196
+ };
197
+
198
+ // src/event-graph/constants.ts
199
+ var TASK_STATUS = {
200
+ RUNNING: "running",
201
+ COMPLETED: "completed",
202
+ FAILED: "failed",
203
+ INACTIVATED: "inactivated"
204
+ };
205
+
206
+ // src/event-graph/graph-helpers.ts
207
+ function getProvides(task) {
208
+ if (!task) return [];
209
+ if (Array.isArray(task.provides)) return task.provides;
210
+ return [];
211
+ }
212
+ function getRequires(task) {
213
+ if (!task) return [];
214
+ if (Array.isArray(task.requires)) return task.requires;
215
+ return [];
216
+ }
217
+ function getAllTasks(graph) {
218
+ return graph.tasks ?? {};
219
+ }
220
+ function isNonActiveTask(taskState) {
221
+ if (!taskState) return false;
222
+ return taskState.status === TASK_STATUS.FAILED || taskState.status === TASK_STATUS.INACTIVATED;
223
+ }
224
+ function getRefreshStrategy(taskConfig, graphSettings) {
225
+ return taskConfig.refreshStrategy ?? graphSettings?.refreshStrategy ?? "data-changed";
226
+ }
227
+ function getMaxExecutions(taskConfig) {
228
+ return taskConfig.maxExecutions;
229
+ }
230
+ function computeAvailableOutputs(graph, taskStates) {
231
+ const outputs = /* @__PURE__ */ new Set();
232
+ for (const [taskName, taskState] of Object.entries(taskStates)) {
233
+ if (taskState.status === TASK_STATUS.COMPLETED) {
234
+ const taskConfig = graph.tasks[taskName];
235
+ if (taskConfig) {
236
+ const provides = getProvides(taskConfig);
237
+ provides.forEach((output) => outputs.add(output));
238
+ }
239
+ }
240
+ }
241
+ return Array.from(outputs);
242
+ }
243
+ function groupTasksByProvides(candidateTaskNames, tasks) {
244
+ const outputGroups = {};
245
+ candidateTaskNames.forEach((taskName) => {
246
+ const task = tasks[taskName];
247
+ if (!task) return;
248
+ const provides = getProvides(task);
249
+ provides.forEach((output) => {
250
+ if (!outputGroups[output]) {
251
+ outputGroups[output] = [];
252
+ }
253
+ outputGroups[output].push(taskName);
254
+ });
255
+ });
256
+ return outputGroups;
257
+ }
258
+
259
+ // src/event-graph/task-transitions.ts
260
+ function applyTaskStart(state, taskName) {
261
+ const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
262
+ const updatedTask = {
263
+ ...existingTask,
264
+ status: "running",
265
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
266
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
267
+ progress: 0,
268
+ error: void 0
269
+ };
270
+ return {
271
+ ...state,
272
+ tasks: { ...state.tasks, [taskName]: updatedTask },
273
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
274
+ };
275
+ }
276
+ function applyTaskCompletion(state, graph, taskName, result, dataHash, data) {
277
+ const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
278
+ const taskConfig = graph.tasks[taskName];
279
+ if (!taskConfig) {
280
+ throw new Error(`Task "${taskName}" not found in graph`);
281
+ }
282
+ let outputTokens;
283
+ if (result && taskConfig.on && taskConfig.on[result]) {
284
+ outputTokens = taskConfig.on[result];
285
+ } else {
286
+ outputTokens = getProvides(taskConfig);
287
+ }
288
+ const lastConsumedHashes = { ...existingTask.lastConsumedHashes };
289
+ const requires = taskConfig.requires ?? [];
290
+ for (const token of requires) {
291
+ for (const [otherName, otherConfig] of Object.entries(graph.tasks)) {
292
+ if (getProvides(otherConfig).includes(token)) {
293
+ const otherState = state.tasks[otherName];
294
+ if (otherState?.lastDataHash) {
295
+ lastConsumedHashes[token] = otherState.lastDataHash;
296
+ }
297
+ break;
298
+ }
299
+ }
300
+ }
301
+ const updatedTask = {
302
+ ...existingTask,
303
+ status: "completed",
304
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
305
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
306
+ executionCount: existingTask.executionCount + 1,
307
+ lastEpoch: existingTask.executionCount + 1,
308
+ lastDataHash: dataHash,
309
+ data,
310
+ lastConsumedHashes,
311
+ error: void 0
312
+ };
313
+ const newOutputs = [.../* @__PURE__ */ new Set([...state.availableOutputs, ...outputTokens])];
314
+ return {
315
+ ...state,
316
+ tasks: { ...state.tasks, [taskName]: updatedTask },
317
+ availableOutputs: newOutputs,
318
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
319
+ };
320
+ }
321
+ function applyTaskFailure(state, graph, taskName, error) {
322
+ const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
323
+ const taskConfig = graph.tasks[taskName];
324
+ if (taskConfig?.retry) {
325
+ const retryCount = existingTask.retryCount + 1;
326
+ if (retryCount <= taskConfig.retry.max_attempts) {
327
+ const updatedTask2 = {
328
+ ...existingTask,
329
+ status: "not-started",
330
+ retryCount,
331
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
332
+ error
333
+ };
334
+ return {
335
+ ...state,
336
+ tasks: { ...state.tasks, [taskName]: updatedTask2 },
337
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
338
+ };
339
+ }
340
+ }
341
+ const updatedTask = {
342
+ ...existingTask,
343
+ status: "failed",
344
+ failedAt: (/* @__PURE__ */ new Date()).toISOString(),
345
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
346
+ error,
347
+ executionCount: existingTask.executionCount + 1
348
+ };
349
+ let newOutputs = state.availableOutputs;
350
+ if (taskConfig?.on_failure && taskConfig.on_failure.length > 0) {
351
+ newOutputs = [.../* @__PURE__ */ new Set([...state.availableOutputs, ...taskConfig.on_failure])];
352
+ }
353
+ if (taskConfig?.circuit_breaker && updatedTask.executionCount >= taskConfig.circuit_breaker.max_executions) {
354
+ const breakTokens = taskConfig.circuit_breaker.on_break;
355
+ newOutputs = [.../* @__PURE__ */ new Set([...newOutputs, ...breakTokens])];
356
+ }
357
+ return {
358
+ ...state,
359
+ tasks: { ...state.tasks, [taskName]: updatedTask },
360
+ availableOutputs: newOutputs,
361
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
362
+ };
363
+ }
364
+ function applyTaskProgress(state, taskName, message, progress) {
365
+ const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
366
+ const updatedTask = {
367
+ ...existingTask,
368
+ progress: typeof progress === "number" ? progress : existingTask.progress,
369
+ messages: [
370
+ ...existingTask.messages ?? [],
371
+ ...message ? [{ message, timestamp: (/* @__PURE__ */ new Date()).toISOString(), status: existingTask.status }] : []
372
+ ],
373
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
374
+ };
375
+ return {
376
+ ...state,
377
+ tasks: { ...state.tasks, [taskName]: updatedTask },
378
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
379
+ };
380
+ }
381
+ function applyTaskRestart(state, taskName) {
382
+ const existingTask = state.tasks[taskName];
383
+ if (!existingTask) return state;
384
+ const updatedTask = {
385
+ ...existingTask,
386
+ status: "not-started",
387
+ startedAt: void 0,
388
+ completedAt: void 0,
389
+ failedAt: void 0,
390
+ error: void 0,
391
+ data: void 0,
392
+ progress: null,
393
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
394
+ };
395
+ return {
396
+ ...state,
397
+ tasks: { ...state.tasks, [taskName]: updatedTask },
398
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
399
+ };
400
+ }
401
+ function createDefaultGraphEngineStore() {
402
+ return {
403
+ status: "not-started",
404
+ executionCount: 0,
405
+ retryCount: 0,
406
+ lastEpoch: 0,
407
+ messages: [],
408
+ progress: null
409
+ };
410
+ }
411
+
412
+ // src/continuous-event-graph/core.ts
413
+ function createLiveGraph(config, executionId) {
414
+ const id = executionId ?? `live-${Date.now()}`;
415
+ const tasks = {};
416
+ for (const taskName of Object.keys(config.tasks)) {
417
+ tasks[taskName] = createDefaultGraphEngineStore2();
418
+ }
419
+ const state = {
420
+ status: "running",
421
+ tasks,
422
+ availableOutputs: [],
423
+ stuckDetection: { is_stuck: false, stuck_description: null, outputs_unresolvable: [], tasks_blocked: [] },
424
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
425
+ executionId: id,
426
+ executionConfig: {
427
+ executionMode: config.settings.execution_mode ?? "eligibility-mode",
428
+ conflictStrategy: config.settings.conflict_strategy ?? "alphabetical",
429
+ completionStrategy: config.settings.completion
430
+ }
431
+ };
432
+ return { config, state };
433
+ }
434
+ function applyEvent(live, event) {
435
+ const { config, state } = live;
436
+ if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
437
+ return live;
438
+ }
439
+ switch (event.type) {
440
+ // --- Execution state transitions ---
441
+ case "task-started":
442
+ return { config, state: applyTaskStart(state, event.taskName) };
443
+ case "task-completed":
444
+ return { config, state: applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data) };
445
+ case "task-failed":
446
+ return { config, state: applyTaskFailure(state, config, event.taskName, event.error) };
447
+ case "task-progress":
448
+ return { config, state: applyTaskProgress(state, event.taskName, event.message, event.progress) };
449
+ case "task-restart":
450
+ return { config, state: applyTaskRestart(state, event.taskName) };
451
+ case "inject-tokens":
452
+ return {
453
+ config,
454
+ state: {
455
+ ...state,
456
+ availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
457
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
458
+ }
459
+ };
460
+ case "agent-action":
461
+ return { config, state: applyAgentAction(state, event.action) };
462
+ // --- Structural mutations ---
463
+ case "task-upsert":
464
+ return addNode(live, event.taskName, event.taskConfig);
465
+ case "task-removal":
466
+ return removeNode(live, event.taskName);
467
+ case "node-requires-add":
468
+ return addRequires(live, event.nodeName, event.tokens);
469
+ case "node-requires-remove":
470
+ return removeRequires(live, event.nodeName, event.tokens);
471
+ case "node-provides-add":
472
+ return addProvides(live, event.nodeName, event.tokens);
473
+ case "node-provides-remove":
474
+ return removeProvides(live, event.nodeName, event.tokens);
475
+ default:
476
+ return live;
477
+ }
478
+ }
479
+ function applyEvents(live, events) {
480
+ return events.reduce((current, event) => applyEvent(current, event), live);
481
+ }
482
+ function addNode(live, name, taskConfig) {
483
+ const exists = !!live.config.tasks[name];
484
+ return {
485
+ config: {
486
+ ...live.config,
487
+ tasks: { ...live.config.tasks, [name]: taskConfig }
488
+ },
489
+ state: {
490
+ ...live.state,
491
+ tasks: {
492
+ ...live.state.tasks,
493
+ [name]: exists ? live.state.tasks[name] : createDefaultGraphEngineStore2()
494
+ },
495
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
496
+ }
497
+ };
498
+ }
499
+ function removeNode(live, name) {
500
+ if (!live.config.tasks[name]) return live;
501
+ const { [name]: _removedConfig, ...remainingTasks } = live.config.tasks;
502
+ const { [name]: _removedState, ...remainingStates } = live.state.tasks;
503
+ return {
504
+ config: {
505
+ ...live.config,
506
+ tasks: remainingTasks
507
+ },
508
+ state: {
509
+ ...live.state,
510
+ tasks: remainingStates,
511
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
512
+ }
513
+ };
514
+ }
515
+ function addRequires(live, nodeName, tokens) {
516
+ const task = live.config.tasks[nodeName];
517
+ if (!task) return live;
518
+ const current = getRequires(task);
519
+ const toAdd = tokens.filter((t) => !current.includes(t));
520
+ if (toAdd.length === 0) return live;
521
+ return {
522
+ config: {
523
+ ...live.config,
524
+ tasks: {
525
+ ...live.config.tasks,
526
+ [nodeName]: { ...task, requires: [...current, ...toAdd] }
527
+ }
528
+ },
529
+ state: live.state
530
+ };
531
+ }
532
+ function removeRequires(live, nodeName, tokens) {
533
+ const task = live.config.tasks[nodeName];
534
+ if (!task) return live;
535
+ const current = getRequires(task);
536
+ const remaining = current.filter((t) => !tokens.includes(t));
537
+ if (remaining.length === current.length) return live;
538
+ return {
539
+ config: {
540
+ ...live.config,
541
+ tasks: {
542
+ ...live.config.tasks,
543
+ [nodeName]: { ...task, requires: remaining }
544
+ }
545
+ },
546
+ state: live.state
547
+ };
548
+ }
549
+ function addProvides(live, nodeName, tokens) {
550
+ const task = live.config.tasks[nodeName];
551
+ if (!task) return live;
552
+ const current = getProvides(task);
553
+ const toAdd = tokens.filter((t) => !current.includes(t));
554
+ if (toAdd.length === 0) return live;
555
+ return {
556
+ config: {
557
+ ...live.config,
558
+ tasks: {
559
+ ...live.config.tasks,
560
+ [nodeName]: { ...task, provides: [...current, ...toAdd] }
561
+ }
562
+ },
563
+ state: live.state
564
+ };
565
+ }
566
+ function removeProvides(live, nodeName, tokens) {
567
+ const task = live.config.tasks[nodeName];
568
+ if (!task) return live;
569
+ const current = getProvides(task);
570
+ const remaining = current.filter((t) => !tokens.includes(t));
571
+ if (remaining.length === current.length) return live;
572
+ return {
573
+ config: {
574
+ ...live.config,
575
+ tasks: {
576
+ ...live.config.tasks,
577
+ [nodeName]: { ...task, provides: remaining }
578
+ }
579
+ },
580
+ state: live.state
581
+ };
582
+ }
583
+ function snapshot(live) {
584
+ return {
585
+ version: 1,
586
+ config: live.config,
587
+ state: live.state,
588
+ snapshotAt: (/* @__PURE__ */ new Date()).toISOString()
589
+ };
590
+ }
591
+ function createDefaultGraphEngineStore2() {
592
+ return {
593
+ status: "not-started",
594
+ executionCount: 0,
595
+ retryCount: 0,
596
+ lastEpoch: 0,
597
+ messages: [],
598
+ progress: null
599
+ };
600
+ }
601
+ function applyAgentAction(state, action) {
602
+ const now = (/* @__PURE__ */ new Date()).toISOString();
603
+ switch (action) {
604
+ case "stop":
605
+ return { ...state, status: "stopped", lastUpdated: now };
606
+ case "pause":
607
+ return { ...state, status: "paused", lastUpdated: now };
608
+ case "resume":
609
+ return { ...state, status: "running", lastUpdated: now };
610
+ default:
611
+ return state;
612
+ }
613
+ }
614
+
615
+ // src/continuous-event-graph/schedule.ts
616
+ function schedule(live) {
617
+ const { config, state } = live;
618
+ const graphTasks = getAllTasks(config);
619
+ const taskNames = Object.keys(graphTasks);
620
+ if (taskNames.length === 0) {
621
+ return { eligible: [], pending: [], unresolved: [], blocked: [], conflicts: {} };
622
+ }
623
+ const producerMap = buildProducerMap(graphTasks);
624
+ const computedOutputs = computeAvailableOutputs(config, state.tasks);
625
+ const availableOutputs = /* @__PURE__ */ new Set([...computedOutputs, ...state.availableOutputs]);
626
+ const eligible = [];
627
+ const pending = [];
628
+ const unresolved = [];
629
+ const blocked = [];
630
+ for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
631
+ const taskState = state.tasks[taskName];
632
+ const strategy = getRefreshStrategy(taskConfig, config.settings);
633
+ const rerunnable = strategy !== "once";
634
+ if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
635
+ continue;
636
+ }
637
+ const maxExec = getMaxExecutions(taskConfig);
638
+ if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
639
+ continue;
640
+ }
641
+ if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
642
+ continue;
643
+ }
644
+ if (!rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
645
+ continue;
646
+ }
647
+ if (rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
648
+ const requires2 = getRequires(taskConfig);
649
+ let shouldSkip = false;
650
+ switch (strategy) {
651
+ case "data-changed": {
652
+ if (requires2.length > 0) {
653
+ const hasChangedData = requires2.some((req) => {
654
+ for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
655
+ if (getProvides(otherConfig).includes(req)) {
656
+ const otherState = state.tasks[otherName];
657
+ if (!otherState) continue;
658
+ const consumed = taskState.lastConsumedHashes?.[req];
659
+ if (otherState.lastDataHash == null) {
660
+ return otherState.executionCount > taskState.lastEpoch;
661
+ }
662
+ return otherState.lastDataHash !== consumed;
663
+ }
664
+ }
665
+ return false;
666
+ });
667
+ if (!hasChangedData) shouldSkip = true;
668
+ } else {
669
+ shouldSkip = true;
670
+ }
671
+ break;
672
+ }
673
+ case "epoch-changed": {
674
+ if (requires2.length > 0) {
675
+ const hasRefreshed = requires2.some((req) => {
676
+ for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
677
+ if (getProvides(otherConfig).includes(req)) {
678
+ const otherState = state.tasks[otherName];
679
+ if (otherState && otherState.executionCount > taskState.lastEpoch) return true;
680
+ }
681
+ }
682
+ return false;
683
+ });
684
+ if (!hasRefreshed) shouldSkip = true;
685
+ } else {
686
+ shouldSkip = true;
687
+ }
688
+ break;
689
+ }
690
+ case "time-based": {
691
+ const interval = taskConfig.refreshInterval ?? 0;
692
+ if (interval <= 0) {
693
+ shouldSkip = true;
694
+ break;
695
+ }
696
+ const completedAt = taskState.completedAt;
697
+ if (!completedAt) {
698
+ shouldSkip = true;
699
+ break;
700
+ }
701
+ const elapsedSec = (Date.now() - Date.parse(completedAt)) / 1e3;
702
+ if (elapsedSec < interval) shouldSkip = true;
703
+ break;
704
+ }
705
+ case "manual":
706
+ shouldSkip = true;
707
+ break;
708
+ }
709
+ if (shouldSkip) continue;
710
+ }
711
+ const requires = getRequires(taskConfig);
712
+ if (requires.length === 0) {
713
+ eligible.push(taskName);
714
+ continue;
715
+ }
716
+ const missingTokens = [];
717
+ const pendingTokens = [];
718
+ const failedTokenInfo = [];
719
+ for (const token of requires) {
720
+ if (availableOutputs.has(token)) continue;
721
+ const producers = producerMap[token] || [];
722
+ if (producers.length === 0) {
723
+ missingTokens.push(token);
724
+ } else {
725
+ const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
726
+ if (allFailed) {
727
+ failedTokenInfo.push({ token, failedProducer: producers[0] });
728
+ } else {
729
+ pendingTokens.push(token);
730
+ }
731
+ }
732
+ }
733
+ if (missingTokens.length > 0) {
734
+ unresolved.push({ taskName, missingTokens });
735
+ } else if (failedTokenInfo.length > 0) {
736
+ blocked.push({
737
+ taskName,
738
+ failedTokens: failedTokenInfo.map((f) => f.token),
739
+ failedProducers: [...new Set(failedTokenInfo.map((f) => f.failedProducer))]
740
+ });
741
+ } else if (pendingTokens.length > 0) {
742
+ pending.push({ taskName, waitingOn: pendingTokens });
743
+ } else {
744
+ eligible.push(taskName);
745
+ }
746
+ }
747
+ const conflicts = {};
748
+ if (eligible.length > 1) {
749
+ const outputGroups = groupTasksByProvides(eligible, graphTasks);
750
+ for (const [outputKey, groupTasks] of Object.entries(outputGroups)) {
751
+ if (groupTasks.length > 1) {
752
+ conflicts[outputKey] = groupTasks;
753
+ }
754
+ }
755
+ }
756
+ return { eligible, pending, unresolved, blocked, conflicts };
757
+ }
758
+ function buildProducerMap(tasks) {
759
+ const map = {};
760
+ for (const [name, config] of Object.entries(tasks)) {
761
+ for (const token of getProvides(config)) {
762
+ if (!map[token]) map[token] = [];
763
+ map[token].push(name);
764
+ }
765
+ if (config.on) {
766
+ for (const tokens of Object.values(config.on)) {
767
+ for (const token of tokens) {
768
+ if (!map[token]) map[token] = [];
769
+ if (!map[token].includes(name)) map[token].push(name);
770
+ }
771
+ }
772
+ }
773
+ if (config.on_failure) {
774
+ for (const token of config.on_failure) {
775
+ if (!map[token]) map[token] = [];
776
+ if (!map[token].includes(name)) map[token].push(name);
777
+ }
778
+ }
779
+ }
780
+ return map;
781
+ }
782
+
783
+ // src/continuous-event-graph/journal.ts
784
+ var MemoryJournal = class {
785
+ buffer = [];
786
+ append(event) {
787
+ this.buffer.push(event);
788
+ }
789
+ drain() {
790
+ const events = this.buffer;
791
+ this.buffer = [];
792
+ return events;
793
+ }
794
+ get size() {
795
+ return this.buffer.length;
796
+ }
797
+ };
798
+
799
+ // src/continuous-event-graph/reactive.ts
800
+ function computeDataHash(data) {
801
+ const json = stableStringify(data);
802
+ return fnv1a64Hex(json);
803
+ }
804
+ function stableStringify(value) {
805
+ if (value === null || value === void 0 || typeof value !== "object") {
806
+ return JSON.stringify(value);
807
+ }
808
+ if (Array.isArray(value)) {
809
+ return "[" + value.map(stableStringify).join(",") + "]";
810
+ }
811
+ const obj = value;
812
+ const keys = Object.keys(obj).sort();
813
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
814
+ }
815
+ function fnv1a64Hex(input) {
816
+ let hash = 0xcbf29ce484222325n;
817
+ const prime = 0x100000001b3n;
818
+ const mod = 0xffffffffffffffffn;
819
+ for (let i = 0; i < input.length; i++) {
820
+ hash ^= BigInt(input.charCodeAt(i));
821
+ hash = hash * prime & mod;
822
+ }
823
+ return hash.toString(16).padStart(16, "0");
824
+ }
825
+ function base64UrlEncode(input) {
826
+ if (typeof Buffer !== "undefined") {
827
+ return Buffer.from(input, "utf8").toString("base64url");
828
+ }
829
+ if (typeof btoa === "function") {
830
+ const bytes = new TextEncoder().encode(input);
831
+ let binary = "";
832
+ for (const b of bytes) binary += String.fromCharCode(b);
833
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
834
+ }
835
+ throw new Error("No base64 encoder available in this runtime");
836
+ }
837
+ function base64UrlDecode(input) {
838
+ if (typeof Buffer !== "undefined") {
839
+ return Buffer.from(input, "base64url").toString("utf8");
840
+ }
841
+ if (typeof atob === "function") {
842
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
843
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
844
+ const binary = atob(padded);
845
+ const bytes = new Uint8Array(binary.length);
846
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
847
+ return new TextDecoder().decode(bytes);
848
+ }
849
+ throw new Error("No base64 decoder available in this runtime");
850
+ }
851
+ function encodeCallbackToken(taskName) {
852
+ const payload = JSON.stringify({ t: taskName, n: Date.now().toString(36) + Math.random().toString(36).slice(2, 6) });
853
+ return base64UrlEncode(payload);
854
+ }
855
+ function decodeCallbackToken(token) {
856
+ try {
857
+ const payload = JSON.parse(base64UrlDecode(token));
858
+ if (typeof payload?.t === "string") return { taskName: payload.t };
859
+ return null;
860
+ } catch {
861
+ return null;
862
+ }
863
+ }
864
+ function createReactiveGraph(configOrLive, options, executionId) {
865
+ const {
866
+ handlers: initialHandlers,
867
+ onDrain
868
+ } = options;
869
+ const inputQueue = new MemoryJournal();
870
+ let live = "state" in configOrLive && "config" in configOrLive ? configOrLive : createLiveGraph(configOrLive, executionId);
871
+ let disposed = false;
872
+ const handlers = new Map(Object.entries(initialHandlers));
873
+ const internalJournal = new MemoryJournal();
874
+ let draining = false;
875
+ let drainQueued = false;
876
+ function drain() {
877
+ if (disposed) return;
878
+ if (draining) {
879
+ drainQueued = true;
880
+ return;
881
+ }
882
+ draining = true;
883
+ try {
884
+ do {
885
+ drainQueued = false;
886
+ drainOnce();
887
+ } while (drainQueued);
888
+ } finally {
889
+ draining = false;
890
+ }
891
+ }
892
+ function drainOnce() {
893
+ const internalEvents = internalJournal.drain();
894
+ const inputEvents = inputQueue.drain();
895
+ const events = [...internalEvents, ...inputEvents];
896
+ if (events.length > 0) {
897
+ live = applyEvents(live, events);
898
+ }
899
+ const result = schedule(live);
900
+ if (events.length > 0) {
901
+ onDrain?.(events, live, result);
902
+ }
903
+ for (const taskName of result.eligible) {
904
+ dispatchTask(taskName);
905
+ }
906
+ for (const event of events) {
907
+ if (event.type === "task-progress") {
908
+ const { taskName, update } = event;
909
+ const taskConfig = live.config.tasks[taskName];
910
+ if (!taskConfig) continue;
911
+ const taskState = live.state.tasks[taskName];
912
+ if (!taskState || taskState.status !== "running") continue;
913
+ const callbackToken = encodeCallbackToken(taskName);
914
+ runPipeline(taskName, callbackToken, update).catch((error) => {
915
+ if (disposed) return;
916
+ internalJournal.append({
917
+ type: "task-failed",
918
+ taskName,
919
+ error: error.message ?? String(error),
920
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
921
+ });
922
+ drain();
923
+ });
924
+ }
925
+ }
926
+ }
927
+ function resolveUpstreamState(taskName) {
928
+ const taskConfig = live.config.tasks[taskName];
929
+ const requires = taskConfig.requires ?? [];
930
+ const tokenToTask = /* @__PURE__ */ new Map();
931
+ for (const [name, cfg] of Object.entries(live.config.tasks)) {
932
+ for (const token of cfg.provides ?? []) {
933
+ tokenToTask.set(token, name);
934
+ }
935
+ }
936
+ const state = {};
937
+ for (const token of requires) {
938
+ const producerTask = tokenToTask.get(token);
939
+ if (producerTask) {
940
+ state[token] = live.state.tasks[producerTask]?.data;
941
+ } else {
942
+ state[token] = void 0;
943
+ }
944
+ }
945
+ return state;
946
+ }
947
+ async function runPipeline(taskName, callbackToken, update) {
948
+ const taskConfig = live.config.tasks[taskName];
949
+ const handlerNames = taskConfig.taskHandlers ?? [];
950
+ const upstreamState = resolveUpstreamState(taskName);
951
+ for (const handlerName of handlerNames) {
952
+ const handler = handlers.get(handlerName);
953
+ if (!handler) {
954
+ throw new Error(`Handler '${handlerName}' not found in registry (task '${taskName}')`);
955
+ }
956
+ const input = {
957
+ nodeId: taskName,
958
+ state: upstreamState,
959
+ taskState: live.state.tasks[taskName],
960
+ config: taskConfig,
961
+ callbackToken,
962
+ update
963
+ };
964
+ const status = await handler(input);
965
+ if (status === "task-initiate-failure") {
966
+ throw new Error(`Handler '${handlerName}' returned task-initiate-failure (task '${taskName}')`);
967
+ }
968
+ }
969
+ }
970
+ function dispatchTask(taskName) {
971
+ const taskConfig = live.config.tasks[taskName];
972
+ const handlerNames = taskConfig?.taskHandlers;
973
+ if (!handlerNames || handlerNames.length === 0) {
974
+ return;
975
+ }
976
+ internalJournal.append({
977
+ type: "task-started",
978
+ taskName,
979
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
980
+ });
981
+ drain();
982
+ const callbackToken = encodeCallbackToken(taskName);
983
+ runPipeline(taskName, callbackToken).catch((error) => {
984
+ if (disposed) return;
985
+ internalJournal.append({
986
+ type: "task-failed",
987
+ taskName,
988
+ error: error.message ?? String(error),
989
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
990
+ });
991
+ drain();
992
+ });
993
+ }
994
+ return {
995
+ push(event) {
996
+ if (disposed) return;
997
+ if (event.type === "task-completed" && event.data && !event.dataHash) {
998
+ event = { ...event, dataHash: computeDataHash(event.data) };
999
+ }
1000
+ inputQueue.append(event);
1001
+ drain();
1002
+ },
1003
+ pushAll(events) {
1004
+ if (disposed) return;
1005
+ for (const event of events) {
1006
+ if (event.type === "task-completed" && event.data && !event.dataHash) {
1007
+ inputQueue.append({ ...event, dataHash: computeDataHash(event.data) });
1008
+ } else {
1009
+ inputQueue.append(event);
1010
+ }
1011
+ }
1012
+ drain();
1013
+ },
1014
+ resolveCallback(callbackToken, data, errors) {
1015
+ if (disposed) return;
1016
+ const decoded = decodeCallbackToken(callbackToken);
1017
+ if (!decoded) return;
1018
+ const { taskName } = decoded;
1019
+ if (!live.config.tasks[taskName]) return;
1020
+ if (errors && errors.length > 0) {
1021
+ inputQueue.append({
1022
+ type: "task-failed",
1023
+ taskName,
1024
+ error: errors.join("; "),
1025
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1026
+ });
1027
+ } else {
1028
+ const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
1029
+ inputQueue.append({
1030
+ type: "task-completed",
1031
+ taskName,
1032
+ data,
1033
+ dataHash,
1034
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1035
+ });
1036
+ }
1037
+ drain();
1038
+ },
1039
+ addNode(name, taskConfig) {
1040
+ if (disposed) return;
1041
+ inputQueue.append({ type: "task-upsert", taskName: name, taskConfig, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1042
+ drain();
1043
+ },
1044
+ removeNode(name) {
1045
+ if (disposed) return;
1046
+ inputQueue.append({ type: "task-removal", taskName: name, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1047
+ drain();
1048
+ },
1049
+ addRequires(nodeName, tokens) {
1050
+ if (disposed) return;
1051
+ inputQueue.append({ type: "node-requires-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1052
+ drain();
1053
+ },
1054
+ removeRequires(nodeName, tokens) {
1055
+ if (disposed) return;
1056
+ inputQueue.append({ type: "node-requires-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1057
+ drain();
1058
+ },
1059
+ addProvides(nodeName, tokens) {
1060
+ if (disposed) return;
1061
+ inputQueue.append({ type: "node-provides-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1062
+ drain();
1063
+ },
1064
+ removeProvides(nodeName, tokens) {
1065
+ if (disposed) return;
1066
+ inputQueue.append({ type: "node-provides-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1067
+ drain();
1068
+ },
1069
+ registerHandler(name, fn) {
1070
+ handlers.set(name, fn);
1071
+ },
1072
+ unregisterHandler(name) {
1073
+ handlers.delete(name);
1074
+ },
1075
+ retrigger(taskName) {
1076
+ if (disposed) return;
1077
+ if (!live.config.tasks[taskName]) return;
1078
+ inputQueue.append({
1079
+ type: "task-restart",
1080
+ taskName,
1081
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1082
+ });
1083
+ drain();
1084
+ },
1085
+ retriggerAll(taskNames) {
1086
+ if (disposed) return;
1087
+ for (const name of taskNames) {
1088
+ if (!live.config.tasks[name]) continue;
1089
+ inputQueue.append({
1090
+ type: "task-restart",
1091
+ taskName: name,
1092
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1093
+ });
1094
+ }
1095
+ drain();
1096
+ },
1097
+ snapshot() {
1098
+ return snapshot(live);
1099
+ },
1100
+ getState() {
1101
+ return live;
1102
+ },
1103
+ getSchedule() {
1104
+ return schedule(live);
1105
+ },
1106
+ dispose() {
1107
+ disposed = true;
1108
+ }
1109
+ };
1110
+ }
1111
+
1112
+ // src/board-livegraph-runtime/index.ts
1113
+ function deepClone(value) {
1114
+ return JSON.parse(JSON.stringify(value));
1115
+ }
1116
+ function toTaskConfig(card) {
1117
+ const provides = card.provides && card.provides.length > 0 ? card.provides.map((p) => p.bindTo) : [card.id];
1118
+ return {
1119
+ requires: card.requires && card.requires.length > 0 ? [...card.requires] : void 0,
1120
+ provides,
1121
+ taskHandlers: [card.id],
1122
+ description: card.meta?.title ?? card.id
1123
+ };
1124
+ }
1125
+ function buildTokenProviders(cards) {
1126
+ const tokenToCardId = /* @__PURE__ */ new Map();
1127
+ for (const [cardId, card] of cards.entries()) {
1128
+ const bindings = card.provides && card.provides.length > 0 ? card.provides : [{ bindTo: cardId, src: "card_data" }];
1129
+ for (const binding of bindings) tokenToCardId.set(binding.bindTo, cardId);
1130
+ }
1131
+ return tokenToCardId;
1132
+ }
1133
+ function validateRequires(cards, changedCardId) {
1134
+ const tokenProviders = buildTokenProviders(cards);
1135
+ const card = cards.get(changedCardId);
1136
+ if (!card) return;
1137
+ for (const req of card.requires ?? []) {
1138
+ if (!tokenProviders.has(req)) {
1139
+ throw new Error(`Card "${changedCardId}" requires token "${req}" but no card provides it`);
1140
+ }
1141
+ }
1142
+ }
1143
+ var LocalStorageService = {
1144
+ // Keys
1145
+ CARD_PREFIX: "yf:cards:",
1146
+ RUNTIME_OUT_PREFIX: "yf:runtime-out:cards:",
1147
+ STATUS_KEY: "yf:runtime-out:status",
1148
+ // Read/write cards (mirrors tmp/cards/<id>.json)
1149
+ writeCard(cardId, cardObject) {
1150
+ try {
1151
+ localStorage.setItem(this.CARD_PREFIX + cardId, JSON.stringify(cardObject));
1152
+ } catch (e) {
1153
+ console.warn(`Failed to write card ${cardId} to localStorage:`, e);
1154
+ }
1155
+ },
1156
+ readCard(cardId) {
1157
+ try {
1158
+ const raw = localStorage.getItem(this.CARD_PREFIX + cardId);
1159
+ return raw ? JSON.parse(raw) : null;
1160
+ } catch (e) {
1161
+ console.warn(`Failed to read card ${cardId} from localStorage:`, e);
1162
+ return null;
1163
+ }
1164
+ },
1165
+ readAllCards(cardIds) {
1166
+ const result = {};
1167
+ for (const id of cardIds) {
1168
+ const card = this.readCard(id);
1169
+ if (card) result[id] = card;
1170
+ }
1171
+ return result;
1172
+ },
1173
+ // Read/write computed artifacts (mirrors runtime-out/cards/<id>.computed.json)
1174
+ writeComputedArtifact(artifact) {
1175
+ if (!artifact || !artifact.card_id) return;
1176
+ try {
1177
+ localStorage.setItem(
1178
+ this.RUNTIME_OUT_PREFIX + String(artifact.card_id),
1179
+ JSON.stringify(artifact)
1180
+ );
1181
+ } catch (e) {
1182
+ console.warn(`Failed to write computed artifact ${artifact.card_id}:`, e);
1183
+ }
1184
+ },
1185
+ readComputedArtifact(cardId) {
1186
+ try {
1187
+ const raw = localStorage.getItem(this.RUNTIME_OUT_PREFIX + cardId);
1188
+ return raw ? JSON.parse(raw) : null;
1189
+ } catch (e) {
1190
+ console.warn(`Failed to read computed artifact ${cardId}:`, e);
1191
+ return null;
1192
+ }
1193
+ },
1194
+ readAllComputedArtifacts(cardIds) {
1195
+ const result = {};
1196
+ for (const id of cardIds) {
1197
+ const artifact = this.readComputedArtifact(id);
1198
+ if (artifact) result[id] = artifact;
1199
+ }
1200
+ return result;
1201
+ },
1202
+ // Read/write board status snapshot (mirrors runtime-out/board-livegraph-status.json)
1203
+ writeStatusSnapshot(snapshot2) {
1204
+ try {
1205
+ localStorage.setItem(this.STATUS_KEY, JSON.stringify(snapshot2));
1206
+ } catch (e) {
1207
+ console.warn("Failed to write status snapshot to localStorage:", e);
1208
+ }
1209
+ },
1210
+ readStatusSnapshot() {
1211
+ try {
1212
+ const raw = localStorage.getItem(this.STATUS_KEY);
1213
+ return raw ? JSON.parse(raw) : null;
1214
+ } catch (e) {
1215
+ console.warn("Failed to read status snapshot from localStorage:", e);
1216
+ return null;
1217
+ }
1218
+ },
1219
+ // Clear all (useful for reset/demo)
1220
+ clear() {
1221
+ const keysToDelete = [];
1222
+ for (let i = 0; i < localStorage.length; i++) {
1223
+ const key = localStorage.key(i);
1224
+ if (key && (key.startsWith(this.CARD_PREFIX) || key.startsWith(this.RUNTIME_OUT_PREFIX) || key === this.STATUS_KEY)) {
1225
+ keysToDelete.push(key);
1226
+ }
1227
+ }
1228
+ for (const key of keysToDelete) {
1229
+ localStorage.removeItem(key);
1230
+ }
1231
+ }
1232
+ };
1233
+ function createBoardLiveGraphRuntime(input, options = {}) {
1234
+ const boardMeta = Array.isArray(input) ? {} : {
1235
+ id: input.id,
1236
+ title: input.title,
1237
+ mode: input.mode,
1238
+ positions: input.positions,
1239
+ settings: input.settings
1240
+ };
1241
+ const initialCards = Array.isArray(input) ? input : input.nodes;
1242
+ const cards = /* @__PURE__ */ new Map();
1243
+ for (const card of initialCards) {
1244
+ if (cards.has(card.id)) throw new Error(`Duplicate card ID: "${card.id}"`);
1245
+ cards.set(card.id, deepClone(card));
1246
+ }
1247
+ const listeners = /* @__PURE__ */ new Set();
1248
+ const taskExecutor = options.taskExecutor;
1249
+ const sourceAdapters = options.sourceAdapters ?? {};
1250
+ const defaultSourceAdapter = options.defaultSourceAdapter;
1251
+ let graphRef = null;
1252
+ const notifyListeners = (events, graph2) => {
1253
+ const update = {
1254
+ events,
1255
+ graph: graph2,
1256
+ nodes: getRenderableNodes()
1257
+ };
1258
+ for (const listener of listeners) listener(update);
1259
+ };
1260
+ const makeHandler = (cardId) => {
1261
+ return async (inputArgs) => {
1262
+ const card = cards.get(cardId);
1263
+ if (!card) return "task-initiate-failure";
1264
+ const requiresData = {};
1265
+ for (const token of card.requires ?? []) {
1266
+ const upstream = inputArgs.state[token];
1267
+ if (!upstream || typeof upstream !== "object") continue;
1268
+ const providesData2 = upstream.provides_data;
1269
+ if (!providesData2 || typeof providesData2 !== "object") continue;
1270
+ if (!Object.prototype.hasOwnProperty.call(providesData2, token)) continue;
1271
+ requiresData[token] = providesData2[token];
1272
+ }
1273
+ const sourcesData = {};
1274
+ if (card.sources && card.sources.length > 0) {
1275
+ const adapter = sourceAdapters[cardId] ?? defaultSourceAdapter;
1276
+ const fetched = taskExecutor ? await taskExecutor({ card, input: inputArgs }) : adapter ? await adapter({ card, input: inputArgs }) : void 0;
1277
+ if (fetched && typeof fetched === "object") {
1278
+ for (const src of card.sources) {
1279
+ if (Object.prototype.hasOwnProperty.call(fetched, src.bindTo)) {
1280
+ sourcesData[src.bindTo] = fetched[src.bindTo];
1281
+ } else if (card.sources.length === 1) {
1282
+ sourcesData[src.bindTo] = fetched;
1283
+ }
1284
+ }
1285
+ }
1286
+ }
1287
+ const computeNode = {
1288
+ id: card.id,
1289
+ card_data: deepClone(card.card_data ?? {}),
1290
+ requires: requiresData,
1291
+ sources: card.sources,
1292
+ compute: card.compute
1293
+ };
1294
+ computeNode._sourcesData = sourcesData;
1295
+ if (computeNode.compute && computeNode.compute.length > 0) {
1296
+ await CardCompute.run(computeNode, { sourcesData });
1297
+ }
1298
+ const providesData = {};
1299
+ if (card.provides && card.provides.length > 0) {
1300
+ for (const { bindTo, src } of card.provides) {
1301
+ providesData[bindTo] = CardCompute.resolve(computeNode, src);
1302
+ }
1303
+ } else {
1304
+ providesData[card.id] = {
1305
+ ...computeNode.card_data ?? {},
1306
+ ...computeNode.computed_values ?? {},
1307
+ ...computeNode._sourcesData ?? {}
1308
+ };
1309
+ }
1310
+ const resultData = {
1311
+ provides_data: providesData,
1312
+ card_data: computeNode.card_data ?? {},
1313
+ computed_values: computeNode.computed_values ?? {},
1314
+ fetched_sources: sourcesData,
1315
+ requires: requiresData
1316
+ };
1317
+ graphRef?.resolveCallback(inputArgs.callbackToken, resultData);
1318
+ return "task-initiated";
1319
+ };
1320
+ };
1321
+ const tasks = {};
1322
+ const handlers = {};
1323
+ for (const [cardId, card] of cards.entries()) {
1324
+ validateRequires(cards, cardId);
1325
+ tasks[cardId] = toTaskConfig(card);
1326
+ handlers[cardId] = makeHandler(cardId);
1327
+ }
1328
+ const config = {
1329
+ id: boardMeta.id ?? `browser-board-${Date.now()}`,
1330
+ settings: {
1331
+ completion: "manual",
1332
+ execution_mode: "eligibility-mode",
1333
+ ...boardMeta.settings ?? {},
1334
+ ...options.graphSettings ?? {}
1335
+ },
1336
+ tasks
1337
+ };
1338
+ const userOnDrain = options.reactiveOptions?.onDrain;
1339
+ const graph = createReactiveGraph(
1340
+ config,
1341
+ {
1342
+ ...options.reactiveOptions ?? {},
1343
+ handlers,
1344
+ onDrain: (events, live, scheduleResult) => {
1345
+ userOnDrain?.(events, live, scheduleResult);
1346
+ notifyListeners(events, live);
1347
+ }
1348
+ },
1349
+ options.executionId
1350
+ );
1351
+ graphRef = graph;
1352
+ function getRenderableNodes() {
1353
+ const live = graph.getState();
1354
+ const out = [];
1355
+ for (const [cardId, baseCard] of cards.entries()) {
1356
+ const data = live.state.tasks[cardId]?.data;
1357
+ const runtimeState = live.state.tasks[cardId];
1358
+ const mergedCardData = {
1359
+ ...baseCard.card_data ?? {},
1360
+ ...data && typeof data.card_data === "object" ? data.card_data : {}
1361
+ };
1362
+ const cardStatus = runtimeState?.status === "running" ? "loading" : runtimeState?.status;
1363
+ const cardDataForView = {
1364
+ ...mergedCardData,
1365
+ ...cardStatus ? { status: cardStatus } : {},
1366
+ ...runtimeState?.lastUpdated ? { lastRun: runtimeState.lastUpdated } : {},
1367
+ ...runtimeState?.status === "failed" && runtimeState.error ? { error: runtimeState.error } : {}
1368
+ };
1369
+ out.push({
1370
+ id: cardId,
1371
+ card: deepClone(baseCard),
1372
+ card_data: cardDataForView,
1373
+ fetched_sources: data && typeof data.fetched_sources === "object" ? deepClone(data.fetched_sources) : {},
1374
+ requires: data && typeof data.requires === "object" ? deepClone(data.requires) : {},
1375
+ computed_values: data && typeof data.computed_values === "object" ? deepClone(data.computed_values) : {},
1376
+ runtime_state: runtimeState ? deepClone(runtimeState) : {}
1377
+ });
1378
+ }
1379
+ return out;
1380
+ }
1381
+ const runtime = {
1382
+ getGraph: () => graph,
1383
+ getState: () => graph.getState(),
1384
+ getSchedule: () => graph.getSchedule(),
1385
+ getNodes: () => getRenderableNodes(),
1386
+ getBoard: () => ({
1387
+ ...boardMeta,
1388
+ nodes: getRenderableNodes()
1389
+ }),
1390
+ subscribe(listener) {
1391
+ listeners.add(listener);
1392
+ listener({ events: [], graph: graph.getState(), nodes: getRenderableNodes() });
1393
+ return () => listeners.delete(listener);
1394
+ },
1395
+ addCard(card) {
1396
+ if (cards.has(card.id)) throw new Error(`Card "${card.id}" already exists`);
1397
+ cards.set(card.id, deepClone(card));
1398
+ validateRequires(cards, card.id);
1399
+ graph.registerHandler(card.id, makeHandler(card.id));
1400
+ graph.addNode(card.id, toTaskConfig(card));
1401
+ },
1402
+ upsertCard(card) {
1403
+ cards.set(card.id, deepClone(card));
1404
+ validateRequires(cards, card.id);
1405
+ graph.registerHandler(card.id, makeHandler(card.id));
1406
+ graph.addNode(card.id, toTaskConfig(card));
1407
+ },
1408
+ removeCard(cardId) {
1409
+ cards.delete(cardId);
1410
+ graph.unregisterHandler(cardId);
1411
+ graph.removeNode(cardId);
1412
+ },
1413
+ patchCardState(cardId, patch) {
1414
+ const card = cards.get(cardId);
1415
+ if (!card) throw new Error(`Card "${cardId}" not found`);
1416
+ card.card_data = { ...card.card_data ?? {}, ...patch };
1417
+ graph.retrigger(cardId);
1418
+ },
1419
+ retrigger(cardId) {
1420
+ graph.retrigger(cardId);
1421
+ },
1422
+ retriggerAll() {
1423
+ graph.retriggerAll(Array.from(cards.keys()));
1424
+ },
1425
+ push(event) {
1426
+ graph.push(event);
1427
+ },
1428
+ pushAll(events) {
1429
+ graph.pushAll(events);
1430
+ },
1431
+ dispose() {
1432
+ listeners.clear();
1433
+ graph.dispose();
1434
+ }
1435
+ };
1436
+ return runtime;
1437
+ }
1438
+
1439
+ export { LocalStorageService, createBoardLiveGraphRuntime };
1440
+ //# sourceMappingURL=index.js.map
1441
+ //# sourceMappingURL=index.js.map