yaml-flow 5.4.2 → 6.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/board-live-cards-cli.js +2 -2
- package/board-livecards-server-runtime.js +486 -547
- package/browser/asset-integrity.json +10 -0
- package/browser/board-livegraph-engine.js +2 -1676
- package/browser/board-livegraph-engine.js.map +1 -1
- package/browser/live-cards.js +347 -26
- package/browser/live-cards.schema.json +418 -132
- package/card-store.js +37 -0
- package/dist/batch/index.cjs +1 -108
- package/dist/batch/index.cjs.map +1 -1
- package/dist/batch/index.js +1 -106
- package/dist/batch/index.js.map +1 -1
- package/dist/board-live-cards-lib-Bg6EvCo5.d.cts +136 -0
- package/dist/board-live-cards-lib-jM2uYG1v.d.ts +136 -0
- package/dist/board-live-cards-public-CltXYgaY.d.cts +314 -0
- package/dist/board-live-cards-public-f-E-FAyp.d.ts +314 -0
- package/dist/board-livegraph-runtime/index.cjs +2 -1671
- package/dist/board-livegraph-runtime/index.cjs.map +1 -1
- package/dist/board-livegraph-runtime/index.d.cts +1 -2
- package/dist/board-livegraph-runtime/index.d.ts +1 -2
- package/dist/board-livegraph-runtime/index.js +2 -1662
- package/dist/board-livegraph-runtime/index.js.map +1 -1
- package/dist/board-livegraph-runtime/jsonata-sync.cjs +7587 -0
- package/dist/card-compute/index.cjs +9 -7159
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +22 -0
- package/dist/card-compute/index.d.ts +22 -0
- package/dist/card-compute/index.js +9 -7145
- package/dist/card-compute/index.js.map +1 -1
- package/dist/card-compute/jsonata-sync.cjs +7587 -0
- package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -0
- package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -0
- package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +24 -0
- package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +24 -0
- package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -0
- package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -0
- package/dist/cli/browser-api/card-store-browser-api.cjs +2 -0
- package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -0
- package/dist/cli/browser-api/card-store-browser-api.d.cts +26 -0
- package/dist/cli/browser-api/card-store-browser-api.d.ts +26 -0
- package/dist/cli/browser-api/card-store-browser-api.js +2 -0
- package/dist/cli/browser-api/card-store-browser-api.js.map +1 -0
- package/dist/cli/browser-api/jsonata-sync.cjs +7587 -0
- package/dist/cli/node/artifacts-store-cli.cjs +11 -0
- package/dist/cli/node/artifacts-store-cli.cjs.map +1 -0
- package/dist/cli/node/artifacts-store-cli.d.cts +8 -0
- package/dist/cli/node/artifacts-store-cli.d.ts +8 -0
- package/dist/cli/node/artifacts-store-cli.js +11 -0
- package/dist/cli/node/artifacts-store-cli.js.map +1 -0
- package/dist/cli/node/board-live-cards-cli.cjs +15 -0
- package/dist/cli/node/board-live-cards-cli.cjs.map +1 -0
- package/dist/cli/node/board-live-cards-cli.d.cts +20 -0
- package/dist/cli/node/board-live-cards-cli.d.ts +20 -0
- package/dist/cli/node/board-live-cards-cli.js +15 -0
- package/dist/cli/node/board-live-cards-cli.js.map +1 -0
- package/dist/cli/node/card-store-cli.cjs +8 -0
- package/dist/cli/node/card-store-cli.cjs.map +1 -0
- package/dist/cli/node/card-store-cli.d.cts +15 -0
- package/dist/cli/node/card-store-cli.d.ts +15 -0
- package/dist/cli/node/card-store-cli.js +8 -0
- package/dist/cli/node/card-store-cli.js.map +1 -0
- package/dist/cli/node/fs-board-adapter.cjs +14 -0
- package/dist/cli/node/fs-board-adapter.cjs.map +1 -0
- package/dist/cli/node/fs-board-adapter.d.cts +204 -0
- package/dist/cli/node/fs-board-adapter.d.ts +204 -0
- package/dist/cli/node/fs-board-adapter.js +14 -0
- package/dist/cli/node/fs-board-adapter.js.map +1 -0
- package/dist/cli/node/jsonata-sync.cjs +7587 -0
- package/dist/cli/node/source-cli-task-executor.cjs +11 -0
- package/dist/cli/node/source-cli-task-executor.cjs.map +1 -0
- package/dist/cli/node/source-cli-task-executor.d.cts +1 -0
- package/dist/cli/node/source-cli-task-executor.d.ts +1 -0
- package/dist/cli/node/source-cli-task-executor.js +11 -0
- package/dist/cli/node/source-cli-task-executor.js.map +1 -0
- package/dist/config/index.cjs +1 -79
- package/dist/config/index.cjs.map +1 -1
- package/dist/config/index.js +1 -76
- package/dist/config/index.js.map +1 -1
- package/dist/continuous-event-graph/index.cjs +2 -2129
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +81 -5
- package/dist/continuous-event-graph/index.d.ts +81 -5
- package/dist/continuous-event-graph/index.js +2 -2088
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/continuous-event-graph/jsonata-sync.cjs +7587 -0
- package/dist/event-graph/index.cjs +22 -8292
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.js +22 -8237
- package/dist/event-graph/index.js.map +1 -1
- package/dist/execution-refs.cjs +2 -0
- package/dist/execution-refs.cjs.map +1 -0
- package/dist/execution-refs.d.cts +222 -0
- package/dist/execution-refs.d.ts +222 -0
- package/dist/execution-refs.js +2 -0
- package/dist/execution-refs.js.map +1 -0
- package/dist/index.cjs +29 -13221
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +29 -13112
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +5 -617
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.js +5 -610
- package/dist/inference/index.js.map +1 -1
- package/dist/jsonata-sync.cjs +7587 -0
- package/dist/{live-cards-bridge-x5XREkXm.d.cts → live-cards-bridge-BXbVTsna.d.cts} +27 -4
- package/dist/{live-cards-bridge-EQjytzI_.d.ts → live-cards-bridge-Ds28XR15.d.ts} +27 -4
- package/dist/pycli/quickjs-board-runtime.global.js +9 -0
- package/dist/pycli/quickjs-board-runtime.global.js.map +1 -0
- package/dist/pycli/quickjs-step-machine-runtime.global.js +5 -0
- package/dist/pycli/quickjs-step-machine-runtime.global.js.map +1 -0
- package/dist/step-machine/index.cjs +11 -7129
- package/dist/step-machine/index.cjs.map +1 -1
- package/dist/step-machine/index.js +11 -7113
- package/dist/step-machine/index.js.map +1 -1
- package/dist/storage-refs.cjs +10 -0
- package/dist/storage-refs.cjs.map +1 -0
- package/dist/storage-refs.d.cts +92 -0
- package/dist/storage-refs.d.ts +92 -0
- package/dist/storage-refs.js +10 -0
- package/dist/storage-refs.js.map +1 -0
- package/dist/stores/file.cjs +1 -114
- package/dist/stores/file.cjs.map +1 -1
- package/dist/stores/file.js +1 -112
- package/dist/stores/file.js.map +1 -1
- package/dist/stores/index.cjs +1 -231
- package/dist/stores/index.cjs.map +1 -1
- package/dist/stores/index.js +1 -227
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/localStorage.cjs +1 -76
- package/dist/stores/localStorage.cjs.map +1 -1
- package/dist/stores/localStorage.js +1 -74
- package/dist/stores/localStorage.js.map +1 -1
- package/dist/stores/memory.cjs +1 -47
- package/dist/stores/memory.cjs.map +1 -1
- package/dist/stores/memory.js +1 -45
- package/dist/stores/memory.js.map +1 -1
- package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +292 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-fetch-prices.js +218 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-fetch-prices.py +201 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-inference-adapter.js +25 -16
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-public.js +553 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.py +365 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/--base-ref/.runtime-out +1 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/--base-ref/board-graph.json +32 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +53 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +15 -6
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +6 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/poll-status-cli.js +57 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +1 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +1 -1
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +7 -2
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +6 -2
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/_board_pycli.py +97 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/add-cards.py +50 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/init-board.py +44 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/poll-status.py +70 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/reset-board-dir.py +36 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/inline-python-demo.flow.yaml +26 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/inline-python-handlers.py +39 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker-pycli.flow.yaml +80 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +25 -172
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +40 -34
- package/examples/cli/step-machine-cli/portfolio-tracker/run-inline-python-demo-pycli.py +46 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker-pycli.py +77 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +1 -2
- package/examples/example-board/agent-instructions.md +11 -5
- package/examples/example-board/demo-chat-handler.js +14 -4
- package/examples/example-board/demo-server-config.json +1 -0
- package/examples/example-board/demo-server.js +14 -7
- package/examples/example-board/demo-shell-browser.html +5 -4
- package/examples/example-board/demo-shell-with-server.html +6 -5
- package/examples/example-board/demo-task-executor.js +81 -35
- package/examples/index.html +0 -14
- package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +0 -1
- package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +1 -2
- package/package.json +39 -3
- package/schema/live-cards.schema.json +418 -132
- package/dist/cli/board-live-cards-cli.cjs +0 -10650
- package/dist/cli/board-live-cards-cli.cjs.map +0 -1
- package/dist/cli/board-live-cards-cli.d.cts +0 -179
- package/dist/cli/board-live-cards-cli.d.ts +0 -179
- package/dist/cli/board-live-cards-cli.js +0 -10598
- package/dist/cli/board-live-cards-cli.js.map +0 -1
- package/dist/journal-9HEgs7dU.d.ts +0 -28
- package/dist/journal-B-JCfQnh.d.cts +0 -28
- package/dist/schedule-Cszq9LYY.d.ts +0 -21
- package/dist/schedule-qWNL0RQh.d.cts +0 -21
- package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +0 -22
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +0 -16
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-risk-assessment.json +0 -28
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +0 -15
- package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +0 -15
- package/examples/browser/boards/portfolio-tracker/cards/rebalancing-strategy.json +0 -28
- package/examples/browser/boards/portfolio-tracker/fetch-prices.js +0 -43
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +0 -96
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +0 -7
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +0 -351
|
@@ -1,1663 +1,3 @@
|
|
|
1
|
-
import jsonata2 from 'jsonata';
|
|
2
|
-
|
|
3
|
-
import 'child_process';
|
|
4
|
-
|
|
5
|
-
// src/card-compute/index.ts
|
|
6
|
-
|
|
7
|
-
// src/card-compute/index.ts
|
|
8
|
-
function deepGet(obj, path) {
|
|
9
|
-
if (!path || !obj) return void 0;
|
|
10
|
-
const parts = path.split(".");
|
|
11
|
-
let cur = obj;
|
|
12
|
-
for (let i = 0; i < parts.length; i++) {
|
|
13
|
-
if (cur == null) return void 0;
|
|
14
|
-
cur = cur[parts[i]];
|
|
15
|
-
}
|
|
16
|
-
return cur;
|
|
17
|
-
}
|
|
18
|
-
function deepSet(obj, path, value) {
|
|
19
|
-
const parts = path.split(".");
|
|
20
|
-
let cur = obj;
|
|
21
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
22
|
-
if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") cur[parts[i]] = {};
|
|
23
|
-
cur = cur[parts[i]];
|
|
24
|
-
}
|
|
25
|
-
cur[parts[parts.length - 1]] = value;
|
|
26
|
-
}
|
|
27
|
-
async function run(node, options) {
|
|
28
|
-
if (!node?.compute?.length) return node;
|
|
29
|
-
if (!node.card_data) node.card_data = {};
|
|
30
|
-
node.computed_values = {};
|
|
31
|
-
node._sourcesData = options?.sourcesData ?? {};
|
|
32
|
-
const ctx = {
|
|
33
|
-
card_data: node.card_data,
|
|
34
|
-
requires: node.requires ?? {},
|
|
35
|
-
fetched_sources: node._sourcesData,
|
|
36
|
-
computed_values: node.computed_values
|
|
37
|
-
};
|
|
38
|
-
for (const step of node.compute) {
|
|
39
|
-
try {
|
|
40
|
-
const val = await jsonata2(step.expr).evaluate(ctx);
|
|
41
|
-
deepSet(node.computed_values, step.bindTo, val);
|
|
42
|
-
ctx.computed_values = node.computed_values;
|
|
43
|
-
} catch (err) {
|
|
44
|
-
console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return node;
|
|
48
|
-
}
|
|
49
|
-
async function evalExpr(expr, node) {
|
|
50
|
-
const ctx = {
|
|
51
|
-
card_data: node.card_data ?? {},
|
|
52
|
-
requires: node.requires ?? {},
|
|
53
|
-
fetched_sources: node._sourcesData ?? {},
|
|
54
|
-
computed_values: node.computed_values ?? {}
|
|
55
|
-
};
|
|
56
|
-
return jsonata2(expr).evaluate(ctx);
|
|
57
|
-
}
|
|
58
|
-
function resolve(node, path) {
|
|
59
|
-
if (path.startsWith("fetched_sources.")) {
|
|
60
|
-
return deepGet(node._sourcesData ?? {}, path.slice("fetched_sources.".length));
|
|
61
|
-
}
|
|
62
|
-
return deepGet(node, path);
|
|
63
|
-
}
|
|
64
|
-
var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
|
|
65
|
-
"metric",
|
|
66
|
-
"table",
|
|
67
|
-
"editable-table",
|
|
68
|
-
"chart",
|
|
69
|
-
"form",
|
|
70
|
-
"filter",
|
|
71
|
-
"list",
|
|
72
|
-
"notes",
|
|
73
|
-
"todo",
|
|
74
|
-
"alert",
|
|
75
|
-
"narrative",
|
|
76
|
-
"badge",
|
|
77
|
-
"text",
|
|
78
|
-
"markdown",
|
|
79
|
-
"ref",
|
|
80
|
-
"custom",
|
|
81
|
-
"actions"
|
|
82
|
-
]);
|
|
83
|
-
var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "card_data", "compute", "source_defs"]);
|
|
84
|
-
function validateNode(node) {
|
|
85
|
-
const errors = [];
|
|
86
|
-
if (!node || typeof node !== "object" || Array.isArray(node)) {
|
|
87
|
-
return { ok: false, errors: ["Node must be a non-null object"] };
|
|
88
|
-
}
|
|
89
|
-
const n = node;
|
|
90
|
-
if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
|
|
91
|
-
for (const key of Object.keys(n)) {
|
|
92
|
-
if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
|
|
93
|
-
}
|
|
94
|
-
if (n.card_data == null || typeof n.card_data !== "object" || Array.isArray(n.card_data)) {
|
|
95
|
-
errors.push("card_data: required, must be an object");
|
|
96
|
-
}
|
|
97
|
-
if (n.meta != null) {
|
|
98
|
-
if (typeof n.meta !== "object" || Array.isArray(n.meta)) {
|
|
99
|
-
errors.push("meta: must be an object");
|
|
100
|
-
} else {
|
|
101
|
-
const meta = n.meta;
|
|
102
|
-
if (meta.title != null && typeof meta.title !== "string") errors.push("meta.title: must be a string");
|
|
103
|
-
if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (n.requires != null && !Array.isArray(n.requires)) errors.push("requires: must be an array of strings");
|
|
107
|
-
if (n.provides != null) {
|
|
108
|
-
if (!Array.isArray(n.provides)) {
|
|
109
|
-
errors.push("provides: must be an array of { bindTo, ref } bindings");
|
|
110
|
-
} else {
|
|
111
|
-
n.provides.forEach((p, i) => {
|
|
112
|
-
if (!p || typeof p !== "object" || Array.isArray(p)) {
|
|
113
|
-
errors.push(`provides[${i}]: must be an object with bindTo and ref`);
|
|
114
|
-
} else {
|
|
115
|
-
const b = p;
|
|
116
|
-
if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
|
|
117
|
-
if (typeof b.ref !== "string" || !b.ref) errors.push(`provides[${i}]: missing required "ref" string`);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (n.compute != null) {
|
|
123
|
-
if (!Array.isArray(n.compute)) {
|
|
124
|
-
errors.push("compute: must be an array of compute steps");
|
|
125
|
-
} else {
|
|
126
|
-
n.compute.forEach((step, i) => {
|
|
127
|
-
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
128
|
-
errors.push(`compute[${i}]: must be a compute step object`);
|
|
129
|
-
} else {
|
|
130
|
-
const s = step;
|
|
131
|
-
if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
|
|
132
|
-
if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (n.source_defs != null) {
|
|
138
|
-
if (!Array.isArray(n.source_defs)) {
|
|
139
|
-
errors.push("source_defs: must be an array");
|
|
140
|
-
} else {
|
|
141
|
-
const bindTos = /* @__PURE__ */ new Set();
|
|
142
|
-
const outputFiles = /* @__PURE__ */ new Set();
|
|
143
|
-
n.source_defs.forEach((src, i) => {
|
|
144
|
-
if (!src || typeof src !== "object" || Array.isArray(src)) {
|
|
145
|
-
errors.push(`source_defs[${i}]: must be an object`);
|
|
146
|
-
} else {
|
|
147
|
-
const s = src;
|
|
148
|
-
if (typeof s.bindTo !== "string" || !s.bindTo) {
|
|
149
|
-
errors.push(`source_defs[${i}]: missing required "bindTo" property`);
|
|
150
|
-
} else {
|
|
151
|
-
if (bindTos.has(s.bindTo)) {
|
|
152
|
-
errors.push(`source_defs[${i}]: bindTo "${s.bindTo}" is not unique across source_defs`);
|
|
153
|
-
}
|
|
154
|
-
bindTos.add(s.bindTo);
|
|
155
|
-
}
|
|
156
|
-
if (typeof s.outputFile !== "string" || !s.outputFile) {
|
|
157
|
-
errors.push(`source_defs[${i}]: missing required "outputFile" property`);
|
|
158
|
-
} else {
|
|
159
|
-
if (outputFiles.has(s.outputFile)) {
|
|
160
|
-
errors.push(`source_defs[${i}]: outputFile "${s.outputFile}" is not unique across source_defs`);
|
|
161
|
-
}
|
|
162
|
-
outputFiles.add(s.outputFile);
|
|
163
|
-
}
|
|
164
|
-
if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
|
|
165
|
-
errors.push(`source_defs[${i}]: optionalForCompletionGating must be a boolean`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (n.view != null) {
|
|
172
|
-
if (typeof n.view !== "object" || Array.isArray(n.view)) {
|
|
173
|
-
errors.push("view: must be an object");
|
|
174
|
-
} else {
|
|
175
|
-
const view = n.view;
|
|
176
|
-
if (!Array.isArray(view.elements) || view.elements.length === 0) {
|
|
177
|
-
errors.push("view.elements: required, must be a non-empty array");
|
|
178
|
-
} else {
|
|
179
|
-
view.elements.forEach((elem, i) => {
|
|
180
|
-
if (!elem || typeof elem !== "object") {
|
|
181
|
-
errors.push(`view.elements[${i}]: must be an object`);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if (!elem.kind || typeof elem.kind !== "string") {
|
|
185
|
-
errors.push(`view.elements[${i}].kind: required, must be a string`);
|
|
186
|
-
} else if (!VALID_ELEMENT_KINDS.has(elem.kind)) {
|
|
187
|
-
errors.push(`view.elements[${i}].kind: unknown kind "${elem.kind}". Valid: ${[...VALID_ELEMENT_KINDS].join(", ")}`);
|
|
188
|
-
}
|
|
189
|
-
if (elem.data != null && (typeof elem.data !== "object" || Array.isArray(elem.data))) {
|
|
190
|
-
errors.push(`view.elements[${i}].data: must be an object`);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
|
|
195
|
-
if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return { ok: errors.length === 0, errors };
|
|
199
|
-
}
|
|
200
|
-
async function enrichSources(source_defs, context) {
|
|
201
|
-
if (!source_defs || source_defs.length === 0) return [];
|
|
202
|
-
const evalCtx = {
|
|
203
|
-
card_data: context.card_data ?? {},
|
|
204
|
-
requires: context.requires ?? {}
|
|
205
|
-
};
|
|
206
|
-
return Promise.all(
|
|
207
|
-
source_defs.map(async (src) => {
|
|
208
|
-
const _projections = {};
|
|
209
|
-
if (src.projections && typeof src.projections === "object" && !Array.isArray(src.projections)) {
|
|
210
|
-
for (const [key, expr] of Object.entries(src.projections)) {
|
|
211
|
-
if (typeof expr === "string" && expr.trim().length > 0) {
|
|
212
|
-
try {
|
|
213
|
-
_projections[key] = await jsonata2(expr).evaluate(evalCtx);
|
|
214
|
-
} catch {
|
|
215
|
-
_projections[key] = void 0;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return { ...src, _projections };
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
var CardCompute = {
|
|
225
|
-
run,
|
|
226
|
-
eval: evalExpr,
|
|
227
|
-
resolve,
|
|
228
|
-
validate: validateNode,
|
|
229
|
-
enrichSources
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// src/event-graph/constants.ts
|
|
233
|
-
var TASK_STATUS = {
|
|
234
|
-
RUNNING: "running",
|
|
235
|
-
COMPLETED: "completed",
|
|
236
|
-
FAILED: "failed",
|
|
237
|
-
INACTIVATED: "inactivated"
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
// src/event-graph/graph-helpers.ts
|
|
241
|
-
function getProvides(task) {
|
|
242
|
-
if (!task) return [];
|
|
243
|
-
if (Array.isArray(task.provides)) return task.provides;
|
|
244
|
-
return [];
|
|
245
|
-
}
|
|
246
|
-
function getRequires(task) {
|
|
247
|
-
if (!task) return [];
|
|
248
|
-
if (Array.isArray(task.requires)) return task.requires;
|
|
249
|
-
return [];
|
|
250
|
-
}
|
|
251
|
-
function getAllTasks(graph) {
|
|
252
|
-
return graph.tasks ?? {};
|
|
253
|
-
}
|
|
254
|
-
function isNonActiveTask(taskState) {
|
|
255
|
-
if (!taskState) return false;
|
|
256
|
-
return taskState.status === TASK_STATUS.FAILED || taskState.status === TASK_STATUS.INACTIVATED;
|
|
257
|
-
}
|
|
258
|
-
function getRefreshStrategy(taskConfig, graphSettings) {
|
|
259
|
-
return taskConfig.refreshStrategy ?? graphSettings?.refreshStrategy ?? "data-changed";
|
|
260
|
-
}
|
|
261
|
-
function getMaxExecutions(taskConfig) {
|
|
262
|
-
return taskConfig.maxExecutions;
|
|
263
|
-
}
|
|
264
|
-
function computeAvailableOutputs(graph, taskStates) {
|
|
265
|
-
const outputs = /* @__PURE__ */ new Set();
|
|
266
|
-
for (const [taskName, taskState] of Object.entries(taskStates)) {
|
|
267
|
-
if (taskState.status === TASK_STATUS.COMPLETED) {
|
|
268
|
-
const taskConfig = graph.tasks[taskName];
|
|
269
|
-
if (taskConfig) {
|
|
270
|
-
const provides = getProvides(taskConfig);
|
|
271
|
-
provides.forEach((output) => outputs.add(output));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return Array.from(outputs);
|
|
276
|
-
}
|
|
277
|
-
function groupTasksByProvides(candidateTaskNames, tasks) {
|
|
278
|
-
const outputGroups = {};
|
|
279
|
-
candidateTaskNames.forEach((taskName) => {
|
|
280
|
-
const task = tasks[taskName];
|
|
281
|
-
if (!task) return;
|
|
282
|
-
const provides = getProvides(task);
|
|
283
|
-
provides.forEach((output) => {
|
|
284
|
-
if (!outputGroups[output]) {
|
|
285
|
-
outputGroups[output] = [];
|
|
286
|
-
}
|
|
287
|
-
outputGroups[output].push(taskName);
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
return outputGroups;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// src/event-graph/task-transitions.ts
|
|
294
|
-
function applyTaskStart(state, taskName, graph) {
|
|
295
|
-
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
|
|
296
|
-
const startConsumedHashes = {};
|
|
297
|
-
if (graph) {
|
|
298
|
-
const taskConfig = graph.tasks[taskName];
|
|
299
|
-
const requires = getRequires(taskConfig);
|
|
300
|
-
for (const token of requires) {
|
|
301
|
-
for (const [otherName, otherConfig] of Object.entries(graph.tasks)) {
|
|
302
|
-
if (getProvides(otherConfig).includes(token)) {
|
|
303
|
-
const otherState = state.tasks[otherName];
|
|
304
|
-
if (otherState?.lastDataHash) startConsumedHashes[token] = otherState.lastDataHash;
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
const updatedTask = {
|
|
311
|
-
...existingTask,
|
|
312
|
-
status: "running",
|
|
313
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
314
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
315
|
-
progress: 0,
|
|
316
|
-
error: void 0,
|
|
317
|
-
startConsumedHashes
|
|
318
|
-
};
|
|
319
|
-
return {
|
|
320
|
-
...state,
|
|
321
|
-
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
322
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function applyTaskCompletion(state, graph, taskName, result, dataHash, data) {
|
|
326
|
-
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
|
|
327
|
-
const taskConfig = graph.tasks[taskName];
|
|
328
|
-
if (!taskConfig) {
|
|
329
|
-
throw new Error(`Task "${taskName}" not found in graph`);
|
|
330
|
-
}
|
|
331
|
-
let outputTokens;
|
|
332
|
-
if (result && taskConfig.on && taskConfig.on[result]) {
|
|
333
|
-
outputTokens = taskConfig.on[result];
|
|
334
|
-
} else {
|
|
335
|
-
outputTokens = getProvides(taskConfig);
|
|
336
|
-
}
|
|
337
|
-
const lastConsumedHashes = existingTask.startConsumedHashes ? { ...existingTask.startConsumedHashes } : { ...existingTask.lastConsumedHashes };
|
|
338
|
-
if (!existingTask.startConsumedHashes) {
|
|
339
|
-
const requires = taskConfig.requires ?? [];
|
|
340
|
-
for (const token of requires) {
|
|
341
|
-
for (const [otherName, otherConfig] of Object.entries(graph.tasks)) {
|
|
342
|
-
if (getProvides(otherConfig).includes(token)) {
|
|
343
|
-
const otherState = state.tasks[otherName];
|
|
344
|
-
if (otherState?.lastDataHash) {
|
|
345
|
-
lastConsumedHashes[token] = otherState.lastDataHash;
|
|
346
|
-
}
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const updatedTask = {
|
|
353
|
-
...existingTask,
|
|
354
|
-
status: "completed",
|
|
355
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
356
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
357
|
-
executionCount: existingTask.executionCount + 1,
|
|
358
|
-
lastEpoch: existingTask.executionCount + 1,
|
|
359
|
-
lastDataHash: dataHash,
|
|
360
|
-
data,
|
|
361
|
-
lastConsumedHashes,
|
|
362
|
-
error: void 0
|
|
363
|
-
};
|
|
364
|
-
const newOutputs = [.../* @__PURE__ */ new Set([...state.availableOutputs, ...outputTokens])];
|
|
365
|
-
return {
|
|
366
|
-
...state,
|
|
367
|
-
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
368
|
-
availableOutputs: newOutputs,
|
|
369
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
function applyTaskFailure(state, graph, taskName, error) {
|
|
373
|
-
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
|
|
374
|
-
const taskConfig = graph.tasks[taskName];
|
|
375
|
-
if (taskConfig?.retry) {
|
|
376
|
-
const retryCount = existingTask.retryCount + 1;
|
|
377
|
-
if (retryCount <= taskConfig.retry.max_attempts) {
|
|
378
|
-
const updatedTask2 = {
|
|
379
|
-
...existingTask,
|
|
380
|
-
status: "not-started",
|
|
381
|
-
retryCount,
|
|
382
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
383
|
-
error
|
|
384
|
-
};
|
|
385
|
-
return {
|
|
386
|
-
...state,
|
|
387
|
-
tasks: { ...state.tasks, [taskName]: updatedTask2 },
|
|
388
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
const updatedTask = {
|
|
393
|
-
...existingTask,
|
|
394
|
-
status: "failed",
|
|
395
|
-
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
396
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
397
|
-
error,
|
|
398
|
-
executionCount: existingTask.executionCount + 1
|
|
399
|
-
};
|
|
400
|
-
let newOutputs = state.availableOutputs;
|
|
401
|
-
if (taskConfig?.on_failure && taskConfig.on_failure.length > 0) {
|
|
402
|
-
newOutputs = [.../* @__PURE__ */ new Set([...state.availableOutputs, ...taskConfig.on_failure])];
|
|
403
|
-
}
|
|
404
|
-
if (taskConfig?.circuit_breaker && updatedTask.executionCount >= taskConfig.circuit_breaker.max_executions) {
|
|
405
|
-
const breakTokens = taskConfig.circuit_breaker.on_break;
|
|
406
|
-
newOutputs = [.../* @__PURE__ */ new Set([...newOutputs, ...breakTokens])];
|
|
407
|
-
}
|
|
408
|
-
return {
|
|
409
|
-
...state,
|
|
410
|
-
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
411
|
-
availableOutputs: newOutputs,
|
|
412
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
function applyTaskProgress(state, taskName, message, progress) {
|
|
416
|
-
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
|
|
417
|
-
const updatedTask = {
|
|
418
|
-
...existingTask,
|
|
419
|
-
progress: typeof progress === "number" ? progress : existingTask.progress,
|
|
420
|
-
messages: [
|
|
421
|
-
...existingTask.messages ?? [],
|
|
422
|
-
...message ? [{ message, timestamp: (/* @__PURE__ */ new Date()).toISOString(), status: existingTask.status }] : []
|
|
423
|
-
],
|
|
424
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
425
|
-
};
|
|
426
|
-
return {
|
|
427
|
-
...state,
|
|
428
|
-
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
429
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
function applyTaskRestart(state, taskName) {
|
|
433
|
-
const existingTask = state.tasks[taskName];
|
|
434
|
-
if (!existingTask) return state;
|
|
435
|
-
const updatedTask = {
|
|
436
|
-
...existingTask,
|
|
437
|
-
status: "not-started",
|
|
438
|
-
startedAt: void 0,
|
|
439
|
-
completedAt: void 0,
|
|
440
|
-
failedAt: void 0,
|
|
441
|
-
error: void 0,
|
|
442
|
-
data: void 0,
|
|
443
|
-
progress: null,
|
|
444
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
445
|
-
};
|
|
446
|
-
return {
|
|
447
|
-
...state,
|
|
448
|
-
tasks: { ...state.tasks, [taskName]: updatedTask },
|
|
449
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
function createDefaultGraphEngineStore() {
|
|
453
|
-
return {
|
|
454
|
-
status: "not-started",
|
|
455
|
-
executionCount: 0,
|
|
456
|
-
retryCount: 0,
|
|
457
|
-
lastEpoch: 0,
|
|
458
|
-
messages: [],
|
|
459
|
-
progress: null
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// src/continuous-event-graph/core.ts
|
|
464
|
-
function createLiveGraph(config, executionId) {
|
|
465
|
-
const id = executionId ?? `live-${Date.now()}`;
|
|
466
|
-
const tasks = {};
|
|
467
|
-
for (const taskName of Object.keys(config.tasks)) {
|
|
468
|
-
tasks[taskName] = createDefaultGraphEngineStore2();
|
|
469
|
-
}
|
|
470
|
-
const state = {
|
|
471
|
-
status: "running",
|
|
472
|
-
tasks,
|
|
473
|
-
availableOutputs: [],
|
|
474
|
-
stuckDetection: { is_stuck: false, stuck_description: null, outputs_unresolvable: [], tasks_blocked: [] },
|
|
475
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
476
|
-
executionId: id,
|
|
477
|
-
executionConfig: {
|
|
478
|
-
executionMode: config.settings.execution_mode ?? "eligibility-mode",
|
|
479
|
-
conflictStrategy: config.settings.conflict_strategy ?? "alphabetical",
|
|
480
|
-
completionStrategy: config.settings.completion
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
return { config, state };
|
|
484
|
-
}
|
|
485
|
-
function applyEvent(live, event) {
|
|
486
|
-
const { config, state } = live;
|
|
487
|
-
if ("executionId" in event && event.executionId && event.executionId !== state.executionId) {
|
|
488
|
-
return live;
|
|
489
|
-
}
|
|
490
|
-
switch (event.type) {
|
|
491
|
-
// --- Execution state transitions ---
|
|
492
|
-
case "task-started":
|
|
493
|
-
return { config, state: applyTaskStart(state, event.taskName, config) };
|
|
494
|
-
case "task-completed":
|
|
495
|
-
return { config, state: applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data) };
|
|
496
|
-
case "task-failed":
|
|
497
|
-
return { config, state: applyTaskFailure(state, config, event.taskName, event.error) };
|
|
498
|
-
case "task-progress":
|
|
499
|
-
return { config, state: applyTaskProgress(state, event.taskName, event.message, event.progress) };
|
|
500
|
-
case "task-restart":
|
|
501
|
-
return { config, state: applyTaskRestart(state, event.taskName) };
|
|
502
|
-
case "inject-tokens":
|
|
503
|
-
return {
|
|
504
|
-
config,
|
|
505
|
-
state: {
|
|
506
|
-
...state,
|
|
507
|
-
availableOutputs: [.../* @__PURE__ */ new Set([...state.availableOutputs, ...event.tokens])],
|
|
508
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
case "agent-action":
|
|
512
|
-
return { config, state: applyAgentAction(state, event.action) };
|
|
513
|
-
// --- Structural mutations ---
|
|
514
|
-
case "task-upsert":
|
|
515
|
-
return addNode(live, event.taskName, event.taskConfig);
|
|
516
|
-
case "task-removal":
|
|
517
|
-
return removeNode(live, event.taskName);
|
|
518
|
-
case "node-requires-add":
|
|
519
|
-
return addRequires(live, event.nodeName, event.tokens);
|
|
520
|
-
case "node-requires-remove":
|
|
521
|
-
return removeRequires(live, event.nodeName, event.tokens);
|
|
522
|
-
case "node-provides-add":
|
|
523
|
-
return addProvides(live, event.nodeName, event.tokens);
|
|
524
|
-
case "node-provides-remove":
|
|
525
|
-
return removeProvides(live, event.nodeName, event.tokens);
|
|
526
|
-
default:
|
|
527
|
-
return live;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
function applyEvents(live, events) {
|
|
531
|
-
return events.reduce((current, event) => applyEvent(current, event), live);
|
|
532
|
-
}
|
|
533
|
-
function addNode(live, name, taskConfig) {
|
|
534
|
-
const exists = !!live.config.tasks[name];
|
|
535
|
-
return {
|
|
536
|
-
config: {
|
|
537
|
-
...live.config,
|
|
538
|
-
tasks: { ...live.config.tasks, [name]: taskConfig }
|
|
539
|
-
},
|
|
540
|
-
state: {
|
|
541
|
-
...live.state,
|
|
542
|
-
tasks: {
|
|
543
|
-
...live.state.tasks,
|
|
544
|
-
[name]: exists ? live.state.tasks[name] : createDefaultGraphEngineStore2()
|
|
545
|
-
},
|
|
546
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
function removeNode(live, name) {
|
|
551
|
-
if (!live.config.tasks[name]) return live;
|
|
552
|
-
const { [name]: _removedConfig, ...remainingTasks } = live.config.tasks;
|
|
553
|
-
const { [name]: _removedState, ...remainingStates } = live.state.tasks;
|
|
554
|
-
return {
|
|
555
|
-
config: {
|
|
556
|
-
...live.config,
|
|
557
|
-
tasks: remainingTasks
|
|
558
|
-
},
|
|
559
|
-
state: {
|
|
560
|
-
...live.state,
|
|
561
|
-
tasks: remainingStates,
|
|
562
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
function addRequires(live, nodeName, tokens) {
|
|
567
|
-
const task = live.config.tasks[nodeName];
|
|
568
|
-
if (!task) return live;
|
|
569
|
-
const current = getRequires(task);
|
|
570
|
-
const toAdd = tokens.filter((t) => !current.includes(t));
|
|
571
|
-
if (toAdd.length === 0) return live;
|
|
572
|
-
return {
|
|
573
|
-
config: {
|
|
574
|
-
...live.config,
|
|
575
|
-
tasks: {
|
|
576
|
-
...live.config.tasks,
|
|
577
|
-
[nodeName]: { ...task, requires: [...current, ...toAdd] }
|
|
578
|
-
}
|
|
579
|
-
},
|
|
580
|
-
state: live.state
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
function removeRequires(live, nodeName, tokens) {
|
|
584
|
-
const task = live.config.tasks[nodeName];
|
|
585
|
-
if (!task) return live;
|
|
586
|
-
const current = getRequires(task);
|
|
587
|
-
const remaining = current.filter((t) => !tokens.includes(t));
|
|
588
|
-
if (remaining.length === current.length) return live;
|
|
589
|
-
return {
|
|
590
|
-
config: {
|
|
591
|
-
...live.config,
|
|
592
|
-
tasks: {
|
|
593
|
-
...live.config.tasks,
|
|
594
|
-
[nodeName]: { ...task, requires: remaining }
|
|
595
|
-
}
|
|
596
|
-
},
|
|
597
|
-
state: live.state
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
function addProvides(live, nodeName, tokens) {
|
|
601
|
-
const task = live.config.tasks[nodeName];
|
|
602
|
-
if (!task) return live;
|
|
603
|
-
const current = getProvides(task);
|
|
604
|
-
const toAdd = tokens.filter((t) => !current.includes(t));
|
|
605
|
-
if (toAdd.length === 0) return live;
|
|
606
|
-
return {
|
|
607
|
-
config: {
|
|
608
|
-
...live.config,
|
|
609
|
-
tasks: {
|
|
610
|
-
...live.config.tasks,
|
|
611
|
-
[nodeName]: { ...task, provides: [...current, ...toAdd] }
|
|
612
|
-
}
|
|
613
|
-
},
|
|
614
|
-
state: live.state
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
function removeProvides(live, nodeName, tokens) {
|
|
618
|
-
const task = live.config.tasks[nodeName];
|
|
619
|
-
if (!task) return live;
|
|
620
|
-
const current = getProvides(task);
|
|
621
|
-
const remaining = current.filter((t) => !tokens.includes(t));
|
|
622
|
-
if (remaining.length === current.length) return live;
|
|
623
|
-
return {
|
|
624
|
-
config: {
|
|
625
|
-
...live.config,
|
|
626
|
-
tasks: {
|
|
627
|
-
...live.config.tasks,
|
|
628
|
-
[nodeName]: { ...task, provides: remaining }
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
state: live.state
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
function snapshot(live) {
|
|
635
|
-
return {
|
|
636
|
-
version: 1,
|
|
637
|
-
config: live.config,
|
|
638
|
-
state: live.state,
|
|
639
|
-
snapshotAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
function createDefaultGraphEngineStore2() {
|
|
643
|
-
return {
|
|
644
|
-
status: "not-started",
|
|
645
|
-
executionCount: 0,
|
|
646
|
-
retryCount: 0,
|
|
647
|
-
lastEpoch: 0,
|
|
648
|
-
messages: [],
|
|
649
|
-
progress: null
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
function applyAgentAction(state, action) {
|
|
653
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
654
|
-
switch (action) {
|
|
655
|
-
case "stop":
|
|
656
|
-
return { ...state, status: "stopped", lastUpdated: now };
|
|
657
|
-
case "pause":
|
|
658
|
-
return { ...state, status: "paused", lastUpdated: now };
|
|
659
|
-
case "resume":
|
|
660
|
-
return { ...state, status: "running", lastUpdated: now };
|
|
661
|
-
default:
|
|
662
|
-
return state;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// src/continuous-event-graph/schedule.ts
|
|
667
|
-
function schedule(live) {
|
|
668
|
-
const { config, state } = live;
|
|
669
|
-
const graphTasks = getAllTasks(config);
|
|
670
|
-
const taskNames = Object.keys(graphTasks);
|
|
671
|
-
if (taskNames.length === 0) {
|
|
672
|
-
return { eligible: [], pending: [], unresolved: [], blocked: [], conflicts: {} };
|
|
673
|
-
}
|
|
674
|
-
const producerMap = buildProducerMap(graphTasks);
|
|
675
|
-
const computedOutputs = computeAvailableOutputs(config, state.tasks);
|
|
676
|
-
const availableOutputs = /* @__PURE__ */ new Set([...computedOutputs, ...state.availableOutputs]);
|
|
677
|
-
const eligible = [];
|
|
678
|
-
const pending = [];
|
|
679
|
-
const unresolved = [];
|
|
680
|
-
const blocked = [];
|
|
681
|
-
for (const [taskName, taskConfig] of Object.entries(graphTasks)) {
|
|
682
|
-
const taskState = state.tasks[taskName];
|
|
683
|
-
const strategy = getRefreshStrategy(taskConfig, config.settings);
|
|
684
|
-
const rerunnable = strategy !== "once";
|
|
685
|
-
if (taskState?.status === TASK_STATUS.RUNNING || isNonActiveTask(taskState)) {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
const maxExec = getMaxExecutions(taskConfig);
|
|
689
|
-
if (maxExec !== void 0 && taskState && taskState.executionCount >= maxExec) {
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
if (taskConfig.circuit_breaker && taskState && taskState.executionCount >= taskConfig.circuit_breaker.max_executions) {
|
|
693
|
-
continue;
|
|
694
|
-
}
|
|
695
|
-
if (!rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
if (rerunnable && taskState?.status === TASK_STATUS.COMPLETED) {
|
|
699
|
-
const requires2 = getRequires(taskConfig);
|
|
700
|
-
let shouldSkip = false;
|
|
701
|
-
switch (strategy) {
|
|
702
|
-
case "data-changed": {
|
|
703
|
-
if (requires2.length > 0) {
|
|
704
|
-
const hasChangedData = requires2.some((req) => {
|
|
705
|
-
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
706
|
-
if (getProvides(otherConfig).includes(req)) {
|
|
707
|
-
const otherState = state.tasks[otherName];
|
|
708
|
-
if (!otherState) continue;
|
|
709
|
-
const consumed = taskState.lastConsumedHashes?.[req];
|
|
710
|
-
if (otherState.lastDataHash == null) {
|
|
711
|
-
return otherState.executionCount > taskState.lastEpoch;
|
|
712
|
-
}
|
|
713
|
-
return otherState.lastDataHash !== consumed;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return false;
|
|
717
|
-
});
|
|
718
|
-
if (!hasChangedData) shouldSkip = true;
|
|
719
|
-
} else {
|
|
720
|
-
shouldSkip = true;
|
|
721
|
-
}
|
|
722
|
-
break;
|
|
723
|
-
}
|
|
724
|
-
case "epoch-changed": {
|
|
725
|
-
if (requires2.length > 0) {
|
|
726
|
-
const hasRefreshed = requires2.some((req) => {
|
|
727
|
-
for (const [otherName, otherConfig] of Object.entries(graphTasks)) {
|
|
728
|
-
if (getProvides(otherConfig).includes(req)) {
|
|
729
|
-
const otherState = state.tasks[otherName];
|
|
730
|
-
if (otherState && otherState.executionCount > taskState.lastEpoch) return true;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return false;
|
|
734
|
-
});
|
|
735
|
-
if (!hasRefreshed) shouldSkip = true;
|
|
736
|
-
} else {
|
|
737
|
-
shouldSkip = true;
|
|
738
|
-
}
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
case "time-based": {
|
|
742
|
-
const interval = taskConfig.refreshInterval ?? 0;
|
|
743
|
-
if (interval <= 0) {
|
|
744
|
-
shouldSkip = true;
|
|
745
|
-
break;
|
|
746
|
-
}
|
|
747
|
-
const completedAt = taskState.completedAt;
|
|
748
|
-
if (!completedAt) {
|
|
749
|
-
shouldSkip = true;
|
|
750
|
-
break;
|
|
751
|
-
}
|
|
752
|
-
const elapsedSec = (Date.now() - Date.parse(completedAt)) / 1e3;
|
|
753
|
-
if (elapsedSec < interval) shouldSkip = true;
|
|
754
|
-
break;
|
|
755
|
-
}
|
|
756
|
-
case "manual":
|
|
757
|
-
shouldSkip = true;
|
|
758
|
-
break;
|
|
759
|
-
}
|
|
760
|
-
if (shouldSkip) continue;
|
|
761
|
-
}
|
|
762
|
-
const requires = getRequires(taskConfig);
|
|
763
|
-
if (requires.length === 0) {
|
|
764
|
-
eligible.push(taskName);
|
|
765
|
-
continue;
|
|
766
|
-
}
|
|
767
|
-
const missingTokens = [];
|
|
768
|
-
const pendingTokens = [];
|
|
769
|
-
const failedTokenInfo = [];
|
|
770
|
-
for (const token of requires) {
|
|
771
|
-
if (availableOutputs.has(token)) continue;
|
|
772
|
-
const producers = producerMap[token] || [];
|
|
773
|
-
if (producers.length === 0) {
|
|
774
|
-
missingTokens.push(token);
|
|
775
|
-
} else {
|
|
776
|
-
const allFailed = producers.every((p) => isNonActiveTask(state.tasks[p]));
|
|
777
|
-
if (allFailed) {
|
|
778
|
-
failedTokenInfo.push({ token, failedProducer: producers[0] });
|
|
779
|
-
} else {
|
|
780
|
-
pendingTokens.push(token);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
if (missingTokens.length > 0) {
|
|
785
|
-
unresolved.push({ taskName, missingTokens });
|
|
786
|
-
} else if (failedTokenInfo.length > 0) {
|
|
787
|
-
blocked.push({
|
|
788
|
-
taskName,
|
|
789
|
-
failedTokens: failedTokenInfo.map((f) => f.token),
|
|
790
|
-
failedProducers: [...new Set(failedTokenInfo.map((f) => f.failedProducer))]
|
|
791
|
-
});
|
|
792
|
-
} else if (pendingTokens.length > 0) {
|
|
793
|
-
pending.push({ taskName, waitingOn: pendingTokens });
|
|
794
|
-
} else {
|
|
795
|
-
eligible.push(taskName);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
const conflicts = {};
|
|
799
|
-
if (eligible.length > 1) {
|
|
800
|
-
const outputGroups = groupTasksByProvides(eligible, graphTasks);
|
|
801
|
-
for (const [outputKey, groupTasks] of Object.entries(outputGroups)) {
|
|
802
|
-
if (groupTasks.length > 1) {
|
|
803
|
-
conflicts[outputKey] = groupTasks;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
return { eligible, pending, unresolved, blocked, conflicts };
|
|
808
|
-
}
|
|
809
|
-
function buildProducerMap(tasks) {
|
|
810
|
-
const map = {};
|
|
811
|
-
for (const [name, config] of Object.entries(tasks)) {
|
|
812
|
-
for (const token of getProvides(config)) {
|
|
813
|
-
if (!map[token]) map[token] = [];
|
|
814
|
-
map[token].push(name);
|
|
815
|
-
}
|
|
816
|
-
if (config.on) {
|
|
817
|
-
for (const tokens of Object.values(config.on)) {
|
|
818
|
-
for (const token of tokens) {
|
|
819
|
-
if (!map[token]) map[token] = [];
|
|
820
|
-
if (!map[token].includes(name)) map[token].push(name);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
if (config.on_failure) {
|
|
825
|
-
for (const token of config.on_failure) {
|
|
826
|
-
if (!map[token]) map[token] = [];
|
|
827
|
-
if (!map[token].includes(name)) map[token].push(name);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
return map;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// src/continuous-event-graph/journal.ts
|
|
835
|
-
var MemoryJournal = class {
|
|
836
|
-
buffer = [];
|
|
837
|
-
append(event) {
|
|
838
|
-
this.buffer.push(event);
|
|
839
|
-
}
|
|
840
|
-
drain() {
|
|
841
|
-
const events = this.buffer;
|
|
842
|
-
this.buffer = [];
|
|
843
|
-
return events;
|
|
844
|
-
}
|
|
845
|
-
get size() {
|
|
846
|
-
return this.buffer.length;
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
|
|
850
|
-
// src/continuous-event-graph/reactive.ts
|
|
851
|
-
function computeDataHash(data) {
|
|
852
|
-
const json = stableStringify(data);
|
|
853
|
-
return fnv1a64Hex(json);
|
|
854
|
-
}
|
|
855
|
-
function stableStringify(value) {
|
|
856
|
-
if (value === null || value === void 0 || typeof value !== "object") {
|
|
857
|
-
return JSON.stringify(value);
|
|
858
|
-
}
|
|
859
|
-
if (Array.isArray(value)) {
|
|
860
|
-
return "[" + value.map(stableStringify).join(",") + "]";
|
|
861
|
-
}
|
|
862
|
-
const obj = value;
|
|
863
|
-
const keys = Object.keys(obj).sort();
|
|
864
|
-
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k])).join(",") + "}";
|
|
865
|
-
}
|
|
866
|
-
function fnv1a64Hex(input) {
|
|
867
|
-
let hash = 0xcbf29ce484222325n;
|
|
868
|
-
const prime = 0x100000001b3n;
|
|
869
|
-
const mod = 0xffffffffffffffffn;
|
|
870
|
-
for (let i = 0; i < input.length; i++) {
|
|
871
|
-
hash ^= BigInt(input.charCodeAt(i));
|
|
872
|
-
hash = hash * prime & mod;
|
|
873
|
-
}
|
|
874
|
-
return hash.toString(16).padStart(16, "0");
|
|
875
|
-
}
|
|
876
|
-
function base64UrlEncode(input) {
|
|
877
|
-
if (typeof Buffer !== "undefined") {
|
|
878
|
-
return Buffer.from(input, "utf8").toString("base64url");
|
|
879
|
-
}
|
|
880
|
-
if (typeof btoa === "function") {
|
|
881
|
-
const bytes = new TextEncoder().encode(input);
|
|
882
|
-
let binary = "";
|
|
883
|
-
for (const b of bytes) binary += String.fromCharCode(b);
|
|
884
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
885
|
-
}
|
|
886
|
-
throw new Error("No base64 encoder available in this runtime");
|
|
887
|
-
}
|
|
888
|
-
function base64UrlDecode(input) {
|
|
889
|
-
if (typeof Buffer !== "undefined") {
|
|
890
|
-
return Buffer.from(input, "base64url").toString("utf8");
|
|
891
|
-
}
|
|
892
|
-
if (typeof atob === "function") {
|
|
893
|
-
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
894
|
-
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
895
|
-
const binary = atob(padded);
|
|
896
|
-
const bytes = new Uint8Array(binary.length);
|
|
897
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
898
|
-
return new TextDecoder().decode(bytes);
|
|
899
|
-
}
|
|
900
|
-
throw new Error("No base64 decoder available in this runtime");
|
|
901
|
-
}
|
|
902
|
-
function encodeCallbackToken(taskName) {
|
|
903
|
-
const payload = JSON.stringify({ t: taskName, n: Date.now().toString(36) + Math.random().toString(36).slice(2, 6) });
|
|
904
|
-
return base64UrlEncode(payload);
|
|
905
|
-
}
|
|
906
|
-
function decodeCallbackToken(token) {
|
|
907
|
-
try {
|
|
908
|
-
const payload = JSON.parse(base64UrlDecode(token));
|
|
909
|
-
if (typeof payload?.t === "string") return { taskName: payload.t };
|
|
910
|
-
return null;
|
|
911
|
-
} catch {
|
|
912
|
-
return null;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
function createReactiveGraph(configOrLive, options, executionId) {
|
|
916
|
-
const {
|
|
917
|
-
handlers: initialHandlers,
|
|
918
|
-
onDrain
|
|
919
|
-
} = options;
|
|
920
|
-
const inputQueue = new MemoryJournal();
|
|
921
|
-
let live = "state" in configOrLive && "config" in configOrLive ? configOrLive : createLiveGraph(configOrLive, executionId);
|
|
922
|
-
let disposed = false;
|
|
923
|
-
const pendingHandlers = /* @__PURE__ */ new Set();
|
|
924
|
-
const handlers = new Map(Object.entries(initialHandlers));
|
|
925
|
-
const internalJournal = new MemoryJournal();
|
|
926
|
-
let draining = false;
|
|
927
|
-
let drainQueued = false;
|
|
928
|
-
function drain() {
|
|
929
|
-
if (disposed) return;
|
|
930
|
-
if (draining) {
|
|
931
|
-
drainQueued = true;
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
draining = true;
|
|
935
|
-
try {
|
|
936
|
-
do {
|
|
937
|
-
drainQueued = false;
|
|
938
|
-
drainOnce();
|
|
939
|
-
} while (drainQueued);
|
|
940
|
-
} finally {
|
|
941
|
-
draining = false;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
function drainOnce() {
|
|
945
|
-
const internalEvents = internalJournal.drain();
|
|
946
|
-
const inputEvents = inputQueue.drain();
|
|
947
|
-
const events = [...internalEvents, ...inputEvents];
|
|
948
|
-
if (events.length > 0) {
|
|
949
|
-
live = applyEvents(live, events);
|
|
950
|
-
}
|
|
951
|
-
const result = schedule(live);
|
|
952
|
-
if (events.length > 0) {
|
|
953
|
-
onDrain?.(events, live, result);
|
|
954
|
-
}
|
|
955
|
-
for (const taskName of result.eligible) {
|
|
956
|
-
dispatchTask(taskName);
|
|
957
|
-
}
|
|
958
|
-
for (const event of events) {
|
|
959
|
-
if (event.type === "task-progress") {
|
|
960
|
-
const { taskName, update } = event;
|
|
961
|
-
const taskConfig = live.config.tasks[taskName];
|
|
962
|
-
if (!taskConfig) continue;
|
|
963
|
-
const taskState = live.state.tasks[taskName];
|
|
964
|
-
if (!taskState || taskState.status !== "running") continue;
|
|
965
|
-
const callbackToken = encodeCallbackToken(taskName);
|
|
966
|
-
const p = runPipeline(taskName, callbackToken, update).catch((error) => {
|
|
967
|
-
if (disposed) return;
|
|
968
|
-
internalJournal.append({
|
|
969
|
-
type: "task-failed",
|
|
970
|
-
taskName,
|
|
971
|
-
error: error.message ?? String(error),
|
|
972
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
973
|
-
});
|
|
974
|
-
drain();
|
|
975
|
-
}).finally(() => {
|
|
976
|
-
pendingHandlers.delete(p);
|
|
977
|
-
});
|
|
978
|
-
pendingHandlers.add(p);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
function resolveUpstreamState(taskName) {
|
|
983
|
-
const taskConfig = live.config.tasks[taskName];
|
|
984
|
-
const requires = taskConfig.requires ?? [];
|
|
985
|
-
const tokenToTask = /* @__PURE__ */ new Map();
|
|
986
|
-
for (const [name, cfg] of Object.entries(live.config.tasks)) {
|
|
987
|
-
for (const token of cfg.provides ?? []) {
|
|
988
|
-
tokenToTask.set(token, name);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
const state = {};
|
|
992
|
-
for (const token of requires) {
|
|
993
|
-
const producerTask = tokenToTask.get(token);
|
|
994
|
-
if (producerTask) {
|
|
995
|
-
state[token] = live.state.tasks[producerTask]?.data;
|
|
996
|
-
} else {
|
|
997
|
-
state[token] = void 0;
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
return state;
|
|
1001
|
-
}
|
|
1002
|
-
async function runPipeline(taskName, callbackToken, update) {
|
|
1003
|
-
const taskConfig = live.config.tasks[taskName];
|
|
1004
|
-
const handlerNames = taskConfig.taskHandlers ?? [];
|
|
1005
|
-
const upstreamState = resolveUpstreamState(taskName);
|
|
1006
|
-
for (const handlerName of handlerNames) {
|
|
1007
|
-
const handler = handlers.get(handlerName);
|
|
1008
|
-
if (!handler) {
|
|
1009
|
-
throw new Error(`Handler '${handlerName}' not found in registry (task '${taskName}')`);
|
|
1010
|
-
}
|
|
1011
|
-
const input = {
|
|
1012
|
-
nodeId: taskName,
|
|
1013
|
-
state: upstreamState,
|
|
1014
|
-
taskState: live.state.tasks[taskName],
|
|
1015
|
-
config: taskConfig,
|
|
1016
|
-
callbackToken,
|
|
1017
|
-
update
|
|
1018
|
-
};
|
|
1019
|
-
const status = await handler(input);
|
|
1020
|
-
if (status === "task-initiate-failure") {
|
|
1021
|
-
throw new Error(`Handler '${handlerName}' returned task-initiate-failure (task '${taskName}')`);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
function dispatchTask(taskName) {
|
|
1026
|
-
const taskConfig = live.config.tasks[taskName];
|
|
1027
|
-
const handlerNames = taskConfig?.taskHandlers;
|
|
1028
|
-
if (!handlerNames || handlerNames.length === 0) {
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
internalJournal.append({
|
|
1032
|
-
type: "task-started",
|
|
1033
|
-
taskName,
|
|
1034
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1035
|
-
});
|
|
1036
|
-
drain();
|
|
1037
|
-
const callbackToken = encodeCallbackToken(taskName);
|
|
1038
|
-
const p = runPipeline(taskName, callbackToken).catch((error) => {
|
|
1039
|
-
if (disposed) return;
|
|
1040
|
-
internalJournal.append({
|
|
1041
|
-
type: "task-failed",
|
|
1042
|
-
taskName,
|
|
1043
|
-
error: error.message ?? String(error),
|
|
1044
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1045
|
-
});
|
|
1046
|
-
drain();
|
|
1047
|
-
}).finally(() => {
|
|
1048
|
-
pendingHandlers.delete(p);
|
|
1049
|
-
});
|
|
1050
|
-
pendingHandlers.add(p);
|
|
1051
|
-
}
|
|
1052
|
-
return {
|
|
1053
|
-
push(event) {
|
|
1054
|
-
if (disposed) return;
|
|
1055
|
-
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
1056
|
-
event = { ...event, dataHash: computeDataHash(event.data) };
|
|
1057
|
-
}
|
|
1058
|
-
inputQueue.append(event);
|
|
1059
|
-
drain();
|
|
1060
|
-
},
|
|
1061
|
-
pushAll(events) {
|
|
1062
|
-
if (disposed) return;
|
|
1063
|
-
for (const event of events) {
|
|
1064
|
-
if (event.type === "task-completed" && event.data && !event.dataHash) {
|
|
1065
|
-
inputQueue.append({ ...event, dataHash: computeDataHash(event.data) });
|
|
1066
|
-
} else {
|
|
1067
|
-
inputQueue.append(event);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
drain();
|
|
1071
|
-
},
|
|
1072
|
-
resolveCallback(callbackToken, data, errors) {
|
|
1073
|
-
if (disposed) return;
|
|
1074
|
-
const decoded = decodeCallbackToken(callbackToken);
|
|
1075
|
-
if (!decoded) return;
|
|
1076
|
-
const { taskName } = decoded;
|
|
1077
|
-
if (!live.config.tasks[taskName]) return;
|
|
1078
|
-
if (errors && errors.length > 0) {
|
|
1079
|
-
inputQueue.append({
|
|
1080
|
-
type: "task-failed",
|
|
1081
|
-
taskName,
|
|
1082
|
-
error: errors.join("; "),
|
|
1083
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1084
|
-
});
|
|
1085
|
-
} else {
|
|
1086
|
-
const dataHash = data && Object.keys(data).length > 0 ? computeDataHash(data) : void 0;
|
|
1087
|
-
inputQueue.append({
|
|
1088
|
-
type: "task-completed",
|
|
1089
|
-
taskName,
|
|
1090
|
-
data,
|
|
1091
|
-
dataHash,
|
|
1092
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
drain();
|
|
1096
|
-
},
|
|
1097
|
-
addNode(name, taskConfig) {
|
|
1098
|
-
if (disposed) return;
|
|
1099
|
-
inputQueue.append({ type: "task-upsert", taskName: name, taskConfig, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1100
|
-
drain();
|
|
1101
|
-
},
|
|
1102
|
-
removeNode(name) {
|
|
1103
|
-
if (disposed) return;
|
|
1104
|
-
inputQueue.append({ type: "task-removal", taskName: name, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1105
|
-
drain();
|
|
1106
|
-
},
|
|
1107
|
-
addRequires(nodeName, tokens) {
|
|
1108
|
-
if (disposed) return;
|
|
1109
|
-
inputQueue.append({ type: "node-requires-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1110
|
-
drain();
|
|
1111
|
-
},
|
|
1112
|
-
removeRequires(nodeName, tokens) {
|
|
1113
|
-
if (disposed) return;
|
|
1114
|
-
inputQueue.append({ type: "node-requires-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1115
|
-
drain();
|
|
1116
|
-
},
|
|
1117
|
-
addProvides(nodeName, tokens) {
|
|
1118
|
-
if (disposed) return;
|
|
1119
|
-
inputQueue.append({ type: "node-provides-add", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1120
|
-
drain();
|
|
1121
|
-
},
|
|
1122
|
-
removeProvides(nodeName, tokens) {
|
|
1123
|
-
if (disposed) return;
|
|
1124
|
-
inputQueue.append({ type: "node-provides-remove", nodeName, tokens, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1125
|
-
drain();
|
|
1126
|
-
},
|
|
1127
|
-
registerHandler(name, fn) {
|
|
1128
|
-
handlers.set(name, fn);
|
|
1129
|
-
},
|
|
1130
|
-
unregisterHandler(name) {
|
|
1131
|
-
handlers.delete(name);
|
|
1132
|
-
},
|
|
1133
|
-
retrigger(taskName) {
|
|
1134
|
-
if (disposed) return;
|
|
1135
|
-
if (!live.config.tasks[taskName]) return;
|
|
1136
|
-
inputQueue.append({
|
|
1137
|
-
type: "task-restart",
|
|
1138
|
-
taskName,
|
|
1139
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1140
|
-
});
|
|
1141
|
-
drain();
|
|
1142
|
-
},
|
|
1143
|
-
retriggerAll(taskNames) {
|
|
1144
|
-
if (disposed) return;
|
|
1145
|
-
for (const name of taskNames) {
|
|
1146
|
-
if (!live.config.tasks[name]) continue;
|
|
1147
|
-
inputQueue.append({
|
|
1148
|
-
type: "task-restart",
|
|
1149
|
-
taskName: name,
|
|
1150
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1153
|
-
drain();
|
|
1154
|
-
},
|
|
1155
|
-
snapshot() {
|
|
1156
|
-
return snapshot(live);
|
|
1157
|
-
},
|
|
1158
|
-
getState() {
|
|
1159
|
-
return live;
|
|
1160
|
-
},
|
|
1161
|
-
getSchedule() {
|
|
1162
|
-
return schedule(live);
|
|
1163
|
-
},
|
|
1164
|
-
async dispose(options2) {
|
|
1165
|
-
if (options2?.wait && pendingHandlers.size > 0) {
|
|
1166
|
-
await Promise.allSettled([...pendingHandlers]);
|
|
1167
|
-
}
|
|
1168
|
-
disposed = true;
|
|
1169
|
-
}
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
// src/board-livegraph-runtime/index.ts
|
|
1174
|
-
function deepClone(value) {
|
|
1175
|
-
return JSON.parse(JSON.stringify(value));
|
|
1176
|
-
}
|
|
1177
|
-
function toTaskConfig(card) {
|
|
1178
|
-
const provides = card.provides && card.provides.length > 0 ? card.provides.map((p) => p.bindTo) : [card.id];
|
|
1179
|
-
return {
|
|
1180
|
-
requires: card.requires && card.requires.length > 0 ? [...card.requires] : void 0,
|
|
1181
|
-
provides,
|
|
1182
|
-
taskHandlers: [card.id],
|
|
1183
|
-
description: card.meta?.title ?? card.id
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
function buildTokenProviders(cards) {
|
|
1187
|
-
const tokenToCardId = /* @__PURE__ */ new Map();
|
|
1188
|
-
for (const [cardId, card] of cards.entries()) {
|
|
1189
|
-
const bindings = card.provides && card.provides.length > 0 ? card.provides : [{ bindTo: cardId, ref: "card_data" }];
|
|
1190
|
-
for (const binding of bindings) tokenToCardId.set(binding.bindTo, cardId);
|
|
1191
|
-
}
|
|
1192
|
-
return tokenToCardId;
|
|
1193
|
-
}
|
|
1194
|
-
function validateRequires(cards, changedCardId) {
|
|
1195
|
-
const tokenProviders = buildTokenProviders(cards);
|
|
1196
|
-
const card = cards.get(changedCardId);
|
|
1197
|
-
if (!card) return;
|
|
1198
|
-
for (const req of card.requires ?? []) {
|
|
1199
|
-
if (!tokenProviders.has(req)) {
|
|
1200
|
-
throw new Error(`Card "${changedCardId}" requires token "${req}" but no card provides it`);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
var LocalStorageService = {
|
|
1205
|
-
// Keys
|
|
1206
|
-
CARD_PREFIX: "yf:cards:",
|
|
1207
|
-
RUNTIME_OUT_PREFIX: "yf:runtime-out:cards:",
|
|
1208
|
-
STATUS_KEY: "yf:runtime-out:status",
|
|
1209
|
-
// Read/write cards (mirrors tmp/cards/<id>.json)
|
|
1210
|
-
writeCard(cardId, cardObject) {
|
|
1211
|
-
try {
|
|
1212
|
-
localStorage.setItem(this.CARD_PREFIX + cardId, JSON.stringify(cardObject));
|
|
1213
|
-
} catch (e) {
|
|
1214
|
-
console.warn(`Failed to write card ${cardId} to localStorage:`, e);
|
|
1215
|
-
}
|
|
1216
|
-
},
|
|
1217
|
-
readCard(cardId) {
|
|
1218
|
-
try {
|
|
1219
|
-
const raw = localStorage.getItem(this.CARD_PREFIX + cardId);
|
|
1220
|
-
return raw ? JSON.parse(raw) : null;
|
|
1221
|
-
} catch (e) {
|
|
1222
|
-
console.warn(`Failed to read card ${cardId} from localStorage:`, e);
|
|
1223
|
-
return null;
|
|
1224
|
-
}
|
|
1225
|
-
},
|
|
1226
|
-
readAllCards(cardIds) {
|
|
1227
|
-
const result = {};
|
|
1228
|
-
for (const id of cardIds) {
|
|
1229
|
-
const card = this.readCard(id);
|
|
1230
|
-
if (card) result[id] = card;
|
|
1231
|
-
}
|
|
1232
|
-
return result;
|
|
1233
|
-
},
|
|
1234
|
-
// Read/write computed artifacts (mirrors runtime-out/cards/<id>.computed.json)
|
|
1235
|
-
writeComputedArtifact(artifact) {
|
|
1236
|
-
if (!artifact || !artifact.card_id) return;
|
|
1237
|
-
try {
|
|
1238
|
-
localStorage.setItem(
|
|
1239
|
-
this.RUNTIME_OUT_PREFIX + String(artifact.card_id),
|
|
1240
|
-
JSON.stringify(artifact)
|
|
1241
|
-
);
|
|
1242
|
-
} catch (e) {
|
|
1243
|
-
console.warn(`Failed to write computed artifact ${artifact.card_id}:`, e);
|
|
1244
|
-
}
|
|
1245
|
-
},
|
|
1246
|
-
readComputedArtifact(cardId) {
|
|
1247
|
-
try {
|
|
1248
|
-
const raw = localStorage.getItem(this.RUNTIME_OUT_PREFIX + cardId);
|
|
1249
|
-
return raw ? JSON.parse(raw) : null;
|
|
1250
|
-
} catch (e) {
|
|
1251
|
-
console.warn(`Failed to read computed artifact ${cardId}:`, e);
|
|
1252
|
-
return null;
|
|
1253
|
-
}
|
|
1254
|
-
},
|
|
1255
|
-
readAllComputedArtifacts(cardIds) {
|
|
1256
|
-
const result = {};
|
|
1257
|
-
for (const id of cardIds) {
|
|
1258
|
-
const artifact = this.readComputedArtifact(id);
|
|
1259
|
-
if (artifact) result[id] = artifact;
|
|
1260
|
-
}
|
|
1261
|
-
return result;
|
|
1262
|
-
},
|
|
1263
|
-
// Read/write board status snapshot (mirrors runtime-out/board-livegraph-status.json)
|
|
1264
|
-
writeStatusSnapshot(snapshot2) {
|
|
1265
|
-
try {
|
|
1266
|
-
localStorage.setItem(this.STATUS_KEY, JSON.stringify(snapshot2));
|
|
1267
|
-
} catch (e) {
|
|
1268
|
-
console.warn("Failed to write status snapshot to localStorage:", e);
|
|
1269
|
-
}
|
|
1270
|
-
},
|
|
1271
|
-
readStatusSnapshot() {
|
|
1272
|
-
try {
|
|
1273
|
-
const raw = localStorage.getItem(this.STATUS_KEY);
|
|
1274
|
-
return raw ? JSON.parse(raw) : null;
|
|
1275
|
-
} catch (e) {
|
|
1276
|
-
console.warn("Failed to read status snapshot from localStorage:", e);
|
|
1277
|
-
return null;
|
|
1278
|
-
}
|
|
1279
|
-
},
|
|
1280
|
-
// Clear all (useful for reset/demo)
|
|
1281
|
-
clear() {
|
|
1282
|
-
const keysToDelete = [];
|
|
1283
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
1284
|
-
const key = localStorage.key(i);
|
|
1285
|
-
if (key && (key.startsWith(this.CARD_PREFIX) || key.startsWith(this.RUNTIME_OUT_PREFIX) || key === this.STATUS_KEY)) {
|
|
1286
|
-
keysToDelete.push(key);
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
for (const key of keysToDelete) {
|
|
1290
|
-
localStorage.removeItem(key);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
};
|
|
1294
|
-
function createBoardLiveGraphRuntime(input, options = {}) {
|
|
1295
|
-
const boardMeta = Array.isArray(input) ? {} : {
|
|
1296
|
-
id: input.id,
|
|
1297
|
-
title: input.title,
|
|
1298
|
-
mode: input.mode,
|
|
1299
|
-
positions: input.positions,
|
|
1300
|
-
settings: input.settings
|
|
1301
|
-
};
|
|
1302
|
-
const initialCards = Array.isArray(input) ? input : input.nodes;
|
|
1303
|
-
const cards = /* @__PURE__ */ new Map();
|
|
1304
|
-
for (const card of initialCards) {
|
|
1305
|
-
if (cards.has(card.id)) throw new Error(`Duplicate card ID: "${card.id}"`);
|
|
1306
|
-
cards.set(card.id, deepClone(card));
|
|
1307
|
-
}
|
|
1308
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1309
|
-
const taskExecutor = options.taskExecutor;
|
|
1310
|
-
const sourceAdapters = options.sourceAdapters ?? {};
|
|
1311
|
-
const defaultSourceAdapter = options.defaultSourceAdapter;
|
|
1312
|
-
let graphRef = null;
|
|
1313
|
-
const notifyListeners = (events, graph2) => {
|
|
1314
|
-
const update = {
|
|
1315
|
-
events,
|
|
1316
|
-
graph: graph2,
|
|
1317
|
-
nodes: getRenderableNodes()
|
|
1318
|
-
};
|
|
1319
|
-
for (const listener of listeners) listener(update);
|
|
1320
|
-
};
|
|
1321
|
-
const makeHandler = (cardId) => {
|
|
1322
|
-
return async (inputArgs) => {
|
|
1323
|
-
const card = cards.get(cardId);
|
|
1324
|
-
if (!card) return "task-initiate-failure";
|
|
1325
|
-
const requiresData = {};
|
|
1326
|
-
for (const token of card.requires ?? []) {
|
|
1327
|
-
const upstream = inputArgs.state[token];
|
|
1328
|
-
if (!upstream || typeof upstream !== "object") continue;
|
|
1329
|
-
const providesData2 = upstream.provides_data;
|
|
1330
|
-
if (!providesData2 || typeof providesData2 !== "object") continue;
|
|
1331
|
-
if (!Object.prototype.hasOwnProperty.call(providesData2, token)) continue;
|
|
1332
|
-
requiresData[token] = providesData2[token];
|
|
1333
|
-
}
|
|
1334
|
-
const sourcesData = {};
|
|
1335
|
-
if (card.source_defs && card.source_defs.length > 0) {
|
|
1336
|
-
const adapter = sourceAdapters[cardId] ?? defaultSourceAdapter;
|
|
1337
|
-
const fetched = taskExecutor ? await taskExecutor({ card, input: inputArgs }) : adapter ? await adapter({ card, input: inputArgs }) : void 0;
|
|
1338
|
-
if (fetched && typeof fetched === "object") {
|
|
1339
|
-
for (const src of card.source_defs) {
|
|
1340
|
-
if (Object.prototype.hasOwnProperty.call(fetched, src.bindTo)) {
|
|
1341
|
-
sourcesData[src.bindTo] = fetched[src.bindTo];
|
|
1342
|
-
} else if (card.source_defs.length === 1) {
|
|
1343
|
-
sourcesData[src.bindTo] = fetched;
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
const computeNode = {
|
|
1349
|
-
id: card.id,
|
|
1350
|
-
card_data: deepClone(card.card_data ?? {}),
|
|
1351
|
-
requires: requiresData,
|
|
1352
|
-
source_defs: card.source_defs,
|
|
1353
|
-
compute: card.compute
|
|
1354
|
-
};
|
|
1355
|
-
computeNode._sourcesData = sourcesData;
|
|
1356
|
-
if (computeNode.compute && computeNode.compute.length > 0) {
|
|
1357
|
-
await CardCompute.run(computeNode, { sourcesData });
|
|
1358
|
-
}
|
|
1359
|
-
const providesData = {};
|
|
1360
|
-
if (card.provides && card.provides.length > 0) {
|
|
1361
|
-
for (const { bindTo, ref } of card.provides) {
|
|
1362
|
-
providesData[bindTo] = CardCompute.resolve(computeNode, ref);
|
|
1363
|
-
}
|
|
1364
|
-
} else {
|
|
1365
|
-
providesData[card.id] = {
|
|
1366
|
-
...computeNode.card_data ?? {},
|
|
1367
|
-
...computeNode.computed_values ?? {},
|
|
1368
|
-
...computeNode._sourcesData ?? {}
|
|
1369
|
-
};
|
|
1370
|
-
}
|
|
1371
|
-
const resultData = {
|
|
1372
|
-
provides_data: providesData,
|
|
1373
|
-
card_data: computeNode.card_data ?? {},
|
|
1374
|
-
computed_values: computeNode.computed_values ?? {},
|
|
1375
|
-
fetched_sources: sourcesData,
|
|
1376
|
-
requires: requiresData
|
|
1377
|
-
};
|
|
1378
|
-
graphRef?.resolveCallback(inputArgs.callbackToken, resultData);
|
|
1379
|
-
return "task-initiated";
|
|
1380
|
-
};
|
|
1381
|
-
};
|
|
1382
|
-
const tasks = {};
|
|
1383
|
-
const handlers = {};
|
|
1384
|
-
for (const [cardId, card] of cards.entries()) {
|
|
1385
|
-
validateRequires(cards, cardId);
|
|
1386
|
-
tasks[cardId] = toTaskConfig(card);
|
|
1387
|
-
handlers[cardId] = makeHandler(cardId);
|
|
1388
|
-
}
|
|
1389
|
-
const config = {
|
|
1390
|
-
id: boardMeta.id ?? `browser-board-${Date.now()}`,
|
|
1391
|
-
settings: {
|
|
1392
|
-
completion: "manual",
|
|
1393
|
-
execution_mode: "eligibility-mode",
|
|
1394
|
-
...boardMeta.settings ?? {},
|
|
1395
|
-
...options.graphSettings ?? {}
|
|
1396
|
-
},
|
|
1397
|
-
tasks
|
|
1398
|
-
};
|
|
1399
|
-
const userOnDrain = options.reactiveOptions?.onDrain;
|
|
1400
|
-
const graph = createReactiveGraph(
|
|
1401
|
-
config,
|
|
1402
|
-
{
|
|
1403
|
-
...options.reactiveOptions ?? {},
|
|
1404
|
-
handlers,
|
|
1405
|
-
onDrain: (events, live, scheduleResult) => {
|
|
1406
|
-
userOnDrain?.(events, live, scheduleResult);
|
|
1407
|
-
notifyListeners(events, live);
|
|
1408
|
-
}
|
|
1409
|
-
},
|
|
1410
|
-
options.executionId
|
|
1411
|
-
);
|
|
1412
|
-
graphRef = graph;
|
|
1413
|
-
function getRenderableNodes() {
|
|
1414
|
-
const live = graph.getState();
|
|
1415
|
-
const out = [];
|
|
1416
|
-
for (const [cardId, baseCard] of cards.entries()) {
|
|
1417
|
-
const data = live.state.tasks[cardId]?.data;
|
|
1418
|
-
const runtimeState = live.state.tasks[cardId];
|
|
1419
|
-
const mergedCardData = {
|
|
1420
|
-
...baseCard.card_data ?? {},
|
|
1421
|
-
...data && typeof data.card_data === "object" ? data.card_data : {}
|
|
1422
|
-
};
|
|
1423
|
-
const cardStatus = runtimeState?.status === "running" ? "loading" : runtimeState?.status;
|
|
1424
|
-
const cardDataForView = {
|
|
1425
|
-
...mergedCardData,
|
|
1426
|
-
...cardStatus ? { status: cardStatus } : {},
|
|
1427
|
-
...runtimeState?.lastUpdated ? { lastRun: runtimeState.lastUpdated } : {},
|
|
1428
|
-
...runtimeState?.status === "failed" && runtimeState.error ? { error: runtimeState.error } : {}
|
|
1429
|
-
};
|
|
1430
|
-
out.push({
|
|
1431
|
-
id: cardId,
|
|
1432
|
-
card: deepClone(baseCard),
|
|
1433
|
-
card_data: cardDataForView,
|
|
1434
|
-
fetched_sources: data && typeof data.fetched_sources === "object" ? deepClone(data.fetched_sources) : {},
|
|
1435
|
-
requires: data && typeof data.requires === "object" ? deepClone(data.requires) : {},
|
|
1436
|
-
computed_values: data && typeof data.computed_values === "object" ? deepClone(data.computed_values) : {},
|
|
1437
|
-
runtime_state: runtimeState ? deepClone(runtimeState) : {}
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
return out;
|
|
1441
|
-
}
|
|
1442
|
-
const runtime = {
|
|
1443
|
-
getGraph: () => graph,
|
|
1444
|
-
getState: () => graph.getState(),
|
|
1445
|
-
getSchedule: () => graph.getSchedule(),
|
|
1446
|
-
getNodes: () => getRenderableNodes(),
|
|
1447
|
-
getBoard: () => ({
|
|
1448
|
-
...boardMeta,
|
|
1449
|
-
nodes: getRenderableNodes()
|
|
1450
|
-
}),
|
|
1451
|
-
subscribe(listener) {
|
|
1452
|
-
listeners.add(listener);
|
|
1453
|
-
listener({ events: [], graph: graph.getState(), nodes: getRenderableNodes() });
|
|
1454
|
-
return () => listeners.delete(listener);
|
|
1455
|
-
},
|
|
1456
|
-
addCard(card) {
|
|
1457
|
-
if (cards.has(card.id)) throw new Error(`Card "${card.id}" already exists`);
|
|
1458
|
-
cards.set(card.id, deepClone(card));
|
|
1459
|
-
validateRequires(cards, card.id);
|
|
1460
|
-
graph.registerHandler(card.id, makeHandler(card.id));
|
|
1461
|
-
graph.addNode(card.id, toTaskConfig(card));
|
|
1462
|
-
},
|
|
1463
|
-
upsertCard(card) {
|
|
1464
|
-
cards.set(card.id, deepClone(card));
|
|
1465
|
-
validateRequires(cards, card.id);
|
|
1466
|
-
graph.registerHandler(card.id, makeHandler(card.id));
|
|
1467
|
-
graph.addNode(card.id, toTaskConfig(card));
|
|
1468
|
-
},
|
|
1469
|
-
removeCard(cardId) {
|
|
1470
|
-
cards.delete(cardId);
|
|
1471
|
-
graph.unregisterHandler(cardId);
|
|
1472
|
-
graph.removeNode(cardId);
|
|
1473
|
-
},
|
|
1474
|
-
patchCardState(cardId, patch) {
|
|
1475
|
-
const card = cards.get(cardId);
|
|
1476
|
-
if (!card) throw new Error(`Card "${cardId}" not found`);
|
|
1477
|
-
card.card_data = { ...card.card_data ?? {}, ...patch };
|
|
1478
|
-
graph.retrigger(cardId);
|
|
1479
|
-
},
|
|
1480
|
-
retrigger(cardId) {
|
|
1481
|
-
graph.retrigger(cardId);
|
|
1482
|
-
},
|
|
1483
|
-
retriggerAll() {
|
|
1484
|
-
graph.retriggerAll(Array.from(cards.keys()));
|
|
1485
|
-
},
|
|
1486
|
-
push(event) {
|
|
1487
|
-
graph.push(event);
|
|
1488
|
-
},
|
|
1489
|
-
pushAll(events) {
|
|
1490
|
-
graph.pushAll(events);
|
|
1491
|
-
},
|
|
1492
|
-
dispose() {
|
|
1493
|
-
listeners.clear();
|
|
1494
|
-
graph.dispose();
|
|
1495
|
-
}
|
|
1496
|
-
};
|
|
1497
|
-
return runtime;
|
|
1498
|
-
}
|
|
1499
|
-
function taskStatusToCardStatus(taskStatus) {
|
|
1500
|
-
if (taskStatus === "running" || taskStatus === "in-progress") return "loading";
|
|
1501
|
-
if (taskStatus === "failed") return "error";
|
|
1502
|
-
if (taskStatus === "completed") return "fresh";
|
|
1503
|
-
return "fresh";
|
|
1504
|
-
}
|
|
1505
|
-
function cardStatusToTaskStatus(cardStatus) {
|
|
1506
|
-
if (cardStatus === "loading") return "in-progress";
|
|
1507
|
-
if (cardStatus === "error") return "failed";
|
|
1508
|
-
if (cardStatus === "stale") return "pending";
|
|
1509
|
-
if (cardStatus === "fresh") return "completed";
|
|
1510
|
-
return "pending";
|
|
1511
|
-
}
|
|
1512
|
-
function normalizeCardRuntimeArtifact(cardId, artifact) {
|
|
1513
|
-
const safe = artifact && typeof artifact === "object" && !Array.isArray(artifact) ? artifact : {};
|
|
1514
|
-
return {
|
|
1515
|
-
schema_version: safe.schema_version || "v1",
|
|
1516
|
-
card_id: typeof safe.card_id === "string" ? safe.card_id : cardId,
|
|
1517
|
-
card_data: safe.card_data && typeof safe.card_data === "object" && !Array.isArray(safe.card_data) ? structuredClone(safe.card_data) : {},
|
|
1518
|
-
computed_values: safe.computed_values && typeof safe.computed_values === "object" && !Array.isArray(safe.computed_values) ? structuredClone(safe.computed_values) : {},
|
|
1519
|
-
fetched_sources: safe.fetched_sources && typeof safe.fetched_sources === "object" && !Array.isArray(safe.fetched_sources) ? structuredClone(safe.fetched_sources) : {},
|
|
1520
|
-
requires: safe.requires && typeof safe.requires === "object" && !Array.isArray(safe.requires) ? structuredClone(safe.requires) : {}
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
1523
|
-
function buildLiveCardModelsFromArtifacts(payload) {
|
|
1524
|
-
if (!payload || typeof payload !== "object") throw new Error("payload must be an object");
|
|
1525
|
-
const cardDefinitions = Array.isArray(payload.cardDefinitions) ? payload.cardDefinitions : [];
|
|
1526
|
-
const statusSnapshot = payload.statusSnapshot && typeof payload.statusSnapshot === "object" ? payload.statusSnapshot : {};
|
|
1527
|
-
const cardRuntimeById = payload.cardRuntimeById && typeof payload.cardRuntimeById === "object" ? payload.cardRuntimeById : {};
|
|
1528
|
-
const dataObjectsByToken = payload.dataObjectsByToken && typeof payload.dataObjectsByToken === "object" ? payload.dataObjectsByToken : {};
|
|
1529
|
-
const statusCards = Array.isArray(statusSnapshot.cards) ? statusSnapshot.cards : [];
|
|
1530
|
-
const statusById = new Map(statusCards.map((c) => [c.name, c]));
|
|
1531
|
-
return cardDefinitions.map((cardDefinition) => {
|
|
1532
|
-
const card = structuredClone(cardDefinition);
|
|
1533
|
-
const cardId = card.id;
|
|
1534
|
-
if (!cardId) throw new Error("cardDefinitions entry missing id");
|
|
1535
|
-
const statusCard = statusById.get(cardId);
|
|
1536
|
-
const runtimeArtifact = normalizeCardRuntimeArtifact(cardId, cardRuntimeById[cardId]);
|
|
1537
|
-
const baseCardData = card.card_data && typeof card.card_data === "object" && !Array.isArray(card.card_data) ? card.card_data : {};
|
|
1538
|
-
const card_data = {
|
|
1539
|
-
...baseCardData,
|
|
1540
|
-
...runtimeArtifact.card_data || {},
|
|
1541
|
-
status: taskStatusToCardStatus(statusCard?.status),
|
|
1542
|
-
lastRun: statusCard?.runtime?.last_transition_at ?? null
|
|
1543
|
-
};
|
|
1544
|
-
if (statusCard?.error?.message) card_data["error"] = statusCard.error.message;
|
|
1545
|
-
const runtime_state = statusCard ? {
|
|
1546
|
-
task_status: statusCard.status ?? null,
|
|
1547
|
-
card_status: taskStatusToCardStatus(statusCard.status),
|
|
1548
|
-
runtime: structuredClone(statusCard.runtime ?? {}),
|
|
1549
|
-
error: statusCard.error ? structuredClone(statusCard.error) : null,
|
|
1550
|
-
blocked_by: Array.isArray(statusCard.blocked_by) ? structuredClone(statusCard.blocked_by) : [],
|
|
1551
|
-
requires_missing: Array.isArray(statusCard.requires_missing) ? structuredClone(statusCard.requires_missing) : []
|
|
1552
|
-
} : {
|
|
1553
|
-
task_status: null,
|
|
1554
|
-
card_status: card_data["status"] ?? "fresh",
|
|
1555
|
-
runtime: { last_transition_at: card_data["lastRun"] ?? null },
|
|
1556
|
-
error: card_data["error"] ? { message: card_data["error"] } : null,
|
|
1557
|
-
blocked_by: [],
|
|
1558
|
-
requires_missing: []
|
|
1559
|
-
};
|
|
1560
|
-
const requiresTokens = Array.isArray(card.requires) ? card.requires : [];
|
|
1561
|
-
const requires = {};
|
|
1562
|
-
for (const token of requiresTokens) {
|
|
1563
|
-
if (Object.prototype.hasOwnProperty.call(dataObjectsByToken, token)) {
|
|
1564
|
-
requires[token] = structuredClone(dataObjectsByToken[token]);
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
return {
|
|
1568
|
-
id: cardId,
|
|
1569
|
-
card,
|
|
1570
|
-
card_data,
|
|
1571
|
-
fetched_sources: runtimeArtifact.fetched_sources,
|
|
1572
|
-
requires,
|
|
1573
|
-
computed_values: runtimeArtifact.computed_values,
|
|
1574
|
-
runtime_state
|
|
1575
|
-
};
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
function buildBrowserArtifactsFromRuntime({
|
|
1579
|
-
boardPath,
|
|
1580
|
-
cardDefinitions,
|
|
1581
|
-
runtimeModels,
|
|
1582
|
-
graphState
|
|
1583
|
-
}) {
|
|
1584
|
-
const safeCardDefs = Array.isArray(cardDefinitions) ? cardDefinitions : [];
|
|
1585
|
-
const safeModels = Array.isArray(runtimeModels) ? runtimeModels : [];
|
|
1586
|
-
const runtimeModelById = new Map(safeModels.map((m) => [m.id, m]));
|
|
1587
|
-
const graphStateAny = graphState;
|
|
1588
|
-
const taskStates = graphStateAny.state?.tasks ?? {};
|
|
1589
|
-
const cardRuntimeById = {};
|
|
1590
|
-
for (const model of safeModels) {
|
|
1591
|
-
if (!model?.id) continue;
|
|
1592
|
-
cardRuntimeById[model.id] = {
|
|
1593
|
-
schema_version: "v1",
|
|
1594
|
-
card_id: model.id,
|
|
1595
|
-
card_data: structuredClone(model.card_data ?? {}),
|
|
1596
|
-
computed_values: structuredClone(model.computed_values ?? {}),
|
|
1597
|
-
fetched_sources: structuredClone(model.fetched_sources ?? {}),
|
|
1598
|
-
requires: structuredClone(model.requires ?? {})
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
const dataObjectsByToken = {};
|
|
1602
|
-
for (const taskName of Object.keys(taskStates)) {
|
|
1603
|
-
const providesData = taskStates[taskName]?.data?.provides_data;
|
|
1604
|
-
if (providesData && typeof providesData === "object") {
|
|
1605
|
-
for (const token of Object.keys(providesData)) {
|
|
1606
|
-
dataObjectsByToken[token] = structuredClone(providesData[token]);
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
const statusCards = safeCardDefs.map((cardDef) => {
|
|
1611
|
-
const model = runtimeModelById.get(cardDef.id) ?? {};
|
|
1612
|
-
const taskState = taskStates[cardDef.id];
|
|
1613
|
-
const taskStatus = typeof taskState?.status === "string" ? taskState.status : cardStatusToTaskStatus(model.card_data?.["status"]);
|
|
1614
|
-
const errorMessage = typeof taskState?.error === "string" ? taskState.error : typeof model.card_data?.["error"] === "string" ? model.card_data["error"] : null;
|
|
1615
|
-
return {
|
|
1616
|
-
name: cardDef.id,
|
|
1617
|
-
status: taskStatus,
|
|
1618
|
-
...errorMessage ? { error: { message: errorMessage, code: "TASK_FAILED", at: taskState?.failedAt ?? null, source: "browser-runtime" } } : {},
|
|
1619
|
-
requires: Array.isArray(cardDef.requires) ? cardDef.requires : [],
|
|
1620
|
-
requires_satisfied: [],
|
|
1621
|
-
requires_missing: [],
|
|
1622
|
-
provides_declared: Array.isArray(cardDef.provides) ? cardDef.provides.map((e) => e.bindTo) : [cardDef.id],
|
|
1623
|
-
provides_runtime: Object.keys(taskState?.data?.provides_data ?? {}).sort(),
|
|
1624
|
-
blocked_by: [],
|
|
1625
|
-
unblocks: [],
|
|
1626
|
-
runtime: {
|
|
1627
|
-
attempt_count: taskState?.executionCount ?? 0,
|
|
1628
|
-
restart_count: taskState?.retryCount ?? 0,
|
|
1629
|
-
in_progress_since: taskStatus === "in-progress" ? taskState?.startedAt ?? null : null,
|
|
1630
|
-
last_transition_at: taskState?.lastUpdated ?? model.card_data?.["lastRun"] ?? null,
|
|
1631
|
-
last_completed_at: taskState?.completedAt ?? null,
|
|
1632
|
-
last_restarted_at: taskState?.startedAt ?? null,
|
|
1633
|
-
status_age_ms: null
|
|
1634
|
-
}
|
|
1635
|
-
};
|
|
1636
|
-
});
|
|
1637
|
-
return {
|
|
1638
|
-
cardDefinitions: structuredClone(safeCardDefs),
|
|
1639
|
-
cardRuntimeById,
|
|
1640
|
-
dataObjectsByToken,
|
|
1641
|
-
statusSnapshot: {
|
|
1642
|
-
schema_version: "v1",
|
|
1643
|
-
meta: { board: { path: boardPath ?? "browser-runtime" } },
|
|
1644
|
-
summary: {
|
|
1645
|
-
card_count: statusCards.length,
|
|
1646
|
-
completed: statusCards.filter((c) => c.status === "completed").length,
|
|
1647
|
-
eligible: 0,
|
|
1648
|
-
pending: statusCards.filter((c) => c.status === "pending").length,
|
|
1649
|
-
blocked: 0,
|
|
1650
|
-
unresolved: 0,
|
|
1651
|
-
failed: statusCards.filter((c) => c.status === "failed").length,
|
|
1652
|
-
in_progress: statusCards.filter((c) => c.status === "in-progress").length,
|
|
1653
|
-
orphan_cards: 0,
|
|
1654
|
-
topology: { edge_count: 0, max_fan_out_card: null, max_fan_out: 0 }
|
|
1655
|
-
},
|
|
1656
|
-
cards: statusCards
|
|
1657
|
-
}
|
|
1658
|
-
};
|
|
1659
|
-
}
|
|
1660
|
-
|
|
1661
|
-
export { LocalStorageService, buildBrowserArtifactsFromRuntime, buildLiveCardModelsFromArtifacts, createBoardLiveGraphRuntime };
|
|
1662
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import j from'jsonata';import {createRequire}from'module';import'ajv-formats';import'child_process';var be=createRequire(import.meta.url),ne=be("./jsonata-sync.cjs")??j;function te(e,t){if(!t||!e)return;let r=t.split("."),n=e;for(let s=0;s<r.length;s++){if(n==null)return;n=n[r[s]];}return n}function se(e,t,r){let n=t.split("."),s=e;for(let o=0;o<n.length-1;o++)(s[n[o]]==null||typeof s[n[o]]!="object")&&(s[n[o]]={}),s=s[n[o]];s[n[n.length-1]]=r;}async function Re(e,t){if(!e?.compute?.length)return e;e.card_data||(e.card_data={}),e.computed_values={},e._sourcesData=t?.sourcesData??{};let r={card_data:e.card_data,requires:e.requires??{},fetched_sources:e._sourcesData,computed_values:e.computed_values};for(let n of e.compute)try{let s=await j(n.expr).evaluate(r);se(e.computed_values,n.bindTo,s),r.computed_values=e.computed_values;}catch(s){console.error(`CardCompute.run error on "${e.id??"?"}.${n.bindTo}":`,s);}return e}function Se(e,t){if(!e?.compute?.length)return {ok:true,node:e};e.card_data||(e.card_data={}),e.computed_values={},e._sourcesData=t?.sourcesData??{};let r={card_data:e.card_data,requires:e.requires??{},fetched_sources:e._sourcesData,computed_values:e.computed_values};for(let n of e.compute)try{let s=ne(n.expr).evaluate(r);se(e.computed_values,n.bindTo,s),r.computed_values=e.computed_values;}catch(s){console.error(`CardCompute.runSync error on "${e.id??"?"}.${n.bindTo}":`,s);}return {ok:true,node:e}}async function Te(e,t){let r={card_data:t.card_data??{},requires:t.requires??{},fetched_sources:t._sourcesData??{},computed_values:t.computed_values??{}};return j(e).evaluate(r)}function we(e,t){return t.startsWith("fetched_sources.")?te(e._sourcesData??{},t.slice(16)):te(e,t)}var re=new Set(["metric","table","editable-table","chart","form","filter","list","notes","todo","alert","narrative","badge","text","markdown","ref","custom","actions"]),Ce=new Set(["id","meta","requires","provides","view","card_data","compute","source_defs"]);function _e(e){let t=[];if(!e||typeof e!="object"||Array.isArray(e))return {ok:false,errors:["Node must be a non-null object"]};let r=e;(typeof r.id!="string"||!r.id)&&t.push("id: required, must be a non-empty string");for(let n of Object.keys(r))Ce.has(n)||t.push(`Unknown top-level key: "${n}"`);if((r.card_data==null||typeof r.card_data!="object"||Array.isArray(r.card_data))&&t.push("card_data: required, must be an object"),r.meta!=null)if(typeof r.meta!="object"||Array.isArray(r.meta))t.push("meta: must be an object");else {let n=r.meta;n.title!=null&&typeof n.title!="string"&&t.push("meta.title: must be a string"),n.tags!=null&&!Array.isArray(n.tags)&&t.push("meta.tags: must be an array");}if(r.requires!=null&&!Array.isArray(r.requires)&&t.push("requires: must be an array of strings"),r.provides!=null&&(Array.isArray(r.provides)?r.provides.forEach((n,s)=>{if(!n||typeof n!="object"||Array.isArray(n))t.push(`provides[${s}]: must be an object with bindTo and ref`);else {let o=n;(typeof o.bindTo!="string"||!o.bindTo)&&t.push(`provides[${s}]: missing required "bindTo" string`),(typeof o.ref!="string"||!o.ref)&&t.push(`provides[${s}]: missing required "ref" string`);}}):t.push("provides: must be an array of { bindTo, ref } bindings")),r.compute!=null&&(Array.isArray(r.compute)?r.compute.forEach((n,s)=>{if(!n||typeof n!="object"||Array.isArray(n))t.push(`compute[${s}]: must be a compute step object`);else {let o=n;(typeof o.bindTo!="string"||!o.bindTo)&&t.push(`compute[${s}]: missing required "bindTo" property`),(typeof o.expr!="string"||!o.expr)&&t.push(`compute[${s}]: missing required "expr" string (JSONata expression)`);}}):t.push("compute: must be an array of compute steps")),r.source_defs!=null)if(!Array.isArray(r.source_defs))t.push("source_defs: must be an array");else {let n=new Set,s=new Set;r.source_defs.forEach((o,i)=>{if(!o||typeof o!="object"||Array.isArray(o))t.push(`source_defs[${i}]: must be an object`);else {let d=o;typeof d.bindTo!="string"||!d.bindTo?t.push(`source_defs[${i}]: missing required "bindTo" property`):(n.has(d.bindTo)&&t.push(`source_defs[${i}]: bindTo "${d.bindTo}" is not unique across source_defs`),n.add(d.bindTo)),typeof d.outputFile!="string"||!d.outputFile?t.push(`source_defs[${i}]: missing required "outputFile" property`):(s.has(d.outputFile)&&t.push(`source_defs[${i}]: outputFile "${d.outputFile}" is not unique across source_defs`),s.add(d.outputFile)),d.optionalForCompletionGating!=null&&typeof d.optionalForCompletionGating!="boolean"&&t.push(`source_defs[${i}]: optionalForCompletionGating must be a boolean`);}});}if(r.view!=null)if(typeof r.view!="object"||Array.isArray(r.view))t.push("view: must be an object");else {let n=r.view;!Array.isArray(n.elements)||n.elements.length===0?t.push("view.elements: required, must be a non-empty array"):n.elements.forEach((s,o)=>{if(!s||typeof s!="object"){t.push(`view.elements[${o}]: must be an object`);return}!s.kind||typeof s.kind!="string"?t.push(`view.elements[${o}].kind: required, must be a string`):re.has(s.kind)||t.push(`view.elements[${o}].kind: unknown kind "${s.kind}". Valid: ${[...re].join(", ")}`),s.data!=null&&(typeof s.data!="object"||Array.isArray(s.data))&&t.push(`view.elements[${o}].data: must be an object`);}),n.layout!=null&&(typeof n.layout!="object"||Array.isArray(n.layout))&&t.push("view.layout: must be an object"),n.features!=null&&(typeof n.features!="object"||Array.isArray(n.features))&&t.push("view.features: must be an object");}return {ok:t.length===0,errors:t}}async function xe(e,t){if(!e||e.length===0)return [];let r={card_data:t.card_data??{},requires:t.requires??{}};return Promise.all(e.map(async n=>{let s={};if(n.projections&&typeof n.projections=="object"&&!Array.isArray(n.projections)){for(let[o,i]of Object.entries(n.projections))if(typeof i=="string"&&i.trim().length>0)try{s[o]=await j(i).evaluate(r);}catch{s[o]=void 0;}}return {...n,_projections:s}}))}function Ae(e,t){if(!e||e.length===0)return [];let r={card_data:t.card_data??{},requires:t.requires??{}};return e.map(n=>{let s={};if(n.projections&&typeof n.projections=="object"&&!Array.isArray(n.projections)){for(let[o,i]of Object.entries(n.projections))if(typeof i=="string"&&i.trim().length>0)try{s[o]=ne(i).evaluate(r);}catch{s[o]=void 0;}}return {...n,_projections:s}})}var D={run:Re,runSync:Se,eval:Te,resolve:we,validate:_e,enrichSources:xe,enrichSourcesSync:Ae};var G={RUNNING:"running",COMPLETED:"completed",FAILED:"failed",INACTIVATED:"inactivated"};function O(e){return e?Array.isArray(e.provides)?e.provides:[]:[]}function N(e){return e?Array.isArray(e.requires)?e.requires:[]:[]}function H(e){return e.tasks??{}}function P(e){return e?e.status===G.FAILED||e.status===G.INACTIVATED:false}function oe(e,t){return e.refreshStrategy??t?.refreshStrategy??"data-changed"}function ae(e){return e.maxExecutions}function ie(e,t){let r=new Set;for(let[n,s]of Object.entries(t))if(s.status===G.COMPLETED){let o=e.tasks[n];o&&O(o).forEach(d=>r.add(d));}return Array.from(r)}function ce(e,t){let r={};return e.forEach(n=>{let s=t[n];if(!s)return;O(s).forEach(i=>{r[i]||(r[i]=[]),r[i].push(n);});}),r}function de(e,t,r){let n=e.tasks[t]??q(),s={};if(r){let i=r.tasks[t],d=N(i);for(let m of d)for(let[T,y]of Object.entries(r.tasks))if(O(y).includes(m)){let R=e.tasks[T];R?.lastDataHash&&(s[m]=R.lastDataHash);break}}let o={...n,status:"running",startedAt:new Date().toISOString(),lastUpdated:new Date().toISOString(),progress:0,error:void 0,startConsumedHashes:s};return {...e,tasks:{...e.tasks,[t]:o},lastUpdated:new Date().toISOString()}}function ue(e,t,r,n,s,o){let i=e.tasks[r]??q(),d=t.tasks[r];if(!d)throw new Error(`Task "${r}" not found in graph`);let m;n&&d.on&&d.on[n]?m=d.on[n]:m=O(d);let T=i.startConsumedHashes?{...i.startConsumedHashes}:{...i.lastConsumedHashes};if(!i.startConsumedHashes){let g=d.requires??[];for(let p of g)for(let[h,S]of Object.entries(t.tasks))if(O(S).includes(p)){let v=e.tasks[h];v?.lastDataHash&&(T[p]=v.lastDataHash);break}}let y={...i,status:"completed",completedAt:new Date().toISOString(),lastUpdated:new Date().toISOString(),executionCount:i.executionCount+1,lastEpoch:i.executionCount+1,lastDataHash:s,data:o,lastConsumedHashes:T,error:void 0},R=[...new Set([...e.availableOutputs,...m])];return {...e,tasks:{...e.tasks,[r]:y},availableOutputs:R,lastUpdated:new Date().toISOString()}}function pe(e,t,r,n){let s=e.tasks[r]??q(),o=t.tasks[r];if(o?.retry){let m=s.retryCount+1;if(m<=o.retry.max_attempts){let T={...s,status:"not-started",retryCount:m,lastUpdated:new Date().toISOString(),error:n};return {...e,tasks:{...e.tasks,[r]:T},lastUpdated:new Date().toISOString()}}}let i={...s,status:"failed",failedAt:new Date().toISOString(),lastUpdated:new Date().toISOString(),error:n,executionCount:s.executionCount+1},d=e.availableOutputs;if(o?.on_failure&&o.on_failure.length>0&&(d=[...new Set([...e.availableOutputs,...o.on_failure])]),o?.circuit_breaker&&i.executionCount>=o.circuit_breaker.max_executions){let m=o.circuit_breaker.on_break;d=[...new Set([...d,...m])];}return {...e,tasks:{...e.tasks,[r]:i},availableOutputs:d,lastUpdated:new Date().toISOString()}}function fe(e,t,r,n){let s=e.tasks[t]??q(),o={...s,progress:typeof n=="number"?n:s.progress,messages:[...s.messages??[],...r?[{message:r,timestamp:new Date().toISOString(),status:s.status}]:[]],lastUpdated:new Date().toISOString()};return {...e,tasks:{...e.tasks,[t]:o},lastUpdated:new Date().toISOString()}}function le(e,t){let r=e.tasks[t];if(!r)return e;let n={...r,status:"not-started",startedAt:void 0,completedAt:void 0,failedAt:void 0,error:void 0,data:void 0,progress:null,lastUpdated:new Date().toISOString()};return {...e,tasks:{...e.tasks,[t]:n},lastUpdated:new Date().toISOString()}}function q(){return {status:"not-started",executionCount:0,retryCount:0,lastEpoch:0,messages:[],progress:null}}function B(e,t){let r=t??`live-${Date.now()}`,n={};for(let o of Object.keys(e.tasks))n[o]=me();let s={status:"running",tasks:n,availableOutputs:[],stuckDetection:{is_stuck:false,stuck_description:null,outputs_unresolvable:[],tasks_blocked:[]},lastUpdated:new Date().toISOString(),executionId:r,executionConfig:{executionMode:e.settings.execution_mode??"eligibility-mode",conflictStrategy:e.settings.conflict_strategy??"alphabetical",completionStrategy:e.settings.completion}};return {config:e,state:s}}function ge(e,t){let{config:r,state:n}=e;if("executionId"in t&&t.executionId&&t.executionId!==n.executionId)return e;switch(t.type){case "task-started":return {config:r,state:de(n,t.taskName,r)};case "task-completed":return {config:r,state:ue(n,r,t.taskName,t.result,t.dataHash,t.data)};case "task-failed":return {config:r,state:pe(n,r,t.taskName,t.error)};case "task-progress":return {config:r,state:fe(n,t.taskName,t.message,t.progress)};case "task-restart":return {config:r,state:le(n,t.taskName)};case "inject-tokens":return {config:r,state:{...n,availableOutputs:[...new Set([...n.availableOutputs,...t.tokens])],lastUpdated:new Date().toISOString()}};case "agent-action":return {config:r,state:Ee(n,t.action)};case "task-upsert":return K(e,t.taskName,t.taskConfig);case "task-removal":return V(e,t.taskName);case "node-requires-add":return W(e,t.nodeName,t.tokens);case "node-requires-remove":return J(e,t.nodeName,t.tokens);case "node-provides-add":return X(e,t.nodeName,t.tokens);case "node-provides-remove":return Y(e,t.nodeName,t.tokens);default:return e}}function M(e,t){return t.reduce((r,n)=>ge(r,n),e)}function K(e,t,r){let n=!!e.config.tasks[t];return {config:{...e.config,tasks:{...e.config.tasks,[t]:r}},state:{...e.state,tasks:{...e.state.tasks,[t]:n?e.state.tasks[t]:me()},lastUpdated:new Date().toISOString()}}}function V(e,t){if(!e.config.tasks[t])return e;let{[t]:r,...n}=e.config.tasks,{[t]:s,...o}=e.state.tasks;return {config:{...e.config,tasks:n},state:{...e.state,tasks:o,lastUpdated:new Date().toISOString()}}}function W(e,t,r){let n=e.config.tasks[t];if(!n)return e;let s=N(n),o=r.filter(i=>!s.includes(i));return o.length===0?e:{config:{...e.config,tasks:{...e.config.tasks,[t]:{...n,requires:[...s,...o]}}},state:e.state}}function J(e,t,r){let n=e.config.tasks[t];if(!n)return e;let s=N(n),o=s.filter(i=>!r.includes(i));return o.length===s.length?e:{config:{...e.config,tasks:{...e.config.tasks,[t]:{...n,requires:o}}},state:e.state}}function X(e,t,r){let n=e.config.tasks[t];if(!n)return e;let s=O(n),o=r.filter(i=>!s.includes(i));return o.length===0?e:{config:{...e.config,tasks:{...e.config.tasks,[t]:{...n,provides:[...s,...o]}}},state:e.state}}function Y(e,t,r){let n=e.config.tasks[t];if(!n)return e;let s=O(n),o=s.filter(i=>!r.includes(i));return o.length===s.length?e:{config:{...e.config,tasks:{...e.config.tasks,[t]:{...n,provides:o}}},state:e.state}}function z(e){return {version:1,config:e.config,state:e.state,snapshotAt:new Date().toISOString()}}function me(){return {status:"not-started",executionCount:0,retryCount:0,lastEpoch:0,messages:[],progress:null}}function Ee(e,t){let r=new Date().toISOString();switch(t){case "stop":return {...e,status:"stopped",lastUpdated:r};case "pause":return {...e,status:"paused",lastUpdated:r};case "resume":return {...e,status:"running",lastUpdated:r};default:return e}}function F(e){let{config:t,state:r}=e,n=H(t);if(Object.keys(n).length===0)return {eligible:[],pending:[],unresolved:[],blocked:[],conflicts:{}};let o=Oe(n),i=ie(t,r.tasks),d=new Set([...i,...r.availableOutputs]),m=[],T=[],y=[],R=[];for(let[p,h]of Object.entries(n)){let S=r.tasks[p],v=oe(h,t.settings),E=v!=="once";if(S?.status===G.RUNNING||P(S))continue;let c=ae(h);if(c!==void 0&&S&&S.executionCount>=c||h.circuit_breaker&&S&&S.executionCount>=h.circuit_breaker.max_executions||!E&&S?.status===G.COMPLETED)continue;if(E&&S?.status===G.COMPLETED){let u=N(h),l=false;switch(v){case "data-changed":{u.length>0&&u.some(C=>{for(let[_,x]of Object.entries(n))if(O(x).includes(C)){let w=r.tasks[_];if(!w)continue;let ve=S.lastConsumedHashes?.[C];return w.lastDataHash==null?w.executionCount>S.lastEpoch:w.lastDataHash!==ve}return false})||(l=true);break}case "epoch-changed":{u.length>0&&u.some(C=>{for(let[_,x]of Object.entries(n))if(O(x).includes(C)){let w=r.tasks[_];if(w&&w.executionCount>S.lastEpoch)return true}return false})||(l=true);break}case "time-based":{let A=h.refreshInterval??0;if(A<=0){l=true;break}let C=S.completedAt;if(!C){l=true;break}(Date.now()-Date.parse(C))/1e3<A&&(l=true);break}case "manual":l=true;break}if(l)continue}let a=N(h);if(a.length===0){m.push(p);continue}let k=[],f=[],b=[];for(let u of a){if(d.has(u))continue;let l=o[u]||[];l.length===0?k.push(u):l.every(C=>P(r.tasks[C]))?b.push({token:u,failedProducer:l[0]}):f.push(u);}k.length>0?y.push({taskName:p,missingTokens:k}):b.length>0?R.push({taskName:p,failedTokens:b.map(u=>u.token),failedProducers:[...new Set(b.map(u=>u.failedProducer))]}):f.length>0?T.push({taskName:p,waitingOn:f}):m.push(p);}let g={};if(m.length>1){let p=ce(m,n);for(let[h,S]of Object.entries(p))S.length>1&&(g[h]=S);}return {eligible:m,pending:T,unresolved:y,blocked:R,conflicts:g}}function Oe(e){let t={};for(let[r,n]of Object.entries(e)){for(let s of O(n))t[s]||(t[s]=[]),t[s].push(r);if(n.on)for(let s of Object.values(n.on))for(let o of s)t[o]||(t[o]=[]),t[o].includes(r)||t[o].push(r);if(n.on_failure)for(let s of n.on_failure)t[s]||(t[s]=[]),t[s].includes(r)||t[s].push(r);}return t}var L=class{buffer=[];append(t){this.buffer.push(t);}drain(){let t=this.buffer;return this.buffer=[],t}get size(){return this.buffer.length}};function U(e){let t=Q(e);return Ge(t)}function Q(e){if(e==null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return "["+e.map(Q).join(",")+"]";let t=e;return "{"+Object.keys(t).sort().map(n=>JSON.stringify(n)+":"+Q(t[n])).join(",")+"}"}function Ge(e){let t=0xcbf29ce484222325n,r=0x100000001b3n,n=0xffffffffffffffffn;for(let s=0;s<e.length;s++)t^=BigInt(e.charCodeAt(s)),t=t*r&n;return t.toString(16).padStart(16,"0")}function Ne(e){if(typeof Buffer<"u")return Buffer.from(e,"utf8").toString("base64url");if(typeof btoa=="function"){let t=new TextEncoder().encode(e),r="";for(let n of t)r+=String.fromCharCode(n);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}throw new Error("No base64 encoder available in this runtime")}function Ie(e){if(typeof Buffer<"u")return Buffer.from(e,"base64url").toString("utf8");if(typeof atob=="function"){let t=e.replace(/-/g,"+").replace(/_/g,"/"),r=t+"=".repeat((4-t.length%4)%4),n=atob(r),s=new Uint8Array(n.length);for(let o=0;o<n.length;o++)s[o]=n.charCodeAt(o);return new TextDecoder().decode(s)}throw new Error("No base64 decoder available in this runtime")}function ke(e){let t=JSON.stringify({t:e,n:Date.now().toString(36)+Math.random().toString(36).slice(2,6)});return Ne(t)}function Le(e){try{let t=JSON.parse(Ie(e));return typeof t?.t=="string"?{taskName:t.t}:null}catch{return null}}function $(e,t,r){let{handlers:n,onDrain:s}=t,o=new L,i="state"in e&&"config"in e?e:B(e,r),d=false,m=new Set,T=new Map(Object.entries(n)),y=new L,R=false,g=false;function p(){if(!d){if(R){g=true;return}R=true;try{do g=!1,h();while(g)}finally{R=false;}}}function h(){let c=y.drain(),a=o.drain(),k=[...c,...a];k.length>0&&(i=M(i,k));let f=F(i);k.length>0&&s?.(k,i,f);for(let b of f.eligible)E(b);for(let b of k)if(b.type==="task-progress"){let{taskName:u,update:l}=b;if(!i.config.tasks[u])continue;let C=i.state.tasks[u];if(!C||C.status!=="running")continue;let _=ke(u),x=v(u,_,l).catch(w=>{d||(y.append({type:"task-failed",taskName:u,error:w.message??String(w),timestamp:new Date().toISOString()}),p());}).finally(()=>{m.delete(x);});m.add(x);}}function S(c){let k=i.config.tasks[c].requires??[],f=new Map;for(let[u,l]of Object.entries(i.config.tasks))for(let A of l.provides??[])f.set(A,u);let b={};for(let u of k){let l=f.get(u);l?b[u]=i.state.tasks[l]?.data:b[u]=void 0;}return b}async function v(c,a,k){let f=i.config.tasks[c],b=f.taskHandlers??[],u=S(c);for(let l of b){let A=T.get(l);if(!A)throw new Error(`Handler '${l}' not found in registry (task '${c}')`);let C={nodeId:c,state:u,taskState:i.state.tasks[c],config:f,callbackToken:a,update:k};if(await A(C)==="task-initiate-failure")throw new Error(`Handler '${l}' returned task-initiate-failure (task '${c}')`)}}function E(c){let k=i.config.tasks[c]?.taskHandlers;if(!k||k.length===0)return;y.append({type:"task-started",taskName:c,timestamp:new Date().toISOString()}),p();let f=ke(c),b=v(c,f).catch(u=>{d||(y.append({type:"task-failed",taskName:c,error:u.message??String(u),timestamp:new Date().toISOString()}),p());}).finally(()=>{m.delete(b);});m.add(b);}return {push(c){d||(c.type==="task-completed"&&c.data&&!c.dataHash&&(c={...c,dataHash:U(c.data)}),o.append(c),p());},pushAll(c){if(!d){for(let a of c)a.type==="task-completed"&&a.data&&!a.dataHash?o.append({...a,dataHash:U(a.data)}):o.append(a);p();}},resolveCallback(c,a,k){if(d)return;let f=Le(c);if(!f)return;let{taskName:b}=f;if(i.config.tasks[b]){if(k&&k.length>0)o.append({type:"task-failed",taskName:b,error:k.join("; "),timestamp:new Date().toISOString()});else {let u=a&&Object.keys(a).length>0?U(a):void 0;o.append({type:"task-completed",taskName:b,data:a,dataHash:u,timestamp:new Date().toISOString()});}p();}},addNode(c,a){d||(o.append({type:"task-upsert",taskName:c,taskConfig:a,timestamp:new Date().toISOString()}),p());},removeNode(c){d||(o.append({type:"task-removal",taskName:c,timestamp:new Date().toISOString()}),p());},addRequires(c,a){d||(o.append({type:"node-requires-add",nodeName:c,tokens:a,timestamp:new Date().toISOString()}),p());},removeRequires(c,a){d||(o.append({type:"node-requires-remove",nodeName:c,tokens:a,timestamp:new Date().toISOString()}),p());},addProvides(c,a){d||(o.append({type:"node-provides-add",nodeName:c,tokens:a,timestamp:new Date().toISOString()}),p());},removeProvides(c,a){d||(o.append({type:"node-provides-remove",nodeName:c,tokens:a,timestamp:new Date().toISOString()}),p());},registerHandler(c,a){T.set(c,a);},unregisterHandler(c){T.delete(c);},retrigger(c){d||i.config.tasks[c]&&(o.append({type:"task-restart",taskName:c,timestamp:new Date().toISOString()}),p());},retriggerAll(c){if(!d){for(let a of c)i.config.tasks[a]&&o.append({type:"task-restart",taskName:a,timestamp:new Date().toISOString()});p();}},snapshot(){return z(i)},getState(){return i},getSchedule(){return F(i)},async waitForHandlers(){m.size>0&&await Promise.allSettled([...m]);},async dispose(c){c?.wait&&m.size>0&&await Promise.allSettled([...m]),d=true;}}}function I(e){return JSON.parse(JSON.stringify(e))}function Z(e){let t=e.provides&&e.provides.length>0?e.provides.map(r=>r.bindTo):[e.id];return {requires:e.requires&&e.requires.length>0?[...e.requires]:void 0,provides:t,taskHandlers:[e.id],description:e.meta?.title??e.id}}function Me(e){let t=new Map;for(let[r,n]of e.entries()){let s=n.provides&&n.provides.length>0?n.provides:[{bindTo:r,ref:"card_data"}];for(let o of s)t.set(o.bindTo,r);}return t}function ee(e,t){let r=Me(e),n=e.get(t);if(n){for(let s of n.requires??[])if(!r.has(s))throw new Error(`Card "${t}" requires token "${s}" but no card provides it`)}}var ur={CARD_PREFIX:"yf:cards:",RUNTIME_OUT_PREFIX:"yf:runtime-out:cards:",STATUS_KEY:"yf:runtime-out:status",writeCard(e,t){try{localStorage.setItem(this.CARD_PREFIX+e,JSON.stringify(t));}catch(r){console.warn(`Failed to write card ${e} to localStorage:`,r);}},readCard(e){try{let t=localStorage.getItem(this.CARD_PREFIX+e);return t?JSON.parse(t):null}catch(t){return console.warn(`Failed to read card ${e} from localStorage:`,t),null}},readAllCards(e){let t={};for(let r of e){let n=this.readCard(r);n&&(t[r]=n);}return t},writeComputedArtifact(e){if(!(!e||!e.card_id))try{localStorage.setItem(this.RUNTIME_OUT_PREFIX+String(e.card_id),JSON.stringify(e));}catch(t){console.warn(`Failed to write computed artifact ${e.card_id}:`,t);}},readComputedArtifact(e){try{let t=localStorage.getItem(this.RUNTIME_OUT_PREFIX+e);return t?JSON.parse(t):null}catch(t){return console.warn(`Failed to read computed artifact ${e}:`,t),null}},readAllComputedArtifacts(e){let t={};for(let r of e){let n=this.readComputedArtifact(r);n&&(t[r]=n);}return t},writeStatusSnapshot(e){try{localStorage.setItem(this.STATUS_KEY,JSON.stringify(e));}catch(t){console.warn("Failed to write status snapshot to localStorage:",t);}},readStatusSnapshot(){try{let e=localStorage.getItem(this.STATUS_KEY);return e?JSON.parse(e):null}catch(e){return console.warn("Failed to read status snapshot from localStorage:",e),null}},clear(){let e=[];for(let t=0;t<localStorage.length;t++){let r=localStorage.key(t);r&&(r.startsWith(this.CARD_PREFIX)||r.startsWith(this.RUNTIME_OUT_PREFIX)||r===this.STATUS_KEY)&&e.push(r);}for(let t of e)localStorage.removeItem(t);}};function pr(e,t={}){let r=Array.isArray(e)?{}:{id:e.id,title:e.title,mode:e.mode,positions:e.positions,settings:e.settings},n=Array.isArray(e)?e:e.nodes,s=new Map;for(let a of n){if(s.has(a.id))throw new Error(`Duplicate card ID: "${a.id}"`);s.set(a.id,I(a));}let o=new Set,i=t.taskExecutor,d=t.sourceAdapters??{},m=t.defaultSourceAdapter,T=null,y=(a,k)=>{let f={events:a,graph:k,nodes:E()};for(let b of o)b(f);},R=a=>async k=>{let f=s.get(a);if(!f)return "task-initiate-failure";let b={};for(let _ of f.requires??[]){let x=k.state[_];if(!x||typeof x!="object")continue;let w=x.provides_data;!w||typeof w!="object"||Object.prototype.hasOwnProperty.call(w,_)&&(b[_]=w[_]);}let u={};if(f.source_defs&&f.source_defs.length>0){let _=d[a]??m,x=i?await i({card:f,input:k}):_?await _({card:f,input:k}):void 0;if(x&&typeof x=="object")for(let w of f.source_defs)Object.prototype.hasOwnProperty.call(x,w.bindTo)?u[w.bindTo]=x[w.bindTo]:f.source_defs.length===1&&(u[w.bindTo]=x);}let l={id:f.id,card_data:I(f.card_data??{}),requires:b,source_defs:f.source_defs,compute:f.compute};l._sourcesData=u,l.compute&&l.compute.length>0&&await D.run(l,{sourcesData:u});let A={};if(f.provides&&f.provides.length>0)for(let{bindTo:_,ref:x}of f.provides)A[_]=D.resolve(l,x);else A[f.id]={...l.card_data??{},...l.computed_values??{},...l._sourcesData??{}};let C={provides_data:A,card_data:l.card_data??{},computed_values:l.computed_values??{},fetched_sources:u,requires:b};return T?.resolveCallback(k.callbackToken,C),"task-initiated"},g={},p={};for(let[a,k]of s.entries())ee(s,a),g[a]=Z(k),p[a]=R(a);let h={id:r.id??`browser-board-${Date.now()}`,settings:{completion:"manual",execution_mode:"eligibility-mode",...r.settings??{},...t.graphSettings??{}},tasks:g},S=t.reactiveOptions?.onDrain,v=$(h,{...t.reactiveOptions??{},handlers:p,onDrain:(a,k,f)=>{S?.(a,k,f),y(a,k);}},t.executionId);T=v;function E(){let a=v.getState(),k=[];for(let[f,b]of s.entries()){let u=a.state.tasks[f]?.data,l=a.state.tasks[f],A={...b.card_data??{},...u&&typeof u.card_data=="object"?u.card_data:{}},C=l?.status==="running"?"loading":l?.status,_={...A,...C?{status:C}:{},...l?.lastUpdated?{lastRun:l.lastUpdated}:{},...l?.status==="failed"&&l.error?{error:l.error}:{}};k.push({id:f,card:I(b),card_data:_,fetched_sources:u&&typeof u.fetched_sources=="object"?I(u.fetched_sources):{},requires:u&&typeof u.requires=="object"?I(u.requires):{},computed_values:u&&typeof u.computed_values=="object"?I(u.computed_values):{},runtime_state:l?I(l):{}});}return k}return {getGraph:()=>v,getState:()=>v.getState(),getSchedule:()=>v.getSchedule(),getNodes:()=>E(),getBoard:()=>({...r,nodes:E()}),subscribe(a){return o.add(a),a({events:[],graph:v.getState(),nodes:E()}),()=>o.delete(a)},addCard(a){if(s.has(a.id))throw new Error(`Card "${a.id}" already exists`);s.set(a.id,I(a)),ee(s,a.id),v.registerHandler(a.id,R(a.id)),v.addNode(a.id,Z(a));},upsertCard(a){s.set(a.id,I(a)),ee(s,a.id),v.registerHandler(a.id,R(a.id)),v.addNode(a.id,Z(a));},removeCard(a){s.delete(a),v.unregisterHandler(a),v.removeNode(a);},patchCardState(a,k){let f=s.get(a);if(!f)throw new Error(`Card "${a}" not found`);f.card_data={...f.card_data??{},...k},v.retrigger(a);},retrigger(a){v.retrigger(a);},retriggerAll(){v.retriggerAll(Array.from(s.keys()));},push(a){v.push(a);},pushAll(a){v.pushAll(a);},dispose(){o.clear(),v.dispose();}}}function he(e){return e==="running"||e==="in-progress"?"loading":e==="failed"?"error":"fresh"}function Fe(e){return e==="loading"?"in-progress":e==="error"?"failed":e==="stale"?"pending":e==="fresh"?"completed":"pending"}function Ue(e,t){let r=t&&typeof t=="object"&&!Array.isArray(t)?t:{};return {schema_version:r.schema_version||"v1",card_id:typeof r.card_id=="string"?r.card_id:e,card_data:r.card_data&&typeof r.card_data=="object"&&!Array.isArray(r.card_data)?structuredClone(r.card_data):{},computed_values:r.computed_values&&typeof r.computed_values=="object"&&!Array.isArray(r.computed_values)?structuredClone(r.computed_values):{},fetched_sources:r.fetched_sources&&typeof r.fetched_sources=="object"&&!Array.isArray(r.fetched_sources)?structuredClone(r.fetched_sources):{},requires:r.requires&&typeof r.requires=="object"&&!Array.isArray(r.requires)?structuredClone(r.requires):{}}}function fr(e){if(!e||typeof e!="object")throw new Error("payload must be an object");let t=Array.isArray(e.cardDefinitions)?e.cardDefinitions:[],r=e.statusSnapshot&&typeof e.statusSnapshot=="object"?e.statusSnapshot:{},n=e.cardRuntimeById&&typeof e.cardRuntimeById=="object"?e.cardRuntimeById:{},s=e.dataObjectsByToken&&typeof e.dataObjectsByToken=="object"?e.dataObjectsByToken:{},o=Array.isArray(r.cards)?r.cards:[],i=new Map(o.map(d=>[d.name,d]));return t.map(d=>{let m=structuredClone(d),T=m.id;if(!T)throw new Error("cardDefinitions entry missing id");let y=i.get(T),R=Ue(T,n[T]),p={...m.card_data&&typeof m.card_data=="object"&&!Array.isArray(m.card_data)?m.card_data:{},...R.card_data||{},status:he(y?.status),lastRun:y?.runtime?.last_transition_at??null};y?.error?.message&&(p.error=y.error.message);let h=y?{task_status:y.status??null,card_status:he(y.status),runtime:structuredClone(y.runtime??{}),error:y.error?structuredClone(y.error):null,blocked_by:Array.isArray(y.blocked_by)?structuredClone(y.blocked_by):[],requires_missing:Array.isArray(y.requires_missing)?structuredClone(y.requires_missing):[]}:{task_status:null,card_status:p.status??"fresh",runtime:{last_transition_at:p.lastRun??null},error:p.error?{message:p.error}:null,blocked_by:[],requires_missing:[]},S=Array.isArray(m.requires)?m.requires:[],v={};for(let E of S)Object.prototype.hasOwnProperty.call(s,E)&&(v[E]=structuredClone(s[E]));return {id:T,card:m,card_data:p,fetched_sources:R.fetched_sources,requires:v,computed_values:R.computed_values,runtime_state:h}})}function lr({boardPath:e,cardDefinitions:t,runtimeModels:r,graphState:n}){let s=Array.isArray(t)?t:[],o=Array.isArray(r)?r:[],i=new Map(o.map(g=>[g.id,g])),m=n.state?.tasks??{},T={};for(let g of o)g?.id&&(T[g.id]={schema_version:"v1",card_id:g.id,card_data:structuredClone(g.card_data??{}),computed_values:structuredClone(g.computed_values??{}),fetched_sources:structuredClone(g.fetched_sources??{}),requires:structuredClone(g.requires??{})});let y={};for(let g of Object.keys(m)){let p=m[g]?.data?.provides_data;if(p&&typeof p=="object")for(let h of Object.keys(p))y[h]=structuredClone(p[h]);}let R=s.map(g=>{let p=i.get(g.id)??{},h=m[g.id],S=typeof h?.status=="string"?h.status:Fe(p.card_data?.status),v=typeof h?.error=="string"?h.error:typeof p.card_data?.error=="string"?p.card_data.error:null;return {name:g.id,status:S,...v?{error:{message:v,code:"TASK_FAILED",at:h?.failedAt??null,source:"browser-runtime"}}:{},requires:Array.isArray(g.requires)?g.requires:[],requires_satisfied:[],requires_missing:[],provides_declared:Array.isArray(g.provides)?g.provides.map(E=>E.bindTo):[g.id],provides_runtime:Object.keys(h?.data?.provides_data??{}).sort(),blocked_by:[],unblocks:[],runtime:{attempt_count:h?.executionCount??0,restart_count:h?.retryCount??0,in_progress_since:S==="in-progress"?h?.startedAt??null:null,last_transition_at:h?.lastUpdated??p.card_data?.lastRun??null,last_completed_at:h?.completedAt??null,last_restarted_at:h?.startedAt??null,status_age_ms:null}}});return {cardDefinitions:structuredClone(s),cardRuntimeById:T,dataObjectsByToken:y,statusSnapshot:{schema_version:"v1",meta:{board:{path:e??"browser-runtime"}},summary:{card_count:R.length,completed:R.filter(g=>g.status==="completed").length,eligible:0,pending:R.filter(g=>g.status==="pending").length,blocked:0,unresolved:0,failed:R.filter(g=>g.status==="failed").length,in_progress:R.filter(g=>g.status==="in-progress").length,orphan_cards:0,topology:{edge_count:0,max_fan_out_card:null,max_fan_out:0}},cards:R}}}
|
|
2
|
+
export{ur as LocalStorageService,lr as buildBrowserArtifactsFromRuntime,fr as buildLiveCardModelsFromArtifacts,pr as createBoardLiveGraphRuntime};//# sourceMappingURL=index.js.map
|
|
1663
3
|
//# sourceMappingURL=index.js.map
|