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
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import addFormats from 'ajv-formats';
2
- import { existsSync, writeFileSync, appendFileSync, readFileSync } from 'fs';
3
2
  import { createHash } from 'crypto';
4
3
  import { exec, execFile } from 'child_process';
4
+ import jsonata from 'jsonata';
5
5
 
6
6
  var __create = Object.create;
7
7
  var __defProp = Object.defineProperty;
@@ -6439,7 +6439,7 @@ function applyStepResult(flow, state, stepName, stepResult) {
6439
6439
  };
6440
6440
  }
6441
6441
  }
6442
- const nextStep = stepConfig.transitions[stepResult.result];
6442
+ const nextStep = stepConfig.failure_transitions?.[stepResult.result] ?? stepConfig.transitions[stepResult.result];
6443
6443
  if (!nextStep) {
6444
6444
  throw new Error(
6445
6445
  `No transition defined for result "${stepResult.result}" in step "${stepName}"`
@@ -6644,6 +6644,15 @@ var StepMachine = class {
6644
6644
  );
6645
6645
  }
6646
6646
  }
6647
+ if (stepConfig.failure_transitions) {
6648
+ for (const [result, target] of Object.entries(stepConfig.failure_transitions)) {
6649
+ if (!steps[target] && !terminal_states[target]) {
6650
+ throw new Error(
6651
+ `Step "${stepName}" failure_transition "${result}" points to unknown step "${target}"`
6652
+ );
6653
+ }
6654
+ }
6655
+ }
6647
6656
  }
6648
6657
  }
6649
6658
  on(eventType, listener) {
@@ -6873,6 +6882,9 @@ function validateStepFlowConfig(flow) {
6873
6882
  if (!step.transitions || typeof step.transitions !== "object") {
6874
6883
  errors.push(`Step "${stepName}" must have a "transitions" object`);
6875
6884
  }
6885
+ if (step.failure_transitions !== void 0 && typeof step.failure_transitions !== "object") {
6886
+ errors.push(`Step "${stepName}" failure_transitions must be an object when provided`);
6887
+ }
6876
6888
  }
6877
6889
  }
6878
6890
  if (!f.terminal_states || typeof f.terminal_states !== "object") {
@@ -6993,6 +7005,11 @@ var flow_schema_default = {
6993
7005
  additionalProperties: { type: "string" },
6994
7006
  minProperties: 1
6995
7007
  },
7008
+ failure_transitions: {
7009
+ type: "object",
7010
+ description: "Mapping of failure-like results (e.g. failure, timeout) -> next step name",
7011
+ additionalProperties: { type: "string" }
7012
+ },
6996
7013
  retry: {
6997
7014
  $ref: "#/definitions/retry_config"
6998
7015
  },
@@ -7931,8 +7948,8 @@ function apply(state, event, graph) {
7931
7948
  return applyInjectTokens(state, event.tokens);
7932
7949
  case "agent-action":
7933
7950
  return applyAgentAction(state, event.action, graph, event.config);
7934
- case "task-creation":
7935
- return applyTaskCreation(state, event.taskName, event.taskConfig);
7951
+ case "task-upsert":
7952
+ return applyTaskUpsert(state, event.taskName, event.taskConfig);
7936
7953
  default:
7937
7954
  return state;
7938
7955
  }
@@ -7989,7 +8006,7 @@ function applyAgentAction(state, action, graph, config) {
7989
8006
  return state;
7990
8007
  }
7991
8008
  }
7992
- function applyTaskCreation(state, taskName, taskConfig) {
8009
+ function applyTaskUpsert(state, taskName, taskConfig) {
7993
8010
  if (!taskName || !taskConfig || !Array.isArray(taskConfig.provides)) {
7994
8011
  return state;
7995
8012
  }
@@ -7997,7 +8014,7 @@ function applyTaskCreation(state, taskName, taskConfig) {
7997
8014
  ...state,
7998
8015
  tasks: {
7999
8016
  ...state.tasks,
8000
- [taskName]: createDefaultGraphEngineStore()
8017
+ [taskName]: state.tasks[taskName] ?? createDefaultGraphEngineStore()
8001
8018
  },
8002
8019
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
8003
8020
  };
@@ -9269,43 +9286,51 @@ function applyEvent(live, event) {
9269
9286
  if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
9270
9287
  return live;
9271
9288
  }
9272
- let newState;
9273
9289
  switch (event.type) {
9290
+ // --- Execution state transitions ---
9274
9291
  case "task-started":
9275
- newState = applyTaskStart(state, event.taskName);
9276
- break;
9292
+ return { config, state: applyTaskStart(state, event.taskName) };
9277
9293
  case "task-completed":
9278
- newState = applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data);
9279
- break;
9294
+ return { config, state: applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data) };
9280
9295
  case "task-failed":
9281
- newState = applyTaskFailure(state, config, event.taskName, event.error);
9282
- break;
9296
+ return { config, state: applyTaskFailure(state, config, event.taskName, event.error) };
9283
9297
  case "task-progress":
9284
- newState = applyTaskProgress(state, event.taskName, event.message, event.progress);
9285
- break;
9298
+ return { config, state: applyTaskProgress(state, event.taskName, event.message, event.progress) };
9286
9299
  case "task-restart":
9287
- newState = applyTaskRestart(state, event.taskName);
9288
- break;
9300
+ return { config, state: applyTaskRestart(state, event.taskName) };
9289
9301
  case "inject-tokens":
9290
- newState = {
9291
- ...state,
9292
- availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
9293
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
9302
+ return {
9303
+ config,
9304
+ state: {
9305
+ ...state,
9306
+ availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
9307
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
9308
+ }
9294
9309
  };
9295
- break;
9296
9310
  case "agent-action":
9297
- newState = applyAgentAction2(state, event.action);
9298
- break;
9311
+ return { config, state: applyAgentAction2(state, event.action) };
9312
+ // --- Structural mutations ---
9313
+ case "task-upsert":
9314
+ return addNode(live, event.taskName, event.taskConfig);
9315
+ case "task-removal":
9316
+ return removeNode(live, event.taskName);
9317
+ case "node-requires-add":
9318
+ return addRequires(live, event.nodeName, event.tokens);
9319
+ case "node-requires-remove":
9320
+ return removeRequires(live, event.nodeName, event.tokens);
9321
+ case "node-provides-add":
9322
+ return addProvides(live, event.nodeName, event.tokens);
9323
+ case "node-provides-remove":
9324
+ return removeProvides(live, event.nodeName, event.tokens);
9299
9325
  default:
9300
9326
  return live;
9301
9327
  }
9302
- return { config, state: newState };
9303
9328
  }
9304
9329
  function applyEvents(live, events) {
9305
9330
  return events.reduce((current, event) => applyEvent(current, event), live);
9306
9331
  }
9307
9332
  function addNode(live, name, taskConfig) {
9308
- if (live.config.tasks[name]) return live;
9333
+ const exists = !!live.config.tasks[name];
9309
9334
  return {
9310
9335
  config: {
9311
9336
  ...live.config,
@@ -9313,7 +9338,10 @@ function addNode(live, name, taskConfig) {
9313
9338
  },
9314
9339
  state: {
9315
9340
  ...live.state,
9316
- tasks: { ...live.state.tasks, [name]: createDefaultGraphEngineStore3() },
9341
+ tasks: {
9342
+ ...live.state.tasks,
9343
+ [name]: exists ? live.state.tasks[name] : createDefaultGraphEngineStore3()
9344
+ },
9317
9345
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
9318
9346
  }
9319
9347
  };
@@ -10020,6 +10048,8 @@ function getDownstream(live, nodeName) {
10020
10048
  }));
10021
10049
  return { nodeName, nodes, tokens: [...tokenSet] };
10022
10050
  }
10051
+
10052
+ // src/continuous-event-graph/journal.ts
10023
10053
  var MemoryJournal = class {
10024
10054
  buffer = [];
10025
10055
  append(event) {
@@ -10034,36 +10064,6 @@ var MemoryJournal = class {
10034
10064
  return this.buffer.length;
10035
10065
  }
10036
10066
  };
10037
- var FileJournal = class {
10038
- constructor(path) {
10039
- this.path = path;
10040
- if (!existsSync(path)) {
10041
- writeFileSync(path, "", "utf-8");
10042
- }
10043
- }
10044
- path;
10045
- pending = 0;
10046
- append(event) {
10047
- appendFileSync(this.path, JSON.stringify(event) + "\n", "utf-8");
10048
- this.pending++;
10049
- }
10050
- drain() {
10051
- const content = readFileSync(this.path, "utf-8").trim();
10052
- writeFileSync(this.path, "", "utf-8");
10053
- this.pending = 0;
10054
- if (!content) return [];
10055
- return content.split("\n").map((line) => JSON.parse(line));
10056
- }
10057
- get size() {
10058
- try {
10059
- const content = readFileSync(this.path, "utf-8").trim();
10060
- if (!content) return 0;
10061
- return content.split("\n").length;
10062
- } catch {
10063
- return this.pending;
10064
- }
10065
- }
10066
- };
10067
10067
  function computeDataHash(data) {
10068
10068
  const json = stableStringify(data);
10069
10069
  return createHash("sha256").update(json).digest("hex").slice(0, 16);
@@ -10092,15 +10092,16 @@ function decodeCallbackToken(token) {
10092
10092
  return null;
10093
10093
  }
10094
10094
  }
10095
- function createReactiveGraph(config, options, executionId) {
10095
+ function createReactiveGraph(configOrLive, options, executionId) {
10096
10096
  const {
10097
10097
  handlers: initialHandlers,
10098
- journal = new MemoryJournal(),
10099
10098
  onDrain
10100
10099
  } = options;
10101
- let live = createLiveGraph(config, executionId);
10100
+ const inputQueue = new MemoryJournal();
10101
+ let live = "state" in configOrLive && "config" in configOrLive ? configOrLive : createLiveGraph(configOrLive, executionId);
10102
10102
  let disposed = false;
10103
10103
  const handlers = new Map(Object.entries(initialHandlers));
10104
+ const internalJournal = new MemoryJournal();
10104
10105
  let draining = false;
10105
10106
  let drainQueued = false;
10106
10107
  function drain() {
@@ -10120,7 +10121,9 @@ function createReactiveGraph(config, options, executionId) {
10120
10121
  }
10121
10122
  }
10122
10123
  function drainOnce() {
10123
- const events = journal.drain();
10124
+ const internalEvents = internalJournal.drain();
10125
+ const inputEvents = inputQueue.drain();
10126
+ const events = [...internalEvents, ...inputEvents];
10124
10127
  if (events.length > 0) {
10125
10128
  live = applyEvents(live, events);
10126
10129
  }
@@ -10131,6 +10134,26 @@ function createReactiveGraph(config, options, executionId) {
10131
10134
  for (const taskName of result.eligible) {
10132
10135
  dispatchTask(taskName);
10133
10136
  }
10137
+ for (const event of events) {
10138
+ if (event.type === "task-progress") {
10139
+ const { taskName, update } = event;
10140
+ const taskConfig = live.config.tasks[taskName];
10141
+ if (!taskConfig) continue;
10142
+ const taskState = live.state.tasks[taskName];
10143
+ if (!taskState || taskState.status !== "running") continue;
10144
+ const callbackToken = encodeCallbackToken(taskName);
10145
+ runPipeline(taskName, callbackToken, update).catch((error) => {
10146
+ if (disposed) return;
10147
+ internalJournal.append({
10148
+ type: "task-failed",
10149
+ taskName,
10150
+ error: error.message ?? String(error),
10151
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
10152
+ });
10153
+ drain();
10154
+ });
10155
+ }
10156
+ }
10134
10157
  }
10135
10158
  function resolveUpstreamState(taskName) {
10136
10159
  const taskConfig = live.config.tasks[taskName];
@@ -10152,7 +10175,7 @@ function createReactiveGraph(config, options, executionId) {
10152
10175
  }
10153
10176
  return state;
10154
10177
  }
10155
- async function runPipeline(taskName, callbackToken) {
10178
+ async function runPipeline(taskName, callbackToken, update) {
10156
10179
  const taskConfig = live.config.tasks[taskName];
10157
10180
  const handlerNames = taskConfig.taskHandlers ?? [];
10158
10181
  const upstreamState = resolveUpstreamState(taskName);
@@ -10166,7 +10189,8 @@ function createReactiveGraph(config, options, executionId) {
10166
10189
  state: upstreamState,
10167
10190
  taskState: live.state.tasks[taskName],
10168
10191
  config: taskConfig,
10169
- callbackToken
10192
+ callbackToken,
10193
+ update
10170
10194
  };
10171
10195
  const status = await handler(input);
10172
10196
  if (status === "task-initiate-failure") {
@@ -10180,15 +10204,16 @@ function createReactiveGraph(config, options, executionId) {
10180
10204
  if (!handlerNames || handlerNames.length === 0) {
10181
10205
  return;
10182
10206
  }
10183
- journal.append({
10207
+ internalJournal.append({
10184
10208
  type: "task-started",
10185
10209
  taskName,
10186
10210
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10187
10211
  });
10212
+ drain();
10188
10213
  const callbackToken = encodeCallbackToken(taskName);
10189
10214
  runPipeline(taskName, callbackToken).catch((error) => {
10190
10215
  if (disposed) return;
10191
- journal.append({
10216
+ internalJournal.append({
10192
10217
  type: "task-failed",
10193
10218
  taskName,
10194
10219
  error: error.message ?? String(error),
@@ -10203,16 +10228,16 @@ function createReactiveGraph(config, options, executionId) {
10203
10228
  if (event.type === "task-completed" && event.data && !event.dataHash) {
10204
10229
  event = { ...event, dataHash: computeDataHash(event.data) };
10205
10230
  }
10206
- journal.append(event);
10231
+ inputQueue.append(event);
10207
10232
  drain();
10208
10233
  },
10209
10234
  pushAll(events) {
10210
10235
  if (disposed) return;
10211
10236
  for (const event of events) {
10212
10237
  if (event.type === "task-completed" && event.data && !event.dataHash) {
10213
- journal.append({ ...event, dataHash: computeDataHash(event.data) });
10238
+ inputQueue.append({ ...event, dataHash: computeDataHash(event.data) });
10214
10239
  } else {
10215
- journal.append(event);
10240
+ inputQueue.append(event);
10216
10241
  }
10217
10242
  }
10218
10243
  drain();
@@ -10224,7 +10249,7 @@ function createReactiveGraph(config, options, executionId) {
10224
10249
  const { taskName } = decoded;
10225
10250
  if (!live.config.tasks[taskName]) return;
10226
10251
  if (errors && errors.length > 0) {
10227
- journal.append({
10252
+ inputQueue.append({
10228
10253
  type: "task-failed",
10229
10254
  taskName,
10230
10255
  error: errors.join("; "),
@@ -10232,7 +10257,7 @@ function createReactiveGraph(config, options, executionId) {
10232
10257
  });
10233
10258
  } else {
10234
10259
  const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
10235
- journal.append({
10260
+ inputQueue.append({
10236
10261
  type: "task-completed",
10237
10262
  taskName,
10238
10263
  data,
@@ -10244,31 +10269,33 @@ function createReactiveGraph(config, options, executionId) {
10244
10269
  },
10245
10270
  addNode(name, taskConfig) {
10246
10271
  if (disposed) return;
10247
- live = addNode(live, name, taskConfig);
10272
+ inputQueue.append({ type: "task-upsert", taskName: name, taskConfig, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10248
10273
  drain();
10249
10274
  },
10250
10275
  removeNode(name) {
10251
10276
  if (disposed) return;
10252
- live = removeNode(live, name);
10277
+ inputQueue.append({ type: "task-removal", taskName: name, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10278
+ drain();
10253
10279
  },
10254
10280
  addRequires(nodeName, tokens) {
10255
10281
  if (disposed) return;
10256
- live = addRequires(live, nodeName, tokens);
10282
+ inputQueue.append({ type: "node-requires-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10257
10283
  drain();
10258
10284
  },
10259
10285
  removeRequires(nodeName, tokens) {
10260
10286
  if (disposed) return;
10261
- live = removeRequires(live, nodeName, tokens);
10287
+ inputQueue.append({ type: "node-requires-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10262
10288
  drain();
10263
10289
  },
10264
10290
  addProvides(nodeName, tokens) {
10265
10291
  if (disposed) return;
10266
- live = addProvides(live, nodeName, tokens);
10292
+ inputQueue.append({ type: "node-provides-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10267
10293
  drain();
10268
10294
  },
10269
10295
  removeProvides(nodeName, tokens) {
10270
10296
  if (disposed) return;
10271
- live = removeProvides(live, nodeName, tokens);
10297
+ inputQueue.append({ type: "node-provides-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10298
+ drain();
10272
10299
  },
10273
10300
  registerHandler(name, fn) {
10274
10301
  handlers.set(name, fn);
@@ -10279,7 +10306,7 @@ function createReactiveGraph(config, options, executionId) {
10279
10306
  retrigger(taskName) {
10280
10307
  if (disposed) return;
10281
10308
  if (!live.config.tasks[taskName]) return;
10282
- journal.append({
10309
+ inputQueue.append({
10283
10310
  type: "task-restart",
10284
10311
  taskName,
10285
10312
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -10290,7 +10317,7 @@ function createReactiveGraph(config, options, executionId) {
10290
10317
  if (disposed) return;
10291
10318
  for (const name of taskNames) {
10292
10319
  if (!live.config.tasks[name]) continue;
10293
- journal.append({
10320
+ inputQueue.append({
10294
10321
  type: "task-restart",
10295
10322
  taskName: name,
10296
10323
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -10298,6 +10325,9 @@ function createReactiveGraph(config, options, executionId) {
10298
10325
  }
10299
10326
  drain();
10300
10327
  },
10328
+ snapshot() {
10329
+ return snapshot(live);
10330
+ },
10301
10331
  getState() {
10302
10332
  return live;
10303
10333
  },
@@ -10669,9 +10699,9 @@ var live_cards_schema_default = {
10669
10699
  description: "Schema for Card and ExternalSource nodes in the LiveCards Board/Canvas engine",
10670
10700
  definitions: {
10671
10701
  bind_ref: {
10672
- description: "A state path reference, e.g. 'state.raw_quotes' or 'state.compute_vars.total'",
10702
+ description: "A state path reference, e.g. 'state.raw_quotes' or 'requires.upstream'",
10673
10703
  type: "string",
10674
- pattern: "^state\\."
10704
+ pattern: "^(state|requires|computed_values)\\."
10675
10705
  },
10676
10706
  bind_or_literal: {
10677
10707
  description: "A literal value or a bind reference object",
@@ -10764,38 +10794,43 @@ var live_cards_schema_default = {
10764
10794
  tags: { type: "array", items: { type: "string" } }
10765
10795
  }
10766
10796
  },
10767
- data: {
10797
+ requires: {
10798
+ type: "array",
10799
+ items: { type: "string" },
10800
+ description: "IDs of upstream nodes this node depends on"
10801
+ },
10802
+ provides: {
10803
+ type: "array",
10804
+ items: {
10805
+ type: "object",
10806
+ required: ["bindTo", "src"],
10807
+ properties: {
10808
+ bindTo: { type: "string", description: "Token name published downstream" },
10809
+ src: { type: "string", description: "Path to read value from (state.*, requires.*, computed_values.*)" }
10810
+ }
10811
+ },
10812
+ description: "Explicit bindings exposing computed/state values downstream as named tokens"
10813
+ },
10814
+ compute_step: {
10815
+ description: "A single ordered compute step: reads state.*/requires.*/computed_values.*, writes to computed_values[bindTo]",
10768
10816
  type: "object",
10817
+ required: ["bindTo", "expr"],
10769
10818
  properties: {
10770
- requires: {
10771
- type: "array",
10772
- items: { type: "string" },
10773
- description: "IDs of upstream nodes this node depends on"
10774
- },
10775
- provides: {
10776
- type: "object",
10777
- description: "Subset of state exposed downstream. Keys are published names, values are bind refs.",
10778
- additionalProperties: { $ref: "#/definitions/bind_or_literal" }
10779
- }
10819
+ bindTo: { type: "string", description: "Key in computed_values to write result" },
10820
+ expr: { type: "string", description: "JSONata expression evaluated against { state, requires, sources, computed_values }" }
10780
10821
  }
10781
10822
  },
10782
10823
  source_def: {
10824
+ description: "One source entry. The engine only cares about 'bindTo' (compute namespace key) and 'outputFile' (delivery signal). Every other property is yours \u2014 add whatever your task-executor needs: kind, url, headers, mailbox, channel, model, query, etc. The full object is passed verbatim as the --in JSON to the executor.",
10783
10825
  type: "object",
10784
- required: ["kind", "bindTo"],
10826
+ required: ["bindTo"],
10827
+ additionalProperties: true,
10785
10828
  properties: {
10786
- kind: { enum: ["api", "websocket", "static", "llm"] },
10787
- bindTo: { $ref: "#/definitions/bind_ref", description: "state path to write fetched data into" },
10788
- method: { enum: ["GET", "POST", "PUT", "DELETE"], default: "GET" },
10789
- url_template: { type: "string", description: "URL with {{var}} placeholders" },
10790
- headers: { type: "object", additionalProperties: { $ref: "#/definitions/bind_or_literal" } },
10791
- body_template: { type: "object", description: "Request body with {{var}} placeholders or bind refs" },
10792
- template_vars: {
10793
- type: "object",
10794
- additionalProperties: { $ref: "#/definitions/bind_or_literal" },
10795
- description: "Variables for url/body templates \u2014 static values or bind refs"
10796
- },
10797
- poll_interval: { type: "integer", minimum: 0, description: "Auto-refresh in seconds (0 = manual)" },
10798
- transform: { type: "string", description: "Dot-path to extract from response, e.g. 'data.items'" }
10829
+ bindTo: { type: "string", description: "Key under sources.* available in compute expressions" },
10830
+ outputFile: { type: "string", description: "Board-relative path the executor writes its JSON result to. Presence of this file signals delivery." },
10831
+ optionalForCompletionGating: { type: "boolean", default: false, description: "When true this source does not gate card completion. Default false when absent, so sources are completion-gating by default." },
10832
+ timeout: { type: "integer", minimum: 0, default: 12e4, description: "Executor/script timeout in ms. Default: 120 000 (2 min)." },
10833
+ script: { type: "string", description: "Legacy direct-run: shell command executed when no .task-executor is registered. stdout is captured as the result." }
10799
10834
  }
10800
10835
  },
10801
10836
  render_element: {
@@ -10913,64 +10948,37 @@ var live_cards_schema_default = {
10913
10948
  }
10914
10949
  }
10915
10950
  },
10916
- oneOf: [
10917
- {
10918
- title: "Card",
10919
- description: "A renderable card node with view elements",
10951
+ title: "LiveCard",
10952
+ description: "A unified card node. Behavior depends on which sections are present (sources, compute, view, etc.)",
10953
+ type: "object",
10954
+ required: ["id"],
10955
+ additionalProperties: false,
10956
+ properties: {
10957
+ id: { type: "string" },
10958
+ requires: { $ref: "#/definitions/requires" },
10959
+ provides: { $ref: "#/definitions/provides" },
10960
+ meta: { $ref: "#/definitions/meta" },
10961
+ view: { $ref: "#/definitions/view" },
10962
+ state: {
10920
10963
  type: "object",
10921
- required: ["id", "type", "view", "state"],
10922
- additionalProperties: false,
10964
+ additionalProperties: true,
10923
10965
  properties: {
10924
- id: { type: "string" },
10925
- type: { const: "card" },
10926
- meta: { $ref: "#/definitions/meta" },
10927
- data: { $ref: "#/definitions/data" },
10928
- view: { $ref: "#/definitions/view" },
10929
- state: {
10930
- type: "object",
10931
- additionalProperties: true,
10932
- properties: {
10933
- status: { enum: ["fresh", "stale", "loading", "error"] },
10934
- lastRun: { type: "string", format: "date-time" },
10935
- error: { type: "string" }
10936
- }
10937
- },
10938
- compute: {
10939
- type: "object",
10940
- description: "Derived state: key = state path to write, value = compute_expr",
10941
- additionalProperties: { $ref: "#/definitions/compute_expr" }
10942
- }
10966
+ status: { enum: ["fresh", "stale", "loading", "error"] },
10967
+ lastRun: { type: "string", format: "date-time" },
10968
+ error: { type: "string" }
10943
10969
  }
10944
10970
  },
10945
- {
10946
- title: "ExternalSource",
10947
- description: "A data-only node that fetches from external systems (no view)",
10948
- type: "object",
10949
- required: ["id", "type", "source", "state"],
10950
- additionalProperties: false,
10951
- properties: {
10952
- id: { type: "string" },
10953
- type: { const: "source" },
10954
- meta: { $ref: "#/definitions/meta" },
10955
- data: { $ref: "#/definitions/data" },
10956
- source: { $ref: "#/definitions/source_def" },
10957
- state: {
10958
- type: "object",
10959
- additionalProperties: true,
10960
- properties: {
10961
- status: { enum: ["fresh", "stale", "loading", "error"] },
10962
- lastRun: { type: "string", format: "date-time" },
10963
- error: { type: "string" }
10964
- }
10965
- },
10966
- compute: {
10967
- type: "object",
10968
- description: "Derived state: key = state path to write, value = compute_expr",
10969
- additionalProperties: { $ref: "#/definitions/compute_expr" }
10970
- }
10971
- }
10971
+ sources: {
10972
+ type: "array",
10973
+ description: "Source entries. Each entry is passed verbatim to the board's .task-executor (registered via init --task-executor) as the --in JSON file. The executor fetches/generates the data and writes JSON to --out. If no executor is registered, the built-in executor runs the entry's 'cli' command directly. Sources gate completion by default. Set optionalForCompletionGating: true for enrichment-only sources that should not block task-completed.",
10974
+ items: { $ref: "#/definitions/source_def" }
10975
+ },
10976
+ compute: {
10977
+ type: "array",
10978
+ description: "Ordered array of compute steps. Each reads state.*/requires.*/sources.*/computed_values.* and writes to ephemeral computed_values[bindTo].",
10979
+ items: { $ref: "#/definitions/compute_step" }
10972
10980
  }
10973
- ]
10981
+ }
10974
10982
  };
10975
10983
 
10976
10984
  // src/card-compute/schema-validator.ts
@@ -11014,288 +11022,43 @@ function deepSet(obj, path, value) {
11014
11022
  }
11015
11023
  cur[parts[parts.length - 1]] = value;
11016
11024
  }
11017
- var _fns = {};
11018
- _fns.sum = (input, _e, opts) => {
11019
- const a = Array.isArray(input) ? input : [];
11020
- return opts.field ? a.reduce((s, r) => s + (Number(r[opts.field]) || 0), 0) : a.reduce((s, v) => s + (Number(v) || 0), 0);
11021
- };
11022
- _fns.avg = (input, _e, opts) => {
11023
- const s = _fns.sum(input, _e, opts);
11024
- const n = Array.isArray(input) ? input.length : 1;
11025
- return n ? s / n : 0;
11026
- };
11027
- _fns.min = (input, _e, opts) => {
11028
- const a = Array.isArray(input) ? input : [];
11029
- const vals = opts.field ? a.map((r) => Number(r[opts.field])) : a.map(Number);
11030
- return vals.length ? Math.min(...vals) : 0;
11031
- };
11032
- _fns.max = (input, _e, opts) => {
11033
- const a = Array.isArray(input) ? input : [];
11034
- const vals = opts.field ? a.map((r) => Number(r[opts.field])) : a.map(Number);
11035
- return vals.length ? Math.max(...vals) : 0;
11036
- };
11037
- _fns.count = (input) => Array.isArray(input) ? input.length : input != null ? 1 : 0;
11038
- _fns.first = (input) => Array.isArray(input) ? input[0] : input;
11039
- _fns.last = (input) => Array.isArray(input) ? input[input.length - 1] : input;
11040
- _fns.add = (input) => {
11041
- const a = Array.isArray(input) ? input : [];
11042
- return a.reduce((s, v) => s + Number(v), 0);
11043
- };
11044
- _fns.sub = (input) => {
11045
- const a = Array.isArray(input) ? input : [];
11046
- return a.length >= 2 ? Number(a[0]) - Number(a[1]) : 0;
11047
- };
11048
- _fns.mul = (input) => {
11049
- const a = Array.isArray(input) ? input : [];
11050
- return a.reduce((s, v) => s * Number(v), 1);
11051
- };
11052
- _fns.div = (input) => {
11053
- const a = Array.isArray(input) ? input : [];
11054
- return a.length >= 2 && Number(a[1]) !== 0 ? Number(a[0]) / Number(a[1]) : 0;
11055
- };
11056
- _fns.round = (input, _e, opts) => {
11057
- const decimals = opts.decimals != null ? opts.decimals : 0;
11058
- const factor = Math.pow(10, decimals);
11059
- return Math.round(Number(input) * factor) / factor;
11060
- };
11061
- _fns.abs = (input) => Math.abs(Number(input));
11062
- _fns.mod = (input) => {
11063
- const a = Array.isArray(input) ? input : [];
11064
- return a.length >= 2 ? Number(a[0]) % Number(a[1]) : 0;
11065
- };
11066
- _fns.gt = (input) => {
11067
- const a = Array.isArray(input) ? input : [];
11068
- return a.length >= 2 && Number(a[0]) > Number(a[1]);
11069
- };
11070
- _fns.gte = (input) => {
11071
- const a = Array.isArray(input) ? input : [];
11072
- return a.length >= 2 && Number(a[0]) >= Number(a[1]);
11073
- };
11074
- _fns.lt = (input) => {
11075
- const a = Array.isArray(input) ? input : [];
11076
- return a.length >= 2 && Number(a[0]) < Number(a[1]);
11077
- };
11078
- _fns.lte = (input) => {
11079
- const a = Array.isArray(input) ? input : [];
11080
- return a.length >= 2 && Number(a[0]) <= Number(a[1]);
11081
- };
11082
- _fns.eq = (input) => {
11083
- const a = Array.isArray(input) ? input : [];
11084
- return a.length >= 2 && a[0] === a[1];
11085
- };
11086
- _fns.neq = (input) => {
11087
- const a = Array.isArray(input) ? input : [];
11088
- return a.length >= 2 && a[0] !== a[1];
11089
- };
11090
- _fns.and = (input) => {
11091
- const a = Array.isArray(input) ? input : [];
11092
- return a.every(Boolean);
11093
- };
11094
- _fns.or = (input) => {
11095
- const a = Array.isArray(input) ? input : [];
11096
- return a.some(Boolean);
11097
- };
11098
- _fns.not = (input) => !input;
11099
- _fns.concat = (input) => {
11100
- const a = Array.isArray(input) ? input : [];
11101
- return a.map((v) => v != null ? String(v) : "").join("");
11102
- };
11103
- _fns.upper = (input) => String(input || "").toUpperCase();
11104
- _fns.lower = (input) => String(input || "").toLowerCase();
11105
- _fns.template = (input, _e, opts) => {
11106
- let t = String(opts.format || "");
11107
- if (input && typeof input === "object" && !Array.isArray(input)) {
11108
- for (const k of Object.keys(input)) {
11109
- const v = input[k];
11110
- t = t.split("{{" + k + "}}").join(v != null ? String(v) : "");
11111
- }
11112
- }
11113
- return t;
11114
- };
11115
- _fns.join = (input, _e, opts) => {
11116
- const a = Array.isArray(input) ? input : [];
11117
- const sep = opts.separator != null ? String(opts.separator) : ", ";
11118
- return a.map((v) => v != null ? String(v) : "").join(sep);
11119
- };
11120
- _fns.split = (input, _e, opts) => {
11121
- const sep = opts.separator != null ? String(opts.separator) : ",";
11122
- return String(input || "").split(sep).map((s) => s.trim());
11123
- };
11124
- _fns.trim = (input) => String(input || "").trim();
11125
- _fns.pluck = (input, _e, opts) => Array.isArray(input) ? input.map((r) => r[opts.field]) : [];
11126
- _fns.filter = (input, _e, opts) => {
11127
- if (!Array.isArray(input)) return [];
11128
- if (opts.field) return input.filter((r) => !!r[opts.field]);
11129
- return input.filter(Boolean);
11130
- };
11131
- _fns.map = (input) => Array.isArray(input) ? input.slice() : [];
11132
- _fns.sort = (input, _e, opts) => {
11133
- const a = Array.isArray(input) ? input.slice() : [];
11134
- const f = opts.field;
11135
- const dir = opts.direction === "desc" ? -1 : 1;
11136
- if (f) return a.sort((x, y) => x[f] > y[f] ? dir : x[f] < y[f] ? -dir : 0);
11137
- return a.sort((x, y) => x > y ? dir : x < y ? -dir : 0);
11138
- };
11139
- _fns.slice = (input, _e, opts) => Array.isArray(input) ? input.slice(opts.start || 0, opts.end) : input;
11140
- _fns.flat = (input, _e, opts) => {
11141
- const depth = opts.depth != null ? opts.depth : 1;
11142
- return Array.isArray(input) ? input.flat(depth) : [input];
11143
- };
11144
- _fns.unique = (input) => {
11145
- if (!Array.isArray(input)) return [input];
11146
- const seen = /* @__PURE__ */ new Set();
11147
- return input.filter((v) => {
11148
- const key = typeof v === "object" ? JSON.stringify(v) : v;
11149
- if (seen.has(key)) return false;
11150
- seen.add(key);
11151
- return true;
11152
- });
11153
- };
11154
- _fns.group = (input, _e, opts) => {
11155
- const a = Array.isArray(input) ? input : [];
11156
- const g = {};
11157
- a.forEach((r) => {
11158
- const k = String(r[opts.field] || "");
11159
- if (!g[k]) g[k] = [];
11160
- g[k].push(r);
11161
- });
11162
- return g;
11163
- };
11164
- _fns.flatten_keys = (input) => {
11165
- if (!input || typeof input !== "object" || Array.isArray(input)) return [];
11166
- const result = [];
11167
- for (const k of Object.keys(input)) {
11168
- const vals = Array.isArray(input[k]) ? input[k] : [input[k]];
11169
- vals.forEach((v) => result.push({ key: k, value: v }));
11170
- }
11171
- return result;
11172
- };
11173
- _fns.entries = (input) => {
11174
- if (!input || typeof input !== "object" || Array.isArray(input)) return [];
11175
- return Object.keys(input).map((k) => ({ key: k, value: input[k] }));
11176
- };
11177
- _fns.from_entries = (input) => {
11178
- if (!Array.isArray(input)) return {};
11179
- const obj = {};
11180
- input.forEach((item) => {
11181
- if (item.key != null) obj[item.key] = item.value;
11182
- });
11183
- return obj;
11184
- };
11185
- _fns.length = (input) => {
11186
- if (Array.isArray(input)) return input.length;
11187
- if (typeof input === "string") return input.length;
11188
- if (input && typeof input === "object") return Object.keys(input).length;
11189
- return 0;
11190
- };
11191
- _fns.get = (input, _e, opts) => deepGet(input, opts.field || opts.path || "");
11192
- _fns.default = (input, _e, opts) => input != null ? input : opts.value;
11193
- _fns.coalesce = (input) => {
11194
- const a = Array.isArray(input) ? input : [];
11195
- for (let i = 0; i < a.length; i++) {
11196
- if (a[i] != null) return a[i];
11197
- }
11198
- return null;
11199
- };
11200
- _fns.now = () => (/* @__PURE__ */ new Date()).toISOString();
11201
- _fns.diff_days = (input) => {
11202
- const a = Array.isArray(input) ? input : [];
11203
- return a.length >= 2 ? Math.floor((new Date(a[0]).getTime() - new Date(a[1]).getTime()) / 864e5) : 0;
11204
- };
11205
- _fns.format_date = (input, _e, opts) => {
11206
- try {
11207
- const d = new Date(input);
11208
- if (opts.format === "iso") return d.toISOString();
11209
- if (opts.format === "date") return d.toLocaleDateString();
11210
- if (opts.format === "time") return d.toLocaleTimeString();
11211
- return d.toLocaleDateString();
11212
- } catch {
11213
- return String(input);
11214
- }
11215
- };
11216
- _fns.parse_date = (input) => {
11217
- try {
11218
- return new Date(input).toISOString();
11219
- } catch {
11220
- return null;
11221
- }
11222
- };
11223
- _fns.to_number = (input) => Number(input) || 0;
11224
- _fns.to_string = (input) => input != null ? String(input) : "";
11225
- _fns.to_bool = (input) => !!input;
11226
- _fns.type_of = (input) => Array.isArray(input) ? "array" : typeof input;
11227
- _fns.is_null = (input) => input == null;
11228
- _fns.is_empty = (input) => {
11229
- if (input == null) return true;
11230
- if (Array.isArray(input)) return input.length === 0;
11231
- if (typeof input === "string") return input.length === 0;
11232
- if (typeof input === "object") return Object.keys(input).length === 0;
11233
- return false;
11234
- };
11235
- var _customFns = {};
11236
- function evalExpr(expr, node) {
11237
- if (expr == null) return expr;
11238
- if (typeof expr !== "object" || Array.isArray(expr)) return expr;
11239
- const e = expr;
11240
- if (!e.fn) return expr;
11241
- let input = e.input;
11242
- if (typeof input === "string" && input.startsWith("state.")) {
11243
- input = deepGet(node, input);
11244
- } else if (Array.isArray(input)) {
11245
- input = input.map((v) => {
11246
- if (typeof v === "string" && v.startsWith("state.")) return deepGet(node, v);
11247
- if (v && typeof v === "object" && v.fn) return evalExpr(v, node);
11248
- return v;
11249
- });
11250
- } else if (input && typeof input === "object" && input.fn) {
11251
- input = evalExpr(input, node);
11252
- }
11253
- if (e.fn === "if") {
11254
- const cond = evalExpr(e.cond, node);
11255
- if (cond) {
11256
- return e.then && typeof e.then === "object" && e.then.fn ? evalExpr(e.then, node) : e.then;
11257
- } else {
11258
- return e.else && typeof e.else === "object" && e.else.fn ? evalExpr(e.else, node) : e.else;
11259
- }
11260
- }
11261
- if (e.fn === "filter" && Array.isArray(input) && e.where) {
11262
- return input.filter((item) => {
11263
- const tmp = { state: { ...node.state, $: item } };
11264
- return evalExpr(e.where, tmp);
11265
- });
11266
- }
11267
- if (e.fn === "map" && Array.isArray(input) && e.apply) {
11268
- return input.map((item) => {
11269
- const tmp = { state: { ...node.state, $: item } };
11270
- return evalExpr(e.apply, tmp);
11271
- });
11272
- }
11273
- const fn = _customFns[e.fn] || _fns[e.fn];
11274
- if (!fn) {
11275
- console.warn('CardCompute: unknown function "' + e.fn + '"');
11276
- return void 0;
11277
- }
11278
- return fn(input, evalExpr, e);
11279
- }
11280
- function run(node) {
11281
- if (!node || !node.compute) return node;
11025
+ async function run(node, options) {
11026
+ if (!node?.compute?.length) return node;
11282
11027
  if (!node.state) node.state = {};
11283
- for (const key of Object.keys(node.compute)) {
11028
+ node.computed_values = {};
11029
+ node._sourcesData = options?.sourcesData ?? {};
11030
+ const ctx = {
11031
+ state: node.state,
11032
+ requires: node.requires ?? {},
11033
+ sources: node._sourcesData,
11034
+ computed_values: node.computed_values
11035
+ };
11036
+ for (const step of node.compute) {
11284
11037
  try {
11285
- const val = evalExpr(node.compute[key], node);
11286
- deepSet(node.state, key, val);
11038
+ const val = await jsonata(step.expr).evaluate(ctx);
11039
+ deepSet(node.computed_values, step.bindTo, val);
11040
+ ctx.computed_values = node.computed_values;
11287
11041
  } catch (err) {
11288
- console.error(`CardCompute.run error on "${node.id || "?"}.${key}":`, err);
11042
+ console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
11289
11043
  }
11290
11044
  }
11291
11045
  return node;
11292
11046
  }
11047
+ async function evalExpr(expr, node) {
11048
+ const ctx = {
11049
+ state: node.state ?? {},
11050
+ requires: node.requires ?? {},
11051
+ sources: node._sourcesData ?? {},
11052
+ computed_values: node.computed_values ?? {}
11053
+ };
11054
+ return jsonata(expr).evaluate(ctx);
11055
+ }
11293
11056
  function resolve(node, path) {
11057
+ if (path.startsWith("sources.")) {
11058
+ return deepGet(node._sourcesData ?? {}, path.slice("sources.".length));
11059
+ }
11294
11060
  return deepGet(node, path);
11295
11061
  }
11296
- function registerFunction(name, fn) {
11297
- _customFns[name] = fn;
11298
- }
11299
11062
  var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
11300
11063
  "metric",
11301
11064
  "table",
@@ -11312,26 +11075,17 @@ var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
11312
11075
  "markdown",
11313
11076
  "custom"
11314
11077
  ]);
11315
- var VALID_SOURCE_KINDS = /* @__PURE__ */ new Set(["api", "websocket", "static", "llm"]);
11316
11078
  var VALID_STATUSES = /* @__PURE__ */ new Set(["fresh", "stale", "loading", "error"]);
11317
- var CARD_ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "type", "meta", "data", "view", "state", "compute"]);
11318
- var SOURCE_ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "type", "meta", "data", "source", "state", "compute"]);
11079
+ var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "state", "compute", "sources"]);
11319
11080
  function validateNode(node) {
11320
11081
  const errors = [];
11321
11082
  if (!node || typeof node !== "object" || Array.isArray(node)) {
11322
11083
  return { ok: false, errors: ["Node must be a non-null object"] };
11323
11084
  }
11324
11085
  const n = node;
11325
- if (typeof n.id !== "string" || !n.id) {
11326
- errors.push("id: required, must be a non-empty string");
11327
- }
11328
- if (n.type !== "card" && n.type !== "source") {
11329
- errors.push('type: must be "card" or "source"');
11330
- return { ok: false, errors };
11331
- }
11332
- const allowed = n.type === "card" ? CARD_ALLOWED_KEYS : SOURCE_ALLOWED_KEYS;
11086
+ if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
11333
11087
  for (const key of Object.keys(n)) {
11334
- if (!allowed.has(key)) errors.push(`Unknown top-level key: "${key}"`);
11088
+ if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
11335
11089
  }
11336
11090
  if (n.state == null || typeof n.state !== "object" || Array.isArray(n.state)) {
11337
11091
  errors.push("state: required, must be an object");
@@ -11350,37 +11104,58 @@ function validateNode(node) {
11350
11104
  if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
11351
11105
  }
11352
11106
  }
11353
- if (n.data != null) {
11354
- if (typeof n.data !== "object" || Array.isArray(n.data)) {
11355
- errors.push("data: must be an object");
11107
+ if (n.requires != null && !Array.isArray(n.requires)) errors.push("requires: must be an array of strings");
11108
+ if (n.provides != null) {
11109
+ if (!Array.isArray(n.provides)) {
11110
+ errors.push("provides: must be an array of { bindTo, src } bindings");
11356
11111
  } else {
11357
- const data = n.data;
11358
- if (data.requires != null && !Array.isArray(data.requires)) errors.push("data.requires: must be an array of strings");
11359
- if (data.provides != null && (typeof data.provides !== "object" || Array.isArray(data.provides))) errors.push("data.provides: must be an object");
11112
+ n.provides.forEach((p, i) => {
11113
+ if (!p || typeof p !== "object" || Array.isArray(p)) {
11114
+ errors.push(`provides[${i}]: must be an object with bindTo and src`);
11115
+ } else {
11116
+ const b = p;
11117
+ if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
11118
+ if (typeof b.src !== "string" || !b.src) errors.push(`provides[${i}]: missing required "src" string`);
11119
+ }
11120
+ });
11360
11121
  }
11361
11122
  }
11362
11123
  if (n.compute != null) {
11363
- if (typeof n.compute !== "object" || Array.isArray(n.compute)) {
11364
- errors.push("compute: must be an object");
11124
+ if (!Array.isArray(n.compute)) {
11125
+ errors.push("compute: must be an array of compute steps");
11126
+ } else {
11127
+ n.compute.forEach((step, i) => {
11128
+ if (!step || typeof step !== "object" || Array.isArray(step)) {
11129
+ errors.push(`compute[${i}]: must be a compute step object`);
11130
+ } else {
11131
+ const s = step;
11132
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
11133
+ if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
11134
+ }
11135
+ });
11136
+ }
11137
+ }
11138
+ if (n.sources != null) {
11139
+ if (!Array.isArray(n.sources)) {
11140
+ errors.push("sources: must be an array");
11365
11141
  } else {
11366
- for (const [key, expr] of Object.entries(n.compute)) {
11367
- if (!expr || typeof expr !== "object" || Array.isArray(expr)) {
11368
- errors.push(`compute.${key}: must be a compute expression object`);
11369
- } else if (!expr.fn) {
11370
- errors.push(`compute.${key}: missing required "fn" property`);
11142
+ n.sources.forEach((src, i) => {
11143
+ if (!src || typeof src !== "object" || Array.isArray(src)) {
11144
+ errors.push(`sources[${i}]: must be an object`);
11371
11145
  } else {
11372
- const fn = expr.fn;
11373
- if (!_fns[fn] && !_customFns[fn]) {
11374
- errors.push(`compute.${key}: unknown function "${fn}"`);
11146
+ const s = src;
11147
+ if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`sources[${i}]: missing required "bindTo" property`);
11148
+ if (s.outputFile != null && typeof s.outputFile !== "string") errors.push(`sources[${i}]: outputFile must be a string`);
11149
+ if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
11150
+ errors.push(`sources[${i}]: optionalForCompletionGating must be a boolean`);
11375
11151
  }
11376
11152
  }
11377
- }
11153
+ });
11378
11154
  }
11379
11155
  }
11380
- if (n.type === "card") {
11381
- if (n.source != null) errors.push('Card nodes must not have "source" \u2014 use type "source" instead');
11382
- if (n.view == null || typeof n.view !== "object" || Array.isArray(n.view)) {
11383
- errors.push("view: required for card nodes, must be an object");
11156
+ if (n.view != null) {
11157
+ if (typeof n.view !== "object" || Array.isArray(n.view)) {
11158
+ errors.push("view: must be an object");
11384
11159
  } else {
11385
11160
  const view = n.view;
11386
11161
  if (!Array.isArray(view.elements) || view.elements.length === 0) {
@@ -11401,28 +11176,8 @@ function validateNode(node) {
11401
11176
  }
11402
11177
  });
11403
11178
  }
11404
- if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) {
11405
- errors.push("view.layout: must be an object");
11406
- }
11407
- if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) {
11408
- errors.push("view.features: must be an object");
11409
- }
11410
- }
11411
- }
11412
- if (n.type === "source") {
11413
- if (n.view != null) errors.push('Source nodes must not have "view" \u2014 use type "card" instead');
11414
- if (n.source == null || typeof n.source !== "object" || Array.isArray(n.source)) {
11415
- errors.push("source: required for source nodes, must be an object");
11416
- } else {
11417
- const src = n.source;
11418
- if (!src.kind || !VALID_SOURCE_KINDS.has(src.kind)) {
11419
- errors.push(`source.kind: required, must be one of: ${[...VALID_SOURCE_KINDS].join(", ")}`);
11420
- }
11421
- if (typeof src.bindTo !== "string" || !src.bindTo) {
11422
- errors.push("source.bindTo: required, must be a state path string");
11423
- } else if (!src.bindTo.startsWith("state.")) {
11424
- errors.push('source.bindTo: must start with "state."');
11425
- }
11179
+ if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
11180
+ if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
11426
11181
  }
11427
11182
  }
11428
11183
  return { ok: errors.length === 0, errors };
@@ -11431,14 +11186,7 @@ var CardCompute = {
11431
11186
  run,
11432
11187
  eval: evalExpr,
11433
11188
  resolve,
11434
- validate: validateNode,
11435
- registerFunction,
11436
- get functions() {
11437
- const all = {};
11438
- for (const k of Object.keys(_fns)) all[k] = _fns[k];
11439
- for (const k of Object.keys(_customFns)) all[k] = _customFns[k];
11440
- return all;
11441
- }
11189
+ validate: validateNode
11442
11190
  };
11443
11191
 
11444
11192
  // src/continuous-event-graph/live-cards-bridge.ts
@@ -11471,19 +11219,26 @@ function liveCardsToReactiveGraph(input, options = {}) {
11471
11219
  }
11472
11220
  const sharedState = options.sharedState ?? /* @__PURE__ */ new Map();
11473
11221
  const tasks = {};
11222
+ const allTokens = /* @__PURE__ */ new Set();
11223
+ const tokenToCardId = /* @__PURE__ */ new Map();
11224
+ for (const card of cards) {
11225
+ for (const binding of card.provides ?? [{ bindTo: card.id, src: `state.${card.id}` }]) {
11226
+ allTokens.add(binding.bindTo);
11227
+ tokenToCardId.set(binding.bindTo, card.id);
11228
+ }
11229
+ }
11474
11230
  for (const card of cards) {
11475
- const requires = card.data?.requires ?? [];
11231
+ const requires = card.requires ?? [];
11476
11232
  for (const req of requires) {
11477
- if (!cardMap.has(req)) {
11478
- throw new Error(`Card "${card.id}" requires "${req}" but no card with that ID exists`);
11233
+ if (!allTokens.has(req)) {
11234
+ throw new Error(`Card "${card.id}" requires "${req}" but no card provides that token`);
11479
11235
  }
11480
11236
  }
11481
11237
  tasks[card.id] = {
11482
11238
  requires: requires.length > 0 ? requires : void 0,
11483
- provides: [card.id],
11239
+ provides: (card.provides ?? [{ bindTo: card.id, src: `state.${card.id}` }]).map((p) => p.bindTo),
11484
11240
  taskHandlers: [card.id],
11485
- // each card has a named handler matching its ID
11486
- description: card.meta?.title ?? `${card.type}: ${card.id}`
11241
+ description: card.meta?.title ?? card.id
11487
11242
  };
11488
11243
  }
11489
11244
  const config = {
@@ -11502,10 +11257,10 @@ function liveCardsToReactiveGraph(input, options = {}) {
11502
11257
  graphRef.resolveCallback(token, data, errors);
11503
11258
  };
11504
11259
  for (const card of cards) {
11505
- if (card.type === "source") {
11260
+ if (card.sources && card.sources.length > 0) {
11506
11261
  handlers[card.id] = buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedState, getResolve);
11507
11262
  } else {
11508
- handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve);
11263
+ handlers[card.id] = buildCardHandler(card, cardHandlers, sharedState, cardMap, tokenToCardId, getResolve);
11509
11264
  }
11510
11265
  }
11511
11266
  const graph = createReactiveGraph(
@@ -11539,7 +11294,7 @@ function buildSourceHandler(card, sourceHandlers, defaultSourceHandler, sharedSt
11539
11294
  return "task-initiated";
11540
11295
  };
11541
11296
  }
11542
- function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve) {
11297
+ function buildCardHandler(card, cardHandlers, sharedState, _cardMap, tokenToCardId, getResolve) {
11543
11298
  if (cardHandlers[card.id]) {
11544
11299
  const userHandler = cardHandlers[card.id];
11545
11300
  return async (input) => {
@@ -11547,47 +11302,37 @@ function buildCardHandler(card, cardHandlers, sharedState, cardMap, getResolve)
11547
11302
  };
11548
11303
  }
11549
11304
  return async (input) => {
11305
+ const requiresData = {};
11306
+ const requires = card.requires ?? [];
11307
+ for (const token of requires) {
11308
+ const producerId = tokenToCardId.get(token) ?? token;
11309
+ const upstreamState = sharedState.get(producerId);
11310
+ if (upstreamState) {
11311
+ requiresData[token] = upstreamState[token] ?? upstreamState;
11312
+ }
11313
+ }
11550
11314
  const computeNode = {
11551
11315
  id: card.id,
11552
11316
  state: { ...card.state },
11317
+ requires: requiresData,
11553
11318
  compute: card.compute
11554
11319
  };
11555
- const requires = card.data?.requires ?? [];
11556
- for (const upstreamId of requires) {
11557
- const upstreamState = sharedState.get(upstreamId);
11558
- if (upstreamState) {
11559
- computeNode.state[upstreamId] = upstreamState;
11560
- }
11561
- const upstreamCard = cardMap.get(upstreamId);
11562
- if (upstreamCard?.data?.provides && upstreamState) {
11563
- for (const [key, bindRef] of Object.entries(upstreamCard.data.provides)) {
11564
- if (typeof bindRef === "string" && bindRef.startsWith("state.")) {
11565
- const path = bindRef.slice(6);
11566
- const value = deepGet2(upstreamState, path);
11567
- if (value !== void 0) {
11568
- computeNode.state[key] = value;
11569
- }
11570
- }
11571
- }
11320
+ await CardCompute.run(computeNode);
11321
+ let resultData;
11322
+ if (card.provides && card.provides.length > 0) {
11323
+ resultData = {};
11324
+ for (const { bindTo, src } of card.provides) {
11325
+ resultData[bindTo] = CardCompute.resolve(computeNode, src);
11572
11326
  }
11327
+ } else {
11328
+ resultData = { ...computeNode.state, ...computeNode.computed_values };
11573
11329
  }
11574
- CardCompute.run(computeNode);
11575
- const resultState = { ...computeNode.state };
11330
+ const resultState = { ...computeNode.state, ...computeNode.computed_values };
11576
11331
  sharedState.set(card.id, resultState);
11577
- getResolve()(input.callbackToken, resultState);
11332
+ getResolve()(input.callbackToken, resultData);
11578
11333
  return "task-initiated";
11579
11334
  };
11580
11335
  }
11581
- function deepGet2(obj, path) {
11582
- if (!path || !obj) return void 0;
11583
- const parts = path.split(".");
11584
- let cur = obj;
11585
- for (const part of parts) {
11586
- if (cur == null) return void 0;
11587
- cur = cur[part];
11588
- }
11589
- return cur;
11590
- }
11591
11336
 
11592
11337
  // src/inference/core.ts
11593
11338
  var DEFAULT_THRESHOLD = 0.5;
@@ -11840,6 +11585,6 @@ function createHttpAdapter(opts) {
11840
11585
  };
11841
11586
  }
11842
11587
 
11843
- export { COMPLETION_STRATEGIES, CONFLICT_STRATEGIES, CardCompute, DEFAULTS, EXECUTION_MODES, EXECUTION_STATUS, FileJournal, FileStore, StepMachine as FlowEngine, LocalStorageStore, MemoryJournal, MemoryStore, StepMachine, TASK_STATUS, addDynamicTask, addNode, addProvides, addRequires, apply, applyAll, applyEvent, applyEvents, applyInferences, applyStepResult, batch, buildInferencePrompt, checkCircuitBreaker, computeAvailableOutputs, computeStepInput, createCallbackHandler, createCliAdapter, createDefaultGraphEngineStore, createStepMachine as createEngine, createFireAndForgetHandler, createHttpAdapter, createInitialExecutionState, createInitialState, createLiveGraph, createNoopHandler, createReactiveGraph, createScriptHandler, createShellHandler, createStepMachine, createWebhookHandler, detectStuckState, disableNode, drainTokens, enableNode, exportGraphConfig, exportGraphConfigToFile, extractReturnData, flowToMermaid, getAllTasks, getCandidateTasks, getDownstream, getMaxExecutions, getNode, getProvides, getRefreshStrategy, getRequires, getTask, getUnreachableNodes, getUnreachableTokens, getUpstream, graphToMermaid, hasTask, inferAndApply, inferCompletions, injectTokens, inspect, isExecutionComplete, isNonActiveTask, isRerunnable, isTaskCompleted, isTaskRunning, liveCardsToReactiveGraph, loadGraphConfig, loadStepFlow, mutateGraph, next, planExecution, removeNode, removeProvides, removeRequires, resetNode, resolveConfigTemplates, resolveVariables, restore, schedule, snapshot, validateFlowSchema, validateGraph, validateGraphConfig, validateGraphSchema, validateLiveCardSchema, validateLiveGraph, validateReactiveGraph, validateStepFlowConfig };
11588
+ export { COMPLETION_STRATEGIES, CONFLICT_STRATEGIES, CardCompute, DEFAULTS, EXECUTION_MODES, EXECUTION_STATUS, FileStore, StepMachine as FlowEngine, LocalStorageStore, MemoryJournal, MemoryStore, StepMachine, TASK_STATUS, addDynamicTask, addNode, addProvides, addRequires, apply, applyAll, applyEvent, applyEvents, applyInferences, applyStepResult, batch, buildInferencePrompt, checkCircuitBreaker, computeAvailableOutputs, computeStepInput, createCallbackHandler, createCliAdapter, createDefaultGraphEngineStore, createStepMachine as createEngine, createFireAndForgetHandler, createHttpAdapter, createInitialExecutionState, createInitialState, createLiveGraph, createNoopHandler, createReactiveGraph, createScriptHandler, createShellHandler, createStepMachine, createWebhookHandler, detectStuckState, disableNode, drainTokens, enableNode, exportGraphConfig, exportGraphConfigToFile, extractReturnData, flowToMermaid, getAllTasks, getCandidateTasks, getDownstream, getMaxExecutions, getNode, getProvides, getRefreshStrategy, getRequires, getTask, getUnreachableNodes, getUnreachableTokens, getUpstream, graphToMermaid, hasTask, inferAndApply, inferCompletions, injectTokens, inspect, isExecutionComplete, isNonActiveTask, isRerunnable, isTaskCompleted, isTaskRunning, liveCardsToReactiveGraph, loadGraphConfig, loadStepFlow, mutateGraph, next, planExecution, removeNode, removeProvides, removeRequires, resetNode, resolveConfigTemplates, resolveVariables, restore, schedule, snapshot, validateFlowSchema, validateGraph, validateGraphConfig, validateGraphSchema, validateLiveCardSchema, validateLiveGraph, validateReactiveGraph, validateStepFlowConfig };
11844
11589
  //# sourceMappingURL=index.js.map
11845
11590
  //# sourceMappingURL=index.js.map