yaml-flow 3.1.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +81 -20
  2. package/board-live-cards-cli.js +37 -0
  3. package/browser/board-livegraph-runtime.js +1453 -0
  4. package/browser/board-livegraph-runtime.js.map +1 -0
  5. package/browser/card-compute.js +153 -433
  6. package/browser/live-cards.js +868 -115
  7. package/browser/live-cards.schema.json +90 -83
  8. package/dist/board-livegraph-runtime/index.cjs +1448 -0
  9. package/dist/board-livegraph-runtime/index.cjs.map +1 -0
  10. package/dist/board-livegraph-runtime/index.d.cts +101 -0
  11. package/dist/board-livegraph-runtime/index.d.ts +101 -0
  12. package/dist/board-livegraph-runtime/index.js +1441 -0
  13. package/dist/board-livegraph-runtime/index.js.map +1 -0
  14. package/dist/card-compute/index.cjs +266 -431
  15. package/dist/card-compute/index.cjs.map +1 -1
  16. package/dist/card-compute/index.d.cts +77 -49
  17. package/dist/card-compute/index.d.ts +77 -49
  18. package/dist/card-compute/index.js +263 -432
  19. package/dist/card-compute/index.js.map +1 -1
  20. package/dist/cli/board-live-cards-cli.cjs +2750 -0
  21. package/dist/cli/board-live-cards-cli.cjs.map +1 -0
  22. package/dist/cli/board-live-cards-cli.d.cts +205 -0
  23. package/dist/cli/board-live-cards-cli.d.ts +205 -0
  24. package/dist/cli/board-live-cards-cli.js +2702 -0
  25. package/dist/cli/board-live-cards-cli.js.map +1 -0
  26. package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
  27. package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
  28. package/dist/continuous-event-graph/index.cjs +258 -464
  29. package/dist/continuous-event-graph/index.cjs.map +1 -1
  30. package/dist/continuous-event-graph/index.d.cts +18 -358
  31. package/dist/continuous-event-graph/index.d.ts +18 -358
  32. package/dist/continuous-event-graph/index.js +255 -464
  33. package/dist/continuous-event-graph/index.js.map +1 -1
  34. package/dist/event-graph/index.cjs +4 -4
  35. package/dist/event-graph/index.cjs.map +1 -1
  36. package/dist/event-graph/index.d.cts +5 -5
  37. package/dist/event-graph/index.d.ts +5 -5
  38. package/dist/event-graph/index.js +4 -4
  39. package/dist/event-graph/index.js.map +1 -1
  40. package/dist/index.cjs +1684 -555
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +26 -7
  43. package/dist/index.d.ts +26 -7
  44. package/dist/index.js +1678 -555
  45. package/dist/index.js.map +1 -1
  46. package/dist/inference/index.cjs +138 -19
  47. package/dist/inference/index.cjs.map +1 -1
  48. package/dist/inference/index.d.cts +2 -2
  49. package/dist/inference/index.d.ts +2 -2
  50. package/dist/inference/index.js +138 -19
  51. package/dist/inference/index.js.map +1 -1
  52. package/dist/journal-DRfJiheM.d.cts +28 -0
  53. package/dist/journal-NLYuqege.d.ts +28 -0
  54. package/dist/live-cards-bridge-Or7fdEJV.d.ts +316 -0
  55. package/dist/live-cards-bridge-vGJ6tMzN.d.cts +316 -0
  56. package/dist/schedule-CMcZe5Ny.d.ts +21 -0
  57. package/dist/schedule-CiucyCan.d.cts +21 -0
  58. package/dist/step-machine/index.cjs +18 -1
  59. package/dist/step-machine/index.cjs.map +1 -1
  60. package/dist/step-machine/index.d.cts +2 -2
  61. package/dist/step-machine/index.d.ts +2 -2
  62. package/dist/step-machine/index.js +18 -1
  63. package/dist/step-machine/index.js.map +1 -1
  64. package/dist/stores/file.d.cts +1 -1
  65. package/dist/stores/file.d.ts +1 -1
  66. package/dist/stores/index.d.cts +1 -1
  67. package/dist/stores/index.d.ts +1 -1
  68. package/dist/stores/localStorage.d.cts +1 -1
  69. package/dist/stores/localStorage.d.ts +1 -1
  70. package/dist/stores/memory.d.cts +1 -1
  71. package/dist/stores/memory.d.ts +1 -1
  72. package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
  73. package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
  74. package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
  75. package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
  76. package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
  77. package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
  78. package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
  79. package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
  80. package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
  81. package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
  82. package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
  83. package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
  84. package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
  85. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  86. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
  87. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +217 -0
  88. package/examples/browser/livecards-browser/index.html +41 -0
  89. package/examples/browser/{index.html → step-machine-browser/index.html} +53 -53
  90. package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  91. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  92. package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  93. package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  94. package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  95. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  96. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  97. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  98. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  99. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  100. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  101. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  102. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  103. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  104. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  105. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  106. package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  107. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
  108. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
  109. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
  110. package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
  111. package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
  112. package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
  113. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
  114. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
  115. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
  116. package/examples/example-board/board.yaml +23 -0
  117. package/examples/example-board/bootstrap_payload.json +1 -0
  118. package/examples/example-board/cards/card-chain-region-alert.json +39 -0
  119. package/examples/example-board/cards/card-chain-region-totals.json +26 -0
  120. package/examples/example-board/cards/card-chain-top-region.json +24 -0
  121. package/examples/example-board/cards/card-ex-actions.json +32 -0
  122. package/examples/example-board/cards/card-ex-chart.json +30 -0
  123. package/examples/example-board/cards/card-ex-filter.json +36 -0
  124. package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
  125. package/examples/example-board/cards/card-ex-form.json +91 -0
  126. package/examples/example-board/cards/card-ex-list.json +22 -0
  127. package/examples/example-board/cards/card-ex-markdown.json +17 -0
  128. package/examples/example-board/cards/card-ex-metric.json +19 -0
  129. package/examples/example-board/cards/card-ex-narrative.json +36 -0
  130. package/examples/example-board/cards/card-ex-source-http.json +28 -0
  131. package/examples/example-board/cards/card-ex-source.json +21 -0
  132. package/examples/example-board/cards/card-ex-status.json +35 -0
  133. package/examples/example-board/cards/card-ex-table.json +30 -0
  134. package/examples/example-board/cards/card-ex-todo.json +29 -0
  135. package/examples/example-board/demo-chat-handler.js +69 -0
  136. package/examples/example-board/demo-server.js +87 -0
  137. package/examples/example-board/demo-shell-browser.html +806 -0
  138. package/examples/example-board/demo-shell-with-server.html +280 -0
  139. package/examples/example-board/demo-shell.html +62 -0
  140. package/examples/example-board/demo-task-executor.js +255 -0
  141. package/examples/example-board/mock.db +15 -0
  142. package/examples/example-board/reusable-board-runtime-client.js +265 -0
  143. package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
  144. package/examples/example-board/reusable-server-runtime.js +1284 -0
  145. package/examples/index.html +799 -0
  146. package/examples/{batch → npm-libs/batch}/batch-step-machine.ts +1 -1
  147. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-cards-board.ts +18 -18
  148. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-portfolio-dashboard.ts +24 -24
  149. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/portfolio-tracker.ts +1 -1
  150. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-monitoring.ts +1 -1
  151. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-pipeline.ts +1 -1
  152. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/soc-incident-board.ts +1 -1
  153. package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/stock-dashboard.ts +1 -1
  154. package/examples/{event-graph → npm-libs/event-graph}/ci-cd-pipeline.ts +1 -1
  155. package/examples/{event-graph → npm-libs/event-graph}/executor-diamond.ts +1 -1
  156. package/examples/{event-graph → npm-libs/event-graph}/executor-pipeline.ts +1 -1
  157. package/examples/{event-graph → npm-libs/event-graph}/research-pipeline.ts +1 -1
  158. package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/multi-stage-etl.ts +1 -1
  159. package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/url-processing-pipeline.ts +1 -1
  160. package/examples/{inference → npm-libs/inference}/azure-deployment.ts +1 -1
  161. package/examples/{inference → npm-libs/inference}/copilot-cli.ts +1 -1
  162. package/examples/{inference → npm-libs/inference}/data-pipeline.ts +1 -1
  163. package/examples/{inference → npm-libs/inference}/pluggable-adapters.ts +1 -1
  164. package/examples/{node → npm-libs/node}/ai-conversation.ts +1 -1
  165. package/examples/{node → npm-libs/node}/simple-greeting.ts +2 -2
  166. package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
  167. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
  168. package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
  169. package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
  170. package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
  171. package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
  172. package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
  173. package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
  174. package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
  175. package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
  176. package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
  177. package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
  178. package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
  179. package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
  180. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
  181. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
  182. package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
  183. package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
  184. package/package.json +27 -2
  185. package/schema/board-status.schema.json +118 -0
  186. package/schema/card-runtime.schema.json +25 -0
  187. package/schema/flow.schema.json +5 -0
  188. package/schema/live-cards.schema.json +90 -83
  189. package/step-machine-cli.js +674 -0
  190. package/browser/ingest-board.js +0 -296
  191. package/examples/ingest.js +0 -733
  192. /package/examples/{flows → npm-libs/flows}/ai-conversation.yaml +0 -0
  193. /package/examples/{flows → npm-libs/flows}/order-processing.yaml +0 -0
  194. /package/examples/{flows → npm-libs/flows}/simple-greeting.yaml +0 -0
@@ -1,29 +1,28 @@
1
- // card_compute.js — Pure JSON expression evaluator for LiveCards nodes
1
+ // card-compute.js — JSONata-powered compute engine for LiveCards (browser build)
2
2
  //
3
- // Isomorphic: works in browser (global), Node.js (require), and ESM (import).
4
- // No DOM dependency. Usable on both client and server.
3
+ // Requires JSONata to be loaded first:
4
+ // <script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
5
5
  //
6
- // API:
7
- // CardCompute.run(node) mutates node.state with computed values, returns node
8
- // CardCompute.eval(expr, node) evaluates a single compute_expr, returns value
9
- // CardCompute.resolve(node, path) → deep-get a state path like "state.foo.bar"
10
- // CardCompute.registerFunction(name, fn) add custom compute function
11
- // CardCompute.functions → read-only map of all registered functions
6
+ // API (all async where noted):
7
+ // CardCompute.run(node, options) Promise<node> — eval all compute steps → computed_values
8
+ // CardCompute.eval(expr, node) Promise<value> eval single JSONata expression
9
+ // CardCompute.resolve(node, path) value — sync deep-get "card_data.foo" or "fetched_sources.foo"
10
+ // CardCompute.validate(node) → { ok, errors } sync structural validator
12
11
  //
13
- // Compute declarations (node.compute):
14
- // {
15
- // "total_value": { "fn": "sum", "input": "state.raw_quotes" },
16
- // "avg_price": { "fn": "avg", "input": "state.raw_quotes" },
17
- // "direction": { "fn": "if", "cond": { "fn": "gt", "input": ["state.latest", "state.prev"] }, "then": "up", "else": "down" }
18
- // }
12
+ // Compute steps shape: { bindTo: string, expr: string }
13
+ // expr is a JSONata expression evaluated against { card_data, requires, fetched_sources, computed_values }
14
+ // computed_values and _sourcesData are ephemeral — reset on each run(), never persisted.
15
+ //
16
+ // Sequential steps: later steps see earlier results via computed_values.*
17
+ // options.sourcesData: pre-loaded { [bindTo]: data } map for the fetched_sources.* namespace
19
18
 
20
19
  (function (root, factory) {
21
20
  if (typeof module === 'object' && module.exports) {
22
- module.exports = factory(); // Node / CommonJS
21
+ module.exports = factory(); // Node / CommonJS
23
22
  } else if (typeof define === 'function' && define.amd) {
24
- define(factory); // AMD
23
+ define(factory); // AMD
25
24
  } else {
26
- root.CardCompute = factory(); // Browser global
25
+ root.CardCompute = factory(); // Browser global
27
26
  }
28
27
  }(typeof globalThis !== 'undefined' ? globalThis : this, function () {
29
28
  'use strict';
@@ -33,10 +32,10 @@
33
32
  // ===========================================================================
34
33
 
35
34
  function _deepGet(obj, path) {
36
- if (!path || !obj) return undefined;
37
- const parts = path.split('.');
38
- let cur = obj;
39
- for (let i = 0; i < parts.length; i++) {
35
+ if (!path || obj == null) return undefined;
36
+ var parts = path.split('.');
37
+ var cur = obj;
38
+ for (var i = 0; i < parts.length; i++) {
40
39
  if (cur == null) return undefined;
41
40
  cur = cur[parts[i]];
42
41
  }
@@ -44,487 +43,214 @@
44
43
  }
45
44
 
46
45
  function _deepSet(obj, path, value) {
47
- const parts = path.split('.');
48
- let cur = obj;
49
- for (let i = 0; i < parts.length - 1; i++) {
46
+ var parts = path.split('.');
47
+ var cur = obj;
48
+ for (var i = 0; i < parts.length - 1; i++) {
50
49
  if (cur[parts[i]] == null || typeof cur[parts[i]] !== 'object') cur[parts[i]] = {};
51
50
  cur = cur[parts[i]];
52
51
  }
53
52
  cur[parts[parts.length - 1]] = value;
54
53
  }
55
54
 
56
- function resolve(node, path) {
57
- if (!path) return undefined;
58
- return _deepGet(node, path);
59
- }
60
-
61
55
  // ===========================================================================
62
- // Built-in function registry
56
+ // run evaluate all compute steps on a node (async, returns Promise<node>)
63
57
  // ===========================================================================
64
58
 
65
- var _fns = {};
66
-
67
- // ---- Aggregates ----
68
-
69
- _fns.sum = function (input, _eval, opts) {
70
- var a = Array.isArray(input) ? input : [];
71
- return opts.field
72
- ? a.reduce(function (s, r) { return s + (Number(r[opts.field]) || 0); }, 0)
73
- : a.reduce(function (s, v) { return s + (Number(v) || 0); }, 0);
74
- };
75
-
76
- _fns.avg = function (input, _eval, opts) {
77
- var s = _fns.sum(input, _eval, opts);
78
- var n = Array.isArray(input) ? input.length : 1;
79
- return n ? s / n : 0;
80
- };
81
-
82
- _fns.min = function (input, _eval, opts) {
83
- var a = Array.isArray(input) ? input : [];
84
- var vals = opts.field ? a.map(function (r) { return Number(r[opts.field]); }) : a.map(Number);
85
- return vals.length ? Math.min.apply(null, vals) : 0;
86
- };
87
-
88
- _fns.max = function (input, _eval, opts) {
89
- var a = Array.isArray(input) ? input : [];
90
- var vals = opts.field ? a.map(function (r) { return Number(r[opts.field]); }) : a.map(Number);
91
- return vals.length ? Math.max.apply(null, vals) : 0;
92
- };
93
-
94
- _fns.count = function (input) {
95
- return Array.isArray(input) ? input.length : (input != null ? 1 : 0);
96
- };
97
-
98
- _fns.first = function (input) {
99
- return Array.isArray(input) ? input[0] : input;
100
- };
101
-
102
- _fns.last = function (input) {
103
- return Array.isArray(input) ? input[input.length - 1] : input;
104
- };
105
-
106
- // ---- Math ----
107
-
108
- _fns.add = function (input) {
109
- var a = Array.isArray(input) ? input : [];
110
- return a.reduce(function (s, v) { return s + Number(v); }, 0);
111
- };
112
-
113
- _fns.sub = function (input) {
114
- var a = Array.isArray(input) ? input : [];
115
- return a.length >= 2 ? Number(a[0]) - Number(a[1]) : 0;
116
- };
117
-
118
- _fns.mul = function (input) {
119
- var a = Array.isArray(input) ? input : [];
120
- return a.reduce(function (s, v) { return s * Number(v); }, 1);
121
- };
122
-
123
- _fns.div = function (input) {
124
- var a = Array.isArray(input) ? input : [];
125
- return a.length >= 2 && Number(a[1]) !== 0 ? Number(a[0]) / Number(a[1]) : 0;
126
- };
127
-
128
- _fns.round = function (input, _eval, opts) {
129
- var decimals = opts.decimals != null ? opts.decimals : 0;
130
- var factor = Math.pow(10, decimals);
131
- return Math.round(Number(input) * factor) / factor;
132
- };
133
-
134
- _fns.abs = function (input) { return Math.abs(Number(input)); };
135
-
136
- _fns.mod = function (input) {
137
- var a = Array.isArray(input) ? input : [];
138
- return a.length >= 2 ? Number(a[0]) % Number(a[1]) : 0;
139
- };
140
-
141
- // ---- Compare ----
59
+ function run(node, options) {
60
+ if (!node || !node.compute || !node.compute.length) return Promise.resolve(node);
142
61
 
143
- _fns.gt = function (input) { var a = Array.isArray(input) ? input : []; return a.length >= 2 && Number(a[0]) > Number(a[1]); };
144
- _fns.gte = function (input) { var a = Array.isArray(input) ? input : []; return a.length >= 2 && Number(a[0]) >= Number(a[1]); };
145
- _fns.lt = function (input) { var a = Array.isArray(input) ? input : []; return a.length >= 2 && Number(a[0]) < Number(a[1]); };
146
- _fns.lte = function (input) { var a = Array.isArray(input) ? input : []; return a.length >= 2 && Number(a[0]) <= Number(a[1]); };
147
- _fns.eq = function (input) { var a = Array.isArray(input) ? input : []; return a.length >= 2 && a[0] === a[1]; };
148
- _fns.neq = function (input) { var a = Array.isArray(input) ? input : []; return a.length >= 2 && a[0] !== a[1]; };
62
+ if (!node.card_data) node.card_data = {};
63
+ node.computed_values = {};
64
+ node._sourcesData = (options && options.sourcesData) || {};
149
65
 
150
- // ---- Logic ----
151
-
152
- _fns.and = function (input) { var a = Array.isArray(input) ? input : []; return a.every(Boolean); };
153
- _fns.or = function (input) { var a = Array.isArray(input) ? input : []; return a.some(Boolean); };
154
- _fns.not = function (input) { return !input; };
155
- // "if" is handled specially in evalExpr
156
-
157
- // ---- String ----
158
-
159
- _fns.concat = function (input) {
160
- var a = Array.isArray(input) ? input : [];
161
- return a.map(function (v) { return v != null ? String(v) : ''; }).join('');
162
- };
163
-
164
- _fns.upper = function (input) { return String(input || '').toUpperCase(); };
165
- _fns.lower = function (input) { return String(input || '').toLowerCase(); };
166
-
167
- _fns.template = function (input, _eval, opts) {
168
- var t = String(opts.format || '');
169
- if (input && typeof input === 'object') {
170
- Object.keys(input).forEach(function (k) {
171
- t = t.split('{{' + k + '}}').join(input[k] != null ? String(input[k]) : '');
172
- });
66
+ if (typeof jsonata === 'undefined') {
67
+ console.error('CardCompute: JSONata not loaded. Add <script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script> before card-compute.js');
68
+ return Promise.resolve(node);
173
69
  }
174
- return t;
175
- };
176
-
177
- _fns.join = function (input, _eval, opts) {
178
- var a = Array.isArray(input) ? input : [];
179
- var sep = opts.separator != null ? opts.separator : ', ';
180
- return a.map(function (v) { return v != null ? String(v) : ''; }).join(sep);
181
- };
182
-
183
- _fns.split = function (input, _eval, opts) {
184
- var sep = opts.separator != null ? opts.separator : ',';
185
- return String(input || '').split(sep).map(function (s) { return s.trim(); });
186
- };
187
-
188
- _fns.trim = function (input) { return String(input || '').trim(); };
189
-
190
- // ---- Collection ----
191
-
192
- _fns.pluck = function (input, _eval, opts) {
193
- return Array.isArray(input) ? input.map(function (r) { return r[opts.field]; }) : [];
194
- };
195
-
196
- _fns.filter = function (input, evalFn, opts) {
197
- // Handled specially in evalExpr for the where clause; fallback for simple truthy filter
198
- if (!Array.isArray(input)) return [];
199
- if (opts.field) return input.filter(function (r) { return !!r[opts.field]; });
200
- return input.filter(Boolean);
201
- };
202
70
 
203
- _fns.map = function (input) {
204
- // Handled specially in evalExpr for the apply clause
205
- return Array.isArray(input) ? input.slice() : [];
206
- };
207
-
208
- _fns.sort = function (input, _eval, opts) {
209
- var a = Array.isArray(input) ? input.slice() : [];
210
- var f = opts.field;
211
- var dir = opts.direction === 'desc' ? -1 : 1;
212
- if (f) return a.sort(function (x, y) { return x[f] > y[f] ? dir : x[f] < y[f] ? -dir : 0; });
213
- return a.sort(function (x, y) { return x > y ? dir : x < y ? -dir : 0; });
214
- };
215
-
216
- _fns.slice = function (input, _eval, opts) {
217
- return Array.isArray(input) ? input.slice(opts.start || 0, opts.end) : input;
218
- };
219
-
220
- _fns.flat = function (input, _eval, opts) {
221
- var depth = opts.depth != null ? opts.depth : 1;
222
- return Array.isArray(input) ? input.flat(depth) : [input];
223
- };
224
-
225
- _fns.unique = function (input) {
226
- if (!Array.isArray(input)) return [input];
227
- // For primitives, use Set. For objects, use JSON comparison.
228
- var seen = new Set();
229
- return input.filter(function (v) {
230
- var key = typeof v === 'object' ? JSON.stringify(v) : v;
231
- if (seen.has(key)) return false;
232
- seen.add(key);
233
- return true;
234
- });
235
- };
236
-
237
- _fns.group = function (input, _eval, opts) {
238
- var a = Array.isArray(input) ? input : [];
239
- var g = {};
240
- a.forEach(function (r) {
241
- var k = String(r[opts.field] || '');
242
- if (!g[k]) g[k] = [];
243
- g[k].push(r);
244
- });
245
- return g;
246
- };
247
-
248
- _fns.flatten_keys = function (input) {
249
- // { a: [1,2], b: [3] } → [{ key: "a", value: 1 }, { key: "a", value: 2 }, { key: "b", value: 3 }]
250
- if (!input || typeof input !== 'object' || Array.isArray(input)) return [];
251
- var result = [];
252
- Object.keys(input).forEach(function (k) {
253
- var vals = Array.isArray(input[k]) ? input[k] : [input[k]];
254
- vals.forEach(function (v) { result.push({ key: k, value: v }); });
71
+ var ctx = {
72
+ card_data: node.card_data,
73
+ requires: node.requires || {},
74
+ fetched_sources: node._sourcesData,
75
+ computed_values: node.computed_values,
76
+ };
77
+
78
+ var chain = Promise.resolve();
79
+ node.compute.forEach(function (step) {
80
+ chain = chain.then(function () {
81
+ if (!step || typeof step.expr !== 'string') return;
82
+ return jsonata(step.expr).evaluate(ctx).then(function (val) {
83
+ _deepSet(node.computed_values, step.bindTo, val);
84
+ ctx.computed_values = node.computed_values; // subsequent steps see earlier results
85
+ }).catch(function (e) {
86
+ console.error('CardCompute.run error on "' + (node.id || '?') + '.' + step.bindTo + '":', e);
87
+ });
88
+ });
255
89
  });
256
- return result;
257
- };
258
90
 
259
- _fns.entries = function (input) {
260
- if (!input || typeof input !== 'object' || Array.isArray(input)) return [];
261
- return Object.keys(input).map(function (k) { return { key: k, value: input[k] }; });
262
- };
263
-
264
- _fns.from_entries = function (input) {
265
- if (!Array.isArray(input)) return {};
266
- var obj = {};
267
- input.forEach(function (item) { if (item.key != null) obj[item.key] = item.value; });
268
- return obj;
269
- };
270
-
271
- _fns.length = function (input) {
272
- if (Array.isArray(input)) return input.length;
273
- if (typeof input === 'string') return input.length;
274
- if (input && typeof input === 'object') return Object.keys(input).length;
275
- return 0;
276
- };
277
-
278
- // ---- Lookup ----
279
-
280
- _fns.get = function (input, _eval, opts) {
281
- return _deepGet(input, opts.field || opts.path || '');
282
- };
283
-
284
- _fns.default = function (input, _eval, opts) {
285
- return input != null ? input : opts.value;
286
- };
287
-
288
- _fns.coalesce = function (input) {
289
- var a = Array.isArray(input) ? input : [];
290
- for (var i = 0; i < a.length; i++) { if (a[i] != null) return a[i]; }
291
- return null;
292
- };
293
-
294
- // ---- Date ----
295
-
296
- _fns.now = function () { return new Date().toISOString(); };
297
-
298
- _fns.diff_days = function (input) {
299
- var a = Array.isArray(input) ? input : [];
300
- return a.length >= 2 ? Math.floor((new Date(a[0]) - new Date(a[1])) / 86400000) : 0;
301
- };
302
-
303
- _fns.format_date = function (input, _eval, opts) {
304
- try {
305
- var d = new Date(input);
306
- if (opts.format === 'iso') return d.toISOString();
307
- if (opts.format === 'date') return d.toLocaleDateString();
308
- if (opts.format === 'time') return d.toLocaleTimeString();
309
- return d.toLocaleDateString();
310
- } catch (e) {
311
- return String(input);
312
- }
313
- };
314
-
315
- _fns.parse_date = function (input) {
316
- try { return new Date(input).toISOString(); } catch (e) { return null; }
317
- };
318
-
319
- // ---- Type ----
320
-
321
- _fns.to_number = function (input) { return Number(input) || 0; };
322
- _fns.to_string = function (input) { return input != null ? String(input) : ''; };
323
- _fns.to_bool = function (input) { return !!input; };
324
- _fns.type_of = function (input) { return Array.isArray(input) ? 'array' : typeof input; };
325
- _fns.is_null = function (input) { return input == null; };
326
- _fns.is_empty = function (input) {
327
- if (input == null) return true;
328
- if (Array.isArray(input)) return input.length === 0;
329
- if (typeof input === 'string') return input.length === 0;
330
- if (typeof input === 'object') return Object.keys(input).length === 0;
331
- return false;
332
- };
91
+ return chain.then(function () { return node; });
92
+ }
333
93
 
334
94
  // ===========================================================================
335
- // Expression evaluator
95
+ // eval — evaluate a single JSONata expression (async, returns Promise<value>)
336
96
  // ===========================================================================
337
97
 
338
- var _customFns = {};
339
-
340
98
  function evalExpr(expr, node) {
341
- if (expr == null) return expr;
342
-
343
- // Literal values pass through
344
- if (typeof expr !== 'object' || Array.isArray(expr)) return expr;
345
-
346
- // Must have fn to be an expression
347
- if (!expr.fn) return expr;
348
-
349
- // Resolve input
350
- var input = expr.input;
351
- if (typeof input === 'string' && input.startsWith('state.')) {
352
- input = resolve(node, input);
353
- } else if (Array.isArray(input)) {
354
- input = input.map(function (v) {
355
- if (typeof v === 'string' && v.startsWith('state.')) return resolve(node, v);
356
- if (v && typeof v === 'object' && v.fn) return evalExpr(v, node);
357
- return v;
358
- });
359
- } else if (input && typeof input === 'object' && input.fn) {
360
- input = evalExpr(input, node);
361
- }
362
-
363
- // Special: if
364
- if (expr.fn === 'if') {
365
- var cond = evalExpr(expr.cond, node);
366
- if (cond) {
367
- return (expr.then && typeof expr.then === 'object' && expr.then.fn) ? evalExpr(expr.then, node) : expr.then;
368
- } else {
369
- return (expr.else && typeof expr.else === 'object' && expr.else.fn) ? evalExpr(expr.else, node) : expr.else;
370
- }
371
- }
372
-
373
- // Special: filter with where clause
374
- if (expr.fn === 'filter' && Array.isArray(input) && expr.where) {
375
- return input.filter(function (item) {
376
- var tmp = { state: Object.assign({}, node.state, { $: item }) };
377
- return evalExpr(expr.where, tmp);
378
- });
379
- }
380
-
381
- // Special: map with apply clause
382
- if (expr.fn === 'map' && Array.isArray(input) && expr.apply) {
383
- return input.map(function (item) {
384
- var tmp = { state: Object.assign({}, node.state, { $: item }) };
385
- return evalExpr(expr.apply, tmp);
386
- });
387
- }
388
-
389
- // Look up function
390
- var fn = _customFns[expr.fn] || _fns[expr.fn];
391
- if (!fn) {
392
- console.warn('CardCompute: unknown function "' + expr.fn + '"');
393
- return undefined;
99
+ if (typeof jsonata === 'undefined') {
100
+ console.error('CardCompute: JSONata not loaded.');
101
+ return Promise.resolve(undefined);
394
102
  }
395
-
396
- return fn(input, evalExpr, expr);
103
+ var ctx = {
104
+ card_data: (node && node.card_data) || {},
105
+ requires: (node && node.requires) || {},
106
+ fetched_sources: (node && node._sourcesData) || {},
107
+ computed_values: (node && node.computed_values) || {},
108
+ };
109
+ return jsonata(expr).evaluate(ctx);
397
110
  }
398
111
 
399
112
  // ===========================================================================
400
- // runevaluate all compute declarations on a node
113
+ // resolvesynchronous deep-get from a node
401
114
  // ===========================================================================
402
115
 
403
- function run(node) {
404
- if (!node || !node.compute) return node;
405
- if (!node.state) node.state = {};
406
-
407
- var keys = Object.keys(node.compute);
408
- for (var i = 0; i < keys.length; i++) {
409
- var key = keys[i];
410
- try {
411
- var val = evalExpr(node.compute[key], node);
412
- _deepSet(node.state, key, val);
413
- } catch (e) {
414
- console.error('CardCompute.run error on "' + (node.id || '?') + '.' + key + '":', e);
415
- }
116
+ function resolve(node, path) {
117
+ if (path && path.indexOf('fetched_sources.') === 0) {
118
+ return _deepGet((node && node._sourcesData) || {}, path.slice('fetched_sources.'.length));
416
119
  }
417
-
418
- return node;
419
- }
420
-
421
- // ===========================================================================
422
- // registerFunction — extend the vocabulary
423
- // ===========================================================================
424
-
425
- function registerFunction(name, fn) {
426
- _customFns[name] = fn;
120
+ return _deepGet(node, path);
427
121
  }
428
122
 
429
123
  // ===========================================================================
430
- // validate — lightweight structural validator for LiveCards nodes
124
+ // validate — lightweight structural validator (sync)
431
125
  // ===========================================================================
432
126
 
433
127
  var VALID_ELEMENT_KINDS = ['metric','table','chart','form','filter','list','notes','todo','alert','narrative','badge','text','markdown','custom'];
434
- var VALID_SOURCE_KINDS = ['api','websocket','static','llm'];
435
128
  var VALID_STATUSES = ['fresh','stale','loading','error'];
436
- var CARD_KEYS = ['id','type','meta','data','view','state','compute'];
437
- var SOURCE_KEYS = ['id','type','meta','data','source','state','compute'];
129
+ var ALLOWED_KEYS = ['id','meta','requires','provides','view','card_data','compute','sources'];
438
130
 
439
131
  function validateNode(node) {
440
132
  var errors = [];
133
+
441
134
  if (!node || typeof node !== 'object' || Array.isArray(node)) {
442
135
  return { ok: false, errors: ['Node must be a non-null object'] };
443
136
  }
444
137
 
445
138
  if (typeof node.id !== 'string' || !node.id) errors.push('id: required, must be a non-empty string');
446
139
 
447
- if (node.type !== 'card' && node.type !== 'source') {
448
- errors.push('type: must be "card" or "source"');
449
- return { ok: false, errors: errors };
450
- }
451
-
452
- var allowed = node.type === 'card' ? CARD_KEYS : SOURCE_KEYS;
453
140
  Object.keys(node).forEach(function (k) {
454
- if (allowed.indexOf(k) === -1) errors.push('Unknown top-level key: "' + k + '"');
141
+ if (ALLOWED_KEYS.indexOf(k) === -1) errors.push('Unknown top-level key: "' + k + '"');
455
142
  });
456
143
 
457
- // state
458
- if (node.state == null || typeof node.state !== 'object' || Array.isArray(node.state)) {
459
- errors.push('state: required, must be an object');
460
- } else if (node.state.status != null && VALID_STATUSES.indexOf(node.state.status) === -1) {
461
- errors.push('state.status: must be one of: ' + VALID_STATUSES.join(', '));
144
+ // card_data
145
+ if (node.card_data == null || typeof node.card_data !== 'object' || Array.isArray(node.card_data)) {
146
+ errors.push('card_data: required, must be an object');
462
147
  }
463
148
 
464
149
  // meta
465
150
  if (node.meta != null) {
466
- if (typeof node.meta !== 'object' || Array.isArray(node.meta)) errors.push('meta: must be an object');
467
- else {
151
+ if (typeof node.meta !== 'object' || Array.isArray(node.meta)) {
152
+ errors.push('meta: must be an object');
153
+ } else {
468
154
  if (node.meta.title != null && typeof node.meta.title !== 'string') errors.push('meta.title: must be a string');
469
155
  if (node.meta.tags != null && !Array.isArray(node.meta.tags)) errors.push('meta.tags: must be an array');
470
156
  }
471
157
  }
472
158
 
473
- // data
474
- if (node.data != null) {
475
- if (typeof node.data !== 'object' || Array.isArray(node.data)) errors.push('data: must be an object');
476
- else {
477
- if (node.data.requires != null && !Array.isArray(node.data.requires)) errors.push('data.requires: must be an array');
478
- if (node.data.provides != null && (typeof node.data.provides !== 'object' || Array.isArray(node.data.provides))) errors.push('data.provides: must be an object');
159
+ // requires
160
+ if (node.requires != null && !Array.isArray(node.requires)) errors.push('requires: must be an array of strings');
161
+
162
+ // provides
163
+ if (node.provides != null) {
164
+ if (!Array.isArray(node.provides)) {
165
+ errors.push('provides: must be an array of { bindTo, src } bindings');
166
+ } else {
167
+ node.provides.forEach(function (p, i) {
168
+ if (!p || typeof p !== 'object' || Array.isArray(p)) {
169
+ errors.push('provides[' + i + ']: must be an object with bindTo and src');
170
+ } else {
171
+ if (typeof p.bindTo !== 'string' || !p.bindTo) errors.push('provides[' + i + ']: missing required "bindTo" string');
172
+ if (typeof p.src !== 'string' || !p.src) errors.push('provides[' + i + ']: missing required "src" string');
173
+ }
174
+ });
479
175
  }
480
176
  }
481
177
 
482
- // compute
178
+ // compute — ordered array of { bindTo, expr } steps
483
179
  if (node.compute != null) {
484
- if (typeof node.compute !== 'object' || Array.isArray(node.compute)) errors.push('compute: must be an object');
485
- else {
486
- Object.keys(node.compute).forEach(function (key) {
487
- var expr = node.compute[key];
488
- if (!expr || typeof expr !== 'object' || Array.isArray(expr)) errors.push('compute.' + key + ': must be a compute expression object');
489
- else if (!expr.fn) errors.push('compute.' + key + ': missing required "fn" property');
490
- else if (!_fns[expr.fn] && !_customFns[expr.fn]) errors.push('compute.' + key + ': unknown function "' + expr.fn + '"');
180
+ if (!Array.isArray(node.compute)) {
181
+ errors.push('compute: must be an array of compute steps');
182
+ } else {
183
+ node.compute.forEach(function (step, i) {
184
+ if (!step || typeof step !== 'object' || Array.isArray(step)) {
185
+ errors.push('compute[' + i + ']: must be a compute step object');
186
+ } else {
187
+ if (typeof step.bindTo !== 'string' || !step.bindTo) errors.push('compute[' + i + ']: missing required "bindTo" property');
188
+ if (typeof step.expr !== 'string' || !step.expr) errors.push('compute[' + i + ']: missing required "expr" string (JSONata expression)');
189
+ }
491
190
  });
492
191
  }
493
192
  }
494
193
 
495
- // Card-specific
496
- if (node.type === 'card') {
497
- if (node.source != null) errors.push('Card nodes must not have "source"');
498
- if (node.view == null || typeof node.view !== 'object' || Array.isArray(node.view)) {
499
- errors.push('view: required for card nodes, must be an object');
194
+ // sources
195
+ if (node.sources != null) {
196
+ if (!Array.isArray(node.sources)) {
197
+ errors.push('sources: must be an array');
198
+ } else {
199
+ node.sources.forEach(function (src, i) {
200
+ if (!src || typeof src !== 'object' || Array.isArray(src)) errors.push('sources[' + i + ']: must be an object');
201
+ else if (typeof src.bindTo !== 'string' || !src.bindTo) errors.push('sources[' + i + ']: missing required "bindTo" property');
202
+ else {
203
+ if (src.outputFile != null && typeof src.outputFile !== 'string') errors.push('sources[' + i + ']: outputFile must be a string');
204
+ if (src.optional != null && typeof src.optional !== 'boolean') errors.push('sources[' + i + ']: optional must be a boolean');
205
+ }
206
+ });
207
+ }
208
+ }
209
+
210
+ // view
211
+ if (node.view != null) {
212
+ if (typeof node.view !== 'object' || Array.isArray(node.view)) {
213
+ errors.push('view: must be an object');
500
214
  } else {
501
215
  if (!Array.isArray(node.view.elements) || node.view.elements.length === 0) {
502
216
  errors.push('view.elements: required, must be a non-empty array');
503
217
  } else {
504
218
  node.view.elements.forEach(function (elem, i) {
505
219
  if (!elem || typeof elem !== 'object') { errors.push('view.elements[' + i + ']: must be an object'); return; }
506
- if (!elem.kind || typeof elem.kind !== 'string') errors.push('view.elements[' + i + '].kind: required');
507
- else if (VALID_ELEMENT_KINDS.indexOf(elem.kind) === -1) errors.push('view.elements[' + i + '].kind: unknown "' + elem.kind + '"');
220
+ if (!elem.kind || typeof elem.kind !== 'string') {
221
+ errors.push('view.elements[' + i + '].kind: required, must be a string');
222
+ } else if (VALID_ELEMENT_KINDS.indexOf(elem.kind) === -1) {
223
+ errors.push('view.elements[' + i + '].kind: unknown kind "' + elem.kind + '". Valid: ' + VALID_ELEMENT_KINDS.join(', '));
224
+ }
508
225
  });
509
226
  }
510
227
  }
511
228
  }
512
229
 
513
- // Source-specific
514
- if (node.type === 'source') {
515
- if (node.view != null) errors.push('Source nodes must not have "view"');
516
- if (node.source == null || typeof node.source !== 'object' || Array.isArray(node.source)) {
517
- errors.push('source: required for source nodes, must be an object');
518
- } else {
519
- if (!node.source.kind || VALID_SOURCE_KINDS.indexOf(node.source.kind) === -1) errors.push('source.kind: required, must be one of: ' + VALID_SOURCE_KINDS.join(', '));
520
- if (typeof node.source.bindTo !== 'string' || !node.source.bindTo) errors.push('source.bindTo: required, must be a state path string');
521
- else if (node.source.bindTo.indexOf('state.') !== 0) errors.push('source.bindTo: must start with "state."');
522
- }
523
- }
524
-
525
230
  return { ok: errors.length === 0, errors: errors };
526
231
  }
527
232
 
233
+ /**
234
+ * Enrich sources with execution context for template interpolation and prompt rendering.
235
+ * Pure function: no side effects, returns new enriched sources array.
236
+ *
237
+ * @param {Array} sources - Array of source definitions
238
+ * @param {Object} context - Execution context containing requires, sourcesData, computed_values
239
+ * @returns {Array} New array of sources with _requires, _sourcesData, _computed_values attached
240
+ */
241
+ function enrichSources(sources, context) {
242
+ if (!sources || sources.length === 0) return [];
243
+ context = context || {};
244
+
245
+ return sources.map(function (src) {
246
+ return Object.assign({}, src, {
247
+ _requires: context.requires || {},
248
+ _sourcesData: context.sourcesData || {},
249
+ _computed_values: context.computed_values || {},
250
+ });
251
+ });
252
+ }
253
+
528
254
  // ===========================================================================
529
255
  // Export
530
256
  // ===========================================================================
@@ -534,13 +260,7 @@
534
260
  eval: evalExpr,
535
261
  resolve: resolve,
536
262
  validate: validateNode,
537
- registerFunction: registerFunction,
538
- get functions() {
539
- var all = {};
540
- Object.keys(_fns).forEach(function (k) { all[k] = _fns[k]; });
541
- Object.keys(_customFns).forEach(function (k) { all[k] = _customFns[k]; });
542
- return all;
543
- }
263
+ enrichSources: enrichSources,
544
264
  };
545
265
 
546
266
  }));