@d34dman/flowdrop 0.0.46 → 0.0.47
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/components/App.svelte +6 -0
- package/dist/components/ConfigForm.svelte +52 -2
- package/dist/components/ConfigForm.svelte.d.ts +11 -1
- package/dist/components/SettingsPanel.svelte +5 -2
- package/dist/components/WorkflowEditor.svelte +26 -0
- package/dist/components/WorkflowEditor.svelte.d.ts +1 -0
- package/dist/components/form/FormField.svelte +13 -11
- package/dist/components/form/FormFieldLight.svelte +16 -12
- package/dist/components/form/FormTemplateEditor.svelte +124 -10
- package/dist/components/form/FormTemplateEditor.svelte.d.ts +17 -1
- package/dist/components/form/index.d.ts +1 -0
- package/dist/components/form/index.js +2 -0
- package/dist/components/form/templateAutocomplete.d.ts +38 -0
- package/dist/components/form/templateAutocomplete.js +309 -0
- package/dist/components/form/types.d.ts +34 -2
- package/dist/registry/nodeComponentRegistry.d.ts +9 -9
- package/dist/registry/nodeComponentRegistry.js +10 -10
- package/dist/services/variableService.d.ts +100 -0
- package/dist/services/variableService.js +367 -0
- package/dist/types/index.d.ts +120 -0
- package/dist/utils/nodeTypes.d.ts +15 -10
- package/dist/utils/nodeTypes.js +24 -22
- package/package.json +2 -2
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable Service
|
|
3
|
+
* Derives available template variables from connected upstream nodes' output schemas.
|
|
4
|
+
* Used by the template editor for autocomplete suggestions.
|
|
5
|
+
*
|
|
6
|
+
* @module services/variableService
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Converts a JSON Schema property type to a TemplateVariableType.
|
|
10
|
+
*
|
|
11
|
+
* @param schemaType - The type from JSON Schema
|
|
12
|
+
* @returns The corresponding TemplateVariableType
|
|
13
|
+
*/
|
|
14
|
+
function toTemplateVariableType(schemaType) {
|
|
15
|
+
switch (schemaType) {
|
|
16
|
+
case "string":
|
|
17
|
+
return "string";
|
|
18
|
+
case "number":
|
|
19
|
+
return "number";
|
|
20
|
+
case "integer":
|
|
21
|
+
return "integer";
|
|
22
|
+
case "boolean":
|
|
23
|
+
return "boolean";
|
|
24
|
+
case "array":
|
|
25
|
+
return "array";
|
|
26
|
+
case "object":
|
|
27
|
+
return "object";
|
|
28
|
+
case "float":
|
|
29
|
+
return "float";
|
|
30
|
+
default:
|
|
31
|
+
return "mixed";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Converts a JSON Schema property to a TemplateVariable.
|
|
36
|
+
* Recursively processes nested objects and arrays.
|
|
37
|
+
*
|
|
38
|
+
* @param name - The property name
|
|
39
|
+
* @param property - The JSON Schema property definition
|
|
40
|
+
* @param sourcePort - Optional source port ID
|
|
41
|
+
* @param sourceNode - Optional source node ID
|
|
42
|
+
* @returns A TemplateVariable representing the property
|
|
43
|
+
*/
|
|
44
|
+
function propertyToTemplateVariable(name, property, sourcePort, sourceNode) {
|
|
45
|
+
const variable = {
|
|
46
|
+
name,
|
|
47
|
+
label: property.title ?? name,
|
|
48
|
+
description: property.description,
|
|
49
|
+
type: toTemplateVariableType(property.type),
|
|
50
|
+
sourcePort,
|
|
51
|
+
sourceNode
|
|
52
|
+
};
|
|
53
|
+
// Handle nested object properties
|
|
54
|
+
if (property.type === "object" && property.properties) {
|
|
55
|
+
variable.properties = {};
|
|
56
|
+
for (const [propName, propValue] of Object.entries(property.properties)) {
|
|
57
|
+
variable.properties[propName] = propertyToTemplateVariable(propName, propValue, sourcePort, sourceNode);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Handle array items
|
|
61
|
+
if (property.type === "array" && property.items) {
|
|
62
|
+
variable.items = propertyToTemplateVariable("item", property.items, sourcePort, sourceNode);
|
|
63
|
+
}
|
|
64
|
+
return variable;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates a TemplateVariable from a NodePort.
|
|
68
|
+
* Uses the port's schema if available, otherwise creates a basic variable.
|
|
69
|
+
*
|
|
70
|
+
* @param port - The output port to convert
|
|
71
|
+
* @param sourceNode - The source node ID
|
|
72
|
+
* @returns A TemplateVariable representing the port's data
|
|
73
|
+
*/
|
|
74
|
+
function portToTemplateVariable(port, sourceNode) {
|
|
75
|
+
// If the port has a schema, use it to build a detailed variable
|
|
76
|
+
if (port.schema && port.schema.properties) {
|
|
77
|
+
const variable = {
|
|
78
|
+
name: port.id,
|
|
79
|
+
label: port.name,
|
|
80
|
+
description: port.description,
|
|
81
|
+
type: "object",
|
|
82
|
+
sourcePort: port.id,
|
|
83
|
+
sourceNode,
|
|
84
|
+
properties: {}
|
|
85
|
+
};
|
|
86
|
+
for (const [propName, propValue] of Object.entries(port.schema.properties)) {
|
|
87
|
+
variable.properties[propName] = propertyToTemplateVariable(propName, propValue, port.id, sourceNode);
|
|
88
|
+
}
|
|
89
|
+
return variable;
|
|
90
|
+
}
|
|
91
|
+
// Otherwise, create a basic variable based on dataType
|
|
92
|
+
return {
|
|
93
|
+
name: port.id,
|
|
94
|
+
label: port.name,
|
|
95
|
+
description: port.description,
|
|
96
|
+
type: toTemplateVariableType(port.dataType),
|
|
97
|
+
sourcePort: port.id,
|
|
98
|
+
sourceNode
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Extracts the port ID from a handle ID.
|
|
103
|
+
* Handle IDs follow the format: {nodeId}-{input|output}-{portId}
|
|
104
|
+
*
|
|
105
|
+
* @param handleId - The handle ID (e.g., "http_request.1-output-json")
|
|
106
|
+
* @returns The port ID (e.g., "json") or the original handleId if parsing fails
|
|
107
|
+
*/
|
|
108
|
+
function extractPortIdFromHandle(handleId) {
|
|
109
|
+
if (!handleId)
|
|
110
|
+
return undefined;
|
|
111
|
+
// Handle format: {nodeId}-{input|output}-{portId}
|
|
112
|
+
// Example: "http_request.1-output-json" -> "json"
|
|
113
|
+
const outputMatch = handleId.match(/-output-(.+)$/);
|
|
114
|
+
if (outputMatch) {
|
|
115
|
+
return outputMatch[1];
|
|
116
|
+
}
|
|
117
|
+
const inputMatch = handleId.match(/-input-(.+)$/);
|
|
118
|
+
if (inputMatch) {
|
|
119
|
+
return inputMatch[1];
|
|
120
|
+
}
|
|
121
|
+
// Fallback: return the handle ID as-is (might be a simple port ID)
|
|
122
|
+
return handleId;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Finds all upstream connections to a node.
|
|
126
|
+
* Returns information about each connection including source node and ports.
|
|
127
|
+
*
|
|
128
|
+
* @param node - The node to find upstream connections for
|
|
129
|
+
* @param nodes - All nodes in the workflow
|
|
130
|
+
* @param edges - All edges in the workflow
|
|
131
|
+
* @returns Array of upstream connection information
|
|
132
|
+
*/
|
|
133
|
+
function findUpstreamConnections(node, nodes, edges) {
|
|
134
|
+
const connections = [];
|
|
135
|
+
// Find all edges that target this node
|
|
136
|
+
const incomingEdges = edges.filter((edge) => edge.target === node.id);
|
|
137
|
+
for (const edge of incomingEdges) {
|
|
138
|
+
// Find the source node
|
|
139
|
+
const sourceNode = nodes.find((n) => n.id === edge.source);
|
|
140
|
+
if (!sourceNode)
|
|
141
|
+
continue;
|
|
142
|
+
// Extract port IDs from handle IDs
|
|
143
|
+
// Handle format: {nodeId}-{input|output}-{portId}
|
|
144
|
+
const sourcePortId = extractPortIdFromHandle(edge.sourceHandle);
|
|
145
|
+
const targetPortId = extractPortIdFromHandle(edge.targetHandle);
|
|
146
|
+
// Find the source output port
|
|
147
|
+
const sourcePort = sourceNode.data.metadata.outputs.find((p) => p.id === sourcePortId);
|
|
148
|
+
// Find the target input port
|
|
149
|
+
const targetPort = node.data.metadata.inputs.find((p) => p.id === targetPortId);
|
|
150
|
+
connections.push({
|
|
151
|
+
edge,
|
|
152
|
+
sourceNode,
|
|
153
|
+
sourcePort,
|
|
154
|
+
targetPort
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return connections;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Derives available template variables from connected upstream nodes.
|
|
161
|
+
* Variables are extracted from the output schemas of nodes connected to
|
|
162
|
+
* the current node's input ports.
|
|
163
|
+
*
|
|
164
|
+
* @param node - The current node being configured
|
|
165
|
+
* @param nodes - All nodes in the workflow
|
|
166
|
+
* @param edges - All edges in the workflow
|
|
167
|
+
* @param options - Optional configuration for filtering variables
|
|
168
|
+
* @returns A VariableSchema containing all available variables
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* // Get variables from all connected ports
|
|
173
|
+
* const variableSchema = getAvailableVariables(currentNode, allNodes, allEdges);
|
|
174
|
+
*
|
|
175
|
+
* // Get variables only from specific input ports
|
|
176
|
+
* const filteredSchema = getAvailableVariables(currentNode, allNodes, allEdges, {
|
|
177
|
+
* targetPortIds: ["data", "context"]
|
|
178
|
+
* });
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function getAvailableVariables(node, nodes, edges, options) {
|
|
182
|
+
const variables = {};
|
|
183
|
+
const { targetPortIds, includePortName } = options ?? {};
|
|
184
|
+
// Find all upstream connections
|
|
185
|
+
const connections = findUpstreamConnections(node, nodes, edges);
|
|
186
|
+
for (const connection of connections) {
|
|
187
|
+
const { sourceNode, sourcePort, targetPort } = connection;
|
|
188
|
+
// Skip trigger ports - they don't carry data
|
|
189
|
+
if (sourcePort?.dataType === "trigger")
|
|
190
|
+
continue;
|
|
191
|
+
if (targetPort?.dataType === "trigger")
|
|
192
|
+
continue;
|
|
193
|
+
// Get the target port ID for filtering
|
|
194
|
+
const targetPortId = targetPort?.id ?? sourcePort?.id ?? "data";
|
|
195
|
+
// Filter by target port IDs if specified
|
|
196
|
+
if (targetPortIds !== undefined) {
|
|
197
|
+
if (!targetPortIds.includes(targetPortId))
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (!sourcePort)
|
|
201
|
+
continue;
|
|
202
|
+
// If the source port has a schema with top-level properties,
|
|
203
|
+
// unpack them as top-level variables (unless includePortName is true)
|
|
204
|
+
if (sourcePort.schema?.properties && !includePortName) {
|
|
205
|
+
// Unpack schema properties as top-level variables
|
|
206
|
+
for (const [propName, propValue] of Object.entries(sourcePort.schema.properties)) {
|
|
207
|
+
// Skip if we already have a variable with this name
|
|
208
|
+
if (variables[propName])
|
|
209
|
+
continue;
|
|
210
|
+
variables[propName] = propertyToTemplateVariable(propName, propValue, sourcePort.id, sourceNode.id);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
// No schema or includePortName is true - use port name as the variable
|
|
215
|
+
const variableName = includePortName ? targetPortId : (targetPortId);
|
|
216
|
+
// Skip if we already have a variable with this name
|
|
217
|
+
if (variables[variableName])
|
|
218
|
+
continue;
|
|
219
|
+
const variable = portToTemplateVariable(sourcePort, sourceNode.id);
|
|
220
|
+
variable.name = variableName;
|
|
221
|
+
variable.label = targetPort?.name ?? sourcePort.name;
|
|
222
|
+
variables[variableName] = variable;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { variables };
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Converts simple variable hints (string array) to a VariableSchema.
|
|
229
|
+
* Used for backward compatibility with the old variableHints format.
|
|
230
|
+
*
|
|
231
|
+
* @param hints - Array of variable names
|
|
232
|
+
* @returns A VariableSchema with basic string variables
|
|
233
|
+
*/
|
|
234
|
+
export function hintsToVariableSchema(hints) {
|
|
235
|
+
const variables = {};
|
|
236
|
+
for (const hint of hints) {
|
|
237
|
+
variables[hint] = {
|
|
238
|
+
name: hint,
|
|
239
|
+
type: "mixed"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { variables };
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Gets the child variables for a given path in the variable schema.
|
|
246
|
+
* Used for drilling down into nested objects and arrays.
|
|
247
|
+
*
|
|
248
|
+
* @param schema - The variable schema
|
|
249
|
+
* @param path - The path to the parent variable (e.g., "user", "user.address")
|
|
250
|
+
* @returns Array of child variables, or empty array if none found
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* // For path "user" with schema containing user.name, user.email, user.address
|
|
255
|
+
* getChildVariables(schema, "user");
|
|
256
|
+
* // Returns: [{ name: "name", ... }, { name: "email", ... }, { name: "address", ... }]
|
|
257
|
+
*
|
|
258
|
+
* // For path "user.address" with schema containing city, country
|
|
259
|
+
* getChildVariables(schema, "user.address");
|
|
260
|
+
* // Returns: [{ name: "city", ... }, { name: "country", ... }]
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
export function getChildVariables(schema, path) {
|
|
264
|
+
const parts = path.split(".");
|
|
265
|
+
let current;
|
|
266
|
+
// Navigate to the target variable
|
|
267
|
+
for (let i = 0; i < parts.length; i++) {
|
|
268
|
+
const part = parts[i];
|
|
269
|
+
// Handle array index access (e.g., "items[0]")
|
|
270
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+|\*)\]$/);
|
|
271
|
+
if (arrayMatch) {
|
|
272
|
+
const [, varName] = arrayMatch;
|
|
273
|
+
if (i === 0) {
|
|
274
|
+
current = schema.variables[varName];
|
|
275
|
+
}
|
|
276
|
+
else if (current?.properties) {
|
|
277
|
+
current = current.properties[varName];
|
|
278
|
+
}
|
|
279
|
+
// After array access, move to items schema
|
|
280
|
+
if (current?.items) {
|
|
281
|
+
current = current.items;
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
// Regular property access
|
|
286
|
+
if (i === 0) {
|
|
287
|
+
current = schema.variables[part];
|
|
288
|
+
}
|
|
289
|
+
else if (current?.properties) {
|
|
290
|
+
current = current.properties[part];
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Return child properties if available
|
|
297
|
+
if (current?.properties) {
|
|
298
|
+
return Object.values(current.properties);
|
|
299
|
+
}
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Gets suggestions for array index access.
|
|
304
|
+
* Returns common index options like [0], [1], [2], and [*] for all items.
|
|
305
|
+
*
|
|
306
|
+
* @param maxIndex - Maximum index to suggest (default: 2)
|
|
307
|
+
* @returns Array of index suggestion strings
|
|
308
|
+
*/
|
|
309
|
+
export function getArrayIndexSuggestions(maxIndex = 2) {
|
|
310
|
+
const suggestions = [];
|
|
311
|
+
for (let i = 0; i <= maxIndex; i++) {
|
|
312
|
+
suggestions.push(`${i}]`);
|
|
313
|
+
}
|
|
314
|
+
// Add wildcard for "all items"
|
|
315
|
+
suggestions.push("*]");
|
|
316
|
+
return suggestions;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Checks if a variable at the given path is an array.
|
|
320
|
+
*
|
|
321
|
+
* @param schema - The variable schema
|
|
322
|
+
* @param path - The path to check (e.g., "items", "user.orders")
|
|
323
|
+
* @returns True if the variable is an array type
|
|
324
|
+
*/
|
|
325
|
+
export function isArrayVariable(schema, path) {
|
|
326
|
+
const parts = path.split(".");
|
|
327
|
+
let current;
|
|
328
|
+
for (let i = 0; i < parts.length; i++) {
|
|
329
|
+
const part = parts[i];
|
|
330
|
+
// Handle array index access
|
|
331
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+|\*)\]$/);
|
|
332
|
+
if (arrayMatch) {
|
|
333
|
+
const [, varName] = arrayMatch;
|
|
334
|
+
if (i === 0) {
|
|
335
|
+
current = schema.variables[varName];
|
|
336
|
+
}
|
|
337
|
+
else if (current?.properties) {
|
|
338
|
+
current = current.properties[varName];
|
|
339
|
+
}
|
|
340
|
+
if (current?.items) {
|
|
341
|
+
current = current.items;
|
|
342
|
+
}
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (i === 0) {
|
|
346
|
+
current = schema.variables[part];
|
|
347
|
+
}
|
|
348
|
+
else if (current?.properties) {
|
|
349
|
+
current = current.properties[part];
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return current?.type === "array";
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Checks if a variable at the given path has child properties.
|
|
359
|
+
*
|
|
360
|
+
* @param schema - The variable schema
|
|
361
|
+
* @param path - The path to check
|
|
362
|
+
* @returns True if the variable has children that can be drilled into
|
|
363
|
+
*/
|
|
364
|
+
export function hasChildren(schema, path) {
|
|
365
|
+
const children = getChildVariables(schema, path);
|
|
366
|
+
return children.length > 0;
|
|
367
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -69,6 +69,11 @@ export interface NodePort {
|
|
|
69
69
|
required?: boolean;
|
|
70
70
|
description?: string;
|
|
71
71
|
defaultValue?: unknown;
|
|
72
|
+
/**
|
|
73
|
+
* Optional JSON Schema describing the structure of data on this port.
|
|
74
|
+
* Used for template variable autocomplete to drill into nested properties.
|
|
75
|
+
*/
|
|
76
|
+
schema?: OutputSchema | InputSchema;
|
|
72
77
|
}
|
|
73
78
|
/**
|
|
74
79
|
* Dynamic port configuration for user-defined inputs/outputs
|
|
@@ -612,6 +617,121 @@ export interface OutputProperty extends BaseProperty {
|
|
|
612
617
|
export interface OutputSchema extends BaseSchema {
|
|
613
618
|
properties: Record<string, OutputProperty>;
|
|
614
619
|
}
|
|
620
|
+
/**
|
|
621
|
+
* Primitive types for template variables
|
|
622
|
+
*/
|
|
623
|
+
export type TemplateVariableType = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'integer' | 'mixed' | 'float';
|
|
624
|
+
/**
|
|
625
|
+
* Represents a variable available for template interpolation.
|
|
626
|
+
* Used by the template editor for autocomplete suggestions.
|
|
627
|
+
*
|
|
628
|
+
* Supports hierarchical drilling:
|
|
629
|
+
* - Objects have `properties` for dot notation (e.g., `user.name`)
|
|
630
|
+
* - Arrays have `items` for index access (e.g., `items[0].name`)
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```typescript
|
|
634
|
+
* const userVariable: TemplateVariable = {
|
|
635
|
+
* name: "user",
|
|
636
|
+
* label: "User Data",
|
|
637
|
+
* type: "object",
|
|
638
|
+
* properties: {
|
|
639
|
+
* name: { name: "name", type: "string", label: "User Name" },
|
|
640
|
+
* email: { name: "email", type: "string", label: "Email Address" },
|
|
641
|
+
* address: {
|
|
642
|
+
* name: "address",
|
|
643
|
+
* type: "object",
|
|
644
|
+
* label: "Address",
|
|
645
|
+
* properties: {
|
|
646
|
+
* city: { name: "city", type: "string", label: "City" },
|
|
647
|
+
* country: { name: "country", type: "string", label: "Country" }
|
|
648
|
+
* }
|
|
649
|
+
* }
|
|
650
|
+
* }
|
|
651
|
+
* };
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
export interface TemplateVariable {
|
|
655
|
+
/** Variable name (used in template as {{ name }}) */
|
|
656
|
+
name: string;
|
|
657
|
+
/** Display label for the variable in autocomplete dropdown */
|
|
658
|
+
label?: string;
|
|
659
|
+
/** Description shown in autocomplete tooltip */
|
|
660
|
+
description?: string;
|
|
661
|
+
/** Data type of the variable */
|
|
662
|
+
type: TemplateVariableType;
|
|
663
|
+
/** For objects: child properties accessible via dot notation */
|
|
664
|
+
properties?: Record<string, TemplateVariable>;
|
|
665
|
+
/** For arrays: schema of array items accessible via index notation */
|
|
666
|
+
items?: TemplateVariable;
|
|
667
|
+
/** Source port ID this variable comes from */
|
|
668
|
+
sourcePort?: string;
|
|
669
|
+
/** Source node ID */
|
|
670
|
+
sourceNode?: string;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Schema passed to template editor for autocomplete functionality.
|
|
674
|
+
* Contains all available variables derived from connected upstream nodes.
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```typescript
|
|
678
|
+
* const variableSchema: VariableSchema = {
|
|
679
|
+
* variables: {
|
|
680
|
+
* user: { name: "user", type: "object", properties: { ... } },
|
|
681
|
+
* items: { name: "items", type: "array", items: { ... } },
|
|
682
|
+
* config: { name: "config", type: "object", properties: { ... } }
|
|
683
|
+
* }
|
|
684
|
+
* };
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
687
|
+
export interface VariableSchema {
|
|
688
|
+
/** Map of available variables keyed by variable name */
|
|
689
|
+
variables: Record<string, TemplateVariable>;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Configuration for template variable autocomplete.
|
|
693
|
+
* Used in template fields to control which variables are available
|
|
694
|
+
* and how they are derived.
|
|
695
|
+
*
|
|
696
|
+
* @example
|
|
697
|
+
* ```json
|
|
698
|
+
* {
|
|
699
|
+
* "type": "string",
|
|
700
|
+
* "format": "template",
|
|
701
|
+
* "variables": {
|
|
702
|
+
* "ports": ["data", "context"],
|
|
703
|
+
* "includePortName": true
|
|
704
|
+
* }
|
|
705
|
+
* }
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
export interface TemplateVariablesConfig {
|
|
709
|
+
/**
|
|
710
|
+
* Specifies which input port IDs should provide variables for autocomplete.
|
|
711
|
+
* Only connections to these ports will provide variables.
|
|
712
|
+
*
|
|
713
|
+
* - If not specified, all input ports with connections are used.
|
|
714
|
+
* - If specified as an empty array, no variables will be derived from ports.
|
|
715
|
+
*/
|
|
716
|
+
ports?: string[];
|
|
717
|
+
/**
|
|
718
|
+
* Pre-defined variable schema to use instead of (or in addition to) deriving from ports.
|
|
719
|
+
* Useful for providing static variables or overriding derived ones.
|
|
720
|
+
*/
|
|
721
|
+
schema?: VariableSchema;
|
|
722
|
+
/**
|
|
723
|
+
* Whether to include the port name as a prefix for variables.
|
|
724
|
+
* When true, variables are named like `data.user` instead of just `user`.
|
|
725
|
+
* Useful when multiple ports might have overlapping variable names.
|
|
726
|
+
* @default false
|
|
727
|
+
*/
|
|
728
|
+
includePortName?: boolean;
|
|
729
|
+
/**
|
|
730
|
+
* Whether to show available variables as clickable hints below the editor.
|
|
731
|
+
* @default true
|
|
732
|
+
*/
|
|
733
|
+
showHints?: boolean;
|
|
734
|
+
}
|
|
615
735
|
/**
|
|
616
736
|
* Union type for all schema types
|
|
617
737
|
*/
|
|
@@ -66,8 +66,8 @@ export declare function resolveComponentName(metadata: NodeMetadata, configNodeT
|
|
|
66
66
|
*/
|
|
67
67
|
export declare function isNodeTypeSupported(metadata: NodeMetadata, nodeType: NodeType | string): boolean;
|
|
68
68
|
/**
|
|
69
|
-
* Gets
|
|
70
|
-
* Used in config schemas to show available options.
|
|
69
|
+
* Gets oneOf options for node type configuration.
|
|
70
|
+
* Used in config schemas to show available options with labels.
|
|
71
71
|
*
|
|
72
72
|
* This function combines:
|
|
73
73
|
* - Types specified in metadata.supportedTypes
|
|
@@ -75,27 +75,32 @@ export declare function isNodeTypeSupported(metadata: NodeMetadata, nodeType: No
|
|
|
75
75
|
*
|
|
76
76
|
* @param metadata - The node metadata
|
|
77
77
|
* @param includeCustomTypes - Whether to include registered custom types
|
|
78
|
-
* @returns
|
|
78
|
+
* @returns Array of oneOf items with const (type value) and title (display name)
|
|
79
79
|
*/
|
|
80
|
-
export declare function
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
80
|
+
export declare function getNodeTypeOneOfOptions(metadata: NodeMetadata, includeCustomTypes?: boolean): Array<{
|
|
81
|
+
const: string;
|
|
82
|
+
title: string;
|
|
83
|
+
}>;
|
|
84
84
|
/**
|
|
85
85
|
* Creates a nodeType config property that respects supportedTypes.
|
|
86
86
|
* This replaces hardcoded enum values in config schemas.
|
|
87
87
|
*
|
|
88
|
+
* Uses JSON Schema `oneOf` pattern with `const`/`title` for labeled options,
|
|
89
|
+
* which is the standard approach supported by form components.
|
|
90
|
+
*
|
|
88
91
|
* @param metadata - The node metadata
|
|
89
92
|
* @param defaultType - Optional default type override
|
|
90
|
-
* @returns Config schema property object
|
|
93
|
+
* @returns Config schema property object with oneOf for labeled options
|
|
91
94
|
*/
|
|
92
95
|
export declare function createNodeTypeConfigProperty(metadata: NodeMetadata, defaultType?: NodeType | string): {
|
|
93
96
|
type: "string";
|
|
94
97
|
title: string;
|
|
95
98
|
description: string;
|
|
96
99
|
default: string;
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
oneOf: {
|
|
101
|
+
const: string;
|
|
102
|
+
title: string;
|
|
103
|
+
}[];
|
|
99
104
|
};
|
|
100
105
|
/**
|
|
101
106
|
* Check if a type string represents a valid registered or built-in type.
|
package/dist/utils/nodeTypes.js
CHANGED
|
@@ -136,8 +136,8 @@ export function isNodeTypeSupported(metadata, nodeType) {
|
|
|
136
136
|
return false;
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
|
-
* Gets
|
|
140
|
-
* Used in config schemas to show available options.
|
|
139
|
+
* Gets oneOf options for node type configuration.
|
|
140
|
+
* Used in config schemas to show available options with labels.
|
|
141
141
|
*
|
|
142
142
|
* This function combines:
|
|
143
143
|
* - Types specified in metadata.supportedTypes
|
|
@@ -145,58 +145,60 @@ export function isNodeTypeSupported(metadata, nodeType) {
|
|
|
145
145
|
*
|
|
146
146
|
* @param metadata - The node metadata
|
|
147
147
|
* @param includeCustomTypes - Whether to include registered custom types
|
|
148
|
-
* @returns
|
|
148
|
+
* @returns Array of oneOf items with const (type value) and title (display name)
|
|
149
149
|
*/
|
|
150
|
-
export function
|
|
150
|
+
export function getNodeTypeOneOfOptions(metadata, includeCustomTypes = false) {
|
|
151
151
|
const availableTypes = getAvailableNodeTypes(metadata);
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const enumNames = [];
|
|
152
|
+
const options = [];
|
|
153
|
+
const includedTypes = new Set();
|
|
155
154
|
for (const type of availableTypes) {
|
|
156
|
-
|
|
155
|
+
includedTypes.add(type);
|
|
157
156
|
// Get display name from registry or fallback to built-in names
|
|
158
157
|
const registration = nodeComponentRegistry.get(type);
|
|
158
|
+
let title;
|
|
159
159
|
if (registration) {
|
|
160
|
-
|
|
160
|
+
title = registration.displayName;
|
|
161
161
|
}
|
|
162
162
|
else if (type in TYPE_DISPLAY_NAMES) {
|
|
163
|
-
|
|
163
|
+
title = TYPE_DISPLAY_NAMES[type];
|
|
164
164
|
}
|
|
165
165
|
else {
|
|
166
166
|
// Format unknown type nicely
|
|
167
|
-
|
|
167
|
+
title = formatTypeName(type);
|
|
168
168
|
}
|
|
169
|
+
options.push({ const: type, title });
|
|
169
170
|
}
|
|
170
171
|
// Optionally include all registered custom types
|
|
171
172
|
if (includeCustomTypes) {
|
|
172
173
|
const registrations = nodeComponentRegistry.filter({
|
|
173
|
-
predicate: (reg) => !isBuiltinType(reg.type) && !
|
|
174
|
+
predicate: (reg) => !isBuiltinType(reg.type) && !includedTypes.has(reg.type)
|
|
174
175
|
});
|
|
175
176
|
for (const reg of registrations) {
|
|
176
|
-
|
|
177
|
-
enumNames.push(reg.displayName);
|
|
177
|
+
options.push({ const: reg.type, title: reg.displayName });
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
return
|
|
180
|
+
return options;
|
|
181
181
|
}
|
|
182
182
|
/**
|
|
183
183
|
* Creates a nodeType config property that respects supportedTypes.
|
|
184
184
|
* This replaces hardcoded enum values in config schemas.
|
|
185
185
|
*
|
|
186
|
+
* Uses JSON Schema `oneOf` pattern with `const`/`title` for labeled options,
|
|
187
|
+
* which is the standard approach supported by form components.
|
|
188
|
+
*
|
|
186
189
|
* @param metadata - The node metadata
|
|
187
190
|
* @param defaultType - Optional default type override
|
|
188
|
-
* @returns Config schema property object
|
|
191
|
+
* @returns Config schema property object with oneOf for labeled options
|
|
189
192
|
*/
|
|
190
193
|
export function createNodeTypeConfigProperty(metadata, defaultType) {
|
|
191
|
-
const
|
|
194
|
+
const oneOf = getNodeTypeOneOfOptions(metadata);
|
|
192
195
|
const primaryType = defaultType ?? getPrimaryNodeType(metadata);
|
|
193
196
|
return {
|
|
194
|
-
type:
|
|
195
|
-
title:
|
|
196
|
-
description:
|
|
197
|
+
type: "string",
|
|
198
|
+
title: "Node Type",
|
|
199
|
+
description: "Choose the visual representation for this node",
|
|
197
200
|
default: primaryType,
|
|
198
|
-
|
|
199
|
-
enumNames
|
|
201
|
+
oneOf
|
|
200
202
|
};
|
|
201
203
|
}
|
|
202
204
|
/**
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@d34dman/flowdrop",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.47",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite dev",
|
|
8
8
|
"build": "vite build && npm run prepack",
|
|
@@ -214,4 +214,4 @@
|
|
|
214
214
|
"static"
|
|
215
215
|
]
|
|
216
216
|
}
|
|
217
|
-
}
|
|
217
|
+
}
|