@flowdrop/flowdrop 1.6.0 → 1.7.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.
@@ -57,7 +57,8 @@
57
57
  import { getUiSettings, updateSettings } from '../stores/settingsStore.svelte.js';
58
58
  import {
59
59
  initializePortCompatibility,
60
- getPortCompatibilityChecker
60
+ getPortCompatibilityChecker,
61
+ isPortCompatibilityInitialized
61
62
  } from '../utils/connections.js';
62
63
  import { DEFAULT_PORT_CONFIG } from '../config/defaultPortConfig.js';
63
64
  import { workflowFormatRegistry } from '../registry/workflowFormatRegistry.js';
@@ -121,6 +122,8 @@
121
122
  showSettingsResetButton?: boolean;
122
123
  /** Pluggable swap strategies — instance-scoped, checked in order */
123
124
  swapStrategies?: SwapStrategy[];
125
+ /** Additional JSON Schema properties to show in the Workflow Settings panel. Values are persisted in workflow.config. */
126
+ workflowSettingsSchema?: ConfigSchema;
124
127
  }
125
128
 
126
129
  let {
@@ -146,7 +149,8 @@
146
149
  settingsCategories,
147
150
  showSettingsSyncButton,
148
151
  showSettingsResetButton,
149
- swapStrategies
152
+ swapStrategies,
153
+ workflowSettingsSchema
150
154
  }: Props = $props();
151
155
 
152
156
  // svelte-ignore state_referenced_locally — feature flags don't change at runtime
@@ -223,39 +227,60 @@
223
227
  let swapTargetMetadata = $state<NodeMetadata | null>(null);
224
228
  let swapInteractiveState = $state<InteractiveSwapState | null>(null);
225
229
 
230
+ // Built-in workflow settings field names — consumer schemas must not reuse these.
231
+ const WORKFLOW_SETTINGS_RESERVED = new Set(['name', 'description', 'format']);
232
+
226
233
  // Workflow configuration schema (derived to pick up dynamic format options)
227
- let workflowConfigSchema: ConfigSchema = $derived({
228
- type: 'object' as const,
229
- properties: {
230
- name: {
231
- type: 'string',
232
- title: 'Workflow Name',
233
- description: 'The name of the workflow',
234
- default: ''
235
- },
236
- description: {
237
- type: 'string',
238
- title: 'Description',
239
- description: 'A description of the workflow',
240
- format: 'multiline',
241
- default: ''
234
+ let workflowConfigSchema: ConfigSchema = $derived.by(() => {
235
+ const extraProps = Object.fromEntries(
236
+ Object.entries(workflowSettingsSchema?.properties ?? {}).filter(([k]) => {
237
+ if (WORKFLOW_SETTINGS_RESERVED.has(k)) {
238
+ logger.warn(
239
+ `workflowSettingsSchema: property "${k}" is reserved and will be ignored. Choose a different key.`
240
+ );
241
+ return false;
242
+ }
243
+ return true;
244
+ })
245
+ );
246
+ const extraRequired = (workflowSettingsSchema?.required ?? []).filter(
247
+ (k) => !WORKFLOW_SETTINGS_RESERVED.has(k)
248
+ );
249
+ return {
250
+ type: 'object' as const,
251
+ properties: {
252
+ name: {
253
+ type: 'string',
254
+ title: 'Workflow Name',
255
+ description: 'The name of the workflow',
256
+ default: ''
257
+ },
258
+ description: {
259
+ type: 'string',
260
+ title: 'Description',
261
+ description: 'A description of the workflow',
262
+ format: 'multiline',
263
+ default: ''
264
+ },
265
+ format: {
266
+ type: 'string',
267
+ title: 'Workflow Format',
268
+ description: 'The specification format for this workflow',
269
+ oneOf: workflowFormatRegistry.getOneOfOptions(),
270
+ default: 'flowdrop'
271
+ },
272
+ ...extraProps
242
273
  },
243
- format: {
244
- type: 'string',
245
- title: 'Workflow Format',
246
- description: 'The specification format for this workflow',
247
- oneOf: workflowFormatRegistry.getOneOfOptions(),
248
- default: 'flowdrop'
249
- }
250
- },
251
- required: ['name']
274
+ required: ['name', ...extraRequired]
275
+ };
252
276
  });
253
277
 
254
278
  // Workflow configuration values
255
279
  let workflowConfigValues = $derived({
256
280
  name: getWorkflowName() || '',
257
281
  description: getWorkflowStore()?.description || '',
258
- format: getWorkflowStore()?.metadata?.format || 'flowdrop'
282
+ format: getWorkflowStore()?.metadata?.format || 'flowdrop',
283
+ ...(getWorkflowStore()?.config ?? {})
259
284
  });
260
285
 
261
286
  // Get the current node from the workflow store
@@ -727,7 +752,10 @@
727
752
 
728
753
  // Ensure port compatibility checker is initialized (needed for proximity connect, etc.)
729
754
  // mountFlowDropApp initializes this before mounting, but SvelteKit routes need it here.
730
- initializePortCompatibility(DEFAULT_PORT_CONFIG);
755
+ // Only initialize with defaults if not already set — preserves custom port configs.
756
+ if (!isPortCompatibilityInitialized()) {
757
+ initializePortCompatibility(DEFAULT_PORT_CONFIG);
758
+ }
731
759
 
732
760
  await fetchNodeTypes();
733
761
 
@@ -1055,13 +1083,16 @@
1055
1083
  }
1056
1084
  }
1057
1085
 
1086
+ // Extract built-in fields; everything else belongs in workflow.config
1087
+ const { name, description, format: _format, ...customConfig } = config;
1058
1088
  workflowActions.batchUpdate({
1059
- name: config.name as string,
1060
- description: config.description as string | undefined,
1089
+ name: name as string,
1090
+ description: description as string | undefined,
1061
1091
  metadata: {
1062
1092
  ...wf.metadata,
1063
1093
  format: newFormat
1064
- }
1094
+ },
1095
+ ...(workflowSettingsSchema && { config: customConfig as Record<string, unknown> })
1065
1096
  });
1066
1097
  }
1067
1098
  }}
@@ -1,4 +1,4 @@
1
- import type { NodeMetadata, Workflow } from '../types/index.js';
1
+ import type { NodeMetadata, Workflow, ConfigSchema } from '../types/index.js';
2
2
  import type { SwapStrategy } from '../utils/nodeSwap.js';
3
3
  import type { EndpointConfig } from '../config/endpoints.js';
4
4
  import type { AuthProvider } from '../types/auth.js';
@@ -61,6 +61,8 @@ interface Props {
61
61
  showSettingsResetButton?: boolean;
62
62
  /** Pluggable swap strategies — instance-scoped, checked in order */
63
63
  swapStrategies?: SwapStrategy[];
64
+ /** Additional JSON Schema properties to show in the Workflow Settings panel. Values are persisted in workflow.config. */
65
+ workflowSettingsSchema?: ConfigSchema;
64
66
  }
65
67
  declare const App: import("svelte").Component<Props, {}, "">;
66
68
  type App = ReturnType<typeof App>;
@@ -91,13 +91,14 @@
91
91
  return '';
92
92
  }
93
93
  if (typeof val === 'string') {
94
- // Check if it's already a valid JSON string
94
+ // If the string is already a valid JSON representation (e.g. '{"a":1}' or '"foo"'),
95
+ // use it as-is to avoid double-encoding
95
96
  try {
96
97
  JSON.parse(val);
97
98
  return val;
98
99
  } catch {
99
- // Not valid JSON, return as-is
100
- return val;
100
+ // Plain JS string value — serialize it as a JSON string literal (adds quotes)
101
+ return JSON.stringify(val);
101
102
  }
102
103
  }
103
104
  // Convert object to formatted JSON string
@@ -82,9 +82,7 @@
82
82
  * This allows users to customize the node description per-instance via config.
83
83
  */
84
84
  const displayDescription = $derived(
85
- (props.data.config?.instanceDescription as string) ||
86
- props.data.metadata?.description ||
87
- 'A configurable simple node'
85
+ (props.data.config?.instanceDescription as string) || props.data.metadata?.description || null
88
86
  );
89
87
 
90
88
  // Handle configuration sidebar - now using global ConfigSidebar
@@ -226,9 +224,11 @@
226
224
  </div>
227
225
 
228
226
  <!-- Node Description -->
229
- <p class="flowdrop-simple-node__description">
230
- {displayDescription}
231
- </p>
227
+ {#if displayDescription}
228
+ <p class="flowdrop-simple-node__description">
229
+ {displayDescription}
230
+ </p>
231
+ {/if}
232
232
  </div>
233
233
 
234
234
  <!-- Processing indicator -->
@@ -281,7 +281,7 @@
281
281
  cursor: pointer;
282
282
  transition: all var(--fd-transition-fast);
283
283
  box-shadow: var(--fd-shadow-md);
284
- overflow: visible; /* Changed from hidden to visible to allow handles to be properly accessible */
284
+ overflow: hidden;
285
285
  z-index: 10;
286
286
  color: var(--fd-foreground);
287
287
  }
@@ -328,7 +328,7 @@
328
328
  .flowdrop-simple-node__header {
329
329
  padding: var(--fd-space-xl);
330
330
  background: var(--fd-header);
331
- border-radius: var(--fd-radius-xl);
331
+ flex: 1;
332
332
  }
333
333
 
334
334
  .flowdrop-simple-node__header-content {
@@ -36,6 +36,11 @@
36
36
  },
37
37
  "metadata": {
38
38
  "$ref": "#/$defs/WorkflowMetadata"
39
+ },
40
+ "config": {
41
+ "type": "object",
42
+ "description": "Custom workflow-level configuration values. Populated when a workflowSettingsSchema is provided to the editor.",
43
+ "additionalProperties": true
39
44
  }
40
45
  },
41
46
  "required": [
@@ -263,6 +263,7 @@ export declare const workflowActions: {
263
263
  name?: string;
264
264
  description?: string;
265
265
  metadata?: Partial<Workflow["metadata"]>;
266
+ config?: Record<string, unknown>;
266
267
  }) => void;
267
268
  /**
268
269
  * Swap a node — atomically replaces nodes and edges with a descriptive history entry.
@@ -632,6 +632,7 @@ export const workflowActions = {
632
632
  ...(updates.description !== undefined && {
633
633
  description: updates.description
634
634
  }),
635
+ ...(updates.config !== undefined && { config: updates.config }),
635
636
  metadata: buildMetadata(workflowState.metadata, updates.metadata ?? undefined)
636
637
  };
637
638
  bumpVersion();
@@ -1150,6 +1150,8 @@ export interface Workflow {
1150
1150
  /** Workflow format. Determines sidebar filtering and export behavior. */
1151
1151
  format?: WorkflowFormat;
1152
1152
  };
1153
+ /** Custom workflow-level configuration values, populated via workflowSettingsSchema. */
1154
+ config?: Record<string, unknown>;
1153
1155
  }
1154
1156
  /**
1155
1157
  * API response types
@@ -57,6 +57,10 @@ export declare class PortCompatibilityChecker {
57
57
  * Initialize the global port compatibility checker
58
58
  */
59
59
  export declare function initializePortCompatibility(portConfig: PortConfig): void;
60
+ /**
61
+ * Returns true if the port compatibility checker has been initialized.
62
+ */
63
+ export declare function isPortCompatibilityInitialized(): boolean;
60
64
  /**
61
65
  * Get the global port compatibility checker
62
66
  */
@@ -114,6 +114,12 @@ let globalCompatibilityChecker = null;
114
114
  export function initializePortCompatibility(portConfig) {
115
115
  globalCompatibilityChecker = new PortCompatibilityChecker(portConfig);
116
116
  }
117
+ /**
118
+ * Returns true if the port compatibility checker has been initialized.
119
+ */
120
+ export function isPortCompatibilityInitialized() {
121
+ return globalCompatibilityChecker !== null;
122
+ }
117
123
  /**
118
124
  * Get the global port compatibility checker
119
125
  */
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A drop-in visual workflow editor for any web application. You own the backend. You own the data. You own the orchestration.",
4
4
  "license": "MIT",
5
5
  "private": false,
6
- "version": "1.6.0",
6
+ "version": "1.7.0",
7
7
  "author": "Shibin Das (D34dMan)",
8
8
  "bugs": {
9
9
  "url": "https://github.com/flowdrop-io/flowdrop/issues"