@particle-academy/fancy-flow 0.2.2
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 +106 -0
- package/dist/chunk-BCXECQUC.js +110 -0
- package/dist/chunk-BCXECQUC.js.map +1 -0
- package/dist/chunk-JNYYJHNJ.js +247 -0
- package/dist/chunk-JNYYJHNJ.js.map +1 -0
- package/dist/chunk-WNVBXXOL.js +101 -0
- package/dist/chunk-WNVBXXOL.js.map +1 -0
- package/dist/chunk-XZEUFVJ2.js +507 -0
- package/dist/chunk-XZEUFVJ2.js.map +1 -0
- package/dist/index.cjs +1677 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +226 -0
- package/dist/index.d.ts +226 -0
- package/dist/index.js +705 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/index.d.cts +170 -0
- package/dist/registry/index.d.ts +170 -0
- package/dist/registry.cjs +615 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.js +4 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/index.d.cts +93 -0
- package/dist/runtime/index.d.ts +93 -0
- package/dist/runtime.cjs +252 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.js +3 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schema/index.d.cts +81 -0
- package/dist/schema/index.d.ts +81 -0
- package/dist/schema.cjs +170 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.js +4 -0
- package/dist/schema.js.map +1 -0
- package/dist/styles.css +690 -0
- package/dist/styles.css.map +1 -0
- package/dist/types-TemTtb04.d.cts +103 -0
- package/dist/types-TemTtb04.d.ts +103 -0
- package/package.json +55 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var react$1 = require('@xyflow/react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/registry/registry.ts
|
|
8
|
+
var kinds = /* @__PURE__ */ new Map();
|
|
9
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
10
|
+
function registerNodeKind(definition) {
|
|
11
|
+
kinds.set(definition.name, definition);
|
|
12
|
+
notify();
|
|
13
|
+
return () => {
|
|
14
|
+
if (kinds.get(definition.name) === definition) {
|
|
15
|
+
kinds.delete(definition.name);
|
|
16
|
+
notify();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function getNodeKind(name) {
|
|
21
|
+
return kinds.get(name) ?? null;
|
|
22
|
+
}
|
|
23
|
+
function listNodeKinds(category) {
|
|
24
|
+
const all = Array.from(kinds.values());
|
|
25
|
+
return category ? all.filter((k) => k.category === category) : all;
|
|
26
|
+
}
|
|
27
|
+
function onNodeKindsChanged(listener) {
|
|
28
|
+
listeners.add(listener);
|
|
29
|
+
return () => listeners.delete(listener);
|
|
30
|
+
}
|
|
31
|
+
function notify() {
|
|
32
|
+
for (const l of listeners) l();
|
|
33
|
+
}
|
|
34
|
+
function defaultConfigFor(kind) {
|
|
35
|
+
const fromKind = kind.defaultConfig ? { ...kind.defaultConfig } : {};
|
|
36
|
+
for (const field of kind.configSchema ?? []) {
|
|
37
|
+
if (fromKind[field.key] !== void 0) continue;
|
|
38
|
+
if ("default" in field && field.default !== void 0) {
|
|
39
|
+
fromKind[field.key] = field.default;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return fromKind;
|
|
43
|
+
}
|
|
44
|
+
function validateConfig(kind, config) {
|
|
45
|
+
const issues = [];
|
|
46
|
+
for (const field of kind.configSchema ?? []) {
|
|
47
|
+
const value = config[field.key];
|
|
48
|
+
if (field.required && (value === void 0 || value === null || value === "")) {
|
|
49
|
+
issues.push({ key: field.key, message: `${field.label} is required` });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (value === void 0 || value === null) continue;
|
|
53
|
+
const issue = validateField(field, value);
|
|
54
|
+
if (issue) issues.push({ key: field.key, message: issue });
|
|
55
|
+
}
|
|
56
|
+
return issues;
|
|
57
|
+
}
|
|
58
|
+
function validateField(field, value) {
|
|
59
|
+
switch (field.type) {
|
|
60
|
+
case "text":
|
|
61
|
+
case "textarea":
|
|
62
|
+
case "expression":
|
|
63
|
+
case "credential":
|
|
64
|
+
return typeof value === "string" ? null : `${field.label} must be a string`;
|
|
65
|
+
case "number": {
|
|
66
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return `${field.label} must be a number`;
|
|
67
|
+
if (field.min !== void 0 && value < field.min) return `${field.label} must be >= ${field.min}`;
|
|
68
|
+
if (field.max !== void 0 && value > field.max) return `${field.label} must be <= ${field.max}`;
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
case "switch":
|
|
72
|
+
return typeof value === "boolean" ? null : `${field.label} must be a boolean`;
|
|
73
|
+
case "select": {
|
|
74
|
+
const allowed = field.options.map((o) => o.value);
|
|
75
|
+
return allowed.includes(String(value)) ? null : `${field.label} must be one of ${allowed.join(", ")}`;
|
|
76
|
+
}
|
|
77
|
+
case "json":
|
|
78
|
+
return null;
|
|
79
|
+
// permissive — just JSON-shaped
|
|
80
|
+
default:
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function categoryAccent(category) {
|
|
85
|
+
switch (category) {
|
|
86
|
+
case "trigger":
|
|
87
|
+
return "#10b981";
|
|
88
|
+
case "logic":
|
|
89
|
+
return "#f59e0b";
|
|
90
|
+
case "data":
|
|
91
|
+
return "#0ea5e9";
|
|
92
|
+
case "ai":
|
|
93
|
+
return "#8b5cf6";
|
|
94
|
+
case "io":
|
|
95
|
+
return "#3b82f6";
|
|
96
|
+
case "human":
|
|
97
|
+
return "#ec4899";
|
|
98
|
+
case "output":
|
|
99
|
+
return "#a855f7";
|
|
100
|
+
default:
|
|
101
|
+
return "#71717a";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function RegistryNodeInner(props) {
|
|
105
|
+
const kindName = props.data.kind ?? props.type;
|
|
106
|
+
const kind = react.useMemo(() => getNodeKind(kindName), [kindName]);
|
|
107
|
+
if (!kind) {
|
|
108
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-node ff-node--unknown", children: [
|
|
109
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-node__header", style: { background: "#71717a" }, children: [
|
|
110
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__tag", children: "UNKNOWN" }),
|
|
111
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__label", children: kindName })
|
|
112
|
+
] }),
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "ff-node__desc", children: [
|
|
114
|
+
'No registered kind for "',
|
|
115
|
+
kindName,
|
|
116
|
+
'".'
|
|
117
|
+
] })
|
|
118
|
+
] });
|
|
119
|
+
}
|
|
120
|
+
const data = props.data;
|
|
121
|
+
const status = data.status ?? "idle";
|
|
122
|
+
const accent = kind.accent ?? categoryAccent(kind.category);
|
|
123
|
+
const inputs = data.inputs ?? kind.inputs ?? defaultInputs(kind.category);
|
|
124
|
+
const outputs = data.outputs ?? kind.outputs ?? defaultOutputs(kind.category);
|
|
125
|
+
const config = data.config ?? {};
|
|
126
|
+
const label = data.label ?? kind.label;
|
|
127
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
className: [
|
|
131
|
+
"ff-node",
|
|
132
|
+
`ff-node--status-${status}`,
|
|
133
|
+
`ff-node--cat-${kind.category}`,
|
|
134
|
+
props.selected ? "ff-node--selected" : ""
|
|
135
|
+
].filter(Boolean).join(" "),
|
|
136
|
+
style: { borderColor: props.selected ? accent : void 0 },
|
|
137
|
+
children: [
|
|
138
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "ff-node__header", style: { background: accent }, children: [
|
|
139
|
+
kind.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__icon", "aria-hidden": true, children: kind.icon }),
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__tag", children: kind.label.toUpperCase() }),
|
|
141
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__label", children: label }),
|
|
142
|
+
status !== "idle" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `ff-node__dot ff-node__dot--${status}`, "aria-label": `status ${status}` })
|
|
143
|
+
] }),
|
|
144
|
+
data.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-node__desc", children: data.description }),
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-node__body", children: kind.renderBody ? kind.renderBody({ nodeId: props.id, config, selected: props.selected ?? false }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultBody, { config, kind: kind.configSchema }) }),
|
|
146
|
+
data.statusText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-node__status-text", children: data.statusText }),
|
|
147
|
+
inputs.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
148
|
+
react$1.Handle,
|
|
149
|
+
{
|
|
150
|
+
type: "target",
|
|
151
|
+
position: react$1.Position.Left,
|
|
152
|
+
id: p.id,
|
|
153
|
+
style: portStyle(i, inputs.length),
|
|
154
|
+
title: p.label ?? p.id
|
|
155
|
+
},
|
|
156
|
+
p.id
|
|
157
|
+
)),
|
|
158
|
+
outputs.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
159
|
+
react$1.Handle,
|
|
160
|
+
{
|
|
161
|
+
type: "source",
|
|
162
|
+
position: react$1.Position.Right,
|
|
163
|
+
id: p.id,
|
|
164
|
+
style: portStyle(i, outputs.length),
|
|
165
|
+
title: p.label ?? p.id
|
|
166
|
+
},
|
|
167
|
+
p.id
|
|
168
|
+
))
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
var RegistryNode = react.memo(RegistryNodeInner);
|
|
174
|
+
function defaultInputs(category) {
|
|
175
|
+
return category === "trigger" ? [] : [{ id: "in" }];
|
|
176
|
+
}
|
|
177
|
+
function defaultOutputs(category) {
|
|
178
|
+
return category === "output" ? [] : [{ id: "out" }];
|
|
179
|
+
}
|
|
180
|
+
function portStyle(i, total) {
|
|
181
|
+
if (total <= 1) return {};
|
|
182
|
+
const slot = 100 / (total + 1) * (i + 1);
|
|
183
|
+
return { top: `${slot}%` };
|
|
184
|
+
}
|
|
185
|
+
function DefaultBody({ config, kind }) {
|
|
186
|
+
const fields = kind ?? [];
|
|
187
|
+
const visible = fields.map((f) => ({ field: f, value: config[f.key] })).filter(({ value }) => value !== void 0 && value !== "" && value !== null);
|
|
188
|
+
if (visible.length === 0) {
|
|
189
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-node__body-empty", children: "\u2014 configure in the panel" });
|
|
190
|
+
}
|
|
191
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "ff-node__summary", children: [
|
|
192
|
+
visible.slice(0, 4).map(({ field, value }) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
|
|
193
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ff-node__summary-key", children: [
|
|
194
|
+
field.label,
|
|
195
|
+
":"
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__summary-value", children: previewValue(value) })
|
|
198
|
+
] }, field.key)),
|
|
199
|
+
visible.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "ff-node__summary-more", children: [
|
|
200
|
+
"+ ",
|
|
201
|
+
visible.length - 4,
|
|
202
|
+
" more"
|
|
203
|
+
] })
|
|
204
|
+
] });
|
|
205
|
+
}
|
|
206
|
+
function previewValue(v) {
|
|
207
|
+
if (typeof v === "string") return v.length > 30 ? v.slice(0, 27) + "\u2026" : v;
|
|
208
|
+
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
209
|
+
try {
|
|
210
|
+
const j = JSON.stringify(v);
|
|
211
|
+
return j.length > 30 ? j.slice(0, 27) + "\u2026" : j;
|
|
212
|
+
} catch {
|
|
213
|
+
return "[object]";
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/registry/builtin.ts
|
|
218
|
+
var HTTP_METHODS = [
|
|
219
|
+
{ type: "select", key: "method", label: "Method", options: [
|
|
220
|
+
{ value: "GET", label: "GET" },
|
|
221
|
+
{ value: "POST", label: "POST" },
|
|
222
|
+
{ value: "PUT", label: "PUT" },
|
|
223
|
+
{ value: "PATCH", label: "PATCH" },
|
|
224
|
+
{ value: "DELETE", label: "DELETE" }
|
|
225
|
+
], default: "GET", required: true }
|
|
226
|
+
];
|
|
227
|
+
var KINDS = [
|
|
228
|
+
// ───────────── Triggers ─────────────
|
|
229
|
+
{
|
|
230
|
+
name: "manual_trigger",
|
|
231
|
+
category: "trigger",
|
|
232
|
+
label: "Manual",
|
|
233
|
+
description: "Entry point fired when the user clicks Run.",
|
|
234
|
+
icon: "\u26A1",
|
|
235
|
+
inputs: [],
|
|
236
|
+
outputs: [{ id: "out" }]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "webhook_trigger",
|
|
240
|
+
category: "trigger",
|
|
241
|
+
label: "Webhook",
|
|
242
|
+
description: "Triggered by an inbound HTTP request to a host-provided URL.",
|
|
243
|
+
icon: "\u{1F4E1}",
|
|
244
|
+
inputs: [],
|
|
245
|
+
outputs: [{ id: "out", label: "payload" }],
|
|
246
|
+
configSchema: [
|
|
247
|
+
{ type: "text", key: "path", label: "Path", placeholder: "/hooks/my-flow", required: true },
|
|
248
|
+
{ type: "select", key: "method", label: "Method", options: [
|
|
249
|
+
{ value: "POST", label: "POST" },
|
|
250
|
+
{ value: "GET", label: "GET" }
|
|
251
|
+
], default: "POST" },
|
|
252
|
+
{ type: "credential", key: "secret", label: "Verifying secret", credentialType: "webhook_secret" }
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "schedule_trigger",
|
|
257
|
+
category: "trigger",
|
|
258
|
+
label: "Schedule",
|
|
259
|
+
description: "Fires on a cron schedule (host-implemented).",
|
|
260
|
+
icon: "\u23F1",
|
|
261
|
+
inputs: [],
|
|
262
|
+
outputs: [{ id: "out" }],
|
|
263
|
+
configSchema: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
key: "cron",
|
|
267
|
+
label: "Cron",
|
|
268
|
+
placeholder: "*/5 * * * *",
|
|
269
|
+
required: true,
|
|
270
|
+
description: "Standard 5-field cron expression."
|
|
271
|
+
},
|
|
272
|
+
{ type: "text", key: "timezone", label: "Timezone", placeholder: "UTC", default: "UTC" }
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "user_input",
|
|
277
|
+
category: "human",
|
|
278
|
+
label: "User Input",
|
|
279
|
+
description: "Pause the flow until the user submits the configured form.",
|
|
280
|
+
icon: "\u270E",
|
|
281
|
+
inputs: [{ id: "in" }],
|
|
282
|
+
outputs: [{ id: "out", label: "values" }],
|
|
283
|
+
configSchema: [
|
|
284
|
+
{ type: "text", key: "title", label: "Form title", default: "Need your input" },
|
|
285
|
+
{
|
|
286
|
+
type: "json",
|
|
287
|
+
key: "fields",
|
|
288
|
+
label: "Fields (JSON)",
|
|
289
|
+
language: "json",
|
|
290
|
+
rows: 6,
|
|
291
|
+
default: [{ key: "answer", label: "Your answer", type: "textarea" }]
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
},
|
|
295
|
+
// ───────────── Logic ─────────────
|
|
296
|
+
{
|
|
297
|
+
name: "branch",
|
|
298
|
+
category: "logic",
|
|
299
|
+
label: "Branch",
|
|
300
|
+
description: "Multi-way branch on a condition or value.",
|
|
301
|
+
icon: "\u25C7",
|
|
302
|
+
inputs: [{ id: "in" }],
|
|
303
|
+
outputs: [{ id: "true", label: "true" }, { id: "false", label: "false" }],
|
|
304
|
+
configSchema: [
|
|
305
|
+
{ type: "expression", key: "condition", label: "Condition", example: "{{ $json.active }}", required: true }
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "switch_case",
|
|
310
|
+
category: "logic",
|
|
311
|
+
label: "Switch",
|
|
312
|
+
description: "Route to one of N labelled outputs based on a key.",
|
|
313
|
+
icon: "\u2933",
|
|
314
|
+
inputs: [{ id: "in" }],
|
|
315
|
+
outputs: [{ id: "case_a", label: "a" }, { id: "case_b", label: "b" }, { id: "default", label: "default" }],
|
|
316
|
+
configSchema: [
|
|
317
|
+
{ type: "expression", key: "value", label: "Switch on", example: "{{ $json.kind }}", required: true },
|
|
318
|
+
{ type: "json", key: "cases", label: "Cases (JSON)", default: { a: "case_a", b: "case_b" } }
|
|
319
|
+
]
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: "for_each",
|
|
323
|
+
category: "logic",
|
|
324
|
+
label: "For Each",
|
|
325
|
+
description: "Iterate over a list, emitting each item on `item`.",
|
|
326
|
+
icon: "\u21BB",
|
|
327
|
+
inputs: [{ id: "in" }],
|
|
328
|
+
outputs: [{ id: "item", label: "item" }, { id: "done", label: "done" }],
|
|
329
|
+
configSchema: [
|
|
330
|
+
{ type: "expression", key: "source", label: "List", example: "{{ $json.users }}", required: true },
|
|
331
|
+
{ type: "number", key: "concurrency", label: "Concurrency", default: 1, min: 1, max: 50 }
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "merge",
|
|
336
|
+
category: "logic",
|
|
337
|
+
label: "Merge",
|
|
338
|
+
description: "Combine multiple inputs into one object or array.",
|
|
339
|
+
icon: "\u2295",
|
|
340
|
+
inputs: [{ id: "a" }, { id: "b" }],
|
|
341
|
+
outputs: [{ id: "out" }],
|
|
342
|
+
configSchema: [
|
|
343
|
+
{
|
|
344
|
+
type: "select",
|
|
345
|
+
key: "mode",
|
|
346
|
+
label: "Mode",
|
|
347
|
+
default: "merge",
|
|
348
|
+
options: [{ value: "merge", label: "Object merge" }, { value: "concat", label: "Array concat" }]
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: "wait",
|
|
354
|
+
category: "logic",
|
|
355
|
+
label: "Wait",
|
|
356
|
+
description: "Sleep or wait for an external event.",
|
|
357
|
+
icon: "\u23F8",
|
|
358
|
+
configSchema: [
|
|
359
|
+
{
|
|
360
|
+
type: "select",
|
|
361
|
+
key: "mode",
|
|
362
|
+
label: "Mode",
|
|
363
|
+
default: "duration",
|
|
364
|
+
options: [{ value: "duration", label: "Duration" }, { value: "until", label: "Until timestamp" }, { value: "event", label: "External event" }]
|
|
365
|
+
},
|
|
366
|
+
{ type: "text", key: "duration", label: "Duration", placeholder: "5s, 10m, 1h", description: "Used when mode = duration." }
|
|
367
|
+
]
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "transform",
|
|
371
|
+
category: "logic",
|
|
372
|
+
label: "Transform",
|
|
373
|
+
description: "Reshape data with an expression.",
|
|
374
|
+
icon: "\u0192",
|
|
375
|
+
configSchema: [
|
|
376
|
+
{
|
|
377
|
+
type: "expression",
|
|
378
|
+
key: "expression",
|
|
379
|
+
label: "Expression",
|
|
380
|
+
example: "{{ { id: $json.id, name: $json.first + ' ' + $json.last } }}",
|
|
381
|
+
required: true
|
|
382
|
+
}
|
|
383
|
+
]
|
|
384
|
+
},
|
|
385
|
+
// ───────────── Data ─────────────
|
|
386
|
+
{
|
|
387
|
+
name: "memory_store",
|
|
388
|
+
category: "data",
|
|
389
|
+
label: "Memory Store",
|
|
390
|
+
description: "Read or write per-conversation memory.",
|
|
391
|
+
icon: "\u{1F9E0}",
|
|
392
|
+
configSchema: [
|
|
393
|
+
{
|
|
394
|
+
type: "select",
|
|
395
|
+
key: "operation",
|
|
396
|
+
label: "Operation",
|
|
397
|
+
required: true,
|
|
398
|
+
default: "read",
|
|
399
|
+
options: [{ value: "read", label: "Read" }, { value: "write", label: "Write" }, { value: "append", label: "Append" }]
|
|
400
|
+
},
|
|
401
|
+
{ type: "text", key: "key", label: "Key", placeholder: "user.preferences", required: true },
|
|
402
|
+
{ type: "expression", key: "value", label: "Value (write/append only)", example: "{{ $json }}" },
|
|
403
|
+
{ type: "credential", key: "store", label: "Memory store", credentialType: "memory_store" }
|
|
404
|
+
]
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "data_store",
|
|
408
|
+
category: "data",
|
|
409
|
+
label: "Data Store",
|
|
410
|
+
description: "Key-value or table read/write against a host store.",
|
|
411
|
+
icon: "\u{1F5C3}",
|
|
412
|
+
configSchema: [
|
|
413
|
+
{
|
|
414
|
+
type: "select",
|
|
415
|
+
key: "operation",
|
|
416
|
+
label: "Operation",
|
|
417
|
+
required: true,
|
|
418
|
+
default: "get",
|
|
419
|
+
options: [
|
|
420
|
+
{ value: "get", label: "Get" },
|
|
421
|
+
{ value: "set", label: "Set" },
|
|
422
|
+
{ value: "delete", label: "Delete" },
|
|
423
|
+
{ value: "query", label: "Query" },
|
|
424
|
+
{ value: "list", label: "List" }
|
|
425
|
+
]
|
|
426
|
+
},
|
|
427
|
+
{ type: "text", key: "table", label: "Table / collection", required: true },
|
|
428
|
+
{ type: "text", key: "key", label: "Key" },
|
|
429
|
+
{ type: "json", key: "where", label: "Where (JSON)", description: "For query/list operations." },
|
|
430
|
+
{ type: "expression", key: "value", label: "Value (set only)", example: "{{ $json }}" },
|
|
431
|
+
{ type: "credential", key: "store", label: "Data store", credentialType: "data_store" }
|
|
432
|
+
]
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "variable",
|
|
436
|
+
category: "data",
|
|
437
|
+
label: "Variable",
|
|
438
|
+
description: "Workflow-scoped value used by other nodes.",
|
|
439
|
+
icon: "\u{1D4CD}",
|
|
440
|
+
configSchema: [
|
|
441
|
+
{ type: "text", key: "name", label: "Name", required: true },
|
|
442
|
+
{ type: "expression", key: "value", label: "Value", required: true }
|
|
443
|
+
]
|
|
444
|
+
},
|
|
445
|
+
// ───────────── AI ─────────────
|
|
446
|
+
{
|
|
447
|
+
name: "llm_call",
|
|
448
|
+
category: "ai",
|
|
449
|
+
label: "LLM Call",
|
|
450
|
+
description: "Send a prompt + context to a model and receive a response.",
|
|
451
|
+
icon: "\u2726",
|
|
452
|
+
configSchema: [
|
|
453
|
+
{
|
|
454
|
+
type: "select",
|
|
455
|
+
key: "provider",
|
|
456
|
+
label: "Provider",
|
|
457
|
+
default: "anthropic",
|
|
458
|
+
options: [
|
|
459
|
+
{ value: "anthropic", label: "Anthropic" },
|
|
460
|
+
{ value: "openai", label: "OpenAI" },
|
|
461
|
+
{ value: "custom", label: "Custom" }
|
|
462
|
+
]
|
|
463
|
+
},
|
|
464
|
+
{ type: "text", key: "model", label: "Model", placeholder: "claude-sonnet-4-5", required: true },
|
|
465
|
+
{ type: "textarea", key: "system", label: "System prompt", rows: 4 },
|
|
466
|
+
{ type: "expression", key: "prompt", label: "User prompt", example: "{{ $json.question }}", required: true },
|
|
467
|
+
{ type: "number", key: "temperature", label: "Temperature", min: 0, max: 2, step: 0.1, default: 0.7 },
|
|
468
|
+
{ type: "number", key: "max_tokens", label: "Max tokens", min: 1, max: 8192, default: 1024 },
|
|
469
|
+
{ type: "json", key: "tools", label: "Tools (JSON)", description: "Optional Anthropic-style tool definitions." },
|
|
470
|
+
{ type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
|
|
471
|
+
]
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: "tool_use",
|
|
475
|
+
category: "ai",
|
|
476
|
+
label: "Tool Use",
|
|
477
|
+
description: "Hand control to a host-registered tool by name.",
|
|
478
|
+
icon: "\u{1F6E0}",
|
|
479
|
+
configSchema: [
|
|
480
|
+
{ type: "text", key: "tool", label: "Tool name", placeholder: "search_index", required: true },
|
|
481
|
+
{ type: "expression", key: "args", label: "Arguments", example: "{{ { query: $json.q } }}" }
|
|
482
|
+
]
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: "embed_search",
|
|
486
|
+
category: "ai",
|
|
487
|
+
label: "Embed & Search",
|
|
488
|
+
description: "Embed a query and search a vector store.",
|
|
489
|
+
icon: "\u273A",
|
|
490
|
+
configSchema: [
|
|
491
|
+
{ type: "expression", key: "query", label: "Query", required: true, example: "{{ $json.question }}" },
|
|
492
|
+
{ type: "number", key: "topK", label: "Top K", default: 5, min: 1, max: 50 },
|
|
493
|
+
{ type: "credential", key: "vectorStore", label: "Vector store", credentialType: "vector_store" }
|
|
494
|
+
]
|
|
495
|
+
},
|
|
496
|
+
// ───────────── IO ─────────────
|
|
497
|
+
{
|
|
498
|
+
name: "api_request",
|
|
499
|
+
category: "io",
|
|
500
|
+
label: "API Request",
|
|
501
|
+
description: "HTTP request to any URL.",
|
|
502
|
+
icon: "\u2194",
|
|
503
|
+
configSchema: [
|
|
504
|
+
...HTTP_METHODS,
|
|
505
|
+
{ type: "text", key: "url", label: "URL", placeholder: "https://api.example.com/...", required: true },
|
|
506
|
+
{ type: "json", key: "headers", label: "Headers", default: { "content-type": "application/json" } },
|
|
507
|
+
{ type: "json", key: "body", label: "Body" },
|
|
508
|
+
{ type: "credential", key: "auth", label: "Auth", credentialType: "api_credential" }
|
|
509
|
+
]
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: "webhook_out",
|
|
513
|
+
category: "io",
|
|
514
|
+
label: "Send Webhook",
|
|
515
|
+
description: "POST a payload to a configured URL.",
|
|
516
|
+
icon: "\u2197",
|
|
517
|
+
configSchema: [
|
|
518
|
+
{ type: "text", key: "url", label: "URL", required: true },
|
|
519
|
+
{ type: "json", key: "headers", label: "Headers" },
|
|
520
|
+
{ type: "expression", key: "payload", label: "Payload", required: true, example: "{{ $json }}" }
|
|
521
|
+
]
|
|
522
|
+
},
|
|
523
|
+
// ───────────── Human ─────────────
|
|
524
|
+
{
|
|
525
|
+
name: "human_approval",
|
|
526
|
+
category: "human",
|
|
527
|
+
label: "Human Approval",
|
|
528
|
+
description: "Pause until a human approves or denies.",
|
|
529
|
+
icon: "\u2713",
|
|
530
|
+
inputs: [{ id: "in" }],
|
|
531
|
+
outputs: [{ id: "approved", label: "approved" }, { id: "denied", label: "denied" }],
|
|
532
|
+
configSchema: [
|
|
533
|
+
{ type: "text", key: "title", label: "Approval title", default: "Approve action" },
|
|
534
|
+
{ type: "textarea", key: "description", label: "Description for approver", rows: 3 },
|
|
535
|
+
{ type: "credential", key: "channel", label: "Notify channel", credentialType: "notify_channel" }
|
|
536
|
+
]
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: "notify",
|
|
540
|
+
category: "human",
|
|
541
|
+
label: "Notify",
|
|
542
|
+
description: "Send a message via Slack / email / SMS / etc.",
|
|
543
|
+
icon: "\u{1F514}",
|
|
544
|
+
configSchema: [
|
|
545
|
+
{
|
|
546
|
+
type: "select",
|
|
547
|
+
key: "channel",
|
|
548
|
+
label: "Channel",
|
|
549
|
+
default: "slack",
|
|
550
|
+
options: [
|
|
551
|
+
{ value: "slack", label: "Slack" },
|
|
552
|
+
{ value: "email", label: "Email" },
|
|
553
|
+
{ value: "sms", label: "SMS" },
|
|
554
|
+
{ value: "discord", label: "Discord" }
|
|
555
|
+
]
|
|
556
|
+
},
|
|
557
|
+
{ type: "text", key: "to", label: "To", required: true },
|
|
558
|
+
{ type: "expression", key: "message", label: "Message", required: true, example: "{{ $json.summary }}" }
|
|
559
|
+
]
|
|
560
|
+
},
|
|
561
|
+
// ───────────── Output ─────────────
|
|
562
|
+
{
|
|
563
|
+
name: "output",
|
|
564
|
+
category: "output",
|
|
565
|
+
label: "Output",
|
|
566
|
+
description: "Terminal node \u2014 captures the workflow's result.",
|
|
567
|
+
icon: "\u25CF",
|
|
568
|
+
inputs: [{ id: "in" }],
|
|
569
|
+
outputs: []
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "log",
|
|
573
|
+
category: "output",
|
|
574
|
+
label: "Log",
|
|
575
|
+
description: "Send to the run feed.",
|
|
576
|
+
icon: "\u2261",
|
|
577
|
+
inputs: [{ id: "in" }],
|
|
578
|
+
outputs: [],
|
|
579
|
+
configSchema: [
|
|
580
|
+
{
|
|
581
|
+
type: "select",
|
|
582
|
+
key: "level",
|
|
583
|
+
label: "Level",
|
|
584
|
+
default: "info",
|
|
585
|
+
options: [{ value: "info", label: "info" }, { value: "warn", label: "warn" }, { value: "error", label: "error" }]
|
|
586
|
+
},
|
|
587
|
+
{ type: "expression", key: "message", label: "Message", required: true, example: "{{ $json }}" }
|
|
588
|
+
]
|
|
589
|
+
}
|
|
590
|
+
];
|
|
591
|
+
function registerBuiltinKinds() {
|
|
592
|
+
for (const k of KINDS) registerNodeKind(k);
|
|
593
|
+
}
|
|
594
|
+
var BUILTIN_KINDS = KINDS;
|
|
595
|
+
|
|
596
|
+
// src/registry/index.ts
|
|
597
|
+
function buildNodeTypes() {
|
|
598
|
+
const map = {};
|
|
599
|
+
for (const k of listNodeKinds()) map[k.name] = RegistryNode;
|
|
600
|
+
return map;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
exports.BUILTIN_KINDS = BUILTIN_KINDS;
|
|
604
|
+
exports.RegistryNode = RegistryNode;
|
|
605
|
+
exports.buildNodeTypes = buildNodeTypes;
|
|
606
|
+
exports.categoryAccent = categoryAccent;
|
|
607
|
+
exports.defaultConfigFor = defaultConfigFor;
|
|
608
|
+
exports.getNodeKind = getNodeKind;
|
|
609
|
+
exports.listNodeKinds = listNodeKinds;
|
|
610
|
+
exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
611
|
+
exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
612
|
+
exports.registerNodeKind = registerNodeKind;
|
|
613
|
+
exports.validateConfig = validateConfig;
|
|
614
|
+
//# sourceMappingURL=registry.cjs.map
|
|
615
|
+
//# sourceMappingURL=registry.cjs.map
|