taskflow-core 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +248 -808
- package/dist/agents.d.ts +12 -0
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +29 -1
- package/dist/agents.js.map +1 -1
- package/dist/cwd-bridge.d.ts +61 -0
- package/dist/cwd-bridge.d.ts.map +1 -0
- package/dist/cwd-bridge.js +136 -0
- package/dist/cwd-bridge.js.map +1 -0
- package/dist/detached-runner.js +31 -5
- package/dist/detached-runner.js.map +1 -1
- package/dist/deterministic.d.ts +8 -0
- package/dist/deterministic.d.ts.map +1 -1
- package/dist/deterministic.js +23 -1
- package/dist/deterministic.js.map +1 -1
- package/dist/exec/driver.d.ts +48 -0
- package/dist/exec/driver.d.ts.map +1 -0
- package/dist/exec/driver.js +410 -0
- package/dist/exec/driver.js.map +1 -0
- package/dist/exec/events.d.ts +76 -0
- package/dist/exec/events.d.ts.map +1 -0
- package/dist/exec/events.js +108 -0
- package/dist/exec/events.js.map +1 -0
- package/dist/exec/fold.d.ts +45 -0
- package/dist/exec/fold.d.ts.map +1 -0
- package/dist/exec/fold.js +122 -0
- package/dist/exec/fold.js.map +1 -0
- package/dist/exec/index.d.ts +17 -0
- package/dist/exec/index.d.ts.map +1 -0
- package/dist/exec/index.js +17 -0
- package/dist/exec/index.js.map +1 -0
- package/dist/exec/kernel-policy.d.ts +35 -0
- package/dist/exec/kernel-policy.d.ts.map +1 -0
- package/dist/exec/kernel-policy.js +184 -0
- package/dist/exec/kernel-policy.js.map +1 -0
- package/dist/exec/step-kinds.d.ts +41 -0
- package/dist/exec/step-kinds.d.ts.map +1 -0
- package/dist/exec/step-kinds.js +599 -0
- package/dist/exec/step-kinds.js.map +1 -0
- package/dist/exec/step.d.ts +94 -0
- package/dist/exec/step.d.ts.map +1 -0
- package/dist/exec/step.js +454 -0
- package/dist/exec/step.js.map +1 -0
- package/dist/flowir/canonical-hash.d.ts +101 -0
- package/dist/flowir/canonical-hash.d.ts.map +1 -0
- package/dist/flowir/canonical-hash.js +219 -0
- package/dist/flowir/canonical-hash.js.map +1 -0
- package/dist/flowir/compile.d.ts +49 -0
- package/dist/flowir/compile.d.ts.map +1 -0
- package/dist/flowir/compile.js +232 -0
- package/dist/flowir/compile.js.map +1 -0
- package/dist/flowir/cond.d.ts +76 -0
- package/dist/flowir/cond.d.ts.map +1 -0
- package/dist/flowir/cond.js +214 -0
- package/dist/flowir/cond.js.map +1 -0
- package/dist/flowir/index.d.ts +20 -23
- package/dist/flowir/index.d.ts.map +1 -1
- package/dist/flowir/index.js +47 -35
- package/dist/flowir/index.js.map +1 -1
- package/dist/flowir/meta.d.ts +4 -4
- package/dist/flowir/meta.d.ts.map +1 -1
- package/dist/flowir/phasefp.d.ts.map +1 -1
- package/dist/flowir/phasefp.js +13 -1
- package/dist/flowir/phasefp.js.map +1 -1
- package/dist/flowir/schema.d.ts +260 -0
- package/dist/flowir/schema.d.ts.map +1 -0
- package/dist/flowir/schema.js +234 -0
- package/dist/flowir/schema.js.map +1 -0
- package/dist/flowir/translate.d.ts.map +1 -1
- package/dist/flowir/translate.js +3 -0
- package/dist/flowir/translate.js.map +1 -1
- package/dist/host/runner-types.d.ts +19 -0
- package/dist/host/runner-types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/interpolate.d.ts +4 -0
- package/dist/interpolate.d.ts.map +1 -1
- package/dist/interpolate.js +14 -0
- package/dist/interpolate.js.map +1 -1
- package/dist/rates.d.ts +121 -0
- package/dist/rates.d.ts.map +1 -0
- package/dist/rates.js +187 -0
- package/dist/rates.js.map +1 -0
- package/dist/replay.d.ts +43 -50
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +629 -19
- package/dist/replay.js.map +1 -1
- package/dist/resources/authority.d.ts +35 -0
- package/dist/resources/authority.d.ts.map +1 -0
- package/dist/resources/authority.js +67 -0
- package/dist/resources/authority.js.map +1 -0
- package/dist/resources/backend.d.ts +302 -0
- package/dist/resources/backend.d.ts.map +1 -0
- package/dist/resources/backend.js +16 -0
- package/dist/resources/backend.js.map +1 -0
- package/dist/resources/baseline.d.ts +116 -0
- package/dist/resources/baseline.d.ts.map +1 -0
- package/dist/resources/baseline.js +447 -0
- package/dist/resources/baseline.js.map +1 -0
- package/dist/resources/canonical-json.d.ts +7 -0
- package/dist/resources/canonical-json.d.ts.map +1 -0
- package/dist/resources/canonical-json.js +75 -0
- package/dist/resources/canonical-json.js.map +1 -0
- package/dist/resources/errors.d.ts +23 -0
- package/dist/resources/errors.d.ts.map +1 -0
- package/dist/resources/errors.js +89 -0
- package/dist/resources/errors.js.map +1 -0
- package/dist/resources/execution.d.ts +90 -0
- package/dist/resources/execution.d.ts.map +1 -0
- package/dist/resources/execution.js +581 -0
- package/dist/resources/execution.js.map +1 -0
- package/dist/resources/index.d.ts +15 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +15 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/journal.d.ts +138 -0
- package/dist/resources/journal.d.ts.map +1 -0
- package/dist/resources/journal.js +438 -0
- package/dist/resources/journal.js.map +1 -0
- package/dist/resources/leases.d.ts +51 -0
- package/dist/resources/leases.d.ts.map +1 -0
- package/dist/resources/leases.js +354 -0
- package/dist/resources/leases.js.map +1 -0
- package/dist/resources/permits.d.ts +52 -0
- package/dist/resources/permits.d.ts.map +1 -0
- package/dist/resources/permits.js +240 -0
- package/dist/resources/permits.js.map +1 -0
- package/dist/resources/persistence.d.ts +63 -0
- package/dist/resources/persistence.d.ts.map +1 -0
- package/dist/resources/persistence.js +522 -0
- package/dist/resources/persistence.js.map +1 -0
- package/dist/resources/registry.d.ts +56 -0
- package/dist/resources/registry.d.ts.map +1 -0
- package/dist/resources/registry.js +139 -0
- package/dist/resources/registry.js.map +1 -0
- package/dist/resources/resolve.d.ts +49 -0
- package/dist/resources/resolve.d.ts.map +1 -0
- package/dist/resources/resolve.js +309 -0
- package/dist/resources/resolve.js.map +1 -0
- package/dist/resources/sandbox.d.ts +72 -0
- package/dist/resources/sandbox.d.ts.map +1 -0
- package/dist/resources/sandbox.js +952 -0
- package/dist/resources/sandbox.js.map +1 -0
- package/dist/resources/schema.d.ts +195 -0
- package/dist/resources/schema.d.ts.map +1 -0
- package/dist/resources/schema.js +231 -0
- package/dist/resources/schema.js.map +1 -0
- package/dist/resources/types.d.ts +36 -0
- package/dist/resources/types.d.ts.map +1 -0
- package/dist/resources/types.js +75 -0
- package/dist/resources/types.js.map +1 -0
- package/dist/runner-core.d.ts +53 -0
- package/dist/runner-core.d.ts.map +1 -1
- package/dist/runner-core.js +521 -53
- package/dist/runner-core.js.map +1 -1
- package/dist/runtime/phases/approval.d.ts +23 -0
- package/dist/runtime/phases/approval.d.ts.map +1 -0
- package/dist/runtime/phases/approval.js +38 -0
- package/dist/runtime/phases/approval.js.map +1 -0
- package/dist/runtime/phases/expand.d.ts +29 -0
- package/dist/runtime/phases/expand.d.ts.map +1 -0
- package/dist/runtime/phases/expand.js +126 -0
- package/dist/runtime/phases/expand.js.map +1 -0
- package/dist/runtime/phases/parallel.d.ts +26 -0
- package/dist/runtime/phases/parallel.d.ts.map +1 -0
- package/dist/runtime/phases/parallel.js +15 -0
- package/dist/runtime/phases/parallel.js.map +1 -0
- package/dist/runtime/phases/race.d.ts +31 -0
- package/dist/runtime/phases/race.d.ts.map +1 -0
- package/dist/runtime/phases/race.js +220 -0
- package/dist/runtime/phases/race.js.map +1 -0
- package/dist/runtime/phases/script.d.ts +38 -0
- package/dist/runtime/phases/script.d.ts.map +1 -0
- package/dist/runtime/phases/script.js +177 -0
- package/dist/runtime/phases/script.js.map +1 -0
- package/dist/runtime.d.ts +68 -16
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1091 -337
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +104 -9
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +249 -32
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +13 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js.map +1 -1
- package/dist/trace.d.ts +7 -1
- package/dist/trace.d.ts.map +1 -1
- package/dist/trace.js +33 -26
- package/dist/trace.js.map +1 -1
- package/package.json +7 -5
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Genuine content-addressed hash over canonical FlowIR
|
|
3
|
+
*
|
|
4
|
+
* This is pi-taskflow's **own** content-addressed hash (Q5=own) over the
|
|
5
|
+
* canonical {@link FlowIR} type contract defined in `./schema.ts`. Unlike
|
|
6
|
+
* `./hash.ts`'s `flowDefHash` — which fingerprints the *DSL Taskflow
|
|
7
|
+
* definition* (vendored from overstory, async Web Crypto, truncated to 16
|
|
8
|
+
* bytes / 32 hex) — this module hashes the *compiled IR* synchronously with
|
|
9
|
+
* `node:crypto` SHA-256 (full 64-hex-char digest). The two answer different
|
|
10
|
+
* questions:
|
|
11
|
+
*
|
|
12
|
+
* - `flowDefHash(def)` — "did the *flow definition* change?" (DSL-level)
|
|
13
|
+
* - `hashFlowIR(ir)` — "is this *compiled IR* the same IR?" (IR-level)
|
|
14
|
+
*
|
|
15
|
+
* Wired into `compileTaskflowToIR` via `hashFlowIR` (`ir:<64-hex>`).
|
|
16
|
+
* The IR-level hash is the content-addressed key the event-sourced kernel
|
|
17
|
+
* (batch 2, `flowir/compile.ts`) will use to deduplicate compiled graphs and
|
|
18
|
+
* to key per-phase fingerprints (`hashNode`) independent of surface formatting.
|
|
19
|
+
*
|
|
20
|
+
* ## Canonicalization guarantees
|
|
21
|
+
*
|
|
22
|
+
* {@link canonicalizeFlowIR} produces a DETERMINISTIC serialization that is
|
|
23
|
+
* invariant under the cosmetic differences two logically-equivalent IRs may
|
|
24
|
+
* differ by:
|
|
25
|
+
*
|
|
26
|
+
* - **Object key order** — all object keys are stable-sorted (UTF-16 code
|
|
27
|
+
* units), so reordering fields on a node or on the IR object does not
|
|
28
|
+
* change the canonical form.
|
|
29
|
+
* - **Node array order** — `nodes` is re-sorted by `id` (stable), so two IRs
|
|
30
|
+
* that list the same nodes in different order canonicalize identically.
|
|
31
|
+
* Other arrays (`inject`, `emits`, `deps`, `edges`) keep their order —
|
|
32
|
+
* their ordering is semantically meaningful (declared-read order, edge
|
|
33
|
+
* declaration order) and must be preserved.
|
|
34
|
+
* - **Condition spelling** — a node's `when` guard is canonicalized via
|
|
35
|
+
* {@link normalizeCond} (`./cond.ts`), which collapses whitespace, operator
|
|
36
|
+
* spacing, and redundant enclosing parens while preserving string-literal
|
|
37
|
+
* contents. Equivalent conditions canonicalize identically.
|
|
38
|
+
* - **Insignificant formatting** — `undefined` optional fields are dropped,
|
|
39
|
+
* so their presence/absence does not change the hash.
|
|
40
|
+
*
|
|
41
|
+
* Two logically-equivalent FlowIRs (key reorder, whitespace, equivalent
|
|
42
|
+
* condition spelling) MUST canonicalize to the same string.
|
|
43
|
+
*
|
|
44
|
+
* ## Domain-separated digests
|
|
45
|
+
*
|
|
46
|
+
* Return values are prefixed:
|
|
47
|
+
* - `hashFlowIR` → `ir:<64-hex>`
|
|
48
|
+
* - `hashNode` → `node:<64-hex>`
|
|
49
|
+
*
|
|
50
|
+
* so IR-level and node-level digests never collide in a shared cache key
|
|
51
|
+
* namespace.
|
|
52
|
+
*
|
|
53
|
+
* ## Purity
|
|
54
|
+
*
|
|
55
|
+
* Zero runtime deps besides `node:crypto`. No IO, no `Date`, no randomness,
|
|
56
|
+
* never throws. The hash is synchronous (unlike `flowDefHash`).
|
|
57
|
+
*
|
|
58
|
+
* Exported from the FlowIR barrel (`./index.ts`) for consumers; **not yet
|
|
59
|
+
* wired** into runtime/cache — `usedFallbackHash` is untouched. Wiring
|
|
60
|
+
* happens in batch 2.
|
|
61
|
+
*
|
|
62
|
+
* @see ./schema.ts for the canonical FlowIR type contract.
|
|
63
|
+
* @see ./cond.ts for condition normalization (`normalizeCond`).
|
|
64
|
+
* @see ./hash.ts for the DSL-level `flowDefHash` (vendored from overstory).
|
|
65
|
+
*/
|
|
66
|
+
import type { FlowIR, FlowIRNode } from "./schema.ts";
|
|
67
|
+
/**
|
|
68
|
+
* Deterministic, order- and whitespace-independent canonical serialization of
|
|
69
|
+
* a {@link FlowIR}.
|
|
70
|
+
*
|
|
71
|
+
* - `nodes` is re-sorted by `id` (stable) so node-list order does not affect
|
|
72
|
+
* the result.
|
|
73
|
+
* - Each node's `when` guard is canonicalized via {@link normalizeCond}.
|
|
74
|
+
* - All object keys are stable-sorted; `undefined` optionals are dropped.
|
|
75
|
+
* - `inject`/`emits`/`deps`/`edges` arrays keep their order (semantic).
|
|
76
|
+
*
|
|
77
|
+
* Two logically-equivalent FlowIRs MUST canonicalize to the same string.
|
|
78
|
+
* Never throws.
|
|
79
|
+
*/
|
|
80
|
+
export declare function canonicalizeFlowIR(ir: FlowIR): string;
|
|
81
|
+
/**
|
|
82
|
+
* Deterministic canonical serialization of a single {@link FlowIRNode}, for
|
|
83
|
+
* per-node content addressing. Same canonicalization rules as
|
|
84
|
+
* {@link canonicalizeFlowIR} applied to one node (`when` normalized, keys
|
|
85
|
+
* sorted, `undefined` optionals dropped, `inject`/`emits`/`deps` order
|
|
86
|
+
* preserved). Never throws.
|
|
87
|
+
*/
|
|
88
|
+
export declare function canonicalizeNode(node: FlowIRNode): string;
|
|
89
|
+
/**
|
|
90
|
+
* Domain-separated content hash of a compiled FlowIR.
|
|
91
|
+
* Returns `ir:<64 lowercase hex SHA-256 chars>` over {@link canonicalizeFlowIR}.
|
|
92
|
+
* Synchronous. Never throws.
|
|
93
|
+
*/
|
|
94
|
+
export declare function hashFlowIR(ir: FlowIR): string;
|
|
95
|
+
/**
|
|
96
|
+
* Domain-separated per-node content hash.
|
|
97
|
+
* Returns `node:<64 lowercase hex SHA-256 chars>` over {@link canonicalizeNode}.
|
|
98
|
+
* Synchronous. Never throws.
|
|
99
|
+
*/
|
|
100
|
+
export declare function hashNode(node: FlowIRNode): string;
|
|
101
|
+
//# sourceMappingURL=canonical-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-hash.d.ts","sourceRoot":"","sources":["../../src/flowir/canonical-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8EtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEzD;AAMD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAGjD"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Genuine content-addressed hash over canonical FlowIR
|
|
3
|
+
*
|
|
4
|
+
* This is pi-taskflow's **own** content-addressed hash (Q5=own) over the
|
|
5
|
+
* canonical {@link FlowIR} type contract defined in `./schema.ts`. Unlike
|
|
6
|
+
* `./hash.ts`'s `flowDefHash` — which fingerprints the *DSL Taskflow
|
|
7
|
+
* definition* (vendored from overstory, async Web Crypto, truncated to 16
|
|
8
|
+
* bytes / 32 hex) — this module hashes the *compiled IR* synchronously with
|
|
9
|
+
* `node:crypto` SHA-256 (full 64-hex-char digest). The two answer different
|
|
10
|
+
* questions:
|
|
11
|
+
*
|
|
12
|
+
* - `flowDefHash(def)` — "did the *flow definition* change?" (DSL-level)
|
|
13
|
+
* - `hashFlowIR(ir)` — "is this *compiled IR* the same IR?" (IR-level)
|
|
14
|
+
*
|
|
15
|
+
* Wired into `compileTaskflowToIR` via `hashFlowIR` (`ir:<64-hex>`).
|
|
16
|
+
* The IR-level hash is the content-addressed key the event-sourced kernel
|
|
17
|
+
* (batch 2, `flowir/compile.ts`) will use to deduplicate compiled graphs and
|
|
18
|
+
* to key per-phase fingerprints (`hashNode`) independent of surface formatting.
|
|
19
|
+
*
|
|
20
|
+
* ## Canonicalization guarantees
|
|
21
|
+
*
|
|
22
|
+
* {@link canonicalizeFlowIR} produces a DETERMINISTIC serialization that is
|
|
23
|
+
* invariant under the cosmetic differences two logically-equivalent IRs may
|
|
24
|
+
* differ by:
|
|
25
|
+
*
|
|
26
|
+
* - **Object key order** — all object keys are stable-sorted (UTF-16 code
|
|
27
|
+
* units), so reordering fields on a node or on the IR object does not
|
|
28
|
+
* change the canonical form.
|
|
29
|
+
* - **Node array order** — `nodes` is re-sorted by `id` (stable), so two IRs
|
|
30
|
+
* that list the same nodes in different order canonicalize identically.
|
|
31
|
+
* Other arrays (`inject`, `emits`, `deps`, `edges`) keep their order —
|
|
32
|
+
* their ordering is semantically meaningful (declared-read order, edge
|
|
33
|
+
* declaration order) and must be preserved.
|
|
34
|
+
* - **Condition spelling** — a node's `when` guard is canonicalized via
|
|
35
|
+
* {@link normalizeCond} (`./cond.ts`), which collapses whitespace, operator
|
|
36
|
+
* spacing, and redundant enclosing parens while preserving string-literal
|
|
37
|
+
* contents. Equivalent conditions canonicalize identically.
|
|
38
|
+
* - **Insignificant formatting** — `undefined` optional fields are dropped,
|
|
39
|
+
* so their presence/absence does not change the hash.
|
|
40
|
+
*
|
|
41
|
+
* Two logically-equivalent FlowIRs (key reorder, whitespace, equivalent
|
|
42
|
+
* condition spelling) MUST canonicalize to the same string.
|
|
43
|
+
*
|
|
44
|
+
* ## Domain-separated digests
|
|
45
|
+
*
|
|
46
|
+
* Return values are prefixed:
|
|
47
|
+
* - `hashFlowIR` → `ir:<64-hex>`
|
|
48
|
+
* - `hashNode` → `node:<64-hex>`
|
|
49
|
+
*
|
|
50
|
+
* so IR-level and node-level digests never collide in a shared cache key
|
|
51
|
+
* namespace.
|
|
52
|
+
*
|
|
53
|
+
* ## Purity
|
|
54
|
+
*
|
|
55
|
+
* Zero runtime deps besides `node:crypto`. No IO, no `Date`, no randomness,
|
|
56
|
+
* never throws. The hash is synchronous (unlike `flowDefHash`).
|
|
57
|
+
*
|
|
58
|
+
* Exported from the FlowIR barrel (`./index.ts`) for consumers; **not yet
|
|
59
|
+
* wired** into runtime/cache — `usedFallbackHash` is untouched. Wiring
|
|
60
|
+
* happens in batch 2.
|
|
61
|
+
*
|
|
62
|
+
* @see ./schema.ts for the canonical FlowIR type contract.
|
|
63
|
+
* @see ./cond.ts for condition normalization (`normalizeCond`).
|
|
64
|
+
* @see ./hash.ts for the DSL-level `flowDefHash` (vendored from overstory).
|
|
65
|
+
*/
|
|
66
|
+
import { createHash } from "node:crypto";
|
|
67
|
+
import { normalizeCond } from "./cond.js";
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Canonical serializer (key-sorted, undefined-dropped, array-order-preserving)
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Deterministic serialization of a plain-JSON-compatible value: recursively
|
|
73
|
+
* key-sorts objects (UTF-16 code units), drops `undefined` values, preserves
|
|
74
|
+
* array order, and uses `JSON.stringify` for primitives/null. This mirrors
|
|
75
|
+
* the canonical-JSON convention used by `./hash.ts`'s `canonicalJson` (itself
|
|
76
|
+
* vendored from overstory) so the two hashes share the same primitive
|
|
77
|
+
* encoding — but this serializer is local to the IR hash so the IR-level
|
|
78
|
+
* contract can evolve independently of the overstory-vendored DSL hash.
|
|
79
|
+
*
|
|
80
|
+
* The value passed in MUST be pre-canonicalized (nodes sorted by id, `when`
|
|
81
|
+
* normalized) — this function does not know about FlowIR semantics, it only
|
|
82
|
+
* guarantees key-order independence and `undefined`-dropping.
|
|
83
|
+
*/
|
|
84
|
+
function canonicalSerialize(value) {
|
|
85
|
+
if (value === null)
|
|
86
|
+
return "null";
|
|
87
|
+
if (typeof value === "boolean")
|
|
88
|
+
return JSON.stringify(value);
|
|
89
|
+
if (typeof value === "number")
|
|
90
|
+
return JSON.stringify(value);
|
|
91
|
+
if (typeof value === "string")
|
|
92
|
+
return JSON.stringify(value);
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
return `[${value.map((item) => canonicalSerialize(item)).join(",")}]`;
|
|
95
|
+
}
|
|
96
|
+
if (typeof value === "object") {
|
|
97
|
+
const record = value;
|
|
98
|
+
const keys = Object.keys(record)
|
|
99
|
+
.filter((key) => record[key] !== undefined)
|
|
100
|
+
.sort();
|
|
101
|
+
const body = keys.map((key) => `${JSON.stringify(key)}:${canonicalSerialize(record[key])}`);
|
|
102
|
+
return `{${body.join(",")}}`;
|
|
103
|
+
}
|
|
104
|
+
// undefined / function / symbol / bigint at this layer — not representable.
|
|
105
|
+
return "null";
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Canonical node object (drops undefined, normalizes `when`)
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Project a {@link FlowIRNode} into a plain canonical object form: fields are
|
|
112
|
+
* emitted in a fixed order (order is irrelevant — `canonicalSerialize`
|
|
113
|
+
* re-sorts keys, but emitting canonically makes the intermediate form
|
|
114
|
+
* human-inspectable), `when` is normalized via {@link normalizeCond} so
|
|
115
|
+
* equivalent condition spellings collapse, and `undefined` optionals are
|
|
116
|
+
* omitted so their presence/absence does not affect the hash.
|
|
117
|
+
*
|
|
118
|
+
* `inject`/`emits`/`deps` arrays are preserved verbatim (order is semantic —
|
|
119
|
+
* declared-read order matters for fingerprinting).
|
|
120
|
+
*/
|
|
121
|
+
function canonicalNodeObject(node) {
|
|
122
|
+
const obj = {
|
|
123
|
+
id: node.id,
|
|
124
|
+
kind: node.kind,
|
|
125
|
+
inject: node.inject,
|
|
126
|
+
emits: node.emits,
|
|
127
|
+
};
|
|
128
|
+
if (node.when !== undefined) {
|
|
129
|
+
obj.when = normalizeCond(node.when).canonical;
|
|
130
|
+
}
|
|
131
|
+
if (node.task !== undefined)
|
|
132
|
+
obj.task = node.task;
|
|
133
|
+
if (node.condRef !== undefined)
|
|
134
|
+
obj.condRef = node.condRef;
|
|
135
|
+
if (node.deps !== undefined)
|
|
136
|
+
obj.deps = node.deps;
|
|
137
|
+
if (node.join !== undefined)
|
|
138
|
+
obj.join = node.join;
|
|
139
|
+
if (node.timeout !== undefined)
|
|
140
|
+
obj.timeout = node.timeout;
|
|
141
|
+
if (node.payload !== undefined)
|
|
142
|
+
obj.payload = node.payload;
|
|
143
|
+
return obj;
|
|
144
|
+
}
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Public canonicalization
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
/**
|
|
149
|
+
* Deterministic, order- and whitespace-independent canonical serialization of
|
|
150
|
+
* a {@link FlowIR}.
|
|
151
|
+
*
|
|
152
|
+
* - `nodes` is re-sorted by `id` (stable) so node-list order does not affect
|
|
153
|
+
* the result.
|
|
154
|
+
* - Each node's `when` guard is canonicalized via {@link normalizeCond}.
|
|
155
|
+
* - All object keys are stable-sorted; `undefined` optionals are dropped.
|
|
156
|
+
* - `inject`/`emits`/`deps`/`edges` arrays keep their order (semantic).
|
|
157
|
+
*
|
|
158
|
+
* Two logically-equivalent FlowIRs MUST canonicalize to the same string.
|
|
159
|
+
* Never throws.
|
|
160
|
+
*/
|
|
161
|
+
export function canonicalizeFlowIR(ir) {
|
|
162
|
+
// Stable sort by node id — node-list order is not semantic.
|
|
163
|
+
const sortedNodes = [...ir.nodes].sort((a, b) => {
|
|
164
|
+
if (a.id < b.id)
|
|
165
|
+
return -1;
|
|
166
|
+
if (a.id > b.id)
|
|
167
|
+
return 1;
|
|
168
|
+
return 0;
|
|
169
|
+
});
|
|
170
|
+
const obj = {
|
|
171
|
+
name: ir.name,
|
|
172
|
+
nodes: sortedNodes.map((n) => canonicalNodeObject(n)),
|
|
173
|
+
};
|
|
174
|
+
if (ir.version !== undefined)
|
|
175
|
+
obj.version = ir.version;
|
|
176
|
+
if (ir.edges !== undefined)
|
|
177
|
+
obj.edges = ir.edges;
|
|
178
|
+
if (ir.args !== undefined)
|
|
179
|
+
obj.args = ir.args;
|
|
180
|
+
if (ir.budget !== undefined)
|
|
181
|
+
obj.budget = ir.budget;
|
|
182
|
+
if (ir.concurrency !== undefined)
|
|
183
|
+
obj.concurrency = ir.concurrency;
|
|
184
|
+
if (ir.meta !== undefined)
|
|
185
|
+
obj.meta = ir.meta;
|
|
186
|
+
return canonicalSerialize(obj);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Deterministic canonical serialization of a single {@link FlowIRNode}, for
|
|
190
|
+
* per-node content addressing. Same canonicalization rules as
|
|
191
|
+
* {@link canonicalizeFlowIR} applied to one node (`when` normalized, keys
|
|
192
|
+
* sorted, `undefined` optionals dropped, `inject`/`emits`/`deps` order
|
|
193
|
+
* preserved). Never throws.
|
|
194
|
+
*/
|
|
195
|
+
export function canonicalizeNode(node) {
|
|
196
|
+
return canonicalSerialize(canonicalNodeObject(node));
|
|
197
|
+
}
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Content-addressed hashing (node:crypto SHA-256, domain-prefixed)
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
/**
|
|
202
|
+
* Domain-separated content hash of a compiled FlowIR.
|
|
203
|
+
* Returns `ir:<64 lowercase hex SHA-256 chars>` over {@link canonicalizeFlowIR}.
|
|
204
|
+
* Synchronous. Never throws.
|
|
205
|
+
*/
|
|
206
|
+
export function hashFlowIR(ir) {
|
|
207
|
+
const hex = createHash("sha256").update(canonicalizeFlowIR(ir), "utf8").digest("hex");
|
|
208
|
+
return `ir:${hex}`;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Domain-separated per-node content hash.
|
|
212
|
+
* Returns `node:<64 lowercase hex SHA-256 chars>` over {@link canonicalizeNode}.
|
|
213
|
+
* Synchronous. Never throws.
|
|
214
|
+
*/
|
|
215
|
+
export function hashNode(node) {
|
|
216
|
+
const hex = createHash("sha256").update(canonicalizeNode(node), "utf8").digest("hex");
|
|
217
|
+
return `node:${hex}`;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=canonical-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-hash.js","sourceRoot":"","sources":["../../src/flowir/canonical-hash.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genuine Taskflow → FlowIR compiler (RFC §5.2, S0).
|
|
3
|
+
*
|
|
4
|
+
* This graduates the stub `translateTaskflow` into a **canonical** IR emitter:
|
|
5
|
+
* 1:1 phase→node projection (native multi-node lowering deferred per §12),
|
|
6
|
+
* declared inject/emits, explicit edges, normalized `when` via {@link normalizeCond},
|
|
7
|
+
* and richer optional node fields (`task`, `deps`, `join`, `timeout`, `condRef`).
|
|
8
|
+
*
|
|
9
|
+
* Pure, synchronous, never throws — diagnostics live in the return value so
|
|
10
|
+
* `/tf ir` on a broken flow still yields a structured report.
|
|
11
|
+
*
|
|
12
|
+
* @see docs/rfc-0.2.0-architecture.md §5
|
|
13
|
+
* @see ./translate.ts (stub; still used for sidecar field list parity)
|
|
14
|
+
*/
|
|
15
|
+
import { type Taskflow } from "../schema.ts";
|
|
16
|
+
import type { FlowIR as CanonicalFlowIR } from "./schema.ts";
|
|
17
|
+
import type { CompileError, CompileWarning, FlowIR, TaskflowIRMeta } from "./meta.ts";
|
|
18
|
+
/**
|
|
19
|
+
* Result of {@link compileTaskflowToFlowIR}: canonical IR + declared meta + diagnostics.
|
|
20
|
+
* `usedFallbackHash` is **false** when the IR is well-formed enough to content-address
|
|
21
|
+
* (always, unless hard errors prevent IR emission).
|
|
22
|
+
*
|
|
23
|
+
* Named separately from Mermaid `compileTaskflow` in `../compile.ts` (diagram).
|
|
24
|
+
*/
|
|
25
|
+
export interface CompileTaskflowToFlowIRResult {
|
|
26
|
+
/** Canonical FlowIR (superset of the stub projection). */
|
|
27
|
+
canonical: CanonicalFlowIR;
|
|
28
|
+
/** Stub-compatible IR projection (same nodes without requiring consumers to import schema.ts). */
|
|
29
|
+
ir: FlowIR;
|
|
30
|
+
meta: TaskflowIRMeta;
|
|
31
|
+
warnings: CompileWarning[];
|
|
32
|
+
errors: CompileError[];
|
|
33
|
+
/** False once the genuine compiler owns the hash (S0). */
|
|
34
|
+
usedFallbackHash: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compile a (desugared) Taskflow into canonical FlowIR.
|
|
38
|
+
*
|
|
39
|
+
* - One node per phase (1:1).
|
|
40
|
+
* - `inject` = `{steps.*}` refs ∪ `dependsOn` (minus self).
|
|
41
|
+
* - `emits` = `[phase.id]`.
|
|
42
|
+
* - `edges` synthesized from inject for graph consumers.
|
|
43
|
+
* - `when` kept as source text; `condRef` = normalizeCond(canonical) when present.
|
|
44
|
+
* - Optional fields: task, deps, join, timeout copied when set.
|
|
45
|
+
*
|
|
46
|
+
* Never throws.
|
|
47
|
+
*/
|
|
48
|
+
export declare function compileTaskflowToFlowIR(def: Taskflow): CompileTaskflowToFlowIRResult;
|
|
49
|
+
//# sourceMappingURL=compile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/flowir/compile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAwD,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGnG,OAAO,KAAK,EACX,MAAM,IAAI,eAAe,EAIzB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACX,YAAY,EACZ,cAAc,EAEd,MAAM,EAEN,cAAc,EACd,MAAM,WAAW,CAAC;AAwFnB;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC7C,0DAA0D;IAC1D,SAAS,EAAE,eAAe,CAAC;IAC3B,kGAAkG;IAClG,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,0DAA0D;IAC1D,gBAAgB,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,QAAQ,GAAG,6BAA6B,CA+HpF"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genuine Taskflow → FlowIR compiler (RFC §5.2, S0).
|
|
3
|
+
*
|
|
4
|
+
* This graduates the stub `translateTaskflow` into a **canonical** IR emitter:
|
|
5
|
+
* 1:1 phase→node projection (native multi-node lowering deferred per §12),
|
|
6
|
+
* declared inject/emits, explicit edges, normalized `when` via {@link normalizeCond},
|
|
7
|
+
* and richer optional node fields (`task`, `deps`, `join`, `timeout`, `condRef`).
|
|
8
|
+
*
|
|
9
|
+
* Pure, synchronous, never throws — diagnostics live in the return value so
|
|
10
|
+
* `/tf ir` on a broken flow still yields a structured report.
|
|
11
|
+
*
|
|
12
|
+
* @see docs/rfc-0.2.0-architecture.md §5
|
|
13
|
+
* @see ./translate.ts (stub; still used for sidecar field list parity)
|
|
14
|
+
*/
|
|
15
|
+
import { collectRefs, PHASE_TYPES } from "../schema.js";
|
|
16
|
+
import { cwdArgName } from "../cwd-bridge.js";
|
|
17
|
+
import { normalizeCond } from "./cond.js";
|
|
18
|
+
// Keep in sync with translate.ts SIDECAR_PHASE_FIELDS (round-trip lossless).
|
|
19
|
+
const SIDECAR_PHASE_FIELDS = [
|
|
20
|
+
"agent",
|
|
21
|
+
"task",
|
|
22
|
+
"over",
|
|
23
|
+
"as",
|
|
24
|
+
"branches",
|
|
25
|
+
"from",
|
|
26
|
+
"use",
|
|
27
|
+
"def",
|
|
28
|
+
"with",
|
|
29
|
+
"run",
|
|
30
|
+
"input",
|
|
31
|
+
"timeout",
|
|
32
|
+
"until",
|
|
33
|
+
"maxIterations",
|
|
34
|
+
"convergence",
|
|
35
|
+
"reflexion",
|
|
36
|
+
"variants",
|
|
37
|
+
"judge",
|
|
38
|
+
"judgeAgent",
|
|
39
|
+
"mode",
|
|
40
|
+
"dependsOn",
|
|
41
|
+
"join",
|
|
42
|
+
"when",
|
|
43
|
+
"retry",
|
|
44
|
+
"output",
|
|
45
|
+
"expect",
|
|
46
|
+
"model",
|
|
47
|
+
"thinking",
|
|
48
|
+
"tools",
|
|
49
|
+
"cwd",
|
|
50
|
+
"final",
|
|
51
|
+
"optional",
|
|
52
|
+
"idempotent",
|
|
53
|
+
"concurrency",
|
|
54
|
+
"context",
|
|
55
|
+
"contextLimit",
|
|
56
|
+
"onBlock",
|
|
57
|
+
"eval",
|
|
58
|
+
"score",
|
|
59
|
+
"cache",
|
|
60
|
+
"shareContext",
|
|
61
|
+
"cancelLosers",
|
|
62
|
+
"expandMode",
|
|
63
|
+
"maxNodes",
|
|
64
|
+
];
|
|
65
|
+
const NODE_FIELD_KEYS = new Set(["task", "dependsOn", "join", "when", "timeout"]);
|
|
66
|
+
const VALID_KINDS = new Set(PHASE_TYPES);
|
|
67
|
+
function sidecarForPhase(phase) {
|
|
68
|
+
const out = {};
|
|
69
|
+
const rec = phase;
|
|
70
|
+
for (const k of SIDECAR_PHASE_FIELDS) {
|
|
71
|
+
if (k in rec && rec[k] !== undefined)
|
|
72
|
+
out[k] = rec[k];
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
function payloadForPhase(phase) {
|
|
77
|
+
const sidecar = sidecarForPhase(phase);
|
|
78
|
+
for (const key of NODE_FIELD_KEYS)
|
|
79
|
+
delete sidecar[key];
|
|
80
|
+
const argName = cwdArgName(phase.cwd);
|
|
81
|
+
if (argName !== undefined) {
|
|
82
|
+
// Canonical IR records logical resource semantics only. The authored raw
|
|
83
|
+
// placeholder remains in meta.sidecar for lossless decompilation.
|
|
84
|
+
delete sidecar.cwd;
|
|
85
|
+
sidecar.cwdUse = {
|
|
86
|
+
kind: "invocation-relative-arg",
|
|
87
|
+
arg: argName,
|
|
88
|
+
access: "read-write",
|
|
89
|
+
intent: "existing-directory",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return Object.keys(sidecar).length > 0 ? sidecar : undefined;
|
|
93
|
+
}
|
|
94
|
+
function asKind(type) {
|
|
95
|
+
const k = (type ?? "agent");
|
|
96
|
+
if (VALID_KINDS.has(k))
|
|
97
|
+
return k;
|
|
98
|
+
// Unknown kinds still project as agent for IR shape; validation is source of truth.
|
|
99
|
+
return "agent";
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Compile a (desugared) Taskflow into canonical FlowIR.
|
|
103
|
+
*
|
|
104
|
+
* - One node per phase (1:1).
|
|
105
|
+
* - `inject` = `{steps.*}` refs ∪ `dependsOn` (minus self).
|
|
106
|
+
* - `emits` = `[phase.id]`.
|
|
107
|
+
* - `edges` synthesized from inject for graph consumers.
|
|
108
|
+
* - `when` kept as source text; `condRef` = normalizeCond(canonical) when present.
|
|
109
|
+
* - Optional fields: task, deps, join, timeout copied when set.
|
|
110
|
+
*
|
|
111
|
+
* Never throws.
|
|
112
|
+
*/
|
|
113
|
+
export function compileTaskflowToFlowIR(def) {
|
|
114
|
+
const warnings = [];
|
|
115
|
+
const errors = [];
|
|
116
|
+
const declaredDeps = {};
|
|
117
|
+
const sidecarPhases = {};
|
|
118
|
+
const knownIds = new Set((def.phases ?? []).map((p) => p.id));
|
|
119
|
+
if (!def.name || typeof def.name !== "string") {
|
|
120
|
+
errors.push({ code: "missing-name", message: "Taskflow.name is required" });
|
|
121
|
+
}
|
|
122
|
+
if (!Array.isArray(def.phases) || def.phases.length === 0) {
|
|
123
|
+
errors.push({ code: "empty-phases", message: "Taskflow.phases must be a non-empty array" });
|
|
124
|
+
}
|
|
125
|
+
const nodes = [];
|
|
126
|
+
const edges = [];
|
|
127
|
+
for (const phase of def.phases ?? []) {
|
|
128
|
+
if (!phase?.id) {
|
|
129
|
+
errors.push({ code: "missing-phase-id", message: "Phase missing id" });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const refs = collectRefs(phase);
|
|
133
|
+
const reads = new Set(refs.steps.filter((id) => id !== phase.id));
|
|
134
|
+
for (const d of phase.dependsOn ?? []) {
|
|
135
|
+
if (d !== phase.id)
|
|
136
|
+
reads.add(d);
|
|
137
|
+
}
|
|
138
|
+
for (const d of phase.from ?? []) {
|
|
139
|
+
if (d !== phase.id)
|
|
140
|
+
reads.add(d);
|
|
141
|
+
}
|
|
142
|
+
const inject = Array.from(reads);
|
|
143
|
+
declaredDeps[phase.id] = { reads: inject, writes: [phase.id] };
|
|
144
|
+
for (const r of refs.steps) {
|
|
145
|
+
if (r !== phase.id && !knownIds.has(r)) {
|
|
146
|
+
warnings.push({
|
|
147
|
+
phaseId: phase.id,
|
|
148
|
+
message: `references {steps.${r}.*} but no phase '${r}' exists`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const kind = asKind(phase.type);
|
|
153
|
+
if (phase.type && !VALID_KINDS.has(phase.type)) {
|
|
154
|
+
warnings.push({
|
|
155
|
+
phaseId: phase.id,
|
|
156
|
+
message: `unknown phase type '${phase.type}' — projected as kind 'agent'`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const node = {
|
|
160
|
+
id: phase.id,
|
|
161
|
+
kind,
|
|
162
|
+
inject,
|
|
163
|
+
emits: [phase.id],
|
|
164
|
+
};
|
|
165
|
+
if (phase.when !== undefined) {
|
|
166
|
+
node.when = phase.when;
|
|
167
|
+
const n = normalizeCond(phase.when);
|
|
168
|
+
// condRef is a stable, hashable form of the guard (not a storage key).
|
|
169
|
+
node.condRef = n.canonical || undefined;
|
|
170
|
+
}
|
|
171
|
+
if (typeof phase.task === "string")
|
|
172
|
+
node.task = phase.task;
|
|
173
|
+
if (phase.dependsOn && phase.dependsOn.length > 0)
|
|
174
|
+
node.deps = [...phase.dependsOn];
|
|
175
|
+
if (phase.join === "all" || phase.join === "any")
|
|
176
|
+
node.join = phase.join;
|
|
177
|
+
if (typeof phase.timeout === "number")
|
|
178
|
+
node.timeout = phase.timeout;
|
|
179
|
+
const payload = payloadForPhase(phase);
|
|
180
|
+
if (payload)
|
|
181
|
+
node.payload = payload;
|
|
182
|
+
for (const from of inject) {
|
|
183
|
+
edges.push({ from, to: phase.id });
|
|
184
|
+
}
|
|
185
|
+
sidecarPhases[phase.id] = sidecarForPhase(phase);
|
|
186
|
+
nodes.push(node);
|
|
187
|
+
}
|
|
188
|
+
const canonical = {
|
|
189
|
+
name: def.name || "unnamed",
|
|
190
|
+
version: typeof def.version === "number" ? def.version : 1,
|
|
191
|
+
nodes,
|
|
192
|
+
edges: edges.length > 0 ? edges : undefined,
|
|
193
|
+
args: def.args,
|
|
194
|
+
budget: def.budget,
|
|
195
|
+
concurrency: def.concurrency,
|
|
196
|
+
meta: {
|
|
197
|
+
source: "taskflow-core",
|
|
198
|
+
irVersion: 1,
|
|
199
|
+
// These flow-level switches change agent discovery / runtime inputs and
|
|
200
|
+
// therefore MUST participate in the content-addressed cache identity.
|
|
201
|
+
// Keeping them in canonical meta preserves the 1:1 node projection while
|
|
202
|
+
// preventing two semantically different flows from sharing an IR hash.
|
|
203
|
+
annotations: {
|
|
204
|
+
agentScope: def.agentScope ?? "user",
|
|
205
|
+
contextSharing: def.contextSharing ?? false,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
// Stub-compatible projection: strip fields meta.FlowIR doesn't declare but keep inject/emits/when/kind.
|
|
210
|
+
const ir = {
|
|
211
|
+
name: canonical.name,
|
|
212
|
+
nodes: nodes.map((n) => ({
|
|
213
|
+
id: n.id,
|
|
214
|
+
kind: n.kind,
|
|
215
|
+
inject: n.inject,
|
|
216
|
+
emits: n.emits,
|
|
217
|
+
when: n.when,
|
|
218
|
+
})),
|
|
219
|
+
args: def.args,
|
|
220
|
+
budget: def.budget,
|
|
221
|
+
concurrency: def.concurrency,
|
|
222
|
+
};
|
|
223
|
+
const meta = {
|
|
224
|
+
sourceFlowName: def.name || "unnamed",
|
|
225
|
+
declaredDeps,
|
|
226
|
+
sidecar: { phases: sidecarPhases },
|
|
227
|
+
};
|
|
228
|
+
// Genuine compiler owns the IR hash when we produced nodes.
|
|
229
|
+
const usedFallbackHash = nodes.length === 0 || errors.some((e) => e.code === "empty-phases");
|
|
230
|
+
return { canonical, ir, meta, warnings, errors, usedFallbackHash };
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=compile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/flowir/compile.ts"],"names":[],"mappings":"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"}
|