@particle-academy/fancy-flow 0.33.3 → 0.35.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.
Files changed (36) hide show
  1. package/README.md +2 -2
  2. package/dist/{capabilities-BLOeLACV.d.cts → capabilities-BtJQTbnw.d.cts} +1 -1
  3. package/dist/{capabilities-BH7zmSQQ.d.ts → capabilities-CkhKj_U_.d.ts} +1 -1
  4. package/dist/engine.d.cts +7 -238
  5. package/dist/engine.d.ts +7 -238
  6. package/dist/index.d.cts +7 -7
  7. package/dist/index.d.ts +7 -7
  8. package/dist/layout/index.d.cts +1 -1
  9. package/dist/layout/index.d.ts +1 -1
  10. package/dist/llm/prism.d.cts +2 -2
  11. package/dist/llm/prism.d.ts +2 -2
  12. package/dist/llm/vercel-ai.d.cts +2 -2
  13. package/dist/llm/vercel-ai.d.ts +2 -2
  14. package/dist/registry/index.d.cts +6 -6
  15. package/dist/registry/index.d.ts +6 -6
  16. package/dist/{registry-Dd1Q66uP.d.ts → registry-Df2-bTXZ.d.ts} +1 -1
  17. package/dist/{registry-qv4gRiuQ.d.cts → registry-Ru5jIS5H.d.cts} +1 -1
  18. package/dist/rich-input.cjs +4 -2
  19. package/dist/rich-input.cjs.map +1 -1
  20. package/dist/rich-input.d.cts +14 -2
  21. package/dist/rich-input.d.ts +14 -2
  22. package/dist/rich-input.js +4 -3
  23. package/dist/rich-input.js.map +1 -1
  24. package/dist/{run-cohort-DitsaYgS.d.cts → run-cohort-Bjv2ZU6g.d.cts} +1 -1
  25. package/dist/{run-cohort-CV7EcwGG.d.ts → run-cohort-BnP6ErHi.d.ts} +1 -1
  26. package/dist/runtime/index.d.cts +3 -3
  27. package/dist/runtime/index.d.ts +3 -3
  28. package/dist/schema/index.d.cts +1 -1
  29. package/dist/schema/index.d.ts +1 -1
  30. package/dist/{types-DEmDSByY.d.cts → types-D6lsIGoe.d.ts} +251 -2
  31. package/dist/{types-Bj_ZHwqG.d.ts → types-D8Dezh9v.d.cts} +251 -2
  32. package/dist/{types-CMSrWVYM.d.cts → types-sOmpCitB.d.cts} +1 -1
  33. package/dist/{types-CMSrWVYM.d.ts → types-sOmpCitB.d.ts} +1 -1
  34. package/dist/ux.d.cts +2 -2
  35. package/dist/ux.d.ts +2 -2
  36. package/package.json +6 -3
@@ -1,6 +1,6 @@
1
1
  import { ReactNode, ComponentType } from 'react';
2
2
  import { NodeProps } from '@xyflow/react';
3
- import { P as PortDescriptor, N as NodeExecutor, a as FlowNode } from './types-CMSrWVYM.js';
3
+ import { P as PortDescriptor, N as NodeExecutor, b as FlowNode } from './types-sOmpCitB.cjs';
4
4
 
5
5
  /**
6
6
  * The human-pause contract.
@@ -97,6 +97,237 @@ declare function pauseForHuman(ctx: {
97
97
  abort: (reason?: string) => never;
98
98
  }, awaiting: PauseAwaiting, detail?: unknown): never;
99
99
 
100
+ /**
101
+ * The node package manifest — what a marketplace node declares about itself.
102
+ *
103
+ * A node is not one artifact. It is a kind definition (palette entry, config
104
+ * schema, ports, renderer) plus an executor for EACH runtime the consumer runs.
105
+ * A package shipping only a TS executor is unusable to anyone executing on PHP,
106
+ * and without a manifest that is invisible until a run fails. Requested by the
107
+ * MOIC Suite consumer (fancy-flow#2 §2), who runs the editor in TS and executes
108
+ * in PHP and hit exactly this.
109
+ *
110
+ * The manifest is data, not code: the registry, the CLI, and the MCP all read
111
+ * it without executing anything a package author wrote.
112
+ *
113
+ * ## Why the engine range lives per runtime
114
+ *
115
+ * The first cut of this manifest carried ONE `fancyFlow` range, and it was
116
+ * wrong: the two engines version independently, so a single range cannot say
117
+ * "needs npm >=0.11 AND php >=0.5". A package supporting both runtimes would
118
+ * install cleanly against a host whose OTHER runtime was too old — the 0.9.0
119
+ * failure shape wearing a manifest. The range belongs inside each runtime.
120
+ */
121
+
122
+ /** The current manifest schema version. Bump only on a breaking shape change. */
123
+ declare const NODE_MANIFEST_SCHEMA_VERSION = 1;
124
+ /**
125
+ * A runtime a node can implement.
126
+ *
127
+ * Open rather than a closed union — the PHP and Node twins are what exist
128
+ * today, but the point of a manifest is that a runtime we haven't written can
129
+ * declare itself without a release here.
130
+ */
131
+ type NodeRuntimeId = "ts" | "php" | (string & {});
132
+ /**
133
+ * How one runtime provides this node, and which engine version it needs.
134
+ *
135
+ * `files` are source directories, not a dependency: a node is **vendored**, so
136
+ * `fancy-cli add node` copies them into the project the way it copies a
137
+ * component's source. One node, one source, readable and editable in the app
138
+ * rather than hidden in `node_modules` or `vendor`.
139
+ *
140
+ * This spec covers a runtime's BACKEND only. The surface lives in the
141
+ * manifest's `ui`, because the editor is React on every host — a Laravel
142
+ * project needs the React kind and does not need the TypeScript executor.
143
+ */
144
+ type NodeRuntimeSpec = {
145
+ /** This backend's source directories, relative to the node. */
146
+ files: string[];
147
+ /** Semver range of THIS runtime's engine. Required — see the module note. */
148
+ engine: string;
149
+ };
150
+ /**
151
+ * Whether a capability must be wired for the node to work at all.
152
+ *
153
+ * `required` is checked at AUTHOR time, not run time. The failure that hurts is
154
+ * a node that installs fine, authors fine, and silently no-ops during a run —
155
+ * the same silence as a routing bug. An editor can grey the node and say which
156
+ * capability the host never registered.
157
+ */
158
+ type CapabilityRequirement = "required" | "optional";
159
+ /**
160
+ * Whether a node is safe to run again.
161
+ *
162
+ * Durable runs RETRY. A node that writes needs to say so, or a host has to pick
163
+ * one retry policy for every node and get it wrong somewhere.
164
+ */
165
+ type SideEffects = "none" | "idempotent" | "unsafe-to-replay";
166
+ /**
167
+ * A Fancy suite package this node's source imports.
168
+ *
169
+ * Separate from a plain npm dependency because the suite is polyglot and
170
+ * vendorable: the same capability ships as an npm package, a Composer package,
171
+ * or source you copy in, and which of those a consumer wants depends on the
172
+ * host — so the node names the *package*, and the CLI offers the routes that
173
+ * exist. A bare `dependencies: ["@particle-academy/fancy-screens"]` can only
174
+ * ever offer `npm install`, which is the wrong answer in a Laravel app whose
175
+ * editor is vendored.
176
+ *
177
+ * ## No versions, deliberately
178
+ *
179
+ * Nothing here carries a range, and the validator rejects one. A pinned
180
+ * marketplace node freezes a consumer on an old surface: the suite ships
181
+ * additively and often, and a node that pinned `^0.4` at authoring time would
182
+ * be holding a project back a year later for a constraint nobody revisits.
183
+ * Compatibility is expressed where it can actually be checked — `runtimes[].engine`
184
+ * against the *engine*, which is the contract a node really depends on.
185
+ */
186
+ type FancyDependency = {
187
+ /** Suite slug — what `/packages/<slug>` documents. */
188
+ package: string;
189
+ /** npm name, when the suite ships one. */
190
+ npm?: string;
191
+ /** Composer name, when the suite ships one. */
192
+ composer?: string;
193
+ /**
194
+ * Why the node needs it, in a few words.
195
+ *
196
+ * Printed at install. Without it the CLI can only present a bare name and a
197
+ * command, which reads as an unexplained demand for a second package.
198
+ */
199
+ reason?: string;
200
+ /** Whether the node fails without it, or merely does less. Defaults to required. */
201
+ requirement?: CapabilityRequirement;
202
+ };
203
+ type NodePackageManifest = {
204
+ /** Must equal `NODE_MANIFEST_SCHEMA_VERSION`. */
205
+ schemaVersion: number;
206
+ /** Package name, as installed (`@acme/fancy-flow-salesforce`). */
207
+ name: string;
208
+ /**
209
+ * The canonical kind id this package provides — namespaced, and the string
210
+ * that gets persisted into every document using it.
211
+ */
212
+ kind: string;
213
+ /**
214
+ * Previous ids this kind still answers to.
215
+ *
216
+ * Core renamed `llm_branch` to `llm_router` and kept every old id working.
217
+ * Third-party packages will rename too, and their documents break the same
218
+ * way core's would have — so packages get the same escape hatch, otherwise
219
+ * only first-party nodes can rename safely.
220
+ */
221
+ aliases?: string[];
222
+ /**
223
+ * Version of this node's CONFIG shape, independent of the document schema.
224
+ *
225
+ * A node's config evolves on its own clock. Without a declared version, every
226
+ * executor accretes hand-written read-fallbacks forever (MOIC carries one for
227
+ * `routes[].key` → `routes[].port`), because nothing canonicalises old config
228
+ * the way import canonicalises kind ids.
229
+ */
230
+ configVersion?: number;
231
+ /**
232
+ * The node's SURFACE — its React kind — as source directories.
233
+ *
234
+ * Copied whichever backend the consumer picks, because the editor is React on
235
+ * every host. Kept out of `runtimes` deliberately: fold the two together and
236
+ * a PHP project either loses its palette entry or gains a TypeScript executor
237
+ * it will never run.
238
+ */
239
+ ui?: string[];
240
+ /** Per-runtime backend source and engine ranges. */
241
+ runtimes: Partial<Record<NodeRuntimeId, NodeRuntimeSpec>>;
242
+ /**
243
+ * Host capabilities this node needs, and whether each is mandatory.
244
+ *
245
+ * Declared so the CLI and the editor can say what to wire BEFORE a run,
246
+ * rather than the node silently no-opping or crashing mid-run.
247
+ */
248
+ capabilities?: Record<string, CapabilityRequirement>;
249
+ /** Fancy suite packages this node's source imports. See {@link FancyDependency}. */
250
+ fancyDependencies?: FancyDependency[];
251
+ /**
252
+ * Path to this node's golden fixtures, relative to the package root.
253
+ *
254
+ * REQUIRED. Every runtime the package claims runs these same cases, which is
255
+ * what makes "behaves identically on both runtimes" verified rather than
256
+ * asserted. See `./fixtures`.
257
+ */
258
+ fixtures: string;
259
+ /**
260
+ * Declares the node halts for a person. Mirrors `NodeKindDefinition`.
261
+ *
262
+ * A host-planning fact, not a node internal: a parent that embeds workflows
263
+ * needs to reject a child that can pause, because a paused child wedges the
264
+ * parent — and discovering that at run time means watching a run park.
265
+ */
266
+ pausesForHuman?: PauseAwaiting;
267
+ /** Whether this node is safe to replay. See `SideEffects`. */
268
+ sideEffects?: SideEffects;
269
+ /** One-line summary — what `search_nodes` matches against. */
270
+ description?: string;
271
+ /**
272
+ * Assigned by the registry, never by the author. Present in a manifest being
273
+ * submitted for publication, it is a claim to a trust signal the author does
274
+ * not get to make.
275
+ */
276
+ verified?: boolean;
277
+ };
278
+ type ManifestProblem = {
279
+ level: "error" | "warning";
280
+ field: string;
281
+ message: string;
282
+ };
283
+ type ManifestValidation = {
284
+ /** True when there are no `error`-level problems. Warnings do not block. */
285
+ ok: boolean;
286
+ manifest?: NodePackageManifest;
287
+ problems: ManifestProblem[];
288
+ };
289
+ /**
290
+ * Validate a manifest read from disk or a registry.
291
+ *
292
+ * Returns every problem rather than throwing on the first, because an author
293
+ * fixing a package wants the whole list — a validator that reveals one error
294
+ * per run turns a five-minute fix into five round trips.
295
+ */
296
+ declare function validateNodeManifest(input: unknown): ManifestValidation;
297
+ /**
298
+ * Check a node against the runtimes a host executes on, and their versions.
299
+ *
300
+ * Two failures live here, and both are errors because the node genuinely
301
+ * cannot run: a runtime the package does not implement at all, and a runtime
302
+ * it implements against an engine newer than the host's.
303
+ *
304
+ * `engineVersions` is optional — pass what the host knows. An unchecked range
305
+ * is reported as a warning rather than passed over silently, because "we did
306
+ * not check" and "it is fine" must not look the same.
307
+ */
308
+ declare function checkRuntimeSupport(manifest: Pick<NodePackageManifest, "kind" | "runtimes">, hostRuntimes: readonly string[], engineVersions?: Readonly<Record<string, string>>): ManifestProblem[];
309
+ /**
310
+ * Minimal semver range check — `^x.y.z`, `~x.y.z`, `>=x.y.z`, `x.y.z`, `*`.
311
+ *
312
+ * Deliberately small: this runs in the CLI and in CI, and pulling a semver
313
+ * library into the engine for one comparison is not worth the dependency. It
314
+ * covers the forms a manifest actually uses; anything it cannot parse is
315
+ * treated as unsatisfied rather than silently passed, so an unparseable range
316
+ * fails loudly instead of waving a node through.
317
+ */
318
+ declare function satisfiesRange(version: string, range: string): boolean;
319
+ /**
320
+ * Check that every capability a node needs is wired.
321
+ *
322
+ * A `required` capability that is missing is an ERROR — that is the whole point
323
+ * of the requirement level. It is meant to be surfaced at author time, so an
324
+ * editor can grey the node and name what the host never registered, rather than
325
+ * the node installing cleanly and silently no-opping during a run.
326
+ *
327
+ * An `optional` one is a warning: the node still works, with less.
328
+ */
329
+ declare function checkCapabilities(manifest: Pick<NodePackageManifest, "kind" | "capabilities">, available: Readonly<Record<string, boolean>>): ManifestProblem[];
330
+
100
331
  /** Categories used by the palette for grouping. */
101
332
  type NodeCategory = "trigger" | "logic" | "data" | "ai" | "io" | "human" | "output" | "layout" | "annotation" | "custom";
102
333
  /**
@@ -336,6 +567,24 @@ type NodeKindDefinition<TConfig = Record<string, unknown>, TIn = any, TOut = any
336
567
  * refuse to list a pausing node whose package never says so.
337
568
  */
338
569
  pausesForHuman?: PauseAwaiting;
570
+ /**
571
+ * Whether a node of this kind is safe to run twice.
572
+ *
573
+ * The same three values a marketplace manifest declares (see `SideEffects`),
574
+ * lifted onto the KIND so it is readable at run time. A manifest is install-
575
+ * time data; a durable runner needs this while deciding whether to retry, and
576
+ * cannot go and read a manifest to find out.
577
+ *
578
+ * `fancy-flow-php`'s per-node queue driver keys its retry policy on exactly
579
+ * this: an `unsafe-to-replay` node gets one attempt whatever `tries` says,
580
+ * because a retried `git_pr_open` opens a second pull request. This field is
581
+ * the TypeScript half of that contract — the twins declare the same kind
582
+ * metadata or they are not twins.
583
+ *
584
+ * Declaration only. Nothing here enforces it; a host's runner decides what to
585
+ * do with it, and the TS in-process runner currently does not retry at all.
586
+ */
587
+ sideEffects?: SideEffects;
339
588
  /**
340
589
  * Make nodes of this kind resizable via drag handles (xyflow NodeResizer).
341
590
  * `true` enables it with defaults; pass options to bound it. The resulting
@@ -374,4 +623,4 @@ type NodeKindDefinition<TConfig = Record<string, unknown>, TIn = any, TOut = any
374
623
  reactive?: boolean;
375
624
  };
376
625
 
377
- export { type ConfigField as C, type DocumentConfigField as D, type ExpressionConfigField as E, type JsonConfigField as J, type KeyValueConfigField as K, LEGACY_PAUSE_PREFIXES as L, type NodeKindDefinition as N, type PauseAwaiting as P, type RenderBodyContext as R, type SelectConfigField as S, type TextConfigField as T, type NodeCategory as a, PAUSE_PREFIX as b, type PauseSignal as c, type PortSpec as d, decodePause as e, encodePause as f, type CredentialConfigField as g, type NumberConfigField as h, isPause as i, type RepeaterConfigField as j, type RepeaterRowField as k, type SwitchConfigField as l, type TextareaConfigField as m, pauseForHuman as p };
626
+ export { type CapabilityRequirement as C, type DocumentConfigField as D, type ExpressionConfigField as E, type JsonConfigField as J, type KeyValueConfigField as K, LEGACY_PAUSE_PREFIXES as L, type ManifestProblem as M, NODE_MANIFEST_SCHEMA_VERSION as N, type PauseAwaiting as P, type RenderBodyContext as R, type SideEffects as S, type TextConfigField as T, type ConfigField as a, type ManifestValidation as b, type NodeCategory as c, type NodeKindDefinition as d, type NodePackageManifest as e, type NodeRuntimeId as f, type NodeRuntimeSpec as g, PAUSE_PREFIX as h, type PauseSignal as i, type PortSpec as j, checkCapabilities as k, checkRuntimeSupport as l, decodePause as m, encodePause as n, isPause as o, pauseForHuman as p, type CredentialConfigField as q, type NumberConfigField as r, satisfiesRange as s, type RepeaterConfigField as t, type RepeaterRowField as u, validateNodeManifest as v, type SelectConfigField as w, type SwitchConfigField as x, type TextareaConfigField as y };
@@ -106,4 +106,4 @@ type RunEvent = {
106
106
  error: string;
107
107
  };
108
108
 
109
- export type { ActionNodeData as A, BaseNodeData as B, DecisionNodeData as D, ExecutorRegistry as E, FlowGraph as F, NodeExecutor as N, OutputNodeData as O, PortDescriptor as P, RunEvent as R, SubgraphNodeData as S, TriggerNodeData as T, FlowNode as a, FlowEdge as b, FlowNodeData as c, FlowNodeKind as d, NodeRunStatus as e, NoteNodeData as f };
109
+ export type { ActionNodeData as A, BaseNodeData as B, DecisionNodeData as D, ExecutorRegistry as E, FlowGraph as F, NodeExecutor as N, OutputNodeData as O, PortDescriptor as P, RunEvent as R, SubgraphNodeData as S, TriggerNodeData as T, FlowEdge as a, FlowNode as b, FlowNodeData as c, FlowNodeKind as d, NodeRunStatus as e, NoteNodeData as f };
@@ -106,4 +106,4 @@ type RunEvent = {
106
106
  error: string;
107
107
  };
108
108
 
109
- export type { ActionNodeData as A, BaseNodeData as B, DecisionNodeData as D, ExecutorRegistry as E, FlowGraph as F, NodeExecutor as N, OutputNodeData as O, PortDescriptor as P, RunEvent as R, SubgraphNodeData as S, TriggerNodeData as T, FlowNode as a, FlowEdge as b, FlowNodeData as c, FlowNodeKind as d, NodeRunStatus as e, NoteNodeData as f };
109
+ export type { ActionNodeData as A, BaseNodeData as B, DecisionNodeData as D, ExecutorRegistry as E, FlowGraph as F, NodeExecutor as N, OutputNodeData as O, PortDescriptor as P, RunEvent as R, SubgraphNodeData as S, TriggerNodeData as T, FlowEdge as a, FlowNode as b, FlowNodeData as c, FlowNodeKind as d, NodeRunStatus as e, NoteNodeData as f };
package/dist/ux.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { EffectRegistry, DispatchActor } from '@particle-academy/fancy-auto-common';
2
2
  export { AutoActivityEvent } from '@particle-academy/fancy-auto-common';
3
- import { a as NodeCategory, C as ConfigField } from './types-DEmDSByY.cjs';
4
- import { E as ExecutorRegistry } from './types-CMSrWVYM.cjs';
3
+ import { c as NodeCategory, a as ConfigField } from './types-D8Dezh9v.cjs';
4
+ import { E as ExecutorRegistry } from './types-sOmpCitB.cjs';
5
5
  import 'react';
6
6
  import '@xyflow/react';
7
7
 
package/dist/ux.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { EffectRegistry, DispatchActor } from '@particle-academy/fancy-auto-common';
2
2
  export { AutoActivityEvent } from '@particle-academy/fancy-auto-common';
3
- import { a as NodeCategory, C as ConfigField } from './types-Bj_ZHwqG.js';
4
- import { E as ExecutorRegistry } from './types-CMSrWVYM.js';
3
+ import { c as NodeCategory, a as ConfigField } from './types-D6lsIGoe.js';
4
+ import { E as ExecutorRegistry } from './types-sOmpCitB.js';
5
5
  import 'react';
6
6
  import '@xyflow/react';
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-academy/fancy-flow",
3
- "version": "0.33.3",
3
+ "version": "0.35.0",
4
4
  "description": "Workflow editor + runner. Six built-in node kits (trigger / action / decision / output / note / subgraph), tokenized theme, topological execution with per-node status. React-flow bundled; consumers npm install fancy-flow and get nothing extra.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -123,7 +123,7 @@
123
123
  "scripts": {
124
124
  "build": "tsup",
125
125
  "dev": "tsup --watch",
126
- "lint": "tsc --noEmit",
126
+ "lint": "tsc --noEmit && eslint .",
127
127
  "test": "vitest run",
128
128
  "clean": "rm -rf dist",
129
129
  "prepublishOnly": "tsup"
@@ -144,7 +144,7 @@
144
144
  "react-dom": "^18.0.0 || ^19.0.0"
145
145
  },
146
146
  "dependencies": {
147
- "@particle-academy/fancy-auto-common": "^0.1.0"
147
+ "@particle-academy/fancy-auto-common": ">=0.1 <2.0"
148
148
  },
149
149
  "devDependencies": {
150
150
  "@dagrejs/dagre": "^3.0.0",
@@ -157,11 +157,14 @@
157
157
  "@xyflow/react": "^12.4.0",
158
158
  "ai": "^7.0.31",
159
159
  "clsx": "^2.1.0",
160
+ "eslint": "^10.8.0",
161
+ "eslint-plugin-react-hooks": "^7.1.1",
160
162
  "jsdom": "^29.1.1",
161
163
  "react": "^19.0.0",
162
164
  "react-dom": "^19.0.0",
163
165
  "tsup": "^8.0.0",
164
166
  "typescript": "^5.7.0",
167
+ "typescript-eslint": "^8.65.0",
165
168
  "vitest": "^3.2.6",
166
169
  "zod": "^4.4.3"
167
170
  },