yaml-flow 3.1.0 → 4.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 (149) hide show
  1. package/README.md +81 -20
  2. package/board-live-cards-cli.js +37 -0
  3. package/browser/card-compute.js +132 -431
  4. package/browser/live-cards.js +41 -27
  5. package/browser/live-cards.schema.json +59 -77
  6. package/dist/card-compute/index.cjs +135 -415
  7. package/dist/card-compute/index.cjs.map +1 -1
  8. package/dist/card-compute/index.d.cts +52 -49
  9. package/dist/card-compute/index.d.ts +52 -49
  10. package/dist/card-compute/index.js +134 -415
  11. package/dist/card-compute/index.js.map +1 -1
  12. package/dist/cli/board-live-cards-cli.cjs +2379 -0
  13. package/dist/cli/board-live-cards-cli.cjs.map +1 -0
  14. package/dist/cli/board-live-cards-cli.d.cts +213 -0
  15. package/dist/cli/board-live-cards-cli.d.ts +213 -0
  16. package/dist/cli/board-live-cards-cli.js +2332 -0
  17. package/dist/cli/board-live-cards-cli.js.map +1 -0
  18. package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
  19. package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
  20. package/dist/continuous-event-graph/index.cjs +201 -448
  21. package/dist/continuous-event-graph/index.cjs.map +1 -1
  22. package/dist/continuous-event-graph/index.d.cts +16 -340
  23. package/dist/continuous-event-graph/index.d.ts +16 -340
  24. package/dist/continuous-event-graph/index.js +198 -448
  25. package/dist/continuous-event-graph/index.js.map +1 -1
  26. package/dist/event-graph/index.cjs +4 -4
  27. package/dist/event-graph/index.cjs.map +1 -1
  28. package/dist/event-graph/index.d.cts +5 -5
  29. package/dist/event-graph/index.d.ts +5 -5
  30. package/dist/event-graph/index.js +4 -4
  31. package/dist/event-graph/index.js.map +1 -1
  32. package/dist/index.cjs +278 -533
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +8 -7
  35. package/dist/index.d.ts +8 -7
  36. package/dist/index.js +278 -533
  37. package/dist/index.js.map +1 -1
  38. package/dist/inference/index.cjs +138 -19
  39. package/dist/inference/index.cjs.map +1 -1
  40. package/dist/inference/index.d.cts +2 -2
  41. package/dist/inference/index.d.ts +2 -2
  42. package/dist/inference/index.js +138 -19
  43. package/dist/inference/index.js.map +1 -1
  44. package/dist/journal-BJDjWb5Q.d.cts +343 -0
  45. package/dist/journal-B_2JnBMF.d.ts +343 -0
  46. package/dist/step-machine/index.cjs +18 -1
  47. package/dist/step-machine/index.cjs.map +1 -1
  48. package/dist/step-machine/index.d.cts +2 -2
  49. package/dist/step-machine/index.d.ts +2 -2
  50. package/dist/step-machine/index.js +18 -1
  51. package/dist/step-machine/index.js.map +1 -1
  52. package/dist/stores/file.d.cts +1 -1
  53. package/dist/stores/file.d.ts +1 -1
  54. package/dist/stores/index.d.cts +1 -1
  55. package/dist/stores/index.d.ts +1 -1
  56. package/dist/stores/localStorage.d.cts +1 -1
  57. package/dist/stores/localStorage.d.ts +1 -1
  58. package/dist/stores/memory.d.cts +1 -1
  59. package/dist/stores/memory.d.ts +1 -1
  60. package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
  61. package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
  62. package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
  63. package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
  64. package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
  65. package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
  66. package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
  67. package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
  68. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
  69. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
  70. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
  71. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
  72. package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
  73. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
  74. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +189 -0
  75. package/examples/browser/livecards-browser/index.html +688 -0
  76. package/examples/browser/step-machine-browser/index.html +367 -0
  77. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  78. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  79. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  80. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  81. package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  82. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  83. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  84. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  85. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  86. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  87. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  88. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  89. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  90. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  91. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  92. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  93. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  94. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
  95. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
  96. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
  97. package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
  98. package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
  99. package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
  100. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
  101. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
  102. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
  103. package/examples/index.html +792 -0
  104. package/examples/ingest.js +733 -0
  105. package/examples/npm-libs/batch/batch-step-machine.ts +121 -0
  106. package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +215 -0
  107. package/examples/npm-libs/continuous-event-graph/live-portfolio-dashboard.ts +555 -0
  108. package/examples/npm-libs/continuous-event-graph/portfolio-tracker.ts +287 -0
  109. package/examples/npm-libs/continuous-event-graph/reactive-monitoring.ts +265 -0
  110. package/examples/npm-libs/continuous-event-graph/reactive-pipeline.ts +168 -0
  111. package/examples/npm-libs/continuous-event-graph/soc-incident-board.ts +287 -0
  112. package/examples/npm-libs/continuous-event-graph/stock-dashboard.ts +229 -0
  113. package/examples/npm-libs/event-graph/ci-cd-pipeline.ts +243 -0
  114. package/examples/npm-libs/event-graph/executor-diamond.ts +165 -0
  115. package/examples/npm-libs/event-graph/executor-pipeline.ts +161 -0
  116. package/examples/npm-libs/event-graph/research-pipeline.ts +137 -0
  117. package/examples/npm-libs/flows/ai-conversation.yaml +116 -0
  118. package/examples/npm-libs/flows/order-processing.yaml +143 -0
  119. package/examples/npm-libs/flows/simple-greeting.yaml +54 -0
  120. package/examples/npm-libs/graph-of-graphs/multi-stage-etl.ts +307 -0
  121. package/examples/npm-libs/graph-of-graphs/url-processing-pipeline.ts +254 -0
  122. package/examples/npm-libs/inference/azure-deployment.ts +149 -0
  123. package/examples/npm-libs/inference/copilot-cli.ts +138 -0
  124. package/examples/npm-libs/inference/data-pipeline.ts +145 -0
  125. package/examples/npm-libs/inference/pluggable-adapters.ts +254 -0
  126. package/examples/npm-libs/node/ai-conversation.ts +195 -0
  127. package/examples/npm-libs/node/simple-greeting.ts +101 -0
  128. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  129. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  130. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  131. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  132. package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  133. package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  134. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  135. package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  136. package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  137. package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  138. package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  139. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  140. package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  141. package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  142. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  143. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  144. package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  145. package/package.json +14 -2
  146. package/schema/board-status.schema.json +118 -0
  147. package/schema/flow.schema.json +5 -0
  148. package/schema/live-cards.schema.json +59 -77
  149. package/step-machine-cli.js +674 -0
@@ -1,10 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
3
  var crypto = require('crypto');
5
4
  var child_process = require('child_process');
5
+ var jsonata = require('jsonata');
6
6
  require('ajv-formats');
7
7
 
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var jsonata__default = /*#__PURE__*/_interopDefault(jsonata);
11
+
8
12
  // src/event-graph/constants.ts
9
13
  var TASK_STATUS = {
10
14
  NOT_STARTED: "not-started",
@@ -247,43 +251,51 @@ function applyEvent(live, event) {
247
251
  if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
248
252
  return live;
249
253
  }
250
- let newState;
251
254
  switch (event.type) {
255
+ // --- Execution state transitions ---
252
256
  case "task-started":
253
- newState = applyTaskStart(state, event.taskName);
254
- break;
257
+ return { config, state: applyTaskStart(state, event.taskName) };
255
258
  case "task-completed":
256
- newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data);
257
- break;
259
+ return { config, state: applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data) };
258
260
  case "task-failed":
259
- newState = applyTaskFailure(state, config, event.taskName, event.error);
260
- break;
261
+ return { config, state: applyTaskFailure(state, config, event.taskName, event.error) };
261
262
  case "task-progress":
262
- newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
263
- break;
263
+ return { config, state: applyTaskProgress(state, event.taskName, event.message, event.progress) };
264
264
  case "task-restart":
265
- newState = applyTaskRestart(state, event.taskName);
266
- break;
265
+ return { config, state: applyTaskRestart(state, event.taskName) };
267
266
  case "inject-tokens":
268
- newState = {
269
- ...state,
270
- availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
271
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
267
+ return {
268
+ config,
269
+ state: {
270
+ ...state,
271
+ availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
272
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
273
+ }
272
274
  };
273
- break;
274
275
  case "agent-action":
275
- newState = applyAgentAction(state, event.action);
276
- break;
276
+ return { config, state: applyAgentAction(state, event.action) };
277
+ // --- Structural mutations ---
278
+ case "task-upsert":
279
+ return addNode(live, event.taskName, event.taskConfig);
280
+ case "task-removal":
281
+ return removeNode(live, event.taskName);
282
+ case "node-requires-add":
283
+ return addRequires(live, event.nodeName, event.tokens);
284
+ case "node-requires-remove":
285
+ return removeRequires(live, event.nodeName, event.tokens);
286
+ case "node-provides-add":
287
+ return addProvides(live, event.nodeName, event.tokens);
288
+ case "node-provides-remove":
289
+ return removeProvides(live, event.nodeName, event.tokens);
277
290
  default:
278
291
  return live;
279
292
  }
280
- return { config, state: newState };
281
293
  }
282
294
  function applyEvents(live, events) {
283
295
  return events.reduce((current, event) => applyEvent(current, event), live);
284
296
  }
285
297
  function addNode(live, name, taskConfig) {
286
- if (live.config.tasks[name]) return live;
298
+ const exists = !!live.config.tasks[name];
287
299
  return {
288
300
  config: {
289
301
  ...live.config,
@@ -291,7 +303,10 @@ function addNode(live, name, taskConfig) {
291
303
  },
292
304
  state: {
293
305
  ...live.state,
294
- tasks: { ...live.state.tasks, [name]: createDefaultGraphEngineStore2() },
306
+ tasks: {
307
+ ...live.state.tasks,
308
+ [name]: exists ? live.state.tasks[name] : createDefaultGraphEngineStore2()
309
+ },
295
310
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
296
311
  }
297
312
  };
@@ -998,6 +1013,8 @@ function getDownstream(live, nodeName) {
998
1013
  }));
999
1014
  return { nodeName, nodes, tokens: [...tokenSet] };
1000
1015
  }
1016
+
1017
+ // src/continuous-event-graph/journal.ts
1001
1018
  var MemoryJournal = class {
1002
1019
  buffer = [];
1003
1020
  append(event) {
@@ -1012,36 +1029,6 @@ var MemoryJournal = class {
1012
1029
  return this.buffer.length;
1013
1030
  }
1014
1031
  };
1015
- var FileJournal = class {
1016
- constructor(path) {
1017
- this.path = path;
1018
- if (!fs.existsSync(path)) {
1019
- fs.writeFileSync(path, "", "utf-8");
1020
- }
1021
- }
1022
- path;
1023
- pending = 0;
1024
- append(event) {
1025
- fs.appendFileSync(this.path, JSON.stringify(event) + "\n", "utf-8");
1026
- this.pending++;
1027
- }
1028
- drain() {
1029
- const content = fs.readFileSync(this.path, "utf-8").trim();
1030
- fs.writeFileSync(this.path, "", "utf-8");
1031
- this.pending = 0;
1032
- if (!content) return [];
1033
- return content.split("\n").map((line) => JSON.parse(line));
1034
- }
1035
- get size() {
1036
- try {
1037
- const content = fs.readFileSync(this.path, "utf-8").trim();
1038
- if (!content) return 0;
1039
- return content.split("\n").length;
1040
- } catch {
1041
- return this.pending;
1042
- }
1043
- }
1044
- };
1045
1032
  function computeDataHash(data) {
1046
1033
  const json = stableStringify(data);
1047
1034
  return crypto.createHash("sha256").update(json).digest("hex").slice(0, 16);
@@ -1070,15 +1057,16 @@ function decodeCallbackToken(token) {
1070
1057
  return null;
1071
1058
  }
1072
1059
  }
1073
- function createReactiveGraph(config, options, executionId) {
1060
+ function createReactiveGraph(configOrLive, options, executionId) {
1074
1061
  const {
1075
1062
  handlers: initialHandlers,
1076
- journal = new MemoryJournal(),
1077
1063
  onDrain
1078
1064
  } = options;
1079
- let live = createLiveGraph(config, executionId);
1065
+ const inputQueue = new MemoryJournal();
1066
+ let live = "state" in configOrLive && "config" in configOrLive ? configOrLive : createLiveGraph(configOrLive, executionId);
1080
1067
  let disposed = false;
1081
1068
  const handlers = new Map(Object.entries(initialHandlers));
1069
+ const internalJournal = new MemoryJournal();
1082
1070
  let draining = false;
1083
1071
  let drainQueued = false;
1084
1072
  function drain() {
@@ -1098,7 +1086,9 @@ function createReactiveGraph(config, options, executionId) {
1098
1086
  }
1099
1087
  }
1100
1088
  function drainOnce() {
1101
- const events = journal.drain();
1089
+ const internalEvents = internalJournal.drain();
1090
+ const inputEvents = inputQueue.drain();
1091
+ const events = [...internalEvents, ...inputEvents];
1102
1092
  if (events.length > 0) {
1103
1093
  live = applyEvents(live, events);
1104
1094
  }
@@ -1109,6 +1099,26 @@ function createReactiveGraph(config, options, executionId) {
1109
1099
  for (const taskName of result.eligible) {
1110
1100
  dispatchTask(taskName);
1111
1101
  }
1102
+ for (const event of events) {
1103
+ if (event.type === "task-progress") {
1104
+ const { taskName, update } = event;
1105
+ const taskConfig = live.config.tasks[taskName];
1106
+ if (!taskConfig) continue;
1107
+ const taskState = live.state.tasks[taskName];
1108
+ if (!taskState || taskState.status !== "running") continue;
1109
+ const callbackToken = encodeCallbackToken(taskName);
1110
+ runPipeline(taskName, callbackToken, update).catch((error) => {
1111
+ if (disposed) return;
1112
+ internalJournal.append({
1113
+ type: "task-failed",
1114
+ taskName,
1115
+ error: error.message ?? String(error),
1116
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1117
+ });
1118
+ drain();
1119
+ });
1120
+ }
1121
+ }
1112
1122
  }
1113
1123
  function resolveUpstreamState(taskName) {
1114
1124
  const taskConfig = live.config.tasks[taskName];
@@ -1130,7 +1140,7 @@ function createReactiveGraph(config, options, executionId) {
1130
1140
  }
1131
1141
  return state;
1132
1142
  }
1133
- async function runPipeline(taskName, callbackToken) {
1143
+ async function runPipeline(taskName, callbackToken, update) {
1134
1144
  const taskConfig = live.config.tasks[taskName];
1135
1145
  const handlerNames = taskConfig.taskHandlers ?? [];
1136
1146
  const upstreamState = resolveUpstreamState(taskName);
@@ -1144,7 +1154,8 @@ function createReactiveGraph(config, options, executionId) {
1144
1154
  state: upstreamState,
1145
1155
  taskState: live.state.tasks[taskName],
1146
1156
  config: taskConfig,
1147
- callbackToken
1157
+ callbackToken,
1158
+ update
1148
1159
  };
1149
1160
  const status = await handler(input);
1150
1161
  if (status === "task-initiate-failure") {
@@ -1158,15 +1169,16 @@ function createReactiveGraph(config, options, executionId) {
1158
1169
  if (!handlerNames || handlerNames.length === 0) {
1159
1170
  return;
1160
1171
  }
1161
- journal.append({
1172
+ internalJournal.append({
1162
1173
  type: "task-started",
1163
1174
  taskName,
1164
1175
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1165
1176
  });
1177
+ drain();
1166
1178
  const callbackToken = encodeCallbackToken(taskName);
1167
1179
  runPipeline(taskName, callbackToken).catch((error) => {
1168
1180
  if (disposed) return;
1169
- journal.append({
1181
+ internalJournal.append({
1170
1182
  type: "task-failed",
1171
1183
  taskName,
1172
1184
  error: error.message ?? String(error),
@@ -1181,16 +1193,16 @@ function createReactiveGraph(config, options, executionId) {
1181
1193
  if (event.type === "task-completed" && event.data && !event.dataHash) {
1182
1194
  event = { ...event, dataHash: computeDataHash(event.data) };
1183
1195
  }
1184
- journal.append(event);
1196
+ inputQueue.append(event);
1185
1197
  drain();
1186
1198
  },
1187
1199
  pushAll(events) {
1188
1200
  if (disposed) return;
1189
1201
  for (const event of events) {
1190
1202
  if (event.type === "task-completed" && event.data && !event.dataHash) {
1191
- journal.append({ ...event, dataHash: computeDataHash(event.data) });
1203
+ inputQueue.append({ ...event, dataHash: computeDataHash(event.data) });
1192
1204
  } else {
1193
- journal.append(event);
1205
+ inputQueue.append(event);
1194
1206
  }
1195
1207
  }
1196
1208
  drain();
@@ -1202,7 +1214,7 @@ function createReactiveGraph(config, options, executionId) {
1202
1214
  const { taskName } = decoded;
1203
1215
  if (!live.config.tasks[taskName]) return;
1204
1216
  if (errors && errors.length > 0) {
1205
- journal.append({
1217
+ inputQueue.append({
1206
1218
  type: "task-failed",
1207
1219
  taskName,
1208
1220
  error: errors.join("; "),
@@ -1210,7 +1222,7 @@ function createReactiveGraph(config, options, executionId) {
1210
1222
  });
1211
1223
  } else {
1212
1224
  const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
1213
- journal.append({
1225
+ inputQueue.append({
1214
1226
  type: "task-completed",
1215
1227
  taskName,
1216
1228
  data,
@@ -1222,31 +1234,33 @@ function createReactiveGraph(config, options, executionId) {
1222
1234
  },
1223
1235
  addNode(name, taskConfig) {
1224
1236
  if (disposed) return;
1225
- live = addNode(live, name, taskConfig);
1237
+ inputQueue.append({ type: "task-upsert", taskName: name, taskConfig, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1226
1238
  drain();
1227
1239
  },
1228
1240
  removeNode(name) {
1229
1241
  if (disposed) return;
1230
- live = removeNode(live, name);
1242
+ inputQueue.append({ type: "task-removal", taskName: name, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1243
+ drain();
1231
1244
  },
1232
1245
  addRequires(nodeName, tokens) {
1233
1246
  if (disposed) return;
1234
- live = addRequires(live, nodeName, tokens);
1247
+ inputQueue.append({ type: "node-requires-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1235
1248
  drain();
1236
1249
  },
1237
1250
  removeRequires(nodeName, tokens) {
1238
1251
  if (disposed) return;
1239
- live = removeRequires(live, nodeName, tokens);
1252
+ inputQueue.append({ type: "node-requires-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1240
1253
  drain();
1241
1254
  },
1242
1255
  addProvides(nodeName, tokens) {
1243
1256
  if (disposed) return;
1244
- live = addProvides(live, nodeName, tokens);
1257
+ inputQueue.append({ type: "node-provides-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1245
1258
  drain();
1246
1259
  },
1247
1260
  removeProvides(nodeName, tokens) {
1248
1261
  if (disposed) return;
1249
- live = removeProvides(live, nodeName, tokens);
1262
+ inputQueue.append({ type: "node-provides-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1263
+ drain();
1250
1264
  },
1251
1265
  registerHandler(name, fn) {
1252
1266
  handlers.set(name, fn);
@@ -1257,7 +1271,7 @@ function createReactiveGraph(config, options, executionId) {
1257
1271
  retrigger(taskName) {
1258
1272
  if (disposed) return;
1259
1273
  if (!live.config.tasks[taskName]) return;
1260
- journal.append({
1274
+ inputQueue.append({
1261
1275
  type: "task-restart",
1262
1276
  taskName,
1263
1277
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -1268,7 +1282,7 @@ function createReactiveGraph(config, options, executionId) {
1268
1282
  if (disposed) return;
1269
1283
  for (const name of taskNames) {
1270
1284
  if (!live.config.tasks[name]) continue;
1271
- journal.append({
1285
+ inputQueue.append({
1272
1286
  type: "task-restart",
1273
1287
  taskName: name,
1274
1288
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -1276,6 +1290,9 @@ function createReactiveGraph(config, options, executionId) {
1276
1290
  }
1277
1291
  drain();
1278
1292
  },
1293
+ snapshot() {
1294
+ return snapshot(live);
1295
+ },
1279
1296
  getState() {
1280
1297
  return live;
1281
1298
  },
@@ -1659,288 +1676,43 @@ function deepSet(obj, path, value) {
1659
1676
  }
1660
1677
  cur[parts[parts.length - 1]] = value;
1661
1678
  }
1662
- var _fns = {};
1663
- _fns.sum = (input, _e, opts) => {
1664
- const a = Array.isArray(input) ? input : [];
1665
- return opts.field ? a.reduce((s, r) => s + (Number(r[opts.field]) || 0), 0) : a.reduce((s, v) => s + (Number(v) || 0), 0);
1666
- };
1667
- _fns.avg = (input, _e, opts) => {
1668
- const s = _fns.sum(input, _e, opts);
1669
- const n = Array.isArray(input) ? input.length : 1;
1670
- return n ? s / n : 0;
1671
- };
1672
- _fns.min = (input, _e, opts) => {
1673
- const a = Array.isArray(input) ? input : [];
1674
- const vals = opts.field ? a.map((r) => Number(r[opts.field])) : a.map(Number);
1675
- return vals.length ? Math.min(...vals) : 0;
1676
- };
1677
- _fns.max = (input, _e, opts) => {
1678
- const a = Array.isArray(input) ? input : [];
1679
- const vals = opts.field ? a.map((r) => Number(r[opts.field])) : a.map(Number);
1680
- return vals.length ? Math.max(...vals) : 0;
1681
- };
1682
- _fns.count = (input) => Array.isArray(input) ? input.length : input != null ? 1 : 0;
1683
- _fns.first = (input) => Array.isArray(input) ? input[0] : input;
1684
- _fns.last = (input) => Array.isArray(input) ? input[input.length - 1] : input;
1685
- _fns.add = (input) => {
1686
- const a = Array.isArray(input) ? input : [];
1687
- return a.reduce((s, v) => s + Number(v), 0);
1688
- };
1689
- _fns.sub = (input) => {
1690
- const a = Array.isArray(input) ? input : [];
1691
- return a.length >= 2 ? Number(a[0]) - Number(a[1]) : 0;
1692
- };
1693
- _fns.mul = (input) => {
1694
- const a = Array.isArray(input) ? input : [];
1695
- return a.reduce((s, v) => s * Number(v), 1);
1696
- };
1697
- _fns.div = (input) => {
1698
- const a = Array.isArray(input) ? input : [];
1699
- return a.length >= 2 && Number(a[1]) !== 0 ? Number(a[0]) / Number(a[1]) : 0;
1700
- };
1701
- _fns.round = (input, _e, opts) => {
1702
- const decimals = opts.decimals != null ? opts.decimals : 0;
1703
- const factor = Math.pow(10, decimals);
1704
- return Math.round(Number(input) * factor) / factor;
1705
- };
1706
- _fns.abs = (input) => Math.abs(Number(input));
1707
- _fns.mod = (input) => {
1708
- const a = Array.isArray(input) ? input : [];
1709
- return a.length >= 2 ? Number(a[0]) % Number(a[1]) : 0;
1710
- };
1711
- _fns.gt = (input) => {
1712
- const a = Array.isArray(input) ? input : [];
1713
- return a.length >= 2 && Number(a[0]) > Number(a[1]);
1714
- };
1715
- _fns.gte = (input) => {
1716
- const a = Array.isArray(input) ? input : [];
1717
- return a.length >= 2 && Number(a[0]) >= Number(a[1]);
1718
- };
1719
- _fns.lt = (input) => {
1720
- const a = Array.isArray(input) ? input : [];
1721
- return a.length >= 2 && Number(a[0]) < Number(a[1]);
1722
- };
1723
- _fns.lte = (input) => {
1724
- const a = Array.isArray(input) ? input : [];
1725
- return a.length >= 2 && Number(a[0]) <= Number(a[1]);
1726
- };
1727
- _fns.eq = (input) => {
1728
- const a = Array.isArray(input) ? input : [];
1729
- return a.length >= 2 && a[0] === a[1];
1730
- };
1731
- _fns.neq = (input) => {
1732
- const a = Array.isArray(input) ? input : [];
1733
- return a.length >= 2 && a[0] !== a[1];
1734
- };
1735
- _fns.and = (input) => {
1736
- const a = Array.isArray(input) ? input : [];
1737
- return a.every(Boolean);
1738
- };
1739
- _fns.or = (input) => {
1740
- const a = Array.isArray(input) ? input : [];
1741
- return a.some(Boolean);
1742
- };
1743
- _fns.not = (input) => !input;
1744
- _fns.concat = (input) => {
1745
- const a = Array.isArray(input) ? input : [];
1746
- return a.map((v) => v != null ? String(v) : "").join("");
1747
- };
1748
- _fns.upper = (input) => String(input || "").toUpperCase();
1749
- _fns.lower = (input) => String(input || "").toLowerCase();
1750
- _fns.template = (input, _e, opts) => {
1751
- let t = String(opts.format || "");
1752
- if (input && typeof input === "object" && !Array.isArray(input)) {
1753
- for (const k of Object.keys(input)) {
1754
- const v = input[k];
1755
- t = t.split("{{" + k + "}}").join(v != null ? String(v) : "");
1756
- }
1757
- }
1758
- return t;
1759
- };
1760
- _fns.join = (input, _e, opts) => {
1761
- const a = Array.isArray(input) ? input : [];
1762
- const sep = opts.separator != null ? String(opts.separator) : ", ";
1763
- return a.map((v) => v != null ? String(v) : "").join(sep);
1764
- };
1765
- _fns.split = (input, _e, opts) => {
1766
- const sep = opts.separator != null ? String(opts.separator) : ",";
1767
- return String(input || "").split(sep).map((s) => s.trim());
1768
- };
1769
- _fns.trim = (input) => String(input || "").trim();
1770
- _fns.pluck = (input, _e, opts) => Array.isArray(input) ? input.map((r) => r[opts.field]) : [];
1771
- _fns.filter = (input, _e, opts) => {
1772
- if (!Array.isArray(input)) return [];
1773
- if (opts.field) return input.filter((r) => !!r[opts.field]);
1774
- return input.filter(Boolean);
1775
- };
1776
- _fns.map = (input) => Array.isArray(input) ? input.slice() : [];
1777
- _fns.sort = (input, _e, opts) => {
1778
- const a = Array.isArray(input) ? input.slice() : [];
1779
- const f = opts.field;
1780
- const dir = opts.direction === "desc" ? -1 : 1;
1781
- if (f) return a.sort((x, y) => x[f] > y[f] ? dir : x[f] < y[f] ? -dir : 0);
1782
- return a.sort((x, y) => x > y ? dir : x < y ? -dir : 0);
1783
- };
1784
- _fns.slice = (input, _e, opts) => Array.isArray(input) ? input.slice(opts.start || 0, opts.end) : input;
1785
- _fns.flat = (input, _e, opts) => {
1786
- const depth = opts.depth != null ? opts.depth : 1;
1787
- return Array.isArray(input) ? input.flat(depth) : [input];
1788
- };
1789
- _fns.unique = (input) => {
1790
- if (!Array.isArray(input)) return [input];
1791
- const seen = /* @__PURE__ */ new Set();
1792
- return input.filter((v) => {
1793
- const key = typeof v === "object" ? JSON.stringify(v) : v;
1794
- if (seen.has(key)) return false;
1795
- seen.add(key);
1796
- return true;
1797
- });
1798
- };
1799
- _fns.group = (input, _e, opts) => {
1800
- const a = Array.isArray(input) ? input : [];
1801
- const g = {};
1802
- a.forEach((r) => {
1803
- const k = String(r[opts.field] || "");
1804
- if (!g[k]) g[k] = [];
1805
- g[k].push(r);
1806
- });
1807
- return g;
1808
- };
1809
- _fns.flatten_keys = (input) => {
1810
- if (!input || typeof input !== "object" || Array.isArray(input)) return [];
1811
- const result = [];
1812
- for (const k of Object.keys(input)) {
1813
- const vals = Array.isArray(input[k]) ? input[k] : [input[k]];
1814
- vals.forEach((v) => result.push({ key: k, value: v }));
1815
- }
1816
- return result;
1817
- };
1818
- _fns.entries = (input) => {
1819
- if (!input || typeof input !== "object" || Array.isArray(input)) return [];
1820
- return Object.keys(input).map((k) => ({ key: k, value: input[k] }));
1821
- };
1822
- _fns.from_entries = (input) => {
1823
- if (!Array.isArray(input)) return {};
1824
- const obj = {};
1825
- input.forEach((item) => {
1826
- if (item.key != null) obj[item.key] = item.value;
1827
- });
1828
- return obj;
1829
- };
1830
- _fns.length = (input) => {
1831
- if (Array.isArray(input)) return input.length;
1832
- if (typeof input === "string") return input.length;
1833
- if (input && typeof input === "object") return Object.keys(input).length;
1834
- return 0;
1835
- };
1836
- _fns.get = (input, _e, opts) => deepGet(input, opts.field || opts.path || "");
1837
- _fns.default = (input, _e, opts) => input != null ? input : opts.value;
1838
- _fns.coalesce = (input) => {
1839
- const a = Array.isArray(input) ? input : [];
1840
- for (let i = 0; i < a.length; i++) {
1841
- if (a[i] != null) return a[i];
1842
- }
1843
- return null;
1844
- };
1845
- _fns.now = () => (/* @__PURE__ */ new Date()).toISOString();
1846
- _fns.diff_days = (input) => {
1847
- const a = Array.isArray(input) ? input : [];
1848
- return a.length >= 2 ? Math.floor((new Date(a[0]).getTime() - new Date(a[1]).getTime()) / 864e5) : 0;
1849
- };
1850
- _fns.format_date = (input, _e, opts) => {
1851
- try {
1852
- const d = new Date(input);
1853
- if (opts.format === "iso") return d.toISOString();
1854
- if (opts.format === "date") return d.toLocaleDateString();
1855
- if (opts.format === "time") return d.toLocaleTimeString();
1856
- return d.toLocaleDateString();
1857
- } catch {
1858
- return String(input);
1859
- }
1860
- };
1861
- _fns.parse_date = (input) => {
1862
- try {
1863
- return new Date(input).toISOString();
1864
- } catch {
1865
- return null;
1866
- }
1867
- };
1868
- _fns.to_number = (input) => Number(input) || 0;
1869
- _fns.to_string = (input) => input != null ? String(input) : "";
1870
- _fns.to_bool = (input) => !!input;
1871
- _fns.type_of = (input) => Array.isArray(input) ? "array" : typeof input;
1872
- _fns.is_null = (input) => input == null;
1873
- _fns.is_empty = (input) => {
1874
- if (input == null) return true;
1875
- if (Array.isArray(input)) return input.length === 0;
1876
- if (typeof input === "string") return input.length === 0;
1877
- if (typeof input === "object") return Object.keys(input).length === 0;
1878
- return false;
1879
- };
1880
- var _customFns = {};
1881
- function evalExpr(expr, node) {
1882
- if (expr == null) return expr;
1883
- if (typeof expr !== "object" || Array.isArray(expr)) return expr;
1884
- const e = expr;
1885
- if (!e.fn) return expr;
1886
- let input = e.input;
1887
- if (typeof input === "string" && input.startsWith("state.")) {
1888
- input = deepGet(node, input);
1889
- } else if (Array.isArray(input)) {
1890
- input = input.map((v) => {
1891
- if (typeof v === "string" && v.startsWith("state.")) return deepGet(node, v);
1892
- if (v && typeof v === "object" && v.fn) return evalExpr(v, node);
1893
- return v;
1894
- });
1895
- } else if (input && typeof input === "object" && input.fn) {
1896
- input = evalExpr(input, node);
1897
- }
1898
- if (e.fn === "if") {
1899
- const cond = evalExpr(e.cond, node);
1900
- if (cond) {
1901
- return e.then && typeof e.then === "object" && e.then.fn ? evalExpr(e.then, node) : e.then;
1902
- } else {
1903
- return e.else && typeof e.else === "object" && e.else.fn ? evalExpr(e.else, node) : e.else;
1904
- }
1905
- }
1906
- if (e.fn === "filter" && Array.isArray(input) && e.where) {
1907
- return input.filter((item) => {
1908
- const tmp = { state: { ...node.state, $: item } };
1909
- return evalExpr(e.where, tmp);
1910
- });
1911
- }
1912
- if (e.fn === "map" && Array.isArray(input) && e.apply) {
1913
- return input.map((item) => {
1914
- const tmp = { state: { ...node.state, $: item } };
1915
- return evalExpr(e.apply, tmp);
1916
- });
1917
- }
1918
- const fn = _customFns[e.fn] || _fns[e.fn];
1919
- if (!fn) {
1920
- console.warn('CardCompute: unknown function "' + e.fn + '"');
1921
- return void 0;
1922
- }
1923
- return fn(input, evalExpr, e);
1924
- }
1925
- function run(node) {
1926
- if (!node || !node.compute) return node;
1679
+ async function run(node, options) {
1680
+ if (!node?.compute?.length) return node;
1927
1681
  if (!node.state) node.state = {};
1928
- for (const key of Object.keys(node.compute)) {
1682
+ node.computed_values = {};
1683
+ node._sourcesData = options?.sourcesData ?? {};
1684
+ const ctx = {
1685
+ state: node.state,
1686
+ requires: node.requires ?? {},
1687
+ sources: node._sourcesData,
1688
+ computed_values: node.computed_values
1689
+ };
1690
+ for (const step of node.compute) {
1929
1691
  try {
1930
- const val = evalExpr(node.compute[key], node);
1931
- deepSet(node.state, key, val);
1692
+ const val = await jsonata__default.default(step.expr).evaluate(ctx);
1693
+ deepSet(node.computed_values, step.bindTo, val);
1694
+ ctx.computed_values = node.computed_values;
1932
1695
  } catch (err) {
1933
- console.error(`CardCompute.run error on "${node.id || "?"}.${key}":`, err);
1696
+ console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
1934
1697
  }
1935
1698
  }
1936
1699
  return node;
1937
1700
  }
1701
+ async function evalExpr(expr, node) {
1702
+ const ctx = {
1703
+ state: node.state ?? {},
1704
+ requires: node.requires ?? {},
1705
+ sources: node._sourcesData ?? {},
1706
+ computed_values: node.computed_values ?? {}
1707
+ };
1708
+ return jsonata__default.default(expr).evaluate(ctx);
1709
+ }
1938
1710
  function resolve(node, path) {
1711
+ if (path.startsWith("sources.")) {
1712
+ return deepGet(node._sourcesData ?? {}, path.slice("sources.".length));
1713
+ }
1939
1714
  return deepGet(node, path);
1940
1715
  }
1941
- function registerFunction(name, fn) {
1942
- _customFns[name] = fn;
1943
- }
1944
1716
  var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
1945
1717
  "metric",
1946
1718
  "table",
@@ -1957,26 +1729,17 @@ var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
1957
1729
  "markdown",
1958
1730
  "custom"
1959
1731
  ]);
1960
- var VALID_SOURCE_KINDS = /* @__PURE__ */ new Set(["api", "websocket", "static", "llm"]);
1961
1732
  var VALID_STATUSES = /* @__PURE__ */ new Set(["fresh", "stale", "loading", "error"]);
1962
- var CARD_ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "type", "meta", "data", "view", "state", "compute"]);
1963
- var SOURCE_ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "type", "meta", "data", "source", "state", "compute"]);
1733
+ var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "state", "compute", "sources"]);
1964
1734
  function validateNode(node) {
1965
1735
  const errors = [];
1966
1736
  if (!node || typeof node !== "object" || Array.isArray(node)) {
1967
1737
  return { ok: false, errors: ["Node must be a non-null object"] };
1968
1738
  }
1969
1739
  const n = node;
1970
- if (typeof n.id !== "string" || !n.id) {
1971
- errors.push("id: required, must be a non-empty string");
1972
- }
1973
- if (n.type !== "card" && n.type !== "source") {
1974
- errors.push('type: must be "card" or "source"');
1975
- return { ok: false, errors };
1976
- }
1977
- const allowed = n.type === "card" ? CARD_ALLOWED_KEYS : SOURCE_ALLOWED_KEYS;
1740
+ if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
1978
1741
  for (const key of Object.keys(n)) {
1979
- if (!allowed.has(key)) errors.push(`Unknown top-level key: "${key}"`);
1742
+ if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
1980
1743
  }
1981
1744
  if (n.state == null || typeof n.state !== "object" || Array.isArray(n.state)) {
1982
1745
  errors.push("state: required, must be an object");
@@ -1995,37 +1758,58 @@ function validateNode(node) {
1995
1758
  if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
1996
1759
  }
1997
1760
  }
1998
- if (n.data != null) {
1999
- if (typeof n.data !== "object" || Array.isArray(n.data)) {
2000
- errors.push("data: must be an object");
1761
+ if (n.requires != null && !Array.isArray(n.requires)) errors.push("requires: must be an array of strings");
1762
+ if (n.provides != null) {
1763
+ if (!Array.isArray(n.provides)) {
1764
+ errors.push("provides: must be an array of { bindTo, src } bindings");
2001
1765
  } else {
2002
- const data = n.data;
2003
- if (data.requires != null && !Array.isArray(data.requires)) errors.push("data.requires: must be an array of strings");
2004
- if (data.provides != null && (typeof data.provides !== "object" || Array.isArray(data.provides))) errors.push("data.provides: must be an object");
1766
+ n.provides.forEach((p, i) => {
1767
+ if (!p || typeof p !== "object" || Array.isArray(p)) {
1768
+ errors.push(`provides[${i}]: must be an object with bindTo and src`);
1769
+ } else {
1770
+ const b = p;
1771
+ if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
1772
+ if (typeof b.src !== "string" || !b.src) errors.push(`provides[${i}]: missing required "src" string`);
1773
+ }
1774
+ });
2005
1775
  }
2006
1776
  }
2007
1777
  if (n.compute != null) {
2008
- if (typeof n.compute !== "object" || Array.isArray(n.compute)) {
2009
- errors.push("compute: must be an object");
1778
+ if (!Array.isArray(n.compute)) {
1779
+ errors.push("compute: must be an array of compute steps");
2010
1780
  } else {
2011
- for (const [key, expr] of Object.entries(n.compute)) {
2012
- if (!expr || typeof expr !== "object" || Array.isArray(expr)) {
2013
- errors.push(`compute.${key}: must be a compute expression object`);
2014
- } else if (!expr.fn) {
2015
- errors.push(`compute.${key}: missing required "fn" property`);
1781
+ n.compute.forEach((step, i) => {
1782
+ if (!step || typeof step !== "object" || Array.isArray(step)) {
1783
+ errors.push(`compute[${i}]: must be a compute step object`);
2016
1784
  } else {
2017
- const fn = expr.fn;
2018
- if (!_fns[fn] && !_customFns[fn]) {
2019
- errors.push(`compute.${key}: unknown function "${fn}"`);
1785
+ const s = step;
1786
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
1787
+ if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
1788
+ }
1789
+ });
1790
+ }
1791
+ }
1792
+ if (n.sources != null) {
1793
+ if (!Array.isArray(n.sources)) {
1794
+ errors.push("sources: must be an array");
1795
+ } else {
1796
+ n.sources.forEach((src, i) => {
1797
+ if (!src || typeof src !== "object" || Array.isArray(src)) {
1798
+ errors.push(`sources[${i}]: must be an object`);
1799
+ } else {
1800
+ const s = src;
1801
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`sources[${i}]: missing required "bindTo" property`);
1802
+ if (s.outputFile != null && typeof s.outputFile !== "string") errors.push(`sources[${i}]: outputFile must be a string`);
1803
+ if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
1804
+ errors.push(`sources[${i}]: optionalForCompletionGating must be a boolean`);
2020
1805
  }
2021
1806
  }
2022
- }
1807
+ });
2023
1808
  }
2024
1809
  }
2025
- if (n.type === "card") {
2026
- if (n.source != null) errors.push('Card nodes must not have "source" \u2014 use type "source" instead');
2027
- if (n.view == null || typeof n.view !== "object" || Array.isArray(n.view)) {
2028
- errors.push("view: required for card nodes, must be an object");
1810
+ if (n.view != null) {
1811
+ if (typeof n.view !== "object" || Array.isArray(n.view)) {
1812
+ errors.push("view: must be an object");
2029
1813
  } else {
2030
1814
  const view = n.view;
2031
1815
  if (!Array.isArray(view.elements) || view.elements.length === 0) {
@@ -2046,28 +1830,8 @@ function validateNode(node) {
2046
1830
  }
2047
1831
  });
2048
1832
  }
2049
- if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) {
2050
- errors.push("view.layout: must be an object");
2051
- }
2052
- if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) {
2053
- errors.push("view.features: must be an object");
2054
- }
2055
- }
2056
- }
2057
- if (n.type === "source") {
2058
- if (n.view != null) errors.push('Source nodes must not have "view" \u2014 use type "card" instead');
2059
- if (n.source == null || typeof n.source !== "object" || Array.isArray(n.source)) {
2060
- errors.push("source: required for source nodes, must be an object");
2061
- } else {
2062
- const src = n.source;
2063
- if (!src.kind || !VALID_SOURCE_KINDS.has(src.kind)) {
2064
- errors.push(`source.kind: required, must be one of: ${[...VALID_SOURCE_KINDS].join(", ")}`);
2065
- }
2066
- if (typeof src.bindTo !== "string" || !src.bindTo) {
2067
- errors.push("source.bindTo: required, must be a state path string");
2068
- } else if (!src.bindTo.startsWith("state.")) {
2069
- errors.push('source.bindTo: must start with "state."');
2070
- }
1833
+ if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
1834
+ if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
2071
1835
  }
2072
1836
  }
2073
1837
  return { ok: errors.length === 0, errors };
@@ -2076,14 +1840,7 @@ var CardCompute = {
2076
1840
  run,
2077
1841
  eval: evalExpr,
2078
1842
  resolve,
2079
- validate: validateNode,
2080
- registerFunction,
2081
- get functions() {
2082
- const all = {};
2083
- for (const k of Object.keys(_fns)) all[k] = _fns[k];
2084
- for (const k of Object.keys(_customFns)) all[k] = _customFns[k];
2085
- return all;
2086
- }
1843
+ validate: validateNode
2087
1844
  };
2088
1845
 
2089
1846
  // src/continuous-event-graph/live-cards-bridge.ts
@@ -2116,19 +1873,26 @@ function liveCardsToReactiveGraph(input, options = {}) {
2116
1873
  }
2117
1874
  const sharedState = options.sharedState ?? /* @__PURE__ */ new Map();
2118
1875
  const tasks = {};
1876
+ const allTokens = /* @__PURE__ */ new Set();
1877
+ const tokenToCardId = /* @__PURE__ */ new Map();
1878
+ for (const card of cards) {
1879
+ for (const binding of card.provides ?? [{ bindTo: card.id, src: `state.${card.id}` }]) {
1880
+ allTokens.add(binding.bindTo);
1881
+ tokenToCardId.set(binding.bindTo, card.id);
1882
+ }
1883
+ }
2119
1884
  for (const card of cards) {
2120
- const requires = card.data?.requires ?? [];
1885
+ const requires = card.requires ?? [];
2121
1886
  for (const req of requires) {
2122
- if (!cardMap.has(req)) {
2123
- throw new Error(`Card "${card.id}" requires "${req}" but no card with that ID exists`);
1887
+ if (!allTokens.has(req)) {
1888
+ throw new Error(`Card "${card.id}" requires "${req}" but no card provides that token`);
2124
1889
  }
2125
1890
  }
2126
1891
  tasks[card.id] = {
2127
1892
  requires: requires.length > 0 ? requires : void 0,
2128
- provides: [card.id],
1893
+ provides: (card.provides ?? [{ bindTo: card.id, src: `state.${card.id}` }]).map((p) => p.bindTo),
2129
1894
  taskHandlers: [card.id],
2130
- // each card has a named handler matching its ID
2131
- description: card.meta?.title ?? `${card.type}: ${card.id}`
1895
+ description: card.meta?.title ?? card.id
2132
1896
  };
2133
1897
  }
2134
1898
  const config = {
@@ -2147,10 +1911,10 @@ function liveCardsToReactiveGraph(input, options = {}) {
2147
1911
  graphRef.resolveCallback(token, data, errors);
2148
1912
  };
2149
1913
  for (const card of cards) {
2150
- if (card.type === "source") {
1914
+ if (card.sources && card.sources.length > 0) {
2151
1915
  handlers[card.id] = buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve);
2152
1916
  } else {
2153
- handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve);
1917
+ handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, tokenToCardId, getResolve);
2154
1918
  }
2155
1919
  }
2156
1920
  const graph = createReactiveGraph(
@@ -2184,7 +1948,7 @@ function buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedSt
2184
1948
  return "task-initiated";
2185
1949
  };
2186
1950
  }
2187
- function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve) {
1951
+ function buildCardHandler(card, cardHandlers, sharedState, _cardMap, tokenToCardId, getResolve) {
2188
1952
  if (cardHandlers[card.id]) {
2189
1953
  const userHandler = cardHandlers[card.id];
2190
1954
  return async (input) => {
@@ -2192,49 +1956,38 @@ function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve)
2192
1956
  };
2193
1957
  }
2194
1958
  return async (input) => {
1959
+ const requiresData = {};
1960
+ const requires = card.requires ?? [];
1961
+ for (const token of requires) {
1962
+ const producerId = tokenToCardId.get(token) ?? token;
1963
+ const upstreamState = sharedState.get(producerId);
1964
+ if (upstreamState) {
1965
+ requiresData[token] = upstreamState[token] ?? upstreamState;
1966
+ }
1967
+ }
2195
1968
  const computeNode = {
2196
1969
  id: card.id,
2197
1970
  state: { ...card.state },
1971
+ requires: requiresData,
2198
1972
  compute: card.compute
2199
1973
  };
2200
- const requires = card.data?.requires ?? [];
2201
- for (const upstreamId of requires) {
2202
- const upstreamState = sharedState.get(upstreamId);
2203
- if (upstreamState) {
2204
- computeNode.state[upstreamId] = upstreamState;
2205
- }
2206
- const upstreamCard = cardMap.get(upstreamId);
2207
- if (upstreamCard?.data?.provides && upstreamState) {
2208
- for (const [key, bindRef] of Object.entries(upstreamCard.data.provides)) {
2209
- if (typeof bindRef === "string" && bindRef.startsWith("state.")) {
2210
- const path = bindRef.slice(6);
2211
- const value = deepGet2(upstreamState, path);
2212
- if (value !== void 0) {
2213
- computeNode.state[key] = value;
2214
- }
2215
- }
2216
- }
1974
+ await CardCompute.run(computeNode);
1975
+ let resultData;
1976
+ if (card.provides && card.provides.length > 0) {
1977
+ resultData = {};
1978
+ for (const { bindTo, src } of card.provides) {
1979
+ resultData[bindTo] = CardCompute.resolve(computeNode, src);
2217
1980
  }
1981
+ } else {
1982
+ resultData = { ...computeNode.state, ...computeNode.computed_values };
2218
1983
  }
2219
- CardCompute.run(computeNode);
2220
- const resultState = { ...computeNode.state };
1984
+ const resultState = { ...computeNode.state, ...computeNode.computed_values };
2221
1985
  sharedState.set(card.id, resultState);
2222
- getResolve()(input.callbackToken, resultState);
1986
+ getResolve()(input.callbackToken, resultData);
2223
1987
  return "task-initiated";
2224
1988
  };
2225
1989
  }
2226
- function deepGet2(obj, path) {
2227
- if (!path || !obj) return void 0;
2228
- const parts = path.split(".");
2229
- let cur = obj;
2230
- for (const part of parts) {
2231
- if (cur == null) return void 0;
2232
- cur = cur[part];
2233
- }
2234
- return cur;
2235
- }
2236
1990
 
2237
- exports.FileJournal = FileJournal;
2238
1991
  exports.MemoryJournal = MemoryJournal;
2239
1992
  exports.addNode = addNode;
2240
1993
  exports.addProvides = addProvides;