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