@particle-academy/fancy-flow 0.59.0 → 0.64.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/dist/{HumanPrompt-Bw09OyS3.d.ts → HumanPrompt-BKThiyjB.d.ts} +1 -1
- package/dist/{HumanPrompt-DV5-xnDa.d.cts → HumanPrompt-Bpp3JiVH.d.cts} +1 -1
- package/dist/{chunk-M7M6GLFV.js → chunk-5H54OTKT.js} +3 -3
- package/dist/{chunk-M7M6GLFV.js.map → chunk-5H54OTKT.js.map} +1 -1
- package/dist/chunk-77V4QC6Y.js +54 -0
- package/dist/chunk-77V4QC6Y.js.map +1 -0
- package/dist/{chunk-MX4SJDNI.js → chunk-EO6444T2.js} +4 -4
- package/dist/{chunk-MX4SJDNI.js.map → chunk-EO6444T2.js.map} +1 -1
- package/dist/{chunk-HBE4LVPX.js → chunk-JF6WCRBU.js} +9 -7
- package/dist/chunk-JF6WCRBU.js.map +1 -0
- package/dist/{chunk-LGLOBK6Q.js → chunk-MBVX4ZRB.js} +3 -3
- package/dist/{chunk-LGLOBK6Q.js.map → chunk-MBVX4ZRB.js.map} +1 -1
- package/dist/{chunk-A3TLZS5B.js → chunk-RIAFHQT5.js} +3 -3
- package/dist/{chunk-A3TLZS5B.js.map → chunk-RIAFHQT5.js.map} +1 -1
- package/dist/{chunk-BHM5HGXH.js → chunk-UM4C46AF.js} +38 -14
- package/dist/chunk-UM4C46AF.js.map +1 -0
- package/dist/{chunk-FGYG5JVW.js → chunk-W5DPKJY4.js} +5 -3
- package/dist/chunk-W5DPKJY4.js.map +1 -0
- package/dist/connectors.d.cts +1 -1
- package/dist/connectors.d.ts +1 -1
- package/dist/durable.cjs +7 -5
- package/dist/durable.cjs.map +1 -1
- package/dist/durable.js +2 -2
- package/dist/engine.cjs +189 -29
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +67 -5
- package/dist/engine.d.ts +67 -5
- package/dist/engine.js +7 -5
- package/dist/engine.js.map +1 -1
- package/dist/expr-BfXDrs4H.d.cts +149 -0
- package/dist/expr-BfXDrs4H.d.ts +149 -0
- package/dist/fields/react-fancy.d.cts +2 -2
- package/dist/fields/react-fancy.d.ts +2 -2
- package/dist/index.cjs +94 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -84
- package/dist/index.d.ts +7 -84
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/registry/index.d.cts +3 -3
- package/dist/registry/index.d.ts +3 -3
- package/dist/{registry-1qhmRtvC.d.cts → registry-CntAdUcY.d.cts} +1 -1
- package/dist/{registry-D2qocEsY.d.ts → registry-DYoV-MQi.d.ts} +1 -1
- package/dist/registry.cjs +7 -5
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +2 -2
- package/dist/runtime.cjs +7 -5
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +3 -3
- package/dist/schema.cjs +57 -5
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +3 -2
- package/dist/screens.cjs +7 -5
- package/dist/screens.cjs.map +1 -1
- package/dist/screens.js +4 -4
- package/dist/{types-CLSUZW8c.d.ts → types-Ckhz-YwC.d.ts} +7 -2
- package/dist/{types-BLVpNNLN.d.cts → types-D71SKA5A.d.cts} +7 -2
- package/dist/ux.cjs +7 -5
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/dist/ux.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-BHM5HGXH.js.map +0 -1
- package/dist/chunk-FGYG5JVW.js.map +0 -1
- package/dist/chunk-HBE4LVPX.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `{{ }}` resolution — the TypeScript twin of `FancyFlow\Nodes\Support\Expr`.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is opt-in and not wired into `runFlow`
|
|
5
|
+
*
|
|
6
|
+
* The PHP runtime resolves expressions inside its batteries-included executors.
|
|
7
|
+
* The JS runtime never has: `runFlow` hands `node.data.config` to your executor
|
|
8
|
+
* verbatim, so every host that uses `{{ }}` today already resolves it itself.
|
|
9
|
+
* Turning resolution on inside `runFlow` would interpolate a second time over
|
|
10
|
+
* values those hosts had already substituted — silently, and only for graphs
|
|
11
|
+
* that happen to contain `{{` in their DATA.
|
|
12
|
+
*
|
|
13
|
+
* So this exports the semantics and changes no behaviour. Call it yourself:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const url = evaluateExpression(node.data.config.url, inputs);
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* ## The semantics are the PHP file's, deliberately
|
|
20
|
+
*
|
|
21
|
+
* This is not a general expression language and must not grow into one. It
|
|
22
|
+
* resolves a dot-path against a context and nothing else — no arithmetic, no
|
|
23
|
+
* comparisons, no calls. Hosts that want real expressions override the executor
|
|
24
|
+
* (the PHP docblock points at symfony/expression-language for the same reason).
|
|
25
|
+
*
|
|
26
|
+
* Divergence here is a correctness bug rather than a style difference: the same
|
|
27
|
+
* graph is authored once and may run on either runtime. `suites/shared/expr`
|
|
28
|
+
* in `@particle-academy/fancy-conformance` is the fixture table both sides run,
|
|
29
|
+
* so parity is a test result instead of a claim.
|
|
30
|
+
*/
|
|
31
|
+
/** Anything a context or a resolved value can be. */
|
|
32
|
+
type ExprValue = unknown;
|
|
33
|
+
/** The context an expression resolves against — the executor's `inputs`. */
|
|
34
|
+
type ExprContext = Record<string, ExprValue>;
|
|
35
|
+
/**
|
|
36
|
+
* What a resolution attempt ANSWERS — did the path resolve, and to what.
|
|
37
|
+
*
|
|
38
|
+
* `resolvePath` cannot express this, and that is the defect it exists to fix:
|
|
39
|
+
* it returns `null` both for "this path does not exist" and for "this path
|
|
40
|
+
* exists and holds null". One value standing for two states.
|
|
41
|
+
*
|
|
42
|
+
* At the interpolation layer the collapse is worse, because `null` stringifies
|
|
43
|
+
* to `""`. A consumer put it exactly:
|
|
44
|
+
*
|
|
45
|
+
* > "An unresolvable path yields `''`, so a wrong field is indistinguishable
|
|
46
|
+
* > from an empty one at runtime."
|
|
47
|
+
*
|
|
48
|
+
* A misspelled field renders as an empty string, which looks precisely like a
|
|
49
|
+
* field that is legitimately empty. The graph runs, the node succeeds, and the
|
|
50
|
+
* output is quietly missing a value nobody is told about — worst on
|
|
51
|
+
* LLM-authored graphs, where the field name was guessed in the first place.
|
|
52
|
+
*
|
|
53
|
+
* Same shape as the four `??` collapses fixed across all four runtimes on
|
|
54
|
+
* 2026-08-26 (absent vs null), one layer up: **presence is the only correct
|
|
55
|
+
* test, and a return value that cannot express presence cannot be tested for
|
|
56
|
+
* it.** Hence a second return channel rather than a cleverer sentinel — every
|
|
57
|
+
* sentinel is a legal value for somebody.
|
|
58
|
+
*/
|
|
59
|
+
interface Resolution {
|
|
60
|
+
/** Whether the path resolved at all. `false` means it does not exist. */
|
|
61
|
+
resolved: boolean;
|
|
62
|
+
/** The value, when `resolved`. `null` when not — do not read it blind. */
|
|
63
|
+
value: ExprValue;
|
|
64
|
+
}
|
|
65
|
+
/** Thrown by the `"throw"` policy when a path does not resolve. */
|
|
66
|
+
declare class UnresolvedPathError extends Error {
|
|
67
|
+
readonly path: string;
|
|
68
|
+
constructor(path: string);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What evaluation does with a path that does not resolve.
|
|
72
|
+
*
|
|
73
|
+
* - `"empty"` — today's behaviour, and the DEFAULT. Interpolates to `""`; a
|
|
74
|
+
* whole expression yields `null`. Unchanged so that widening this API breaks
|
|
75
|
+
* nobody: 40 call sites across the runtimes assume it.
|
|
76
|
+
* - `"keep"` — leave the `{{ … }}` text in place. The failure becomes VISIBLE
|
|
77
|
+
* in the output without stopping the run, which is what you want for
|
|
78
|
+
* human-reviewed content: a rendered `{{ in.recipient_naem }}` is self-
|
|
79
|
+
* diagnosing in a way an absence never is.
|
|
80
|
+
* - `"throw"` — refuse. For hosts that would rather fail a run than deliver a
|
|
81
|
+
* silently incomplete result.
|
|
82
|
+
*
|
|
83
|
+
* Opt-in before default at the request of the consumer who reported it — the
|
|
84
|
+
* host with the most LLM-authored graphs, and so both the biggest beneficiary
|
|
85
|
+
* and the right place for it to break first if it is going to.
|
|
86
|
+
*/
|
|
87
|
+
type UnresolvedPolicy = "empty" | "keep" | "throw";
|
|
88
|
+
/** Options for {@link evaluateExpression} / {@link evaluateConfig}. */
|
|
89
|
+
interface EvaluateOptions {
|
|
90
|
+
/** What to do with a path that does not resolve. Default `"empty"`. */
|
|
91
|
+
onUnresolved?: UnresolvedPolicy;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a dot-path, reporting WHETHER it resolved.
|
|
95
|
+
*
|
|
96
|
+
* The same walk as `resolvePath` — deliberately, so the two can never disagree
|
|
97
|
+
* about what resolves; `resolvePath` is defined in terms of this one below.
|
|
98
|
+
*
|
|
99
|
+
* A note on JS having two absent values: a key present with the value
|
|
100
|
+
* `undefined` reports `resolved: false`, matching `resolvePath`'s long-standing
|
|
101
|
+
* behaviour. It cannot arise from graph data (JSON has no `undefined`), and
|
|
102
|
+
* changing it would make the two functions disagree for no reachable gain.
|
|
103
|
+
*/
|
|
104
|
+
declare function tryResolvePath(path: string, context: ExprContext): Resolution;
|
|
105
|
+
/**
|
|
106
|
+
* Resolve a dot-path against the context, honouring the `$json` / `$input`
|
|
107
|
+
* alias.
|
|
108
|
+
*
|
|
109
|
+
* Both aliases point at the `in` port value when the context has one, and at
|
|
110
|
+
* the whole context otherwise — the same fallback the PHP does, which is what
|
|
111
|
+
* makes `{{ $json.x }}` work on a trigger node that has no upstream input.
|
|
112
|
+
*
|
|
113
|
+
* A path that does not resolve returns `null`, never `undefined`: PHP has one
|
|
114
|
+
* absent value and JS has two, and letting the difference leak would make the
|
|
115
|
+
* two runtimes disagree about `{{ missing }}` for no useful reason.
|
|
116
|
+
*/
|
|
117
|
+
declare function resolvePath(path: string, context: ExprContext): ExprValue;
|
|
118
|
+
/**
|
|
119
|
+
* Evaluate a template against a context.
|
|
120
|
+
*
|
|
121
|
+
* A string that is EXACTLY one expression returns the resolved value with its
|
|
122
|
+
* type intact — `{{ $json.count }}` gives you a number, not `"3"`. Anything
|
|
123
|
+
* else interpolates each run as text. That distinction is load-bearing: it is
|
|
124
|
+
* what lets a config field carry either a value or a sentence.
|
|
125
|
+
*
|
|
126
|
+
* Non-string templates pass through untouched, so this is safe to map over a
|
|
127
|
+
* whole config object.
|
|
128
|
+
*/
|
|
129
|
+
declare function evaluateExpression(template: ExprValue, context: ExprContext, options?: EvaluateOptions): ExprValue;
|
|
130
|
+
/**
|
|
131
|
+
* Truthiness for branch / switch decisions.
|
|
132
|
+
*
|
|
133
|
+
* Mirrors PHP's rules rather than JavaScript's, because the graph is authored
|
|
134
|
+
* once and may run on either side. The two disagree in exactly the places a
|
|
135
|
+
* workflow hits: `"0"` and `"false"` are truthy in JS and falsy here, and an
|
|
136
|
+
* empty array is truthy in JS and falsy here.
|
|
137
|
+
*/
|
|
138
|
+
declare function truthy(value: ExprValue): boolean;
|
|
139
|
+
/** Coerce a value to text the way interpolation does. */
|
|
140
|
+
declare function text(value: ExprValue): string;
|
|
141
|
+
/**
|
|
142
|
+
* Resolve every string in a config object, one level of nesting at a time.
|
|
143
|
+
*
|
|
144
|
+
* The convenience most hosts actually want, and the shape their hand-rolled
|
|
145
|
+
* version usually takes. Opt-in like the rest of this module.
|
|
146
|
+
*/
|
|
147
|
+
declare function evaluateConfig<T extends Record<string, ExprValue>>(config: T, context: ExprContext, options?: EvaluateOptions): T;
|
|
148
|
+
|
|
149
|
+
export { type EvaluateOptions as E, type Resolution as R, UnresolvedPathError as U, type ExprContext as a, type ExprValue as b, type UnresolvedPolicy as c, evaluateExpression as d, evaluateConfig as e, truthy as f, tryResolvePath as g, resolvePath as r, text as t };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `{{ }}` resolution — the TypeScript twin of `FancyFlow\Nodes\Support\Expr`.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is opt-in and not wired into `runFlow`
|
|
5
|
+
*
|
|
6
|
+
* The PHP runtime resolves expressions inside its batteries-included executors.
|
|
7
|
+
* The JS runtime never has: `runFlow` hands `node.data.config` to your executor
|
|
8
|
+
* verbatim, so every host that uses `{{ }}` today already resolves it itself.
|
|
9
|
+
* Turning resolution on inside `runFlow` would interpolate a second time over
|
|
10
|
+
* values those hosts had already substituted — silently, and only for graphs
|
|
11
|
+
* that happen to contain `{{` in their DATA.
|
|
12
|
+
*
|
|
13
|
+
* So this exports the semantics and changes no behaviour. Call it yourself:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const url = evaluateExpression(node.data.config.url, inputs);
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* ## The semantics are the PHP file's, deliberately
|
|
20
|
+
*
|
|
21
|
+
* This is not a general expression language and must not grow into one. It
|
|
22
|
+
* resolves a dot-path against a context and nothing else — no arithmetic, no
|
|
23
|
+
* comparisons, no calls. Hosts that want real expressions override the executor
|
|
24
|
+
* (the PHP docblock points at symfony/expression-language for the same reason).
|
|
25
|
+
*
|
|
26
|
+
* Divergence here is a correctness bug rather than a style difference: the same
|
|
27
|
+
* graph is authored once and may run on either runtime. `suites/shared/expr`
|
|
28
|
+
* in `@particle-academy/fancy-conformance` is the fixture table both sides run,
|
|
29
|
+
* so parity is a test result instead of a claim.
|
|
30
|
+
*/
|
|
31
|
+
/** Anything a context or a resolved value can be. */
|
|
32
|
+
type ExprValue = unknown;
|
|
33
|
+
/** The context an expression resolves against — the executor's `inputs`. */
|
|
34
|
+
type ExprContext = Record<string, ExprValue>;
|
|
35
|
+
/**
|
|
36
|
+
* What a resolution attempt ANSWERS — did the path resolve, and to what.
|
|
37
|
+
*
|
|
38
|
+
* `resolvePath` cannot express this, and that is the defect it exists to fix:
|
|
39
|
+
* it returns `null` both for "this path does not exist" and for "this path
|
|
40
|
+
* exists and holds null". One value standing for two states.
|
|
41
|
+
*
|
|
42
|
+
* At the interpolation layer the collapse is worse, because `null` stringifies
|
|
43
|
+
* to `""`. A consumer put it exactly:
|
|
44
|
+
*
|
|
45
|
+
* > "An unresolvable path yields `''`, so a wrong field is indistinguishable
|
|
46
|
+
* > from an empty one at runtime."
|
|
47
|
+
*
|
|
48
|
+
* A misspelled field renders as an empty string, which looks precisely like a
|
|
49
|
+
* field that is legitimately empty. The graph runs, the node succeeds, and the
|
|
50
|
+
* output is quietly missing a value nobody is told about — worst on
|
|
51
|
+
* LLM-authored graphs, where the field name was guessed in the first place.
|
|
52
|
+
*
|
|
53
|
+
* Same shape as the four `??` collapses fixed across all four runtimes on
|
|
54
|
+
* 2026-08-26 (absent vs null), one layer up: **presence is the only correct
|
|
55
|
+
* test, and a return value that cannot express presence cannot be tested for
|
|
56
|
+
* it.** Hence a second return channel rather than a cleverer sentinel — every
|
|
57
|
+
* sentinel is a legal value for somebody.
|
|
58
|
+
*/
|
|
59
|
+
interface Resolution {
|
|
60
|
+
/** Whether the path resolved at all. `false` means it does not exist. */
|
|
61
|
+
resolved: boolean;
|
|
62
|
+
/** The value, when `resolved`. `null` when not — do not read it blind. */
|
|
63
|
+
value: ExprValue;
|
|
64
|
+
}
|
|
65
|
+
/** Thrown by the `"throw"` policy when a path does not resolve. */
|
|
66
|
+
declare class UnresolvedPathError extends Error {
|
|
67
|
+
readonly path: string;
|
|
68
|
+
constructor(path: string);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What evaluation does with a path that does not resolve.
|
|
72
|
+
*
|
|
73
|
+
* - `"empty"` — today's behaviour, and the DEFAULT. Interpolates to `""`; a
|
|
74
|
+
* whole expression yields `null`. Unchanged so that widening this API breaks
|
|
75
|
+
* nobody: 40 call sites across the runtimes assume it.
|
|
76
|
+
* - `"keep"` — leave the `{{ … }}` text in place. The failure becomes VISIBLE
|
|
77
|
+
* in the output without stopping the run, which is what you want for
|
|
78
|
+
* human-reviewed content: a rendered `{{ in.recipient_naem }}` is self-
|
|
79
|
+
* diagnosing in a way an absence never is.
|
|
80
|
+
* - `"throw"` — refuse. For hosts that would rather fail a run than deliver a
|
|
81
|
+
* silently incomplete result.
|
|
82
|
+
*
|
|
83
|
+
* Opt-in before default at the request of the consumer who reported it — the
|
|
84
|
+
* host with the most LLM-authored graphs, and so both the biggest beneficiary
|
|
85
|
+
* and the right place for it to break first if it is going to.
|
|
86
|
+
*/
|
|
87
|
+
type UnresolvedPolicy = "empty" | "keep" | "throw";
|
|
88
|
+
/** Options for {@link evaluateExpression} / {@link evaluateConfig}. */
|
|
89
|
+
interface EvaluateOptions {
|
|
90
|
+
/** What to do with a path that does not resolve. Default `"empty"`. */
|
|
91
|
+
onUnresolved?: UnresolvedPolicy;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a dot-path, reporting WHETHER it resolved.
|
|
95
|
+
*
|
|
96
|
+
* The same walk as `resolvePath` — deliberately, so the two can never disagree
|
|
97
|
+
* about what resolves; `resolvePath` is defined in terms of this one below.
|
|
98
|
+
*
|
|
99
|
+
* A note on JS having two absent values: a key present with the value
|
|
100
|
+
* `undefined` reports `resolved: false`, matching `resolvePath`'s long-standing
|
|
101
|
+
* behaviour. It cannot arise from graph data (JSON has no `undefined`), and
|
|
102
|
+
* changing it would make the two functions disagree for no reachable gain.
|
|
103
|
+
*/
|
|
104
|
+
declare function tryResolvePath(path: string, context: ExprContext): Resolution;
|
|
105
|
+
/**
|
|
106
|
+
* Resolve a dot-path against the context, honouring the `$json` / `$input`
|
|
107
|
+
* alias.
|
|
108
|
+
*
|
|
109
|
+
* Both aliases point at the `in` port value when the context has one, and at
|
|
110
|
+
* the whole context otherwise — the same fallback the PHP does, which is what
|
|
111
|
+
* makes `{{ $json.x }}` work on a trigger node that has no upstream input.
|
|
112
|
+
*
|
|
113
|
+
* A path that does not resolve returns `null`, never `undefined`: PHP has one
|
|
114
|
+
* absent value and JS has two, and letting the difference leak would make the
|
|
115
|
+
* two runtimes disagree about `{{ missing }}` for no useful reason.
|
|
116
|
+
*/
|
|
117
|
+
declare function resolvePath(path: string, context: ExprContext): ExprValue;
|
|
118
|
+
/**
|
|
119
|
+
* Evaluate a template against a context.
|
|
120
|
+
*
|
|
121
|
+
* A string that is EXACTLY one expression returns the resolved value with its
|
|
122
|
+
* type intact — `{{ $json.count }}` gives you a number, not `"3"`. Anything
|
|
123
|
+
* else interpolates each run as text. That distinction is load-bearing: it is
|
|
124
|
+
* what lets a config field carry either a value or a sentence.
|
|
125
|
+
*
|
|
126
|
+
* Non-string templates pass through untouched, so this is safe to map over a
|
|
127
|
+
* whole config object.
|
|
128
|
+
*/
|
|
129
|
+
declare function evaluateExpression(template: ExprValue, context: ExprContext, options?: EvaluateOptions): ExprValue;
|
|
130
|
+
/**
|
|
131
|
+
* Truthiness for branch / switch decisions.
|
|
132
|
+
*
|
|
133
|
+
* Mirrors PHP's rules rather than JavaScript's, because the graph is authored
|
|
134
|
+
* once and may run on either side. The two disagree in exactly the places a
|
|
135
|
+
* workflow hits: `"0"` and `"false"` are truthy in JS and falsy here, and an
|
|
136
|
+
* empty array is truthy in JS and falsy here.
|
|
137
|
+
*/
|
|
138
|
+
declare function truthy(value: ExprValue): boolean;
|
|
139
|
+
/** Coerce a value to text the way interpolation does. */
|
|
140
|
+
declare function text(value: ExprValue): string;
|
|
141
|
+
/**
|
|
142
|
+
* Resolve every string in a config object, one level of nesting at a time.
|
|
143
|
+
*
|
|
144
|
+
* The convenience most hosts actually want, and the shape their hand-rolled
|
|
145
|
+
* version usually takes. Opt-in like the rest of this module.
|
|
146
|
+
*/
|
|
147
|
+
declare function evaluateConfig<T extends Record<string, ExprValue>>(config: T, context: ExprContext, options?: EvaluateOptions): T;
|
|
148
|
+
|
|
149
|
+
export { type EvaluateOptions as E, type Resolution as R, UnresolvedPathError as U, type ExprContext as a, type ExprValue as b, type UnresolvedPolicy as c, evaluateExpression as d, evaluateConfig as e, truthy as f, tryResolvePath as g, resolvePath as r, text as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-
|
|
1
|
+
import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-Bpp3JiVH.cjs';
|
|
2
2
|
import 'react';
|
|
3
3
|
import '../types-JFYjPJAG.cjs';
|
|
4
4
|
import '@xyflow/react';
|
|
5
|
-
import '../types-
|
|
5
|
+
import '../types-D71SKA5A.cjs';
|
|
6
6
|
import '../pause-9iT4tCEV.cjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-
|
|
1
|
+
import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-BKThiyjB.js';
|
|
2
2
|
import 'react';
|
|
3
3
|
import '../types-JFYjPJAG.js';
|
|
4
4
|
import '@xyflow/react';
|
|
5
|
-
import '../types-
|
|
5
|
+
import '../types-Ckhz-YwC.js';
|
|
6
6
|
import '../pause-9iT4tCEV.js';
|
|
7
7
|
|
|
8
8
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -11445,7 +11445,7 @@ function activatedPorts(node, result) {
|
|
|
11445
11445
|
return { ports: [r.__port], value: r.value };
|
|
11446
11446
|
}
|
|
11447
11447
|
if (typeof r.branch === "string") {
|
|
11448
|
-
return { ports: [r.branch], value: r.value
|
|
11448
|
+
return { ports: [r.branch], value: Object.prototype.hasOwnProperty.call(r, "value") ? r.value : r };
|
|
11449
11449
|
}
|
|
11450
11450
|
}
|
|
11451
11451
|
const kind = getNodeKind(node.data?.kind ?? node.type ?? "") ?? void 0;
|
|
@@ -11607,8 +11607,9 @@ var KINDS = [
|
|
|
11607
11607
|
// ───────────── Triggers ─────────────
|
|
11608
11608
|
{
|
|
11609
11609
|
name: "@particle-academy/manual_trigger",
|
|
11610
|
-
//
|
|
11611
|
-
|
|
11610
|
+
// Returns the raw inputs MAP, not the `in` port -- flat at an entry
|
|
11611
|
+
// point, port-keyed the moment the node has an inbound edge.
|
|
11612
|
+
emits: "input-map-merged",
|
|
11612
11613
|
aliases: ["manual_trigger", "@fancy/manual_trigger"],
|
|
11613
11614
|
category: "trigger",
|
|
11614
11615
|
label: "Manual",
|
|
@@ -11641,8 +11642,9 @@ var KINDS = [
|
|
|
11641
11642
|
{ path: "cron", type: "string", description: "The cron expression that fired." },
|
|
11642
11643
|
{ path: "timezone", type: "string", description: "The timezone it was evaluated in." }
|
|
11643
11644
|
],
|
|
11644
|
-
//
|
|
11645
|
-
|
|
11645
|
+
// Merges $ctx->inputs ITSELF beside cron/timezone -- the MAP, whose
|
|
11646
|
+
// shape depends on position, not each port's payload the way `merge` does.
|
|
11647
|
+
emits: "input-map-merged",
|
|
11646
11648
|
aliases: ["schedule_trigger", "@fancy/schedule_trigger"],
|
|
11647
11649
|
category: "trigger",
|
|
11648
11650
|
label: "Schedule",
|
|
@@ -14388,6 +14390,55 @@ function preview(v2) {
|
|
|
14388
14390
|
}
|
|
14389
14391
|
}
|
|
14390
14392
|
|
|
14393
|
+
// src/analysis/graph-connectivity.ts
|
|
14394
|
+
function checkGraphConnectivity(graph) {
|
|
14395
|
+
const { nodes, edges } = graph;
|
|
14396
|
+
const issues = [];
|
|
14397
|
+
const hasIncoming = /* @__PURE__ */ new Set();
|
|
14398
|
+
const hasOutgoing = /* @__PURE__ */ new Set();
|
|
14399
|
+
for (const edge of edges) {
|
|
14400
|
+
hasIncoming.add(edge.target);
|
|
14401
|
+
hasOutgoing.add(edge.source);
|
|
14402
|
+
}
|
|
14403
|
+
const single = nodes.length === 1;
|
|
14404
|
+
for (const node of nodes) {
|
|
14405
|
+
if (single || mayFloat(node)) continue;
|
|
14406
|
+
if (!hasIncoming.has(node.id) && !hasOutgoing.has(node.id)) {
|
|
14407
|
+
issues.push({
|
|
14408
|
+
level: "error",
|
|
14409
|
+
nodeId: node.id,
|
|
14410
|
+
message: `Node "${node.id}" is connected to nothing \u2014 no inbound edge and no outbound edge. It still RUNS (a node with no inbound edge is a root), but it receives nothing from the graph and reaches nobody in it, so it is either unwired or left behind by a deletion. Only a note, an annotation or a lane may float.`
|
|
14411
|
+
});
|
|
14412
|
+
}
|
|
14413
|
+
}
|
|
14414
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
14415
|
+
for (const edge of edges) {
|
|
14416
|
+
const source = byId.get(edge.source);
|
|
14417
|
+
if (!source || !isTerminator(source)) continue;
|
|
14418
|
+
issues.push({
|
|
14419
|
+
level: "error",
|
|
14420
|
+
edgeId: edge.id,
|
|
14421
|
+
message: `Edge "${edge.id}" reads from "${edge.source}", which is a TERMINAL node and publishes no output ports at all. Nothing can ever travel this edge: it does not fail at run time, it delivers nothing, and "${edge.target}" runs anyway with an empty input.`
|
|
14422
|
+
});
|
|
14423
|
+
}
|
|
14424
|
+
return issues;
|
|
14425
|
+
}
|
|
14426
|
+
function mayFloat(node) {
|
|
14427
|
+
const type = node.type;
|
|
14428
|
+
if (!type) return false;
|
|
14429
|
+
if (type === "note") return true;
|
|
14430
|
+
const kind = getNodeKind(type);
|
|
14431
|
+
if (!kind) return true;
|
|
14432
|
+
return kindIds(kind).includes("note") || kind.category === "annotation" || kind.category === "layout";
|
|
14433
|
+
}
|
|
14434
|
+
function isTerminator(node) {
|
|
14435
|
+
const own = node.data?.outputs;
|
|
14436
|
+
if (Array.isArray(own)) return own.length === 0;
|
|
14437
|
+
const kind = node.type ? getNodeKind(node.type) : null;
|
|
14438
|
+
if (!kind) return false;
|
|
14439
|
+
return Array.isArray(kind.outputs) && kind.outputs.length === 0;
|
|
14440
|
+
}
|
|
14441
|
+
|
|
14391
14442
|
// src/schema/workflow-schema.ts
|
|
14392
14443
|
var WORKFLOW_SCHEMA_VERSION = 1;
|
|
14393
14444
|
var WORKFLOW_SCHEMA_URL = "https://particle.academy/schemas/workflow/v1.json";
|
|
@@ -14535,6 +14586,7 @@ function importWorkflow(schema, options = {}) {
|
|
|
14535
14586
|
label: e.label
|
|
14536
14587
|
};
|
|
14537
14588
|
}).filter((e) => e !== null);
|
|
14589
|
+
issues.push(...checkGraphConnectivity({ nodes, edges }));
|
|
14538
14590
|
const ok = issues.every((i) => i.level !== "error");
|
|
14539
14591
|
const declaredInputs = Array.isArray(s.inputs) ? s.inputs.filter(
|
|
14540
14592
|
(input) => input !== null && typeof input === "object" && typeof input.name === "string"
|
|
@@ -15874,9 +15926,19 @@ function interpolate(template, resolve) {
|
|
|
15874
15926
|
}
|
|
15875
15927
|
}
|
|
15876
15928
|
var FALSY_STRINGS = /* @__PURE__ */ new Set(["", "0", "false", "no", "off", "null"]);
|
|
15877
|
-
|
|
15929
|
+
var UnresolvedPathError = class extends Error {
|
|
15930
|
+
constructor(path) {
|
|
15931
|
+
super(
|
|
15932
|
+
`Expression path "${path}" did not resolve. Under the "throw" policy an unresolvable path is an error rather than an empty string.`
|
|
15933
|
+
);
|
|
15934
|
+
this.path = path;
|
|
15935
|
+
this.name = "UnresolvedPathError";
|
|
15936
|
+
}
|
|
15937
|
+
};
|
|
15938
|
+
function tryResolvePath(path, context) {
|
|
15939
|
+
const unresolved = { resolved: false, value: null };
|
|
15878
15940
|
const trimmed = path.trim();
|
|
15879
|
-
if (trimmed === "") return
|
|
15941
|
+
if (trimmed === "") return unresolved;
|
|
15880
15942
|
const segments = trimmed.split(".");
|
|
15881
15943
|
let cursor;
|
|
15882
15944
|
const head = segments[0];
|
|
@@ -15887,19 +15949,33 @@ function resolvePath(path, context) {
|
|
|
15887
15949
|
cursor = context;
|
|
15888
15950
|
}
|
|
15889
15951
|
for (const segment of segments) {
|
|
15890
|
-
if (cursor === null || cursor === void 0) return
|
|
15891
|
-
if (typeof cursor !== "object") return
|
|
15952
|
+
if (cursor === null || cursor === void 0) return unresolved;
|
|
15953
|
+
if (typeof cursor !== "object") return unresolved;
|
|
15892
15954
|
const next = cursor[segment];
|
|
15893
|
-
if (next === void 0) return
|
|
15955
|
+
if (next === void 0) return unresolved;
|
|
15894
15956
|
cursor = next;
|
|
15895
15957
|
}
|
|
15896
|
-
return cursor === void 0 ? null : cursor;
|
|
15958
|
+
return { resolved: true, value: cursor === void 0 ? null : cursor };
|
|
15897
15959
|
}
|
|
15898
|
-
function
|
|
15960
|
+
function resolvePath(path, context) {
|
|
15961
|
+
return tryResolvePath(path, context).value;
|
|
15962
|
+
}
|
|
15963
|
+
function evaluateExpression(template, context, options = {}) {
|
|
15899
15964
|
if (typeof template !== "string") return template;
|
|
15965
|
+
const policy = options.onUnresolved ?? "empty";
|
|
15900
15966
|
const whole = wholeExpression(template.trim());
|
|
15901
|
-
if (whole !== null)
|
|
15902
|
-
|
|
15967
|
+
if (whole !== null) {
|
|
15968
|
+
const r = tryResolvePath(whole, context);
|
|
15969
|
+
if (r.resolved) return r.value;
|
|
15970
|
+
if (policy === "throw") throw new UnresolvedPathError(whole);
|
|
15971
|
+
return policy === "keep" ? template : null;
|
|
15972
|
+
}
|
|
15973
|
+
return interpolate(template, (path) => {
|
|
15974
|
+
const r = tryResolvePath(path, context);
|
|
15975
|
+
if (r.resolved) return stringify(r.value);
|
|
15976
|
+
if (policy === "throw") throw new UnresolvedPathError(path);
|
|
15977
|
+
return policy === "keep" ? `{{${path}}}` : "";
|
|
15978
|
+
});
|
|
15903
15979
|
}
|
|
15904
15980
|
function truthy(value) {
|
|
15905
15981
|
if (typeof value === "boolean") return value;
|
|
@@ -15923,10 +15999,10 @@ function stringify(value) {
|
|
|
15923
15999
|
return "";
|
|
15924
16000
|
}
|
|
15925
16001
|
}
|
|
15926
|
-
function evaluateConfig(config, context) {
|
|
16002
|
+
function evaluateConfig(config, context, options = {}) {
|
|
15927
16003
|
const out = {};
|
|
15928
16004
|
for (const [key, value] of Object.entries(config)) {
|
|
15929
|
-
out[key] = Array.isArray(value) ? value.map((v2) => evaluateExpression(v2, context)) : value !== null && typeof value === "object" ? evaluateConfig(value, context) : evaluateExpression(value, context);
|
|
16005
|
+
out[key] = Array.isArray(value) ? value.map((v2) => evaluateExpression(v2, context, options)) : value !== null && typeof value === "object" ? evaluateConfig(value, context, options) : evaluateExpression(value, context, options);
|
|
15930
16006
|
}
|
|
15931
16007
|
return out;
|
|
15932
16008
|
}
|
|
@@ -15963,6 +16039,7 @@ exports.RegistryNode = RegistryNode;
|
|
|
15963
16039
|
exports.RichInputPreview = RichInputPreview;
|
|
15964
16040
|
exports.SubgraphNode = SubgraphNode;
|
|
15965
16041
|
exports.TriggerNode = TriggerNode;
|
|
16042
|
+
exports.UnresolvedPathError = UnresolvedPathError;
|
|
15966
16043
|
exports.WORKFLOW_SCHEMA_URL = WORKFLOW_SCHEMA_URL;
|
|
15967
16044
|
exports.WORKFLOW_SCHEMA_VERSION = WORKFLOW_SCHEMA_VERSION;
|
|
15968
16045
|
exports.alignNodes = alignNodes;
|
|
@@ -16014,6 +16091,7 @@ exports.runCohort = runCohort;
|
|
|
16014
16091
|
exports.runFlow = runFlow;
|
|
16015
16092
|
exports.text = text;
|
|
16016
16093
|
exports.truthy = truthy;
|
|
16094
|
+
exports.tryResolvePath = tryResolvePath;
|
|
16017
16095
|
exports.useFlowEditor = useFlowEditor;
|
|
16018
16096
|
exports.useFlowEditorOptional = useFlowEditorOptional;
|
|
16019
16097
|
exports.useFlowHistory = useFlowHistory;
|