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.
- package/README.md +81 -20
- package/board-live-cards-cli.js +37 -0
- package/browser/board-livegraph-runtime.js +1453 -0
- package/browser/board-livegraph-runtime.js.map +1 -0
- package/browser/card-compute.js +153 -433
- package/browser/live-cards.js +868 -115
- package/browser/live-cards.schema.json +90 -83
- package/dist/board-livegraph-runtime/index.cjs +1448 -0
- package/dist/board-livegraph-runtime/index.cjs.map +1 -0
- package/dist/board-livegraph-runtime/index.d.cts +101 -0
- package/dist/board-livegraph-runtime/index.d.ts +101 -0
- package/dist/board-livegraph-runtime/index.js +1441 -0
- package/dist/board-livegraph-runtime/index.js.map +1 -0
- package/dist/card-compute/index.cjs +266 -431
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +77 -49
- package/dist/card-compute/index.d.ts +77 -49
- package/dist/card-compute/index.js +263 -432
- package/dist/card-compute/index.js.map +1 -1
- package/dist/cli/board-live-cards-cli.cjs +2750 -0
- package/dist/cli/board-live-cards-cli.cjs.map +1 -0
- package/dist/cli/board-live-cards-cli.d.cts +205 -0
- package/dist/cli/board-live-cards-cli.d.ts +205 -0
- package/dist/cli/board-live-cards-cli.js +2702 -0
- package/dist/cli/board-live-cards-cli.js.map +1 -0
- package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
- package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
- package/dist/continuous-event-graph/index.cjs +258 -464
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +18 -358
- package/dist/continuous-event-graph/index.d.ts +18 -358
- package/dist/continuous-event-graph/index.js +255 -464
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +4 -4
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +5 -5
- package/dist/event-graph/index.d.ts +5 -5
- package/dist/event-graph/index.js +4 -4
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +1684 -555
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -7
- package/dist/index.d.ts +26 -7
- package/dist/index.js +1678 -555
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +138 -19
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +138 -19
- package/dist/inference/index.js.map +1 -1
- package/dist/journal-DRfJiheM.d.cts +28 -0
- package/dist/journal-NLYuqege.d.ts +28 -0
- package/dist/live-cards-bridge-Or7fdEJV.d.ts +316 -0
- package/dist/live-cards-bridge-vGJ6tMzN.d.cts +316 -0
- package/dist/schedule-CMcZe5Ny.d.ts +21 -0
- package/dist/schedule-CiucyCan.d.cts +21 -0
- package/dist/step-machine/index.cjs +18 -1
- package/dist/step-machine/index.cjs.map +1 -1
- package/dist/step-machine/index.d.cts +2 -2
- package/dist/step-machine/index.d.ts +2 -2
- package/dist/step-machine/index.js +18 -1
- package/dist/step-machine/index.js.map +1 -1
- package/dist/stores/file.d.cts +1 -1
- package/dist/stores/file.d.ts +1 -1
- package/dist/stores/index.d.cts +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/localStorage.d.cts +1 -1
- package/dist/stores/localStorage.d.ts +1 -1
- package/dist/stores/memory.d.cts +1 -1
- package/dist/stores/memory.d.ts +1 -1
- package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
- package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
- package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
- package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
- package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
- package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
- package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
- package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
- package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +217 -0
- package/examples/browser/livecards-browser/index.html +41 -0
- package/examples/browser/{index.html → step-machine-browser/index.html} +53 -53
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
- package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
- package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
- package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
- package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
- package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
- package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
- package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
- package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
- package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
- package/examples/example-board/board.yaml +23 -0
- package/examples/example-board/bootstrap_payload.json +1 -0
- package/examples/example-board/cards/card-chain-region-alert.json +39 -0
- package/examples/example-board/cards/card-chain-region-totals.json +26 -0
- package/examples/example-board/cards/card-chain-top-region.json +24 -0
- package/examples/example-board/cards/card-ex-actions.json +32 -0
- package/examples/example-board/cards/card-ex-chart.json +30 -0
- package/examples/example-board/cards/card-ex-filter.json +36 -0
- package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
- package/examples/example-board/cards/card-ex-form.json +91 -0
- package/examples/example-board/cards/card-ex-list.json +22 -0
- package/examples/example-board/cards/card-ex-markdown.json +17 -0
- package/examples/example-board/cards/card-ex-metric.json +19 -0
- package/examples/example-board/cards/card-ex-narrative.json +36 -0
- package/examples/example-board/cards/card-ex-source-http.json +28 -0
- package/examples/example-board/cards/card-ex-source.json +21 -0
- package/examples/example-board/cards/card-ex-status.json +35 -0
- package/examples/example-board/cards/card-ex-table.json +30 -0
- package/examples/example-board/cards/card-ex-todo.json +29 -0
- package/examples/example-board/demo-chat-handler.js +69 -0
- package/examples/example-board/demo-server.js +87 -0
- package/examples/example-board/demo-shell-browser.html +806 -0
- package/examples/example-board/demo-shell-with-server.html +280 -0
- package/examples/example-board/demo-shell.html +62 -0
- package/examples/example-board/demo-task-executor.js +255 -0
- package/examples/example-board/mock.db +15 -0
- package/examples/example-board/reusable-board-runtime-client.js +265 -0
- package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
- package/examples/example-board/reusable-server-runtime.js +1284 -0
- package/examples/index.html +799 -0
- package/examples/{batch → npm-libs/batch}/batch-step-machine.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-cards-board.ts +18 -18
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-portfolio-dashboard.ts +24 -24
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/portfolio-tracker.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-monitoring.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-pipeline.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/soc-incident-board.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/stock-dashboard.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/ci-cd-pipeline.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/executor-diamond.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/executor-pipeline.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/research-pipeline.ts +1 -1
- package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/multi-stage-etl.ts +1 -1
- package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/url-processing-pipeline.ts +1 -1
- package/examples/{inference → npm-libs/inference}/azure-deployment.ts +1 -1
- package/examples/{inference → npm-libs/inference}/copilot-cli.ts +1 -1
- package/examples/{inference → npm-libs/inference}/data-pipeline.ts +1 -1
- package/examples/{inference → npm-libs/inference}/pluggable-adapters.ts +1 -1
- package/examples/{node → npm-libs/node}/ai-conversation.ts +1 -1
- package/examples/{node → npm-libs/node}/simple-greeting.ts +2 -2
- package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
- package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
- package/package.json +27 -2
- package/schema/board-status.schema.json +118 -0
- package/schema/card-runtime.schema.json +25 -0
- package/schema/flow.schema.json +5 -0
- package/schema/live-cards.schema.json +90 -83
- package/step-machine-cli.js +674 -0
- package/browser/ingest-board.js +0 -296
- package/examples/ingest.js +0 -733
- /package/examples/{flows → npm-libs/flows}/ai-conversation.yaml +0 -0
- /package/examples/{flows → npm-libs/flows}/order-processing.yaml +0 -0
- /package/examples/{flows → npm-libs/flows}/simple-greeting.yaml +0 -0
package/browser/card-compute.js
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
//
|
|
1
|
+
// card-compute.js — JSONata-powered compute engine for LiveCards (browser build)
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
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)
|
|
8
|
-
// CardCompute.eval(expr, node)
|
|
9
|
-
// CardCompute.resolve(node, path)
|
|
10
|
-
// CardCompute.
|
|
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
|
|
14
|
-
// {
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
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();
|
|
21
|
+
module.exports = factory(); // Node / CommonJS
|
|
23
22
|
} else if (typeof define === 'function' && define.amd) {
|
|
24
|
-
define(factory);
|
|
23
|
+
define(factory); // AMD
|
|
25
24
|
} else {
|
|
26
|
-
root.CardCompute = factory();
|
|
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 ||
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
for (
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
for (
|
|
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
|
-
//
|
|
56
|
+
// run — evaluate all compute steps on a node (async, returns Promise<node>)
|
|
63
57
|
// ===========================================================================
|
|
64
58
|
|
|
65
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
var
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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
|
-
//
|
|
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 (
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
113
|
+
// resolve — synchronous deep-get from a node
|
|
401
114
|
// ===========================================================================
|
|
402
115
|
|
|
403
|
-
function
|
|
404
|
-
if (
|
|
405
|
-
|
|
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
|
|
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
|
|
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 (
|
|
141
|
+
if (ALLOWED_KEYS.indexOf(k) === -1) errors.push('Unknown top-level key: "' + k + '"');
|
|
455
142
|
});
|
|
456
143
|
|
|
457
|
-
//
|
|
458
|
-
if (node.
|
|
459
|
-
errors.push('
|
|
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))
|
|
467
|
-
|
|
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
|
-
//
|
|
474
|
-
if (node.
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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 (
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (!
|
|
489
|
-
|
|
490
|
-
else
|
|
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
|
-
//
|
|
496
|
-
if (node.
|
|
497
|
-
if (node.
|
|
498
|
-
|
|
499
|
-
|
|
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')
|
|
507
|
-
|
|
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
|
-
|
|
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
|
}));
|