automategraph 0.0.0-stage → 1.5.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/CHANGELOG.md +292 -0
- package/LICENSE +15 -0
- package/README.md +418 -2
- package/js/src/automategraph.d.ts +588 -0
- package/js/src/automategraph.d.ts.map +1 -0
- package/js/src/automategraph.js +1682 -0
- package/js/src/automategraph.js.map +1 -0
- package/js/src/graph-json.d.ts +85 -0
- package/js/src/graph-json.d.ts.map +1 -0
- package/js/src/graph-json.js +572 -0
- package/js/src/graph-json.js.map +1 -0
- package/js/src/node/graph-scripts.d.ts +77 -0
- package/js/src/node/graph-scripts.d.ts.map +1 -0
- package/js/src/node/graph-scripts.js +490 -0
- package/js/src/node/graph-scripts.js.map +1 -0
- package/js/src/storage-adapter.d.ts +70 -0
- package/js/src/storage-adapter.d.ts.map +1 -0
- package/js/src/storage-adapter.js +245 -0
- package/js/src/storage-adapter.js.map +1 -0
- package/js/src/storage-conformance.d.ts +31 -0
- package/js/src/storage-conformance.d.ts.map +1 -0
- package/js/src/storage-conformance.js +286 -0
- package/js/src/storage-conformance.js.map +1 -0
- package/package.json +89 -4
- package/src/automategraph.ts +2761 -0
- package/src/graph-json.ts +954 -0
- package/src/node/graph-scripts.ts +829 -0
- package/src/storage-adapter.ts +363 -0
- package/src/storage-conformance.ts +423 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the AutomateGraph Execution Engine.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/); the project uses semver.
|
|
5
|
+
|
|
6
|
+
## [1.5.0] — Script loading
|
|
7
|
+
|
|
8
|
+
A document can now say where its functions live and have them loaded: `scripts` entries point at
|
|
9
|
+
files, inline source or URLs, and the loader executes them in the same realm. Purely additive —
|
|
10
|
+
without a `scripts` field, nothing changes; the core stays isomorphic and the loader is a
|
|
11
|
+
separate Node-only module.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`src/node/graph-scripts.ts`** (Node-only, excluded from the browser build):
|
|
16
|
+
`loadGraphScripts(document, options)` returns
|
|
17
|
+
`{ functions, namespaces, scripts, registry }` — ready to pass as
|
|
18
|
+
`createEngineFromJSON(document, { functions })`. Options: `baseDir` (default
|
|
19
|
+
`process.cwd()`), `collisions` (`"throw"` | `"override"`), `onLog`, and injectable
|
|
20
|
+
`readFile` / `fetchText`.
|
|
21
|
+
- **Script declarations** in the graph document: `scripts: [{ namespace, path | source | url,
|
|
22
|
+
format? }]`. Validated by the isomorphic parser with JSON paths (`scripts[0].path`…): exactly
|
|
23
|
+
one source, identifier-like namespace, unique namespaces per document, known keys only, and
|
|
24
|
+
`parseGraphJSONDocument()` now exposes them. **Validation runs before execution** — a document
|
|
25
|
+
with a broken graph never runs a script.
|
|
26
|
+
- **Two provision strategies**, mixable in one script:
|
|
27
|
+
- EXPORTS — `module.exports = { … }` (CJS), `export const …` / `export default { … }` (ESM);
|
|
28
|
+
non-function exports are ignored and reported in `ignoredExports`;
|
|
29
|
+
- SELF-REGISTRATION — `globalThis.GraphEngine.registerEngineNamespace(namespace, functions)`,
|
|
30
|
+
installed by the loader for the duration of the load only, realm restored afterwards
|
|
31
|
+
(verified by identity, including a pre-existing `GraphEngine` global).
|
|
32
|
+
- **The namespace freeze** — claims are PROCESS-global (`globalThis` + `Symbol.for`), so a
|
|
33
|
+
second claim of the same namespace throws `NAMESPACE_ALREADY_REGISTERED`; a self-registering
|
|
34
|
+
script gets that throw from inside its own body. Claims are made at registration time, so a
|
|
35
|
+
script that registers and then throws leaves the namespace claimed.
|
|
36
|
+
`registeredNamespaces()` inspects the claims; `resetGraphScriptNamespaces()` clears them
|
|
37
|
+
(test/dev hygiene). Namespace mismatch throws `NAMESPACE_MISMATCH`.
|
|
38
|
+
- **Names keep their spelling** — the namespace is a claim slot, never a name prefix.
|
|
39
|
+
Cross-script name collisions throw `FUNCTION_NAME_COLLISION` unless the load opts into
|
|
40
|
+
`{ collisions: "override" }` (which logs a warning).
|
|
41
|
+
- **Format handling** — `.cjs` → `require`, `.mjs` → dynamic `import` (kept real through a
|
|
42
|
+
tsc-CommonJS-safe indirection), `.js`/URL → source-hint detection then CJS, falling back to
|
|
43
|
+
ESM and vice versa; `format: "cjs" | "esm" | "auto"` pins it and disables the fallback.
|
|
44
|
+
- **Errors** — `GraphScriptError` with codes `SCRIPT_LOAD_FAILED`, `SCRIPT_NO_FUNCTIONS`,
|
|
45
|
+
`SCRIPT_EXPORT_INVALID`, `NAMESPACE_ALREADY_REGISTERED`, `NAMESPACE_MISMATCH`,
|
|
46
|
+
`FUNCTION_NAME_COLLISION`; a script-raised registration error surfaces unwrapped, never as a
|
|
47
|
+
generic load failure.
|
|
48
|
+
- **Tests** — `tests/scripts.test.ts` (26) + 15 fixtures under `tests/fixtures/scripts/`;
|
|
49
|
+
suite 256 → 282.
|
|
50
|
+
- **Example** — `examples/json-scripts.ts` + `examples/order-flow.scripted.graph.json` +
|
|
51
|
+
`examples/order-flow.functions.cjs` (`npm run example:scripts`): one document, a path script
|
|
52
|
+
and an inline self-registering script, run end-to-end, then the freeze and the reset.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- `tsconfig.browser.json` excludes `src/node/**` — the isomorphic guard still covers the whole
|
|
57
|
+
core (engine, storage contract, JSON parser), the Node loader is deliberately outside it.
|
|
58
|
+
- `parseGraphJSONDocument()` added to `src/graph-json.ts`; `parseGraphJSON()` is now its
|
|
59
|
+
`.definition` and keeps its exact v1.4 behaviour.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **The format fallback replayed a script that had already run.** A script that executes and
|
|
64
|
+
throws while loading used to be retried in the other module format — running its body a second
|
|
65
|
+
time (double side effects, and a duplicate namespace claim for self-registering scripts).
|
|
66
|
+
The fallback is now restricted to module-FORMAT failures (`ERR_REQUIRE_ESM`, ESM syntax under
|
|
67
|
+
`require`, module-scope references); a script's own error fails the load immediately, after
|
|
68
|
+
exactly one execution. Guarded by scripts case S16.
|
|
69
|
+
|
|
70
|
+
## [1.4.0] — Function registry and graph JSON
|
|
71
|
+
|
|
72
|
+
Moves code out of the graph definition: `run` and edge `test` may name a function that lives in
|
|
73
|
+
a registry, and a graph may be a JSON document. Purely additive — every v1.0–v1.3 graph keeps
|
|
74
|
+
its behaviour, because inline functions are still accepted everywhere.
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **Function registry** — `FunctionRegistry` / `createFunctionRegistry()` /
|
|
79
|
+
`FunctionRegistry.from()`: `register`, `extend`, `has`, `get`, `names()` (sorted), `size`,
|
|
80
|
+
`snapshot()`, `info()`, `fingerprint()`. Keys must match `FUNCTION_NAME_PATTERN`
|
|
81
|
+
(`vendor:retry`, `order.chargeCard` are legal); registering a non-function throws.
|
|
82
|
+
- **Engine-level table** — `new GraphExecutionEngine(graph, { functions })` plus
|
|
83
|
+
`engine.registerFunction(name, fn)` at any time, and `engine.registry` for
|
|
84
|
+
`{ fingerprint, names }`.
|
|
85
|
+
- **Graph-level table** — `GraphDefinition.functions`, merged OVER the engine's table
|
|
86
|
+
(a graph may deliberately override a shared function). Mixed graphs — some nodes inline,
|
|
87
|
+
some named — are normal.
|
|
88
|
+
- **Named references** — `NodeDefinition.run` and `NextEdge.test` accept a name. Resolution is
|
|
89
|
+
LAZY (per evaluation), so registering a function after construction, or while a run is in
|
|
90
|
+
flight, still takes effect.
|
|
91
|
+
- **`UNKNOWN_FUNCTION`** — a missing `run` name records an unhandled error, fails the
|
|
92
|
+
activation, schedules nothing, and is PERMANENT: not retried, never routed through
|
|
93
|
+
`onThrow`/`fallback` (those are error recovery; this is a wiring defect). A missing `test`
|
|
94
|
+
name records the error once per node+name, never matches, and lets the rest of the chain
|
|
95
|
+
evaluate. The run settles FAILED because the error stays unhandled.
|
|
96
|
+
- **Observability** — `fn` on `NodeResult` and on `node.started` / `node.succeeded` /
|
|
97
|
+
`node.failed`; `{ fingerprint, names }` on `graph.ready`, `ExecutionReport.registry` and
|
|
98
|
+
every `ExecutionSnapshot` (omitted when no registry is configured). The fingerprint
|
|
99
|
+
`fnr1:<count>:<hash>` covers sorted names + function source text.
|
|
100
|
+
- **`edgeHasMatcher()`** — shared predicate for conditional vs. else edges; it counts a named
|
|
101
|
+
`test`, which fixed a lint regression (below).
|
|
102
|
+
- **`src/graph-json.ts`** — `parseGraphJSON()`, `createEngineFromJSON()`, `isGraphJSON()`,
|
|
103
|
+
`GraphJSONError` (with `issues: [{ path, message }]`). Node `id` defaults to its key; unknown
|
|
104
|
+
properties are rejected at every level; dangling `next`/`onThrow`/`fallback`/entry/terminal
|
|
105
|
+
references are reported with their JSON paths; `functions` inside the document is rejected
|
|
106
|
+
with a pointer to the engine options. The loader is isomorphic (string or object in, no fs).
|
|
107
|
+
- **`nodeFunctions()` / `edgeTests()`** — typed table builders, because a single table holds
|
|
108
|
+
both signatures and a bare literal cannot infer a parameter type from that union.
|
|
109
|
+
- **Tests** — `tests/registry.test.ts` (28) and `tests/graphjson.test.ts` (19); suite 209 → 256.
|
|
110
|
+
- **Examples** — `examples/json-graph.ts` + `examples/order-flow.graph.json`
|
|
111
|
+
(`npm run example:graphjson`): one document, four registries, including the missing-name case.
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- `NodeDefinition.run`, `NextEdgeObject.test`, `GraphDefinition.functions`,
|
|
116
|
+
`EngineOptions.functions`, `NodeResult.fn`, `ExecutionSnapshot.registry`,
|
|
117
|
+
`ExecutionReport.registry` — all additive; `NormalizedEdge` gained `testName`.
|
|
118
|
+
- Validation: `run` must be a function or an identifier-like name; an edge `test` may be a
|
|
119
|
+
function or a name; the graph table's keys and values are checked at construction.
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
|
|
123
|
+
- **Lint treated a named `test` as an else edge.** After `test` was split into
|
|
124
|
+
`test`/`testName`, `lintGraph()` still asked `edge.test === undefined` — so every edge after a
|
|
125
|
+
named matcher was reported `UNREACHABLE_EDGE`, and `PARALLEL_EDGE_MATCHER_IGNORED` was missed
|
|
126
|
+
for parallel edges carrying a name. Both now use `edgeHasMatcher()`; regression-guarded by
|
|
127
|
+
registry cases R18/R19.
|
|
128
|
+
|
|
129
|
+
## [1.3.0] — Typed edges and explicit routing
|
|
130
|
+
|
|
131
|
+
Turns `next` from a list of destinations into an ordered, conditional edge list, and lets a node
|
|
132
|
+
route itself. Purely additive: every v1.0–v1.2 graph keeps its behaviour, because a string entry
|
|
133
|
+
normalizes to an unconditional parallel edge — exactly its old meaning.
|
|
134
|
+
|
|
135
|
+
### Added
|
|
136
|
+
|
|
137
|
+
- **`NextEdge`** — `next` accepts, in any mix:
|
|
138
|
+
| declared | normalized | meaning |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `"audit"` | `{ nextNode, parallel: true }` | unconditional fan-out (v1.0 behaviour) |
|
|
141
|
+
| `{ nextNode: "b", result: 1 }` | `{ …, parallel: false, hasResult: true }` | exclusive, deep equality |
|
|
142
|
+
| `{ nextNode: "c", test: fn }` | `{ …, parallel: false }` | exclusive, predicate |
|
|
143
|
+
| `{ nextNode: "d" }` | `{ …, parallel: false }` | exclusive **else** edge |
|
|
144
|
+
| `{ nextNode: "x", parallel: true }` | unchanged | fires regardless of the exclusive chain |
|
|
145
|
+
|
|
146
|
+
Exclusive edges are evaluated **in declaration order, first match wins**; a matched edge
|
|
147
|
+
suppresses later exclusive edges, parallel edges are never suppressed. No match schedules
|
|
148
|
+
nothing and records no error (silent). `result` uses deep equality (`deepEquals`: `Object.is`
|
|
149
|
+
for primitives — so `NaN` matches `NaN` — element-wise for arrays, own keys for objects,
|
|
150
|
+
`getTime()` for Dates, cycle-safe); `result` and `test` together must both pass.
|
|
151
|
+
- **Explicit routing** — a node can choose its own successor:
|
|
152
|
+
- `return new Next(target, value)` — `value` becomes the node's output, `target` the
|
|
153
|
+
destination. `Next` with an empty target throws `INVALID_NEXT_TARGET`.
|
|
154
|
+
- `context.goto(target)` followed by the normal `return value` — for void/imperative nodes.
|
|
155
|
+
- An explicit route **replaces the whole edge list** for that activation (parallel edges
|
|
156
|
+
included), may target any node in the graph, and is validated at runtime: an unknown target
|
|
157
|
+
records `INVALID_GOTO_TARGET` and schedules nothing.
|
|
158
|
+
- `Next` wins over `goto` when both are used; each retry attempt starts with no route set.
|
|
159
|
+
- **`NodeContext.goto()`**, **`Next`**, **`isNext()`**, **`normalizeNextEdges()`**,
|
|
160
|
+
**`deepEquals()`** exported.
|
|
161
|
+
- **`node.succeeded` payload** gains an optional `route: { nextNode, via: "next-class" | "goto" }`
|
|
162
|
+
when the activation routed itself.
|
|
163
|
+
- **New lint codes**: `UNREACHABLE_EDGE` (error — an exclusive edge declared after an else edge
|
|
164
|
+
can never fire), `PARALLEL_EDGE_MATCHER_IGNORED` (warn — parallel edges fire unconditionally,
|
|
165
|
+
so their matcher is ignored), `TERMINAL_EDGE_IGNORED` (warn — a terminal node latches completion
|
|
166
|
+
before routing, so its edges never run).
|
|
167
|
+
- **Validation** for edge entries: non-empty `nextNode`, `test` must be a function, `parallel`
|
|
168
|
+
must be a boolean, malformed entries rejected, targets must exist.
|
|
169
|
+
- **`tests/edges.test.ts` — 26 cases** wiring the whole contract, including the `a → b/c/d/e/f`
|
|
170
|
+
picture as a single execution (5 seeds) and the finding that a runtime route is invisible to
|
|
171
|
+
static reachability, so the edge should be declared as a fallback.
|
|
172
|
+
- **`examples/branching-edges.ts`** + comparison of the three approaches in `examples/README.md`.
|
|
173
|
+
|
|
174
|
+
### Changed
|
|
175
|
+
|
|
176
|
+
- `NodeDefinition.next` is now `readonly NextEdge[]` (was `readonly string[]`). Source-compatible:
|
|
177
|
+
string arrays are still valid and behave identically.
|
|
178
|
+
- Terminal nodes: settlement order is unchanged (terminal check → routing), now documented and
|
|
179
|
+
warned about by lint.
|
|
180
|
+
- `lintGraph()` reachability follows declared edges only; runtime `Next`/`goto` targets cannot be
|
|
181
|
+
seen statically.
|
|
182
|
+
|
|
183
|
+
## [1.2.0] — Success dead-end linting
|
|
184
|
+
|
|
185
|
+
Closes a linting gap found while explaining the C-19 resilience fixture: a non-terminal node
|
|
186
|
+
could declare `onThrow` (or a `fallback`) and never be flagged, even though its **success path
|
|
187
|
+
led nowhere** — the node could only "continue" by failing.
|
|
188
|
+
|
|
189
|
+
### Added
|
|
190
|
+
|
|
191
|
+
- **`SUCCESS_DEAD_END` lint code (error tier)** — fires when a node is not a terminal node and
|
|
192
|
+
has no `next` target, but does have at least one live error transition (`onThrow`, or `fallback`
|
|
193
|
+
when the effective policy is `FALLBACK`). Message: *"…cannot continue after success (no next
|
|
194
|
+
target); only its error transitions are declared. Add next targets or declare it as a terminal
|
|
195
|
+
node"*. The rule is intentional about the difference: `DEAD_END_NODE` stays for the total case
|
|
196
|
+
(no `next`, no `onThrow`, no live `fallback`), and the two codes are **mutually exclusive** — a
|
|
197
|
+
node with no `next` always produces exactly one of them.
|
|
198
|
+
- **`tests/lint.test.ts` — 13 cases** covering the rule, its boundaries (terminal nodes, nodes
|
|
199
|
+
with `next`, inert fallbacks, live fallbacks, unreachable nodes) and its engine integration in
|
|
200
|
+
both `lint: "warn"` and `lint: "error"` modes. The C-19 fixture and the C-01 order-pipeline
|
|
201
|
+
fixture are asserted directly, so the two graphs discussed in review are now pinned by CI.
|
|
202
|
+
- New report kind `LINT` in the dashboard (badge, filter, inventory table).
|
|
203
|
+
|
|
204
|
+
### Changed
|
|
205
|
+
|
|
206
|
+
- `lintGraph()` message for `DEAD_END_NODE` now spells out why it fired:
|
|
207
|
+
*"(no next, onThrow or fallback)"*.
|
|
208
|
+
- Lint lines are logged at the **issue** severity (`error` for error-tier codes) even when the
|
|
209
|
+
mode is `"warn"`; the mode only decides whether error-tier issues are enforced. This was already
|
|
210
|
+
the behaviour — it is now covered by a test so it cannot drift.
|
|
211
|
+
|
|
212
|
+
### Upgrade notes
|
|
213
|
+
|
|
214
|
+
`lint: "error"` users may see graphs rejected that previously passed. The fix is one of:
|
|
215
|
+
add a `next` target, handle the failure inside the node so it always succeeds, or declare the node
|
|
216
|
+
in `terminalNodes` (pair it with `completionPolicy: "ALL_TERMINALS"` / `"DRAIN"` if other branches
|
|
217
|
+
must still complete). Linting is off by default, so default behaviour is unchanged.
|
|
218
|
+
|
|
219
|
+
## [1.1.0] — Wave 1: correctness, policy and contract
|
|
220
|
+
|
|
221
|
+
Wave 1 turns the 18 audited behaviours into explicit guarantees. **Defaults preserve
|
|
222
|
+
v1.0 semantics** except for the two behaviour changes marked BREAKING below. Everything
|
|
223
|
+
else is additive or opt-in.
|
|
224
|
+
|
|
225
|
+
### Added
|
|
226
|
+
|
|
227
|
+
- **`CompletionPolicy`** (`GraphDefinition.completionPolicy`): `"FIRST_TERMINAL"` (default,
|
|
228
|
+
v1.0 behaviour) | `"ALL_TERMINALS"` | `"DRAIN"`.
|
|
229
|
+
- **`activation.dropped` event** — every time the scheduler clears pending work it now
|
|
230
|
+
reports the dropped activations and the reason (`logical-completion`, `all-terminals-reached`,
|
|
231
|
+
`fail-fast`, `graph-fail-fast`, `checkpoint-failure`, `step-budget-exceeded`, `cancellation`).
|
|
232
|
+
- **Cancellation drain deadline** (A2): setting `EngineOptions.cancellationTimeoutMs` now also
|
|
233
|
+
bounds the drain. Work still running when the deadline elapses is marked
|
|
234
|
+
`NodeStatus.ABANDONED`, the run settles, and `ExecutionReport.drainTimedOut` /
|
|
235
|
+
`abandonedActivations` report what happened. Unset ⇒ v1.0 behaviour (wait for cooperative drain).
|
|
236
|
+
- **`NodeStatus.ABANDONED`** — a cancellation-deadline outcome distinct from `CANCELLED`.
|
|
237
|
+
- **Real FALLBACK routing** (A5): `NodeDefinition.fallback` is used as the error transition when
|
|
238
|
+
the effective error policy is `FALLBACK` and no `onThrow` is declared. The error is recorded
|
|
239
|
+
as handled; unknown targets are rejected at construction.
|
|
240
|
+
- **Bounded concurrency and step budget** (A8): `EngineOptions.maxConcurrency` (default:
|
|
241
|
+
unbounded) and `EngineOptions.maxActivations` — the latter records `STEP_BUDGET_EXCEEDED`,
|
|
242
|
+
stops scheduling and settles the run, which makes runaway cycles terminate.
|
|
243
|
+
- **Graph linting** (A9): exported `lintGraph(graph)` plus `EngineOptions.lint`
|
|
244
|
+
(`"off"` default | `"warn"` | `"error"`). Issues: `DUPLICATE_ENTRY`, `DUPLICATE_TERMINAL`,
|
|
245
|
+
`UNREACHABLE_NODE`, `UNREACHABLE_TERMINAL`, `DEAD_END_NODE`, `FALLBACK_TARGET_IGNORED`.
|
|
246
|
+
Findings are surfaced on `ExecutionReport.lintIssues`.
|
|
247
|
+
- **`node.cancelled` event** — emitted when an activation settles as cancelled.
|
|
248
|
+
- **Introspection**: `getInFlightActivationIds()`, `getLintIssues()`.
|
|
249
|
+
- **Storage adapter contract** (`src/storage-adapter.ts`): `StorageAdapter`, `StorageCapabilities`,
|
|
250
|
+
shape validation (`storageAdapterViolations`, `assertStorageAdapter`, `defineStorageAdapter`) and
|
|
251
|
+
a canonical JSON wire format (`serializeSnapshot`, `deserializeSnapshot`, `stableStringify`,
|
|
252
|
+
`compareSnapshots`, `SNAPSHOT_FORMAT`/`SNAPSHOT_FORMAT_VERSION`).
|
|
253
|
+
- **Storage conformance suite** (`src/storage-conformance.ts`): `runStorageConformance(fixture)`
|
|
254
|
+
verifies round-trip, missing-key behavior, execution isolation, no aliasing on save/load,
|
|
255
|
+
overwrite, deterministic serialization, JSON stability, failed-save atomicity (when the fixture
|
|
256
|
+
can inject faults), listing, delete idempotency and close.
|
|
257
|
+
- **`InMemoryStorage`** is now a full reference adapter: `capabilities`, `listExecutionIds()`,
|
|
258
|
+
`delete()`, `close()`.
|
|
259
|
+
- **Isomorphic build proof**: `tsconfig.browser.json` (DOM libs, `types: []`, `noEmit`) plus a test
|
|
260
|
+
that asserts the core compiles with no Node APIs. Timers are `setTimeout`-based only.
|
|
261
|
+
- **New test packs**: `tests/upgrade.test.ts` (19 cases) and `tests/conformance.test.ts` (11 cases),
|
|
262
|
+
wired into a kind-driven harness/report. Suite total: **170 tests**.
|
|
263
|
+
|
|
264
|
+
### Changed (BREAKING)
|
|
265
|
+
|
|
266
|
+
- **A1 — cancellation symmetry.** A node callback that *rejects because of* an abort now settles
|
|
267
|
+
`NodeStatus.CANCELLED` with a **handled** `GRAPH_CANCELLED` error record. Previously it settled
|
|
268
|
+
`FAILED` with an unhandled error, which made cooperative cancellation asymmetric.
|
|
269
|
+
- **A4 — `FAIL_FAST` is sticky.** A node-level `FAIL_FAST` now installs the same persistent stop as
|
|
270
|
+
a graph-level one (it previously flushed the pending queue once, which was a no-op under unbounded
|
|
271
|
+
concurrency). A node-level `CONTINUE` still cannot shield other branches from a graph-level stop.
|
|
272
|
+
|
|
273
|
+
### Fixed
|
|
274
|
+
|
|
275
|
+
- **A6 — `NodeResult.error`** is now populated for `FAILED` and `TIMED_OUT` activations (the field
|
|
276
|
+
existed but was never assigned).
|
|
277
|
+
- **A10 — dead code and hygiene**: removed the unused `resolveRun` field; in-flight activations are
|
|
278
|
+
tracked and released deterministically (`getInFlightActivationIds()` is empty after a settle);
|
|
279
|
+
`NodeJS.ErrnoException` replaced with a structural cast so the core compiles without Node types.
|
|
280
|
+
|
|
281
|
+
### Notes
|
|
282
|
+
|
|
283
|
+
- `cancel()` still returns before the graph settles; in-flight work drains cooperatively. Outputs
|
|
284
|
+
produced by abandoned activations are never adopted.
|
|
285
|
+
- `InMemoryStorage.version` moved to `1.1.0` to reflect the adapter surface.
|
|
286
|
+
|
|
287
|
+
## [1.0.0]
|
|
288
|
+
|
|
289
|
+
- Initial single-file engine: graph validation, event bus, in-memory storage, scheduler with
|
|
290
|
+
unbounded concurrency, retry/error policies, cooperative cancellation, atomic checkpoints.
|
|
291
|
+
- Audit suite: 140 tests (100 unit, 20 complex integration, 20 async-callback) documenting
|
|
292
|
+
18 engine behaviours, with a self-contained HTML/JSON report and a live preview server.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ammisaidkaidi
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|