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,6 +1,6 @@
1
- import { existsSync, writeFileSync, appendFileSync, readFileSync } from 'fs';
2
1
  import { createHash } from 'crypto';
3
2
  import { exec } from 'child_process';
3
+ import jsonata from 'jsonata';
4
4
  import 'ajv-formats';
5
5
 
6
6
  // src/event-graph/constants.ts
@@ -245,43 +245,51 @@ function applyEvent(live, event) {
245
245
  if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
246
246
  return live;
247
247
  }
248
- let newState;
249
248
  switch (event.type) {
249
+ // --- Execution state transitions ---
250
250
  case "task-started":
251
- newState = applyTaskStart(state, event.taskName);
252
- break;
251
+ return { config, state: applyTaskStart(state, event.taskName) };
253
252
  case "task-completed":
254
- newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data);
255
- break;
253
+ return { config, state: applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data) };
256
254
  case "task-failed":
257
- newState = applyTaskFailure(state, config, event.taskName, event.error);
258
- break;
255
+ return { config, state: applyTaskFailure(state, config, event.taskName, event.error) };
259
256
  case "task-progress":
260
- newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
261
- break;
257
+ return { config, state: applyTaskProgress(state, event.taskName, event.message, event.progress) };
262
258
  case "task-restart":
263
- newState = applyTaskRestart(state, event.taskName);
264
- break;
259
+ return { config, state: applyTaskRestart(state, event.taskName) };
265
260
  case "inject-tokens":
266
- newState = {
267
- ...state,
268
- availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
269
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
261
+ return {
262
+ config,
263
+ state: {
264
+ ...state,
265
+ availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
266
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
267
+ }
270
268
  };
271
- break;
272
269
  case "agent-action":
273
- newState = applyAgentAction(state, event.action);
274
- break;
270
+ return { config, state: applyAgentAction(state, event.action) };
271
+ // --- Structural mutations ---
272
+ case "task-upsert":
273
+ return addNode(live, event.taskName, event.taskConfig);
274
+ case "task-removal":
275
+ return removeNode(live, event.taskName);
276
+ case "node-requires-add":
277
+ return addRequires(live, event.nodeName, event.tokens);
278
+ case "node-requires-remove":
279
+ return removeRequires(live, event.nodeName, event.tokens);
280
+ case "node-provides-add":
281
+ return addProvides(live, event.nodeName, event.tokens);
282
+ case "node-provides-remove":
283
+ return removeProvides(live, event.nodeName, event.tokens);
275
284
  default:
276
285
  return live;
277
286
  }
278
- return { config, state: newState };
279
287
  }
280
288
  function applyEvents(live, events) {
281
289
  return events.reduce((current, event) => applyEvent(current, event), live);
282
290
  }
283
291
  function addNode(live, name, taskConfig) {
284
- if (live.config.tasks[name]) return live;
292
+ const exists = !!live.config.tasks[name];
285
293
  return {
286
294
  config: {
287
295
  ...live.config,
@@ -289,7 +297,10 @@ function addNode(live, name, taskConfig) {
289
297
  },
290
298
  state: {
291
299
  ...live.state,
292
- tasks: { ...live.state.tasks, [name]: createDefaultGraphEngineStore2() },
300
+ tasks: {
301
+ ...live.state.tasks,
302
+ [name]: exists ? live.state.tasks[name] : createDefaultGraphEngineStore2()
303
+ },
293
304
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
294
305
  }
295
306
  };
@@ -996,6 +1007,8 @@ function getDownstream(live, nodeName) {
996
1007
  }));
997
1008
  return { nodeName, nodes, tokens: [...tokenSet] };
998
1009
  }
1010
+
1011
+ // src/continuous-event-graph/journal.ts
999
1012
  var MemoryJournal = class {
1000
1013
  buffer = [];
1001
1014
  append(event) {
@@ -1010,36 +1023,6 @@ var MemoryJournal = class {
1010
1023
  return this.buffer.length;
1011
1024
  }
1012
1025
  };
1013
- var FileJournal = class {
1014
- constructor(path) {
1015
- this.path = path;
1016
- if (!existsSync(path)) {
1017
- writeFileSync(path, "", "utf-8");
1018
- }
1019
- }
1020
- path;
1021
- pending = 0;
1022
- append(event) {
1023
- appendFileSync(this.path, JSON.stringify(event) + "\n", "utf-8");
1024
- this.pending++;
1025
- }
1026
- drain() {
1027
- const content = readFileSync(this.path, "utf-8").trim();
1028
- writeFileSync(this.path, "", "utf-8");
1029
- this.pending = 0;
1030
- if (!content) return [];
1031
- return content.split("\n").map((line) => JSON.parse(line));
1032
- }
1033
- get size() {
1034
- try {
1035
- const content = readFileSync(this.path, "utf-8").trim();
1036
- if (!content) return 0;
1037
- return content.split("\n").length;
1038
- } catch {
1039
- return this.pending;
1040
- }
1041
- }
1042
- };
1043
1026
  function computeDataHash(data) {
1044
1027
  const json = stableStringify(data);
1045
1028
  return createHash("sha256").update(json).digest("hex").slice(0, 16);
@@ -1068,15 +1051,16 @@ function decodeCallbackToken(token) {
1068
1051
  return null;
1069
1052
  }
1070
1053
  }
1071
- function createReactiveGraph(config, options, executionId) {
1054
+ function createReactiveGraph(configOrLive, options, executionId) {
1072
1055
  const {
1073
1056
  handlers: initialHandlers,
1074
- journal = new MemoryJournal(),
1075
1057
  onDrain
1076
1058
  } = options;
1077
- let live = createLiveGraph(config, executionId);
1059
+ const inputQueue = new MemoryJournal();
1060
+ let live = "state" in configOrLive && "config" in configOrLive ? configOrLive : createLiveGraph(configOrLive, executionId);
1078
1061
  let disposed = false;
1079
1062
  const handlers = new Map(Object.entries(initialHandlers));
1063
+ const internalJournal = new MemoryJournal();
1080
1064
  let draining = false;
1081
1065
  let drainQueued = false;
1082
1066
  function drain() {
@@ -1096,7 +1080,9 @@ function createReactiveGraph(config, options, executionId) {
1096
1080
  }
1097
1081
  }
1098
1082
  function drainOnce() {
1099
- const events = journal.drain();
1083
+ const internalEvents = internalJournal.drain();
1084
+ const inputEvents = inputQueue.drain();
1085
+ const events = [...internalEvents, ...inputEvents];
1100
1086
  if (events.length > 0) {
1101
1087
  live = applyEvents(live, events);
1102
1088
  }
@@ -1107,6 +1093,26 @@ function createReactiveGraph(config, options, executionId) {
1107
1093
  for (const taskName of result.eligible) {
1108
1094
  dispatchTask(taskName);
1109
1095
  }
1096
+ for (const event of events) {
1097
+ if (event.type === "task-progress") {
1098
+ const { taskName, update } = event;
1099
+ const taskConfig = live.config.tasks[taskName];
1100
+ if (!taskConfig) continue;
1101
+ const taskState = live.state.tasks[taskName];
1102
+ if (!taskState || taskState.status !== "running") continue;
1103
+ const callbackToken = encodeCallbackToken(taskName);
1104
+ runPipeline(taskName, callbackToken, update).catch((error) => {
1105
+ if (disposed) return;
1106
+ internalJournal.append({
1107
+ type: "task-failed",
1108
+ taskName,
1109
+ error: error.message ?? String(error),
1110
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1111
+ });
1112
+ drain();
1113
+ });
1114
+ }
1115
+ }
1110
1116
  }
1111
1117
  function resolveUpstreamState(taskName) {
1112
1118
  const taskConfig = live.config.tasks[taskName];
@@ -1128,7 +1134,7 @@ function createReactiveGraph(config, options, executionId) {
1128
1134
  }
1129
1135
  return state;
1130
1136
  }
1131
- async function runPipeline(taskName, callbackToken) {
1137
+ async function runPipeline(taskName, callbackToken, update) {
1132
1138
  const taskConfig = live.config.tasks[taskName];
1133
1139
  const handlerNames = taskConfig.taskHandlers ?? [];
1134
1140
  const upstreamState = resolveUpstreamState(taskName);
@@ -1142,7 +1148,8 @@ function createReactiveGraph(config, options, executionId) {
1142
1148
  state: upstreamState,
1143
1149
  taskState: live.state.tasks[taskName],
1144
1150
  config: taskConfig,
1145
- callbackToken
1151
+ callbackToken,
1152
+ update
1146
1153
  };
1147
1154
  const status = await handler(input);
1148
1155
  if (status === "task-initiate-failure") {
@@ -1156,15 +1163,16 @@ function createReactiveGraph(config, options, executionId) {
1156
1163
  if (!handlerNames || handlerNames.length === 0) {
1157
1164
  return;
1158
1165
  }
1159
- journal.append({
1166
+ internalJournal.append({
1160
1167
  type: "task-started",
1161
1168
  taskName,
1162
1169
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1163
1170
  });
1171
+ drain();
1164
1172
  const callbackToken = encodeCallbackToken(taskName);
1165
1173
  runPipeline(taskName, callbackToken).catch((error) => {
1166
1174
  if (disposed) return;
1167
- journal.append({
1175
+ internalJournal.append({
1168
1176
  type: "task-failed",
1169
1177
  taskName,
1170
1178
  error: error.message ?? String(error),
@@ -1179,16 +1187,16 @@ function createReactiveGraph(config, options, executionId) {
1179
1187
  if (event.type === "task-completed" && event.data && !event.dataHash) {
1180
1188
  event = { ...event, dataHash: computeDataHash(event.data) };
1181
1189
  }
1182
- journal.append(event);
1190
+ inputQueue.append(event);
1183
1191
  drain();
1184
1192
  },
1185
1193
  pushAll(events) {
1186
1194
  if (disposed) return;
1187
1195
  for (const event of events) {
1188
1196
  if (event.type === "task-completed" && event.data && !event.dataHash) {
1189
- journal.append({ ...event, dataHash: computeDataHash(event.data) });
1197
+ inputQueue.append({ ...event, dataHash: computeDataHash(event.data) });
1190
1198
  } else {
1191
- journal.append(event);
1199
+ inputQueue.append(event);
1192
1200
  }
1193
1201
  }
1194
1202
  drain();
@@ -1200,7 +1208,7 @@ function createReactiveGraph(config, options, executionId) {
1200
1208
  const { taskName } = decoded;
1201
1209
  if (!live.config.tasks[taskName]) return;
1202
1210
  if (errors && errors.length > 0) {
1203
- journal.append({
1211
+ inputQueue.append({
1204
1212
  type: "task-failed",
1205
1213
  taskName,
1206
1214
  error: errors.join("; "),
@@ -1208,7 +1216,7 @@ function createReactiveGraph(config, options, executionId) {
1208
1216
  });
1209
1217
  } else {
1210
1218
  const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
1211
- journal.append({
1219
+ inputQueue.append({
1212
1220
  type: "task-completed",
1213
1221
  taskName,
1214
1222
  data,
@@ -1220,31 +1228,33 @@ function createReactiveGraph(config, options, executionId) {
1220
1228
  },
1221
1229
  addNode(name, taskConfig) {
1222
1230
  if (disposed) return;
1223
- live = addNode(live, name, taskConfig);
1231
+ inputQueue.append({ type: "task-upsert", taskName: name, taskConfig, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1224
1232
  drain();
1225
1233
  },
1226
1234
  removeNode(name) {
1227
1235
  if (disposed) return;
1228
- live = removeNode(live, name);
1236
+ inputQueue.append({ type: "task-removal", taskName: name, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1237
+ drain();
1229
1238
  },
1230
1239
  addRequires(nodeName, tokens) {
1231
1240
  if (disposed) return;
1232
- live = addRequires(live, nodeName, tokens);
1241
+ inputQueue.append({ type: "node-requires-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1233
1242
  drain();
1234
1243
  },
1235
1244
  removeRequires(nodeName, tokens) {
1236
1245
  if (disposed) return;
1237
- live = removeRequires(live, nodeName, tokens);
1246
+ inputQueue.append({ type: "node-requires-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1238
1247
  drain();
1239
1248
  },
1240
1249
  addProvides(nodeName, tokens) {
1241
1250
  if (disposed) return;
1242
- live = addProvides(live, nodeName, tokens);
1251
+ inputQueue.append({ type: "node-provides-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1243
1252
  drain();
1244
1253
  },
1245
1254
  removeProvides(nodeName, tokens) {
1246
1255
  if (disposed) return;
1247
- live = removeProvides(live, nodeName, tokens);
1256
+ inputQueue.append({ type: "node-provides-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1257
+ drain();
1248
1258
  },
1249
1259
  registerHandler(name, fn) {
1250
1260
  handlers.set(name, fn);
@@ -1255,7 +1265,7 @@ function createReactiveGraph(config, options, executionId) {
1255
1265
  retrigger(taskName) {
1256
1266
  if (disposed) return;
1257
1267
  if (!live.config.tasks[taskName]) return;
1258
- journal.append({
1268
+ inputQueue.append({
1259
1269
  type: "task-restart",
1260
1270
  taskName,
1261
1271
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -1266,7 +1276,7 @@ function createReactiveGraph(config, options, executionId) {
1266
1276
  if (disposed) return;
1267
1277
  for (const name of taskNames) {
1268
1278
  if (!live.config.tasks[name]) continue;
1269
- journal.append({
1279
+ inputQueue.append({
1270
1280
  type: "task-restart",
1271
1281
  taskName: name,
1272
1282
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -1274,6 +1284,9 @@ function createReactiveGraph(config, options, executionId) {
1274
1284
  }
1275
1285
  drain();
1276
1286
  },
1287
+ snapshot() {
1288
+ return snapshot(live);
1289
+ },
1277
1290
  getState() {
1278
1291
  return live;
1279
1292
  },
@@ -1657,288 +1670,43 @@ function deepSet(obj, path, value) {
1657
1670
  }
1658
1671
  cur[parts[parts.length - 1]] = value;
1659
1672
  }
1660
- var _fns = {};
1661
- _fns.sum = (input, _e, opts) => {
1662
- const a = Array.isArray(input) ? input : [];
1663
- return opts.field ? a.reduce((s, r) => s + (Number(r[opts.field]) || 0), 0) : a.reduce((s, v) => s + (Number(v) || 0), 0);
1664
- };
1665
- _fns.avg = (input, _e, opts) => {
1666
- const s = _fns.sum(input, _e, opts);
1667
- const n = Array.isArray(input) ? input.length : 1;
1668
- return n ? s / n : 0;
1669
- };
1670
- _fns.min = (input, _e, opts) => {
1671
- const a = Array.isArray(input) ? input : [];
1672
- const vals = opts.field ? a.map((r) => Number(r[opts.field])) : a.map(Number);
1673
- return vals.length ? Math.min(...vals) : 0;
1674
- };
1675
- _fns.max = (input, _e, opts) => {
1676
- const a = Array.isArray(input) ? input : [];
1677
- const vals = opts.field ? a.map((r) => Number(r[opts.field])) : a.map(Number);
1678
- return vals.length ? Math.max(...vals) : 0;
1679
- };
1680
- _fns.count = (input) => Array.isArray(input) ? input.length : input != null ? 1 : 0;
1681
- _fns.first = (input) => Array.isArray(input) ? input[0] : input;
1682
- _fns.last = (input) => Array.isArray(input) ? input[input.length - 1] : input;
1683
- _fns.add = (input) => {
1684
- const a = Array.isArray(input) ? input : [];
1685
- return a.reduce((s, v) => s + Number(v), 0);
1686
- };
1687
- _fns.sub = (input) => {
1688
- const a = Array.isArray(input) ? input : [];
1689
- return a.length >= 2 ? Number(a[0]) - Number(a[1]) : 0;
1690
- };
1691
- _fns.mul = (input) => {
1692
- const a = Array.isArray(input) ? input : [];
1693
- return a.reduce((s, v) => s * Number(v), 1);
1694
- };
1695
- _fns.div = (input) => {
1696
- const a = Array.isArray(input) ? input : [];
1697
- return a.length >= 2 && Number(a[1]) !== 0 ? Number(a[0]) / Number(a[1]) : 0;
1698
- };
1699
- _fns.round = (input, _e, opts) => {
1700
- const decimals = opts.decimals != null ? opts.decimals : 0;
1701
- const factor = Math.pow(10, decimals);
1702
- return Math.round(Number(input) * factor) / factor;
1703
- };
1704
- _fns.abs = (input) => Math.abs(Number(input));
1705
- _fns.mod = (input) => {
1706
- const a = Array.isArray(input) ? input : [];
1707
- return a.length >= 2 ? Number(a[0]) % Number(a[1]) : 0;
1708
- };
1709
- _fns.gt = (input) => {
1710
- const a = Array.isArray(input) ? input : [];
1711
- return a.length >= 2 && Number(a[0]) > Number(a[1]);
1712
- };
1713
- _fns.gte = (input) => {
1714
- const a = Array.isArray(input) ? input : [];
1715
- return a.length >= 2 && Number(a[0]) >= Number(a[1]);
1716
- };
1717
- _fns.lt = (input) => {
1718
- const a = Array.isArray(input) ? input : [];
1719
- return a.length >= 2 && Number(a[0]) < Number(a[1]);
1720
- };
1721
- _fns.lte = (input) => {
1722
- const a = Array.isArray(input) ? input : [];
1723
- return a.length >= 2 && Number(a[0]) <= Number(a[1]);
1724
- };
1725
- _fns.eq = (input) => {
1726
- const a = Array.isArray(input) ? input : [];
1727
- return a.length >= 2 && a[0] === a[1];
1728
- };
1729
- _fns.neq = (input) => {
1730
- const a = Array.isArray(input) ? input : [];
1731
- return a.length >= 2 && a[0] !== a[1];
1732
- };
1733
- _fns.and = (input) => {
1734
- const a = Array.isArray(input) ? input : [];
1735
- return a.every(Boolean);
1736
- };
1737
- _fns.or = (input) => {
1738
- const a = Array.isArray(input) ? input : [];
1739
- return a.some(Boolean);
1740
- };
1741
- _fns.not = (input) => !input;
1742
- _fns.concat = (input) => {
1743
- const a = Array.isArray(input) ? input : [];
1744
- return a.map((v) => v != null ? String(v) : "").join("");
1745
- };
1746
- _fns.upper = (input) => String(input || "").toUpperCase();
1747
- _fns.lower = (input) => String(input || "").toLowerCase();
1748
- _fns.template = (input, _e, opts) => {
1749
- let t = String(opts.format || "");
1750
- if (input && typeof input === "object" && !Array.isArray(input)) {
1751
- for (const k of Object.keys(input)) {
1752
- const v = input[k];
1753
- t = t.split("{{" + k + "}}").join(v != null ? String(v) : "");
1754
- }
1755
- }
1756
- return t;
1757
- };
1758
- _fns.join = (input, _e, opts) => {
1759
- const a = Array.isArray(input) ? input : [];
1760
- const sep = opts.separator != null ? String(opts.separator) : ", ";
1761
- return a.map((v) => v != null ? String(v) : "").join(sep);
1762
- };
1763
- _fns.split = (input, _e, opts) => {
1764
- const sep = opts.separator != null ? String(opts.separator) : ",";
1765
- return String(input || "").split(sep).map((s) => s.trim());
1766
- };
1767
- _fns.trim = (input) => String(input || "").trim();
1768
- _fns.pluck = (input, _e, opts) => Array.isArray(input) ? input.map((r) => r[opts.field]) : [];
1769
- _fns.filter = (input, _e, opts) => {
1770
- if (!Array.isArray(input)) return [];
1771
- if (opts.field) return input.filter((r) => !!r[opts.field]);
1772
- return input.filter(Boolean);
1773
- };
1774
- _fns.map = (input) => Array.isArray(input) ? input.slice() : [];
1775
- _fns.sort = (input, _e, opts) => {
1776
- const a = Array.isArray(input) ? input.slice() : [];
1777
- const f = opts.field;
1778
- const dir = opts.direction === "desc" ? -1 : 1;
1779
- if (f) return a.sort((x, y) => x[f] > y[f] ? dir : x[f] < y[f] ? -dir : 0);
1780
- return a.sort((x, y) => x > y ? dir : x < y ? -dir : 0);
1781
- };
1782
- _fns.slice = (input, _e, opts) => Array.isArray(input) ? input.slice(opts.start || 0, opts.end) : input;
1783
- _fns.flat = (input, _e, opts) => {
1784
- const depth = opts.depth != null ? opts.depth : 1;
1785
- return Array.isArray(input) ? input.flat(depth) : [input];
1786
- };
1787
- _fns.unique = (input) => {
1788
- if (!Array.isArray(input)) return [input];
1789
- const seen = /* @__PURE__ */ new Set();
1790
- return input.filter((v) => {
1791
- const key = typeof v === "object" ? JSON.stringify(v) : v;
1792
- if (seen.has(key)) return false;
1793
- seen.add(key);
1794
- return true;
1795
- });
1796
- };
1797
- _fns.group = (input, _e, opts) => {
1798
- const a = Array.isArray(input) ? input : [];
1799
- const g = {};
1800
- a.forEach((r) => {
1801
- const k = String(r[opts.field] || "");
1802
- if (!g[k]) g[k] = [];
1803
- g[k].push(r);
1804
- });
1805
- return g;
1806
- };
1807
- _fns.flatten_keys = (input) => {
1808
- if (!input || typeof input !== "object" || Array.isArray(input)) return [];
1809
- const result = [];
1810
- for (const k of Object.keys(input)) {
1811
- const vals = Array.isArray(input[k]) ? input[k] : [input[k]];
1812
- vals.forEach((v) => result.push({ key: k, value: v }));
1813
- }
1814
- return result;
1815
- };
1816
- _fns.entries = (input) => {
1817
- if (!input || typeof input !== "object" || Array.isArray(input)) return [];
1818
- return Object.keys(input).map((k) => ({ key: k, value: input[k] }));
1819
- };
1820
- _fns.from_entries = (input) => {
1821
- if (!Array.isArray(input)) return {};
1822
- const obj = {};
1823
- input.forEach((item) => {
1824
- if (item.key != null) obj[item.key] = item.value;
1825
- });
1826
- return obj;
1827
- };
1828
- _fns.length = (input) => {
1829
- if (Array.isArray(input)) return input.length;
1830
- if (typeof input === "string") return input.length;
1831
- if (input && typeof input === "object") return Object.keys(input).length;
1832
- return 0;
1833
- };
1834
- _fns.get = (input, _e, opts) => deepGet(input, opts.field || opts.path || "");
1835
- _fns.default = (input, _e, opts) => input != null ? input : opts.value;
1836
- _fns.coalesce = (input) => {
1837
- const a = Array.isArray(input) ? input : [];
1838
- for (let i = 0; i < a.length; i++) {
1839
- if (a[i] != null) return a[i];
1840
- }
1841
- return null;
1842
- };
1843
- _fns.now = () => (/* @__PURE__ */ new Date()).toISOString();
1844
- _fns.diff_days = (input) => {
1845
- const a = Array.isArray(input) ? input : [];
1846
- return a.length >= 2 ? Math.floor((new Date(a[0]).getTime() - new Date(a[1]).getTime()) / 864e5) : 0;
1847
- };
1848
- _fns.format_date = (input, _e, opts) => {
1849
- try {
1850
- const d = new Date(input);
1851
- if (opts.format === "iso") return d.toISOString();
1852
- if (opts.format === "date") return d.toLocaleDateString();
1853
- if (opts.format === "time") return d.toLocaleTimeString();
1854
- return d.toLocaleDateString();
1855
- } catch {
1856
- return String(input);
1857
- }
1858
- };
1859
- _fns.parse_date = (input) => {
1860
- try {
1861
- return new Date(input).toISOString();
1862
- } catch {
1863
- return null;
1864
- }
1865
- };
1866
- _fns.to_number = (input) => Number(input) || 0;
1867
- _fns.to_string = (input) => input != null ? String(input) : "";
1868
- _fns.to_bool = (input) => !!input;
1869
- _fns.type_of = (input) => Array.isArray(input) ? "array" : typeof input;
1870
- _fns.is_null = (input) => input == null;
1871
- _fns.is_empty = (input) => {
1872
- if (input == null) return true;
1873
- if (Array.isArray(input)) return input.length === 0;
1874
- if (typeof input === "string") return input.length === 0;
1875
- if (typeof input === "object") return Object.keys(input).length === 0;
1876
- return false;
1877
- };
1878
- var _customFns = {};
1879
- function evalExpr(expr, node) {
1880
- if (expr == null) return expr;
1881
- if (typeof expr !== "object" || Array.isArray(expr)) return expr;
1882
- const e = expr;
1883
- if (!e.fn) return expr;
1884
- let input = e.input;
1885
- if (typeof input === "string" && input.startsWith("state.")) {
1886
- input = deepGet(node, input);
1887
- } else if (Array.isArray(input)) {
1888
- input = input.map((v) => {
1889
- if (typeof v === "string" && v.startsWith("state.")) return deepGet(node, v);
1890
- if (v && typeof v === "object" && v.fn) return evalExpr(v, node);
1891
- return v;
1892
- });
1893
- } else if (input && typeof input === "object" && input.fn) {
1894
- input = evalExpr(input, node);
1895
- }
1896
- if (e.fn === "if") {
1897
- const cond = evalExpr(e.cond, node);
1898
- if (cond) {
1899
- return e.then && typeof e.then === "object" && e.then.fn ? evalExpr(e.then, node) : e.then;
1900
- } else {
1901
- return e.else && typeof e.else === "object" && e.else.fn ? evalExpr(e.else, node) : e.else;
1902
- }
1903
- }
1904
- if (e.fn === "filter" && Array.isArray(input) && e.where) {
1905
- return input.filter((item) => {
1906
- const tmp = { state: { ...node.state, $: item } };
1907
- return evalExpr(e.where, tmp);
1908
- });
1909
- }
1910
- if (e.fn === "map" && Array.isArray(input) && e.apply) {
1911
- return input.map((item) => {
1912
- const tmp = { state: { ...node.state, $: item } };
1913
- return evalExpr(e.apply, tmp);
1914
- });
1915
- }
1916
- const fn = _customFns[e.fn] || _fns[e.fn];
1917
- if (!fn) {
1918
- console.warn('CardCompute: unknown function "' + e.fn + '"');
1919
- return void 0;
1920
- }
1921
- return fn(input, evalExpr, e);
1922
- }
1923
- function run(node) {
1924
- if (!node || !node.compute) return node;
1673
+ async function run(node, options) {
1674
+ if (!node?.compute?.length) return node;
1925
1675
  if (!node.state) node.state = {};
1926
- for (const key of Object.keys(node.compute)) {
1676
+ node.computed_values = {};
1677
+ node._sourcesData = options?.sourcesData ?? {};
1678
+ const ctx = {
1679
+ state: node.state,
1680
+ requires: node.requires ?? {},
1681
+ sources: node._sourcesData,
1682
+ computed_values: node.computed_values
1683
+ };
1684
+ for (const step of node.compute) {
1927
1685
  try {
1928
- const val = evalExpr(node.compute[key], node);
1929
- deepSet(node.state, key, val);
1686
+ const val = await jsonata(step.expr).evaluate(ctx);
1687
+ deepSet(node.computed_values, step.bindTo, val);
1688
+ ctx.computed_values = node.computed_values;
1930
1689
  } catch (err) {
1931
- console.error(`CardCompute.run error on "${node.id || "?"}.${key}":`, err);
1690
+ console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
1932
1691
  }
1933
1692
  }
1934
1693
  return node;
1935
1694
  }
1695
+ async function evalExpr(expr, node) {
1696
+ const ctx = {
1697
+ state: node.state ?? {},
1698
+ requires: node.requires ?? {},
1699
+ sources: node._sourcesData ?? {},
1700
+ computed_values: node.computed_values ?? {}
1701
+ };
1702
+ return jsonata(expr).evaluate(ctx);
1703
+ }
1936
1704
  function resolve(node, path) {
1705
+ if (path.startsWith("sources.")) {
1706
+ return deepGet(node._sourcesData ?? {}, path.slice("sources.".length));
1707
+ }
1937
1708
  return deepGet(node, path);
1938
1709
  }
1939
- function registerFunction(name, fn) {
1940
- _customFns[name] = fn;
1941
- }
1942
1710
  var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
1943
1711
  "metric",
1944
1712
  "table",
@@ -1955,26 +1723,17 @@ var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
1955
1723
  "markdown",
1956
1724
  "custom"
1957
1725
  ]);
1958
- var VALID_SOURCE_KINDS = /* @__PURE__ */ new Set(["api", "websocket", "static", "llm"]);
1959
1726
  var VALID_STATUSES = /* @__PURE__ */ new Set(["fresh", "stale", "loading", "error"]);
1960
- var CARD_ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "type", "meta", "data", "view", "state", "compute"]);
1961
- var SOURCE_ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "type", "meta", "data", "source", "state", "compute"]);
1727
+ var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "state", "compute", "sources"]);
1962
1728
  function validateNode(node) {
1963
1729
  const errors = [];
1964
1730
  if (!node || typeof node !== "object" || Array.isArray(node)) {
1965
1731
  return { ok: false, errors: ["Node must be a non-null object"] };
1966
1732
  }
1967
1733
  const n = node;
1968
- if (typeof n.id !== "string" || !n.id) {
1969
- errors.push("id: required, must be a non-empty string");
1970
- }
1971
- if (n.type !== "card" && n.type !== "source") {
1972
- errors.push('type: must be "card" or "source"');
1973
- return { ok: false, errors };
1974
- }
1975
- const allowed = n.type === "card" ? CARD_ALLOWED_KEYS : SOURCE_ALLOWED_KEYS;
1734
+ if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
1976
1735
  for (const key of Object.keys(n)) {
1977
- if (!allowed.has(key)) errors.push(`Unknown top-level key: "${key}"`);
1736
+ if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
1978
1737
  }
1979
1738
  if (n.state == null || typeof n.state !== "object" || Array.isArray(n.state)) {
1980
1739
  errors.push("state: required, must be an object");
@@ -1993,37 +1752,58 @@ function validateNode(node) {
1993
1752
  if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
1994
1753
  }
1995
1754
  }
1996
- if (n.data != null) {
1997
- if (typeof n.data !== "object" || Array.isArray(n.data)) {
1998
- errors.push("data: must be an object");
1755
+ if (n.requires != null && !Array.isArray(n.requires)) errors.push("requires: must be an array of strings");
1756
+ if (n.provides != null) {
1757
+ if (!Array.isArray(n.provides)) {
1758
+ errors.push("provides: must be an array of { bindTo, src } bindings");
1999
1759
  } else {
2000
- const data = n.data;
2001
- if (data.requires != null && !Array.isArray(data.requires)) errors.push("data.requires: must be an array of strings");
2002
- if (data.provides != null && (typeof data.provides !== "object" || Array.isArray(data.provides))) errors.push("data.provides: must be an object");
1760
+ n.provides.forEach((p, i) => {
1761
+ if (!p || typeof p !== "object" || Array.isArray(p)) {
1762
+ errors.push(`provides[${i}]: must be an object with bindTo and src`);
1763
+ } else {
1764
+ const b = p;
1765
+ if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
1766
+ if (typeof b.src !== "string" || !b.src) errors.push(`provides[${i}]: missing required "src" string`);
1767
+ }
1768
+ });
2003
1769
  }
2004
1770
  }
2005
1771
  if (n.compute != null) {
2006
- if (typeof n.compute !== "object" || Array.isArray(n.compute)) {
2007
- errors.push("compute: must be an object");
1772
+ if (!Array.isArray(n.compute)) {
1773
+ errors.push("compute: must be an array of compute steps");
1774
+ } else {
1775
+ n.compute.forEach((step, i) => {
1776
+ if (!step || typeof step !== "object" || Array.isArray(step)) {
1777
+ errors.push(`compute[${i}]: must be a compute step object`);
1778
+ } else {
1779
+ const s = step;
1780
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
1781
+ if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
1782
+ }
1783
+ });
1784
+ }
1785
+ }
1786
+ if (n.sources != null) {
1787
+ if (!Array.isArray(n.sources)) {
1788
+ errors.push("sources: must be an array");
2008
1789
  } else {
2009
- for (const [key, expr] of Object.entries(n.compute)) {
2010
- if (!expr || typeof expr !== "object" || Array.isArray(expr)) {
2011
- errors.push(`compute.${key}: must be a compute expression object`);
2012
- } else if (!expr.fn) {
2013
- errors.push(`compute.${key}: missing required "fn" property`);
1790
+ n.sources.forEach((src, i) => {
1791
+ if (!src || typeof src !== "object" || Array.isArray(src)) {
1792
+ errors.push(`sources[${i}]: must be an object`);
2014
1793
  } else {
2015
- const fn = expr.fn;
2016
- if (!_fns[fn] && !_customFns[fn]) {
2017
- errors.push(`compute.${key}: unknown function "${fn}"`);
1794
+ const s = src;
1795
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`sources[${i}]: missing required "bindTo" property`);
1796
+ if (s.outputFile != null && typeof s.outputFile !== "string") errors.push(`sources[${i}]: outputFile must be a string`);
1797
+ if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
1798
+ errors.push(`sources[${i}]: optionalForCompletionGating must be a boolean`);
2018
1799
  }
2019
1800
  }
2020
- }
1801
+ });
2021
1802
  }
2022
1803
  }
2023
- if (n.type === "card") {
2024
- if (n.source != null) errors.push('Card nodes must not have "source" \u2014 use type "source" instead');
2025
- if (n.view == null || typeof n.view !== "object" || Array.isArray(n.view)) {
2026
- errors.push("view: required for card nodes, must be an object");
1804
+ if (n.view != null) {
1805
+ if (typeof n.view !== "object" || Array.isArray(n.view)) {
1806
+ errors.push("view: must be an object");
2027
1807
  } else {
2028
1808
  const view = n.view;
2029
1809
  if (!Array.isArray(view.elements) || view.elements.length === 0) {
@@ -2044,28 +1824,8 @@ function validateNode(node) {
2044
1824
  }
2045
1825
  });
2046
1826
  }
2047
- if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) {
2048
- errors.push("view.layout: must be an object");
2049
- }
2050
- if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) {
2051
- errors.push("view.features: must be an object");
2052
- }
2053
- }
2054
- }
2055
- if (n.type === "source") {
2056
- if (n.view != null) errors.push('Source nodes must not have "view" \u2014 use type "card" instead');
2057
- if (n.source == null || typeof n.source !== "object" || Array.isArray(n.source)) {
2058
- errors.push("source: required for source nodes, must be an object");
2059
- } else {
2060
- const src = n.source;
2061
- if (!src.kind || !VALID_SOURCE_KINDS.has(src.kind)) {
2062
- errors.push(`source.kind: required, must be one of: ${[...VALID_SOURCE_KINDS].join(", ")}`);
2063
- }
2064
- if (typeof src.bindTo !== "string" || !src.bindTo) {
2065
- errors.push("source.bindTo: required, must be a state path string");
2066
- } else if (!src.bindTo.startsWith("state.")) {
2067
- errors.push('source.bindTo: must start with "state."');
2068
- }
1827
+ if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
1828
+ if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
2069
1829
  }
2070
1830
  }
2071
1831
  return { ok: errors.length === 0, errors };
@@ -2074,14 +1834,7 @@ var CardCompute = {
2074
1834
  run,
2075
1835
  eval: evalExpr,
2076
1836
  resolve,
2077
- validate: validateNode,
2078
- registerFunction,
2079
- get functions() {
2080
- const all = {};
2081
- for (const k of Object.keys(_fns)) all[k] = _fns[k];
2082
- for (const k of Object.keys(_customFns)) all[k] = _customFns[k];
2083
- return all;
2084
- }
1837
+ validate: validateNode
2085
1838
  };
2086
1839
 
2087
1840
  // src/continuous-event-graph/live-cards-bridge.ts
@@ -2114,19 +1867,26 @@ function liveCardsToReactiveGraph(input, options = {}) {
2114
1867
  }
2115
1868
  const sharedState = options.sharedState ?? /* @__PURE__ */ new Map();
2116
1869
  const tasks = {};
1870
+ const allTokens = /* @__PURE__ */ new Set();
1871
+ const tokenToCardId = /* @__PURE__ */ new Map();
1872
+ for (const card of cards) {
1873
+ for (const binding of card.provides ?? [{ bindTo: card.id, src: `state.${card.id}` }]) {
1874
+ allTokens.add(binding.bindTo);
1875
+ tokenToCardId.set(binding.bindTo, card.id);
1876
+ }
1877
+ }
2117
1878
  for (const card of cards) {
2118
- const requires = card.data?.requires ?? [];
1879
+ const requires = card.requires ?? [];
2119
1880
  for (const req of requires) {
2120
- if (!cardMap.has(req)) {
2121
- throw new Error(`Card "${card.id}" requires "${req}" but no card with that ID exists`);
1881
+ if (!allTokens.has(req)) {
1882
+ throw new Error(`Card "${card.id}" requires "${req}" but no card provides that token`);
2122
1883
  }
2123
1884
  }
2124
1885
  tasks[card.id] = {
2125
1886
  requires: requires.length > 0 ? requires : void 0,
2126
- provides: [card.id],
1887
+ provides: (card.provides ?? [{ bindTo: card.id, src: `state.${card.id}` }]).map((p) => p.bindTo),
2127
1888
  taskHandlers: [card.id],
2128
- // each card has a named handler matching its ID
2129
- description: card.meta?.title ?? `${card.type}: ${card.id}`
1889
+ description: card.meta?.title ?? card.id
2130
1890
  };
2131
1891
  }
2132
1892
  const config = {
@@ -2145,10 +1905,10 @@ function liveCardsToReactiveGraph(input, options = {}) {
2145
1905
  graphRef.resolveCallback(token, data, errors);
2146
1906
  };
2147
1907
  for (const card of cards) {
2148
- if (card.type === "source") {
1908
+ if (card.sources && card.sources.length > 0) {
2149
1909
  handlers[card.id] = buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve);
2150
1910
  } else {
2151
- handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve);
1911
+ handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, tokenToCardId, getResolve);
2152
1912
  }
2153
1913
  }
2154
1914
  const graph = createReactiveGraph(
@@ -2182,7 +1942,7 @@ function buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedSt
2182
1942
  return "task-initiated";
2183
1943
  };
2184
1944
  }
2185
- function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve) {
1945
+ function buildCardHandler(card, cardHandlers, sharedState, _cardMap, tokenToCardId, getResolve) {
2186
1946
  if (cardHandlers[card.id]) {
2187
1947
  const userHandler = cardHandlers[card.id];
2188
1948
  return async (input) => {
@@ -2190,48 +1950,38 @@ function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve)
2190
1950
  };
2191
1951
  }
2192
1952
  return async (input) => {
1953
+ const requiresData = {};
1954
+ const requires = card.requires ?? [];
1955
+ for (const token of requires) {
1956
+ const producerId = tokenToCardId.get(token) ?? token;
1957
+ const upstreamState = sharedState.get(producerId);
1958
+ if (upstreamState) {
1959
+ requiresData[token] = upstreamState[token] ?? upstreamState;
1960
+ }
1961
+ }
2193
1962
  const computeNode = {
2194
1963
  id: card.id,
2195
1964
  state: { ...card.state },
1965
+ requires: requiresData,
2196
1966
  compute: card.compute
2197
1967
  };
2198
- const requires = card.data?.requires ?? [];
2199
- for (const upstreamId of requires) {
2200
- const upstreamState = sharedState.get(upstreamId);
2201
- if (upstreamState) {
2202
- computeNode.state[upstreamId] = upstreamState;
2203
- }
2204
- const upstreamCard = cardMap.get(upstreamId);
2205
- if (upstreamCard?.data?.provides && upstreamState) {
2206
- for (const [key, bindRef] of Object.entries(upstreamCard.data.provides)) {
2207
- if (typeof bindRef === "string" && bindRef.startsWith("state.")) {
2208
- const path = bindRef.slice(6);
2209
- const value = deepGet2(upstreamState, path);
2210
- if (value !== void 0) {
2211
- computeNode.state[key] = value;
2212
- }
2213
- }
2214
- }
1968
+ await CardCompute.run(computeNode);
1969
+ let resultData;
1970
+ if (card.provides && card.provides.length > 0) {
1971
+ resultData = {};
1972
+ for (const { bindTo, src } of card.provides) {
1973
+ resultData[bindTo] = CardCompute.resolve(computeNode, src);
2215
1974
  }
1975
+ } else {
1976
+ resultData = { ...computeNode.state, ...computeNode.computed_values };
2216
1977
  }
2217
- CardCompute.run(computeNode);
2218
- const resultState = { ...computeNode.state };
1978
+ const resultState = { ...computeNode.state, ...computeNode.computed_values };
2219
1979
  sharedState.set(card.id, resultState);
2220
- getResolve()(input.callbackToken, resultState);
1980
+ getResolve()(input.callbackToken, resultData);
2221
1981
  return "task-initiated";
2222
1982
  };
2223
1983
  }
2224
- function deepGet2(obj, path) {
2225
- if (!path || !obj) return void 0;
2226
- const parts = path.split(".");
2227
- let cur = obj;
2228
- for (const part of parts) {
2229
- if (cur == null) return void 0;
2230
- cur = cur[part];
2231
- }
2232
- return cur;
2233
- }
2234
1984
 
2235
- export { FileJournal, MemoryJournal, addNode, addProvides, addRequires, applyEvent, applyEvents, computeDataHash, createCallbackHandler, createFireAndForgetHandler, createLiveGraph, createNoopHandler, createReactiveGraph, createScriptHandler, createShellHandler, createWebhookHandler, disableNode, drainTokens, enableNode, getDownstream, getNode, getUnreachableNodes, getUnreachableTokens, getUpstream, injectTokens, inspect, liveCardsToReactiveGraph, mutateGraph, removeNode, removeProvides, removeRequires, resetNode, restore, schedule, snapshot, validateLiveGraph, validateReactiveGraph };
1985
+ export { MemoryJournal, addNode, addProvides, addRequires, applyEvent, applyEvents, computeDataHash, createCallbackHandler, createFireAndForgetHandler, createLiveGraph, createNoopHandler, createReactiveGraph, createScriptHandler, createShellHandler, createWebhookHandler, disableNode, drainTokens, enableNode, getDownstream, getNode, getUnreachableNodes, getUnreachableTokens, getUpstream, injectTokens, inspect, liveCardsToReactiveGraph, mutateGraph, removeNode, removeProvides, removeRequires, resetNode, restore, schedule, snapshot, validateLiveGraph, validateReactiveGraph };
2236
1986
  //# sourceMappingURL=index.js.map
2237
1987
  //# sourceMappingURL=index.js.map