@particle-academy/fancy-flow 0.10.1 → 0.12.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/{chunk-2DMBJN2O.js → chunk-D6W5FMCT.js} +6 -5
- package/dist/chunk-D6W5FMCT.js.map +1 -0
- package/dist/{chunk-NCPQDVUE.js → chunk-IK5DS5JP.js} +262 -29
- package/dist/chunk-IK5DS5JP.js.map +1 -0
- package/dist/{chunk-HNBO4HP3.js → chunk-L4AX73Q6.js} +12 -5
- package/dist/chunk-L4AX73Q6.js.map +1 -0
- package/dist/{chunk-VEI743ZX.js → chunk-MFMRTRPO.js} +3 -3
- package/dist/{chunk-VEI743ZX.js.map → chunk-MFMRTRPO.js.map} +1 -1
- package/dist/{chunk-CPSOC27D.js → chunk-U5F22BHV.js} +18 -4
- package/dist/chunk-U5F22BHV.js.map +1 -0
- package/dist/engine.cjs +20 -3
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +4 -2
- package/dist/engine.d.ts +4 -2
- package/dist/engine.js +2 -2
- package/dist/index.cjs +417 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +9 -9
- package/dist/registry/index.d.cts +149 -5
- package/dist/registry/index.d.ts +149 -5
- package/dist/registry.cjs +475 -71
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +3 -2
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime.cjs +20 -3
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +3 -3
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema.cjs +11 -3
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/{types-BS3Gwnkq.d.cts → types-Dg2REAz5.d.cts} +6 -0
- package/dist/{types-BS3Gwnkq.d.ts → types-Dg2REAz5.d.ts} +6 -0
- package/dist/{types-BocBFh6l.d.ts → types-DnQ4TEHN.d.cts} +16 -2
- package/dist/{types-DKqaUjF_.d.cts → types-DtXOwXjA.d.ts} +16 -2
- package/dist/ux.cjs +5 -0
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +2 -2
- package/dist/ux.d.ts +2 -2
- package/dist/ux.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-2DMBJN2O.js.map +0 -1
- package/dist/chunk-CPSOC27D.js.map +0 -1
- package/dist/chunk-HNBO4HP3.js.map +0 -1
- package/dist/chunk-NCPQDVUE.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -12,19 +12,33 @@ var ReactExports__default = /*#__PURE__*/_interopDefault(ReactExports);
|
|
|
12
12
|
|
|
13
13
|
// src/registry/registry.ts
|
|
14
14
|
var kinds = /* @__PURE__ */ new Map();
|
|
15
|
+
var aliases = /* @__PURE__ */ new Map();
|
|
15
16
|
var listeners = /* @__PURE__ */ new Set();
|
|
16
17
|
function registerNodeKind(definition) {
|
|
17
18
|
kinds.set(definition.name, definition);
|
|
19
|
+
for (const alias of definition.aliases ?? []) aliases.set(alias, definition.name);
|
|
18
20
|
notify();
|
|
19
21
|
return () => {
|
|
20
22
|
if (kinds.get(definition.name) === definition) {
|
|
21
23
|
kinds.delete(definition.name);
|
|
24
|
+
for (const alias of definition.aliases ?? []) {
|
|
25
|
+
if (aliases.get(alias) === definition.name) aliases.delete(alias);
|
|
26
|
+
}
|
|
22
27
|
notify();
|
|
23
28
|
}
|
|
24
29
|
};
|
|
25
30
|
}
|
|
31
|
+
function resolveKindId(id2) {
|
|
32
|
+
if (kinds.has(id2)) return id2;
|
|
33
|
+
const canonical = aliases.get(id2);
|
|
34
|
+
return canonical && kinds.has(canonical) ? canonical : null;
|
|
35
|
+
}
|
|
26
36
|
function getNodeKind(name) {
|
|
27
|
-
|
|
37
|
+
const canonical = resolveKindId(name);
|
|
38
|
+
return canonical ? kinds.get(canonical) ?? null : null;
|
|
39
|
+
}
|
|
40
|
+
function kindIds(kind) {
|
|
41
|
+
return [kind.name, ...kind.aliases ?? []];
|
|
28
42
|
}
|
|
29
43
|
function listNodeKinds(category) {
|
|
30
44
|
const all = Array.from(kinds.values());
|
|
@@ -198,6 +212,299 @@ function RichInputPreview({ config }) {
|
|
|
198
212
|
] });
|
|
199
213
|
}
|
|
200
214
|
|
|
215
|
+
// src/registry/capabilities.ts
|
|
216
|
+
var llmClient = null;
|
|
217
|
+
function getLlmClient() {
|
|
218
|
+
return llmClient;
|
|
219
|
+
}
|
|
220
|
+
var workflowResolver = null;
|
|
221
|
+
function getWorkflowResolver() {
|
|
222
|
+
return workflowResolver;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/registry/llm-branch.ts
|
|
226
|
+
function declaredRoutes(config) {
|
|
227
|
+
const raw = config.routes;
|
|
228
|
+
if (!Array.isArray(raw)) return [];
|
|
229
|
+
return raw.map((r) => ({ port: String(r?.port ?? "").trim(), description: r?.description })).filter((r) => r.port !== "");
|
|
230
|
+
}
|
|
231
|
+
function resolveFallbackPort(routes, fallbackEnabled) {
|
|
232
|
+
if (fallbackEnabled) return "fallback";
|
|
233
|
+
return routes[0]?.port ?? "out";
|
|
234
|
+
}
|
|
235
|
+
var llmBranchExecutor = async (ctx) => {
|
|
236
|
+
const config = ctx.node.data?.config ?? {};
|
|
237
|
+
const routes = declaredRoutes(config);
|
|
238
|
+
if (routes.length === 0) {
|
|
239
|
+
ctx.abort("llm_branch has no routes configured");
|
|
240
|
+
}
|
|
241
|
+
const client = getLlmClient();
|
|
242
|
+
{
|
|
243
|
+
ctx.abort(
|
|
244
|
+
"No LLM client registered. Call registerLlmClient() with your provider adapter \u2014 fancy-flow ships the routing, not the model call."
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const fallbackEnabled = config.fallback !== false;
|
|
248
|
+
const choice = await client.chooseRoute({
|
|
249
|
+
system: typeof config.system === "string" ? config.system : void 0,
|
|
250
|
+
prompt: String(config.prompt ?? ctx.inputs ?? ""),
|
|
251
|
+
routes,
|
|
252
|
+
provider: typeof config.provider === "string" ? config.provider : void 0,
|
|
253
|
+
model: typeof config.model === "string" ? config.model : void 0,
|
|
254
|
+
credential: typeof config.credential === "string" ? config.credential : void 0
|
|
255
|
+
});
|
|
256
|
+
const offered = new Set(routes.map((r) => r.port));
|
|
257
|
+
let port = choice?.port ?? "";
|
|
258
|
+
let reason = choice?.reason;
|
|
259
|
+
if (!offered.has(port)) {
|
|
260
|
+
const safe = resolveFallbackPort(routes, fallbackEnabled);
|
|
261
|
+
ctx.emit({
|
|
262
|
+
type: "log",
|
|
263
|
+
nodeId: ctx.node.id,
|
|
264
|
+
level: "warn",
|
|
265
|
+
message: `llm_branch: model returned "${port || "(nothing)"}", which is not a declared route. Routing to "${safe}".`
|
|
266
|
+
});
|
|
267
|
+
reason = reason ?? `unrecognised route "${port}"`;
|
|
268
|
+
port = safe;
|
|
269
|
+
}
|
|
270
|
+
return { __port: port, value: { route: port, reason, input: ctx.inputs } };
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// src/registry/ports.ts
|
|
274
|
+
function resolvePortSpec(spec, config) {
|
|
275
|
+
if (spec === void 0) return void 0;
|
|
276
|
+
if (typeof spec !== "function") return spec;
|
|
277
|
+
try {
|
|
278
|
+
const resolved = spec(config);
|
|
279
|
+
return Array.isArray(resolved) ? resolved : void 0;
|
|
280
|
+
} catch {
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function nodeConfig(node) {
|
|
285
|
+
return node.data?.config ?? {};
|
|
286
|
+
}
|
|
287
|
+
function resolveNodePorts(node, kind) {
|
|
288
|
+
const config = nodeConfig(node);
|
|
289
|
+
const data = node.data;
|
|
290
|
+
return {
|
|
291
|
+
inputs: data?.inputs ?? resolvePortSpec(kind?.inputs, config),
|
|
292
|
+
outputs: data?.outputs ?? resolvePortSpec(kind?.outputs, config)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/runtime/run-flow.ts
|
|
297
|
+
async function runFlow(graph, executors, onEvent = () => {
|
|
298
|
+
}, options = {}) {
|
|
299
|
+
const { signal, initialInputs = {}, timeoutMs, depth = 0 } = options;
|
|
300
|
+
const outputs = {};
|
|
301
|
+
const portValues = /* @__PURE__ */ new Map();
|
|
302
|
+
const completed = /* @__PURE__ */ new Set();
|
|
303
|
+
const errors = [];
|
|
304
|
+
const order = topoSort(graph);
|
|
305
|
+
if (order === null) {
|
|
306
|
+
const msg = "Cycle detected in flow graph \u2014 aborting.";
|
|
307
|
+
onEvent({ type: "run-error", error: msg });
|
|
308
|
+
return { ok: false, outputs, error: msg };
|
|
309
|
+
}
|
|
310
|
+
const incomingByNode = indexIncoming(graph.edges);
|
|
311
|
+
const timer2 = timeoutMs ? setTimeout(() => errors.push(`Run timed out after ${timeoutMs}ms`), timeoutMs) : null;
|
|
312
|
+
onEvent({ type: "run-start" });
|
|
313
|
+
try {
|
|
314
|
+
for (const node of order) {
|
|
315
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
316
|
+
if (errors.length) break;
|
|
317
|
+
const incoming = incomingByNode.get(node.id) ?? [];
|
|
318
|
+
if (incoming.length > 0) {
|
|
319
|
+
const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? "out"}`));
|
|
320
|
+
if (!anyActive) {
|
|
321
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "skipped" });
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (node.type === "note") {
|
|
326
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "annotation" });
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "running" });
|
|
330
|
+
const inputs = collectInputs(node, incoming, portValues, initialInputs);
|
|
331
|
+
const exec = pickExecutor(executors, node);
|
|
332
|
+
if (!exec) {
|
|
333
|
+
const msg = `No executor registered for kind=${node.type}`;
|
|
334
|
+
errors.push(msg);
|
|
335
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "error", text: msg });
|
|
336
|
+
onEvent({ type: "log", nodeId: node.id, level: "error", message: msg });
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
const result = await Promise.resolve(
|
|
341
|
+
exec({
|
|
342
|
+
node,
|
|
343
|
+
inputs,
|
|
344
|
+
abort: (reason) => {
|
|
345
|
+
throw new Error(reason ?? "aborted");
|
|
346
|
+
},
|
|
347
|
+
emit: onEvent,
|
|
348
|
+
depth
|
|
349
|
+
})
|
|
350
|
+
);
|
|
351
|
+
outputs[node.id] = result;
|
|
352
|
+
const activated = activatedPorts(node, result);
|
|
353
|
+
for (const portId of activated.ports) {
|
|
354
|
+
portValues.set(`${node.id}:${portId}`, activated.value);
|
|
355
|
+
onEvent({ type: "node-output", nodeId: node.id, portId, value: activated.value });
|
|
356
|
+
}
|
|
357
|
+
completed.add(node.id);
|
|
358
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "done" });
|
|
359
|
+
} catch (e) {
|
|
360
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
361
|
+
errors.push(msg);
|
|
362
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "error", text: msg });
|
|
363
|
+
onEvent({ type: "log", nodeId: node.id, level: "error", message: msg });
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} finally {
|
|
368
|
+
if (timer2) clearTimeout(timer2);
|
|
369
|
+
}
|
|
370
|
+
const ok = errors.length === 0;
|
|
371
|
+
onEvent({ type: "run-end", ok });
|
|
372
|
+
return ok ? { ok, outputs } : { ok, outputs, error: errors[0] };
|
|
373
|
+
}
|
|
374
|
+
function indexIncoming(edges) {
|
|
375
|
+
const map = /* @__PURE__ */ new Map();
|
|
376
|
+
for (const e of edges) {
|
|
377
|
+
const list = map.get(e.target) ?? [];
|
|
378
|
+
list.push(e);
|
|
379
|
+
map.set(e.target, list);
|
|
380
|
+
}
|
|
381
|
+
return map;
|
|
382
|
+
}
|
|
383
|
+
function topoSort(graph) {
|
|
384
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
385
|
+
for (const n of graph.nodes) inDegree.set(n.id, 0);
|
|
386
|
+
for (const e of graph.edges) inDegree.set(e.target, (inDegree.get(e.target) ?? 0) + 1);
|
|
387
|
+
const queue = [];
|
|
388
|
+
for (const [id2, d] of inDegree) if (d === 0) queue.push(id2);
|
|
389
|
+
const ordered = [];
|
|
390
|
+
while (queue.length) {
|
|
391
|
+
const id2 = queue.shift();
|
|
392
|
+
ordered.push(id2);
|
|
393
|
+
for (const e of graph.edges) {
|
|
394
|
+
if (e.source !== id2) continue;
|
|
395
|
+
const next = (inDegree.get(e.target) ?? 0) - 1;
|
|
396
|
+
inDegree.set(e.target, next);
|
|
397
|
+
if (next === 0) queue.push(e.target);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (ordered.length !== graph.nodes.length) return null;
|
|
401
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
402
|
+
return ordered.map((id2) => byId.get(id2)).filter(Boolean);
|
|
403
|
+
}
|
|
404
|
+
function collectInputs(node, incoming, portValues, initial) {
|
|
405
|
+
const inputs = { ...initial[node.id] ?? {} };
|
|
406
|
+
for (const e of incoming) {
|
|
407
|
+
const portId = e.targetHandle ?? "in";
|
|
408
|
+
const val = portValues.get(`${e.source}:${e.sourceHandle ?? "out"}`);
|
|
409
|
+
inputs[portId] = val;
|
|
410
|
+
}
|
|
411
|
+
return inputs;
|
|
412
|
+
}
|
|
413
|
+
function pickExecutor(executors, node) {
|
|
414
|
+
if (executors[node.id]) return executors[node.id];
|
|
415
|
+
if (node.type && executors[node.type]) return executors[node.type];
|
|
416
|
+
const kind = node.type ? getNodeKind(node.type) : null;
|
|
417
|
+
if (kind) {
|
|
418
|
+
for (const id2 of kindIds(kind)) {
|
|
419
|
+
if (executors[id2]) return executors[id2];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return executors["*"];
|
|
423
|
+
}
|
|
424
|
+
function activatedPorts(node, result) {
|
|
425
|
+
if (result && typeof result === "object") {
|
|
426
|
+
const r = result;
|
|
427
|
+
if (typeof r.__port === "string") {
|
|
428
|
+
return { ports: [r.__port], value: r.value };
|
|
429
|
+
}
|
|
430
|
+
if (typeof r.branch === "string") {
|
|
431
|
+
return { ports: [r.branch], value: r.value ?? r };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const kind = getNodeKind(node.data?.kind ?? node.type ?? "") ?? void 0;
|
|
435
|
+
const declared = resolveNodePorts(node, kind).outputs?.map((p) => p.id);
|
|
436
|
+
return { ports: declared?.length ? declared : ["out"], value: result };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/registry/subflow.ts
|
|
440
|
+
var DEFAULT_MAX_DEPTH = 8;
|
|
441
|
+
function subflowMode(config) {
|
|
442
|
+
const mode = config.mode;
|
|
443
|
+
return mode === "stream" || mode === "both" ? mode : "output";
|
|
444
|
+
}
|
|
445
|
+
function subflowPorts(config) {
|
|
446
|
+
const mode = subflowMode(config);
|
|
447
|
+
const ports = [{ id: "out", label: "result" }];
|
|
448
|
+
if (mode === "stream" || mode === "both") ports.unshift({ id: "stream", label: "stream" });
|
|
449
|
+
return ports;
|
|
450
|
+
}
|
|
451
|
+
var subflowExecutor = async (ctx) => {
|
|
452
|
+
const config = ctx.node.data?.config ?? {};
|
|
453
|
+
const ref = String(config.workflow ?? "").trim();
|
|
454
|
+
if (!ref) ctx.abort("subflow has no workflow reference configured");
|
|
455
|
+
const resolver = getWorkflowResolver();
|
|
456
|
+
{
|
|
457
|
+
ctx.abort(
|
|
458
|
+
"No workflow resolver registered. Call registerWorkflowResolver() so subflow can find the workflow it references."
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
const maxDepth = Number.isFinite(config.maxDepth) ? Number(config.maxDepth) : DEFAULT_MAX_DEPTH;
|
|
462
|
+
const depth = ctx.depth ?? 0;
|
|
463
|
+
if (depth + 1 > maxDepth) {
|
|
464
|
+
ctx.abort(
|
|
465
|
+
`subflow depth limit reached (${maxDepth}) at "${ref}" \u2014 a workflow is referencing itself, directly or through a chain.`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
const child = await resolver(ref);
|
|
469
|
+
if (!child) ctx.abort(`subflow could not resolve workflow "${ref}"`);
|
|
470
|
+
const mode = subflowMode(config);
|
|
471
|
+
const streaming = mode === "stream" || mode === "both";
|
|
472
|
+
const forward = (event) => {
|
|
473
|
+
if (!streaming) return;
|
|
474
|
+
const detail = event.type === "node-status" ? `${event.nodeId} ${event.status}` : event.type === "run-end" ? `finished (${event.ok ? "ok" : "failed"})` : event.type;
|
|
475
|
+
ctx.emit({
|
|
476
|
+
type: "log",
|
|
477
|
+
nodeId: ctx.node.id,
|
|
478
|
+
level: "info",
|
|
479
|
+
message: `[${ref}] ${detail}`
|
|
480
|
+
});
|
|
481
|
+
};
|
|
482
|
+
const result = await runFlow(
|
|
483
|
+
child,
|
|
484
|
+
config.executors ?? {},
|
|
485
|
+
forward,
|
|
486
|
+
{
|
|
487
|
+
initialInputs: config.inputs ?? {
|
|
488
|
+
// With no explicit mapping, hand the parent's inputs to the child's
|
|
489
|
+
// entry points — the obvious default, and it makes the simple case
|
|
490
|
+
// require no configuration at all.
|
|
491
|
+
__parent: ctx.inputs
|
|
492
|
+
},
|
|
493
|
+
depth: depth + 1
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
if (!result.ok) {
|
|
497
|
+
ctx.abort(`subflow "${ref}" failed: ${result.error ?? "unknown error"}`);
|
|
498
|
+
}
|
|
499
|
+
if (mode === "stream") {
|
|
500
|
+
return { __port: "stream", value: result.outputs };
|
|
501
|
+
}
|
|
502
|
+
if (mode === "both") {
|
|
503
|
+
return result.outputs;
|
|
504
|
+
}
|
|
505
|
+
return { __port: "out", value: result.outputs };
|
|
506
|
+
};
|
|
507
|
+
|
|
201
508
|
// src/registry/builtin.ts
|
|
202
509
|
function casePorts(cases) {
|
|
203
510
|
const byPort = /* @__PURE__ */ new Map();
|
|
@@ -244,7 +551,8 @@ var HTTP_METHODS = [
|
|
|
244
551
|
var KINDS = [
|
|
245
552
|
// ───────────── Triggers ─────────────
|
|
246
553
|
{
|
|
247
|
-
name: "manual_trigger",
|
|
554
|
+
name: "@particle-academy/manual_trigger",
|
|
555
|
+
aliases: ["manual_trigger", "@fancy/manual_trigger"],
|
|
248
556
|
category: "trigger",
|
|
249
557
|
label: "Manual",
|
|
250
558
|
description: "Entry point fired when the user clicks Run.",
|
|
@@ -253,7 +561,8 @@ var KINDS = [
|
|
|
253
561
|
outputs: [{ id: "out" }]
|
|
254
562
|
},
|
|
255
563
|
{
|
|
256
|
-
name: "webhook_trigger",
|
|
564
|
+
name: "@particle-academy/webhook_trigger",
|
|
565
|
+
aliases: ["webhook_trigger", "@fancy/webhook_trigger"],
|
|
257
566
|
category: "trigger",
|
|
258
567
|
label: "Webhook",
|
|
259
568
|
description: "Triggered by an inbound HTTP request to a host-provided URL.",
|
|
@@ -270,7 +579,8 @@ var KINDS = [
|
|
|
270
579
|
]
|
|
271
580
|
},
|
|
272
581
|
{
|
|
273
|
-
name: "schedule_trigger",
|
|
582
|
+
name: "@particle-academy/schedule_trigger",
|
|
583
|
+
aliases: ["schedule_trigger", "@fancy/schedule_trigger"],
|
|
274
584
|
category: "trigger",
|
|
275
585
|
label: "Schedule",
|
|
276
586
|
description: "Fires on a cron schedule (host-implemented).",
|
|
@@ -290,7 +600,8 @@ var KINDS = [
|
|
|
290
600
|
]
|
|
291
601
|
},
|
|
292
602
|
{
|
|
293
|
-
name: "user_input",
|
|
603
|
+
name: "@particle-academy/user_input",
|
|
604
|
+
aliases: ["user_input", "@fancy/user_input"],
|
|
294
605
|
category: "human",
|
|
295
606
|
label: "User Input",
|
|
296
607
|
description: "Pause the flow until the user submits the configured form.",
|
|
@@ -330,7 +641,8 @@ var KINDS = [
|
|
|
330
641
|
]
|
|
331
642
|
},
|
|
332
643
|
{
|
|
333
|
-
name: "rich_user_input",
|
|
644
|
+
name: "@particle-academy/rich_user_input",
|
|
645
|
+
aliases: ["rich_user_input", "@fancy/rich_user_input"],
|
|
334
646
|
category: "human",
|
|
335
647
|
label: "Rich User Input",
|
|
336
648
|
description: "Pause the flow on a fully authored page \u2014 content, required reading, multi-section forms.",
|
|
@@ -353,9 +665,61 @@ var KINDS = [
|
|
|
353
665
|
// what the person hitting this step will actually see.
|
|
354
666
|
renderBody: (ctx) => ReactExports.createElement(RichInputPreview, { config: ctx.config ?? {} })
|
|
355
667
|
},
|
|
668
|
+
{
|
|
669
|
+
name: "@particle-academy/subflow",
|
|
670
|
+
aliases: ["subflow", "@fancy/subflow"],
|
|
671
|
+
category: "logic",
|
|
672
|
+
label: "SubFlow",
|
|
673
|
+
description: "Run another workflow and bring its result \u2014 or its live progress \u2014 back into this one.",
|
|
674
|
+
icon: "\u29C9",
|
|
675
|
+
inputs: [{ id: "in" }],
|
|
676
|
+
// The stream port only exists when something actually streams.
|
|
677
|
+
outputs: (config) => subflowPorts(config ?? {}),
|
|
678
|
+
// Core, not marketplace: it runs a child graph through this same engine and
|
|
679
|
+
// needs nothing from outside except where workflows live.
|
|
680
|
+
executor: subflowExecutor,
|
|
681
|
+
configSchema: [
|
|
682
|
+
{
|
|
683
|
+
type: "text",
|
|
684
|
+
key: "workflow",
|
|
685
|
+
label: "Workflow",
|
|
686
|
+
required: true,
|
|
687
|
+
placeholder: "onboarding-v2",
|
|
688
|
+
description: "Reference resolved by the host's registerWorkflowResolver()."
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
type: "select",
|
|
692
|
+
key: "mode",
|
|
693
|
+
label: "Return",
|
|
694
|
+
default: "output",
|
|
695
|
+
options: [
|
|
696
|
+
{ value: "output", label: "Output when it finishes" },
|
|
697
|
+
{ value: "stream", label: "Stream progress as it runs" },
|
|
698
|
+
{ value: "both", label: "Both \u2014 stream, then output" }
|
|
699
|
+
],
|
|
700
|
+
description: "Streaming adds a second port so a parent can show progress instead of a spinner."
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
type: "json",
|
|
704
|
+
key: "inputs",
|
|
705
|
+
label: "Input mapping",
|
|
706
|
+
description: "Entry-point inputs for the child run. Omit to pass this node's inputs straight through."
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
type: "number",
|
|
710
|
+
key: "maxDepth",
|
|
711
|
+
label: "Max nesting depth",
|
|
712
|
+
default: DEFAULT_MAX_DEPTH,
|
|
713
|
+
min: 1,
|
|
714
|
+
max: 32,
|
|
715
|
+
description: "Guards against a workflow referencing itself."
|
|
716
|
+
}
|
|
717
|
+
]
|
|
718
|
+
},
|
|
356
719
|
// ───────────── Logic ─────────────
|
|
357
720
|
{
|
|
358
|
-
name: "branch",
|
|
721
|
+
name: "@particle-academy/branch",
|
|
722
|
+
aliases: ["branch", "@fancy/branch"],
|
|
359
723
|
category: "logic",
|
|
360
724
|
label: "Branch",
|
|
361
725
|
description: "Multi-way branch on a condition or value.",
|
|
@@ -367,7 +731,8 @@ var KINDS = [
|
|
|
367
731
|
]
|
|
368
732
|
},
|
|
369
733
|
{
|
|
370
|
-
name: "switch_case",
|
|
734
|
+
name: "@particle-academy/switch_case",
|
|
735
|
+
aliases: ["switch_case", "@fancy/switch_case"],
|
|
371
736
|
category: "logic",
|
|
372
737
|
label: "Switch",
|
|
373
738
|
description: "Route to one of N labelled outputs based on a key.",
|
|
@@ -394,7 +759,8 @@ var KINDS = [
|
|
|
394
759
|
]
|
|
395
760
|
},
|
|
396
761
|
{
|
|
397
|
-
name: "for_each",
|
|
762
|
+
name: "@particle-academy/for_each",
|
|
763
|
+
aliases: ["for_each", "@fancy/for_each"],
|
|
398
764
|
category: "logic",
|
|
399
765
|
label: "For Each",
|
|
400
766
|
description: "Iterate over a list, emitting each item on `item`.",
|
|
@@ -407,7 +773,8 @@ var KINDS = [
|
|
|
407
773
|
]
|
|
408
774
|
},
|
|
409
775
|
{
|
|
410
|
-
name: "merge",
|
|
776
|
+
name: "@particle-academy/merge",
|
|
777
|
+
aliases: ["merge", "@fancy/merge"],
|
|
411
778
|
category: "logic",
|
|
412
779
|
label: "Merge",
|
|
413
780
|
description: "Combine multiple inputs into one object or array.",
|
|
@@ -425,7 +792,8 @@ var KINDS = [
|
|
|
425
792
|
]
|
|
426
793
|
},
|
|
427
794
|
{
|
|
428
|
-
name: "wait",
|
|
795
|
+
name: "@particle-academy/wait",
|
|
796
|
+
aliases: ["wait", "@fancy/wait"],
|
|
429
797
|
category: "logic",
|
|
430
798
|
label: "Wait",
|
|
431
799
|
description: "Sleep or wait for an external event.",
|
|
@@ -442,7 +810,8 @@ var KINDS = [
|
|
|
442
810
|
]
|
|
443
811
|
},
|
|
444
812
|
{
|
|
445
|
-
name: "transform",
|
|
813
|
+
name: "@particle-academy/transform",
|
|
814
|
+
aliases: ["transform", "@fancy/transform"],
|
|
446
815
|
category: "logic",
|
|
447
816
|
label: "Transform",
|
|
448
817
|
description: "Reshape data with an expression.",
|
|
@@ -459,7 +828,8 @@ var KINDS = [
|
|
|
459
828
|
},
|
|
460
829
|
// ───────────── Data ─────────────
|
|
461
830
|
{
|
|
462
|
-
name: "memory_store",
|
|
831
|
+
name: "@particle-academy/memory_store",
|
|
832
|
+
aliases: ["memory_store", "@fancy/memory_store"],
|
|
463
833
|
category: "data",
|
|
464
834
|
label: "Memory Store",
|
|
465
835
|
description: "Read or write per-conversation memory.",
|
|
@@ -479,7 +849,8 @@ var KINDS = [
|
|
|
479
849
|
]
|
|
480
850
|
},
|
|
481
851
|
{
|
|
482
|
-
name: "data_store",
|
|
852
|
+
name: "@particle-academy/data_store",
|
|
853
|
+
aliases: ["data_store", "@fancy/data_store"],
|
|
483
854
|
category: "data",
|
|
484
855
|
label: "Data Store",
|
|
485
856
|
description: "Key-value or table read/write against a host store.",
|
|
@@ -507,7 +878,8 @@ var KINDS = [
|
|
|
507
878
|
]
|
|
508
879
|
},
|
|
509
880
|
{
|
|
510
|
-
name: "variable",
|
|
881
|
+
name: "@particle-academy/variable",
|
|
882
|
+
aliases: ["variable", "@fancy/variable"],
|
|
511
883
|
category: "data",
|
|
512
884
|
label: "Variable",
|
|
513
885
|
description: "Workflow-scoped value used by other nodes.",
|
|
@@ -519,7 +891,8 @@ var KINDS = [
|
|
|
519
891
|
},
|
|
520
892
|
// ───────────── AI ─────────────
|
|
521
893
|
{
|
|
522
|
-
name: "llm_call",
|
|
894
|
+
name: "@particle-academy/llm_call",
|
|
895
|
+
aliases: ["llm_call", "@fancy/llm_call"],
|
|
523
896
|
category: "ai",
|
|
524
897
|
label: "LLM Call",
|
|
525
898
|
description: "Send a prompt + context to a model and receive a response.",
|
|
@@ -546,7 +919,8 @@ var KINDS = [
|
|
|
546
919
|
]
|
|
547
920
|
},
|
|
548
921
|
{
|
|
549
|
-
name: "llm_branch",
|
|
922
|
+
name: "@particle-academy/llm_branch",
|
|
923
|
+
aliases: ["llm_branch", "@fancy/llm_branch"],
|
|
550
924
|
category: "ai",
|
|
551
925
|
label: "LLM Router",
|
|
552
926
|
description: "Let a model choose which route the flow takes.",
|
|
@@ -555,6 +929,10 @@ var KINDS = [
|
|
|
555
929
|
// Each declared route is a port. The executor returns `{ __port: id }`
|
|
556
930
|
// (or `Port.only(id)` on the PHP runtime) to pick one.
|
|
557
931
|
outputs: (config) => routePorts(config?.routes, config?.fallback),
|
|
932
|
+
// A shuttle, not an engine: it carries the routes out to whatever LLM
|
|
933
|
+
// client the host registered and carries the choice back. No provider SDK
|
|
934
|
+
// reaches core, so this stays a builtin without adding a dependency.
|
|
935
|
+
executor: llmBranchExecutor,
|
|
558
936
|
configSchema: [
|
|
559
937
|
{
|
|
560
938
|
type: "textarea",
|
|
@@ -616,7 +994,8 @@ var KINDS = [
|
|
|
616
994
|
]
|
|
617
995
|
},
|
|
618
996
|
{
|
|
619
|
-
name: "tool_use",
|
|
997
|
+
name: "@particle-academy/tool_use",
|
|
998
|
+
aliases: ["tool_use", "@fancy/tool_use"],
|
|
620
999
|
category: "ai",
|
|
621
1000
|
label: "Tool Use",
|
|
622
1001
|
description: "Hand control to a host-registered tool by name.",
|
|
@@ -627,7 +1006,8 @@ var KINDS = [
|
|
|
627
1006
|
]
|
|
628
1007
|
},
|
|
629
1008
|
{
|
|
630
|
-
name: "embed_search",
|
|
1009
|
+
name: "@particle-academy/embed_search",
|
|
1010
|
+
aliases: ["embed_search", "@fancy/embed_search"],
|
|
631
1011
|
category: "ai",
|
|
632
1012
|
label: "Embed & Search",
|
|
633
1013
|
description: "Embed a query and search a vector store.",
|
|
@@ -640,7 +1020,8 @@ var KINDS = [
|
|
|
640
1020
|
},
|
|
641
1021
|
// ───────────── IO ─────────────
|
|
642
1022
|
{
|
|
643
|
-
name: "api_request",
|
|
1023
|
+
name: "@particle-academy/api_request",
|
|
1024
|
+
aliases: ["api_request", "@fancy/api_request"],
|
|
644
1025
|
category: "io",
|
|
645
1026
|
label: "API Request",
|
|
646
1027
|
description: "HTTP request to any URL.",
|
|
@@ -654,7 +1035,8 @@ var KINDS = [
|
|
|
654
1035
|
]
|
|
655
1036
|
},
|
|
656
1037
|
{
|
|
657
|
-
name: "webhook_out",
|
|
1038
|
+
name: "@particle-academy/webhook_out",
|
|
1039
|
+
aliases: ["webhook_out", "@fancy/webhook_out"],
|
|
658
1040
|
category: "io",
|
|
659
1041
|
label: "Send Webhook",
|
|
660
1042
|
description: "POST a payload to a configured URL.",
|
|
@@ -667,7 +1049,8 @@ var KINDS = [
|
|
|
667
1049
|
},
|
|
668
1050
|
// ───────────── Human ─────────────
|
|
669
1051
|
{
|
|
670
|
-
name: "human_approval",
|
|
1052
|
+
name: "@particle-academy/human_approval",
|
|
1053
|
+
aliases: ["human_approval", "@fancy/human_approval"],
|
|
671
1054
|
category: "human",
|
|
672
1055
|
label: "Human Approval",
|
|
673
1056
|
description: "Pause until a human approves or denies.",
|
|
@@ -681,7 +1064,8 @@ var KINDS = [
|
|
|
681
1064
|
]
|
|
682
1065
|
},
|
|
683
1066
|
{
|
|
684
|
-
name: "notify",
|
|
1067
|
+
name: "@particle-academy/notify",
|
|
1068
|
+
aliases: ["notify", "@fancy/notify"],
|
|
685
1069
|
category: "human",
|
|
686
1070
|
label: "Notify",
|
|
687
1071
|
description: "Send a message via Slack / email / SMS / etc.",
|
|
@@ -705,7 +1089,8 @@ var KINDS = [
|
|
|
705
1089
|
},
|
|
706
1090
|
// ───────────── Output ─────────────
|
|
707
1091
|
{
|
|
708
|
-
name: "output",
|
|
1092
|
+
name: "@particle-academy/output",
|
|
1093
|
+
aliases: ["output", "@fancy/output"],
|
|
709
1094
|
category: "output",
|
|
710
1095
|
label: "Output",
|
|
711
1096
|
description: "Terminal node \u2014 captures the workflow's result.",
|
|
@@ -714,7 +1099,8 @@ var KINDS = [
|
|
|
714
1099
|
outputs: []
|
|
715
1100
|
},
|
|
716
1101
|
{
|
|
717
|
-
name: "log",
|
|
1102
|
+
name: "@particle-academy/log",
|
|
1103
|
+
aliases: ["log", "@fancy/log"],
|
|
718
1104
|
category: "output",
|
|
719
1105
|
label: "Log",
|
|
720
1106
|
description: "Send to the run feed.",
|
|
@@ -10397,167 +10783,6 @@ function useFlowState(initial) {
|
|
|
10397
10783
|
const toGraph = ReactExports.useCallback(() => ({ nodes, edges }), [nodes, edges]);
|
|
10398
10784
|
return { nodes, edges, setNodes, setEdges, onNodesChange, onEdgesChange, onConnect, toGraph };
|
|
10399
10785
|
}
|
|
10400
|
-
|
|
10401
|
-
// src/registry/ports.ts
|
|
10402
|
-
function resolvePortSpec(spec, config) {
|
|
10403
|
-
if (spec === void 0) return void 0;
|
|
10404
|
-
if (typeof spec !== "function") return spec;
|
|
10405
|
-
try {
|
|
10406
|
-
const resolved = spec(config);
|
|
10407
|
-
return Array.isArray(resolved) ? resolved : void 0;
|
|
10408
|
-
} catch {
|
|
10409
|
-
return void 0;
|
|
10410
|
-
}
|
|
10411
|
-
}
|
|
10412
|
-
function nodeConfig(node) {
|
|
10413
|
-
return node.data?.config ?? {};
|
|
10414
|
-
}
|
|
10415
|
-
function resolveNodePorts(node, kind) {
|
|
10416
|
-
const config = nodeConfig(node);
|
|
10417
|
-
const data = node.data;
|
|
10418
|
-
return {
|
|
10419
|
-
inputs: data?.inputs ?? resolvePortSpec(kind?.inputs, config),
|
|
10420
|
-
outputs: data?.outputs ?? resolvePortSpec(kind?.outputs, config)
|
|
10421
|
-
};
|
|
10422
|
-
}
|
|
10423
|
-
|
|
10424
|
-
// src/runtime/run-flow.ts
|
|
10425
|
-
async function runFlow(graph, executors, onEvent = () => {
|
|
10426
|
-
}, options = {}) {
|
|
10427
|
-
const { signal, initialInputs = {}, timeoutMs } = options;
|
|
10428
|
-
const outputs = {};
|
|
10429
|
-
const portValues = /* @__PURE__ */ new Map();
|
|
10430
|
-
const completed = /* @__PURE__ */ new Set();
|
|
10431
|
-
const errors = [];
|
|
10432
|
-
const order = topoSort(graph);
|
|
10433
|
-
if (order === null) {
|
|
10434
|
-
const msg = "Cycle detected in flow graph \u2014 aborting.";
|
|
10435
|
-
onEvent({ type: "run-error", error: msg });
|
|
10436
|
-
return { ok: false, outputs, error: msg };
|
|
10437
|
-
}
|
|
10438
|
-
const incomingByNode = indexIncoming(graph.edges);
|
|
10439
|
-
const timer2 = timeoutMs ? setTimeout(() => errors.push(`Run timed out after ${timeoutMs}ms`), timeoutMs) : null;
|
|
10440
|
-
onEvent({ type: "run-start" });
|
|
10441
|
-
try {
|
|
10442
|
-
for (const node of order) {
|
|
10443
|
-
if (signal?.aborted) throw new Error("aborted");
|
|
10444
|
-
if (errors.length) break;
|
|
10445
|
-
const incoming = incomingByNode.get(node.id) ?? [];
|
|
10446
|
-
if (incoming.length > 0) {
|
|
10447
|
-
const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? "out"}`));
|
|
10448
|
-
if (!anyActive) {
|
|
10449
|
-
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "skipped" });
|
|
10450
|
-
continue;
|
|
10451
|
-
}
|
|
10452
|
-
}
|
|
10453
|
-
if (node.type === "note") {
|
|
10454
|
-
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "annotation" });
|
|
10455
|
-
continue;
|
|
10456
|
-
}
|
|
10457
|
-
onEvent({ type: "node-status", nodeId: node.id, status: "running" });
|
|
10458
|
-
const inputs = collectInputs(node, incoming, portValues, initialInputs);
|
|
10459
|
-
const exec = pickExecutor(executors, node);
|
|
10460
|
-
if (!exec) {
|
|
10461
|
-
const msg = `No executor registered for kind=${node.type}`;
|
|
10462
|
-
errors.push(msg);
|
|
10463
|
-
onEvent({ type: "node-status", nodeId: node.id, status: "error", text: msg });
|
|
10464
|
-
onEvent({ type: "log", nodeId: node.id, level: "error", message: msg });
|
|
10465
|
-
break;
|
|
10466
|
-
}
|
|
10467
|
-
try {
|
|
10468
|
-
const result = await Promise.resolve(
|
|
10469
|
-
exec({
|
|
10470
|
-
node,
|
|
10471
|
-
inputs,
|
|
10472
|
-
abort: (reason) => {
|
|
10473
|
-
throw new Error(reason ?? "aborted");
|
|
10474
|
-
},
|
|
10475
|
-
emit: onEvent
|
|
10476
|
-
})
|
|
10477
|
-
);
|
|
10478
|
-
outputs[node.id] = result;
|
|
10479
|
-
const activated = activatedPorts(node, result);
|
|
10480
|
-
for (const portId of activated.ports) {
|
|
10481
|
-
portValues.set(`${node.id}:${portId}`, activated.value);
|
|
10482
|
-
onEvent({ type: "node-output", nodeId: node.id, portId, value: activated.value });
|
|
10483
|
-
}
|
|
10484
|
-
completed.add(node.id);
|
|
10485
|
-
onEvent({ type: "node-status", nodeId: node.id, status: "done" });
|
|
10486
|
-
} catch (e) {
|
|
10487
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
10488
|
-
errors.push(msg);
|
|
10489
|
-
onEvent({ type: "node-status", nodeId: node.id, status: "error", text: msg });
|
|
10490
|
-
onEvent({ type: "log", nodeId: node.id, level: "error", message: msg });
|
|
10491
|
-
break;
|
|
10492
|
-
}
|
|
10493
|
-
}
|
|
10494
|
-
} finally {
|
|
10495
|
-
if (timer2) clearTimeout(timer2);
|
|
10496
|
-
}
|
|
10497
|
-
const ok = errors.length === 0;
|
|
10498
|
-
onEvent({ type: "run-end", ok });
|
|
10499
|
-
return ok ? { ok, outputs } : { ok, outputs, error: errors[0] };
|
|
10500
|
-
}
|
|
10501
|
-
function indexIncoming(edges) {
|
|
10502
|
-
const map = /* @__PURE__ */ new Map();
|
|
10503
|
-
for (const e of edges) {
|
|
10504
|
-
const list = map.get(e.target) ?? [];
|
|
10505
|
-
list.push(e);
|
|
10506
|
-
map.set(e.target, list);
|
|
10507
|
-
}
|
|
10508
|
-
return map;
|
|
10509
|
-
}
|
|
10510
|
-
function topoSort(graph) {
|
|
10511
|
-
const inDegree = /* @__PURE__ */ new Map();
|
|
10512
|
-
for (const n of graph.nodes) inDegree.set(n.id, 0);
|
|
10513
|
-
for (const e of graph.edges) inDegree.set(e.target, (inDegree.get(e.target) ?? 0) + 1);
|
|
10514
|
-
const queue = [];
|
|
10515
|
-
for (const [id2, d] of inDegree) if (d === 0) queue.push(id2);
|
|
10516
|
-
const ordered = [];
|
|
10517
|
-
while (queue.length) {
|
|
10518
|
-
const id2 = queue.shift();
|
|
10519
|
-
ordered.push(id2);
|
|
10520
|
-
for (const e of graph.edges) {
|
|
10521
|
-
if (e.source !== id2) continue;
|
|
10522
|
-
const next = (inDegree.get(e.target) ?? 0) - 1;
|
|
10523
|
-
inDegree.set(e.target, next);
|
|
10524
|
-
if (next === 0) queue.push(e.target);
|
|
10525
|
-
}
|
|
10526
|
-
}
|
|
10527
|
-
if (ordered.length !== graph.nodes.length) return null;
|
|
10528
|
-
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
10529
|
-
return ordered.map((id2) => byId.get(id2)).filter(Boolean);
|
|
10530
|
-
}
|
|
10531
|
-
function collectInputs(node, incoming, portValues, initial) {
|
|
10532
|
-
const inputs = { ...initial[node.id] ?? {} };
|
|
10533
|
-
for (const e of incoming) {
|
|
10534
|
-
const portId = e.targetHandle ?? "in";
|
|
10535
|
-
const val = portValues.get(`${e.source}:${e.sourceHandle ?? "out"}`);
|
|
10536
|
-
inputs[portId] = val;
|
|
10537
|
-
}
|
|
10538
|
-
return inputs;
|
|
10539
|
-
}
|
|
10540
|
-
function pickExecutor(executors, node) {
|
|
10541
|
-
if (executors[node.id]) return executors[node.id];
|
|
10542
|
-
if (node.type && executors[node.type]) return executors[node.type];
|
|
10543
|
-
return executors["*"];
|
|
10544
|
-
}
|
|
10545
|
-
function activatedPorts(node, result) {
|
|
10546
|
-
if (result && typeof result === "object") {
|
|
10547
|
-
const r = result;
|
|
10548
|
-
if (typeof r.__port === "string") {
|
|
10549
|
-
return { ports: [r.__port], value: r.value };
|
|
10550
|
-
}
|
|
10551
|
-
if (typeof r.branch === "string") {
|
|
10552
|
-
return { ports: [r.branch], value: r.value ?? r };
|
|
10553
|
-
}
|
|
10554
|
-
}
|
|
10555
|
-
const kind = getNodeKind(node.data?.kind ?? node.type ?? "") ?? void 0;
|
|
10556
|
-
const declared = resolveNodePorts(node, kind).outputs?.map((p) => p.id);
|
|
10557
|
-
return { ports: declared?.length ? declared : ["out"], value: result };
|
|
10558
|
-
}
|
|
10559
|
-
|
|
10560
|
-
// src/runtime/use-flow-run.ts
|
|
10561
10786
|
function useFlowRun({ maxFeed = 200 } = {}) {
|
|
10562
10787
|
const [statuses, setStatuses] = ReactExports.useState({});
|
|
10563
10788
|
const [statusText, setStatusText] = ReactExports.useState({});
|
|
@@ -10721,12 +10946,13 @@ function importWorkflow(schema, options = {}) {
|
|
|
10721
10946
|
issues.push({ level: "warning", nodeId: n.id, message: `${iss.key}: ${iss.message}` });
|
|
10722
10947
|
}
|
|
10723
10948
|
}
|
|
10949
|
+
const kindId = kind?.name ?? n.kind;
|
|
10724
10950
|
return {
|
|
10725
10951
|
id: n.id,
|
|
10726
|
-
type:
|
|
10952
|
+
type: kindId,
|
|
10727
10953
|
position: { x: n.position?.x ?? 0, y: n.position?.y ?? 0 },
|
|
10728
10954
|
data: {
|
|
10729
|
-
kind:
|
|
10955
|
+
kind: kindId,
|
|
10730
10956
|
label: n.label ?? kind?.label ?? n.kind,
|
|
10731
10957
|
description: n.description,
|
|
10732
10958
|
config,
|
|
@@ -10879,7 +11105,9 @@ function previewValue(v) {
|
|
|
10879
11105
|
// src/registry/index.ts
|
|
10880
11106
|
function buildNodeTypes() {
|
|
10881
11107
|
const map = {};
|
|
10882
|
-
for (const k of listNodeKinds())
|
|
11108
|
+
for (const k of listNodeKinds()) {
|
|
11109
|
+
for (const id2 of kindIds(k)) map[id2] = RegistryNode;
|
|
11110
|
+
}
|
|
10883
11111
|
return map;
|
|
10884
11112
|
}
|
|
10885
11113
|
|