@flowdrop/flowdrop 2.0.0-beta.1 → 2.0.0-beta.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/MIGRATION-2.0.md +160 -3
  3. package/dist/api/enhanced-client.js +6 -11
  4. package/dist/components/App.svelte +1 -0
  5. package/dist/components/ConfigForm.svelte +6 -2
  6. package/dist/components/PipelineStatus.svelte +6 -1
  7. package/dist/components/PipelineStatus.svelte.d.ts +3 -0
  8. package/dist/components/SchemaForm.svelte +4 -2
  9. package/dist/components/WorkflowEditor.svelte +4 -1
  10. package/dist/components/WorkflowEditor.svelte.d.ts +3 -0
  11. package/dist/components/chat/AIChatPanel.svelte +6 -1
  12. package/dist/components/form/FormAutocomplete.svelte +8 -9
  13. package/dist/components/form/FormFieldLight.svelte +30 -1
  14. package/dist/components/form/FormFieldLight.svelte.d.ts +12 -0
  15. package/dist/components/form/FormUISchemaRenderer.svelte +3 -1
  16. package/dist/components/interrupt/InterruptBubble.svelte +11 -2
  17. package/dist/components/playground/PipelineKanbanView.svelte +4 -2
  18. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +2 -0
  19. package/dist/components/playground/PipelinePanel.svelte +20 -3
  20. package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -0
  21. package/dist/components/playground/PipelineTableView.svelte +4 -2
  22. package/dist/components/playground/PipelineTableView.svelte.d.ts +2 -0
  23. package/dist/components/playground/Playground.svelte +76 -25
  24. package/dist/components/playground/Playground.svelte.d.ts +3 -0
  25. package/dist/components/playground/PlaygroundApp.svelte +5 -0
  26. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  27. package/dist/components/playground/PlaygroundModal.svelte +5 -0
  28. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  29. package/dist/components/playground/PlaygroundStudio.svelte +7 -1
  30. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  31. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +2 -1
  32. package/dist/components/playground/pipelineViewUtils.svelte.js +2 -2
  33. package/dist/config/endpoints.d.ts +23 -0
  34. package/dist/config/endpoints.js +28 -0
  35. package/dist/core/index.d.ts +1 -2
  36. package/dist/core/index.js +2 -6
  37. package/dist/display/index.d.ts +6 -1
  38. package/dist/display/index.js +9 -1
  39. package/dist/editor/index.d.ts +1 -1
  40. package/dist/editor/index.js +1 -1
  41. package/dist/form/full.d.ts +2 -1
  42. package/dist/form/full.js +4 -1
  43. package/dist/form/index.d.ts +0 -1
  44. package/dist/form/index.js +3 -2
  45. package/dist/helpers/workflowEditorHelper.d.ts +4 -2
  46. package/dist/helpers/workflowEditorHelper.js +4 -3
  47. package/dist/playground/index.d.ts +2 -2
  48. package/dist/playground/index.js +2 -2
  49. package/dist/playground/mount.d.ts +10 -0
  50. package/dist/playground/mount.js +8 -4
  51. package/dist/registry/builtinNodeTypes.d.ts +53 -0
  52. package/dist/registry/builtinNodeTypes.js +67 -0
  53. package/dist/registry/builtinNodes.d.ts +2 -39
  54. package/dist/registry/builtinNodes.js +6 -53
  55. package/dist/services/agentSpecExecutionService.d.ts +0 -2
  56. package/dist/services/agentSpecExecutionService.js +0 -2
  57. package/dist/services/apiVariableService.js +12 -26
  58. package/dist/services/categoriesApi.js +3 -6
  59. package/dist/services/chatService.d.ts +4 -3
  60. package/dist/services/chatService.js +13 -18
  61. package/dist/services/interruptService.d.ts +7 -6
  62. package/dist/services/interruptService.js +19 -21
  63. package/dist/services/playgroundService.d.ts +9 -8
  64. package/dist/services/playgroundService.js +23 -25
  65. package/dist/services/portConfigApi.js +3 -6
  66. package/dist/services/settingsService.d.ts +9 -4
  67. package/dist/services/settingsService.js +23 -12
  68. package/dist/stores/apiContext.d.ts +11 -0
  69. package/dist/stores/apiContext.js +15 -0
  70. package/dist/stores/categoriesStore.svelte.js +0 -1
  71. package/dist/stores/playgroundStore.svelte.js +0 -2
  72. package/dist/svelte-app.js +2 -1
  73. package/dist/types/auth.d.ts +9 -51
  74. package/dist/types/auth.js +4 -54
  75. package/dist/types/index.d.ts +4 -2
  76. package/dist/types/index.js +0 -1
  77. package/dist/utils/edgeStyling.js +9 -5
  78. package/dist/utils/fetchWithAuth.d.ts +36 -15
  79. package/dist/utils/fetchWithAuth.js +53 -23
  80. package/dist/utils/nodeTypes.js +1 -1
  81. package/package.json +2 -1
@@ -7,7 +7,8 @@
7
7
  *
8
8
  * @module services/settingsService
9
9
  */
10
- import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
10
+ import { buildEndpointUrl } from '../config/endpoints.js';
11
+ import { authenticatedFetch } from '../utils/fetchWithAuth.js';
11
12
  // =========================================================================
12
13
  // Configuration
13
14
  // =========================================================================
@@ -16,12 +17,20 @@ import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
16
17
  */
17
18
  let endpointConfig = null;
18
19
  /**
19
- * Set the endpoint configuration for settings API calls
20
+ * Auth provider reference, applied to every settings request so the backend
21
+ * sync path authenticates consistently with the rest of the library.
22
+ */
23
+ let authProvider;
24
+ /**
25
+ * Set the endpoint configuration (and optional auth provider) for settings API
26
+ * calls.
20
27
  *
21
28
  * @param config - Endpoint configuration
29
+ * @param provider - Optional auth provider supplying request auth headers
22
30
  */
23
- export function setSettingsEndpointConfig(config) {
31
+ export function setSettingsEndpointConfig(config, provider) {
24
32
  endpointConfig = config;
33
+ authProvider = provider;
25
34
  }
26
35
  /**
27
36
  * Get the current endpoint configuration
@@ -47,10 +56,10 @@ async function settingsRequest(endpointKey, endpointPath, options = {}) {
47
56
  throw new Error('Endpoint configuration not set. Call setSettingsEndpointConfig() first.');
48
57
  }
49
58
  const url = buildEndpointUrl(endpointConfig, endpointPath);
50
- const headers = getEndpointHeaders(endpointConfig, endpointKey);
51
- const response = await fetch(url, {
52
- headers,
53
- ...options
59
+ const response = await authenticatedFetch(url, options, {
60
+ config: endpointConfig,
61
+ endpointKey,
62
+ authProvider
54
63
  });
55
64
  // Check if response is JSON
56
65
  const contentType = response.headers.get('content-type');
@@ -151,11 +160,12 @@ export class SettingsService {
151
160
  * Create a new settings service instance
152
161
  *
153
162
  * @param config - Endpoint configuration
163
+ * @param authProvider - Optional auth provider applied to settings requests
154
164
  */
155
- constructor(config) {
165
+ constructor(config, authProvider) {
156
166
  this.config = config;
157
- // Also set the module-level config
158
- setSettingsEndpointConfig(config);
167
+ // Also set the module-level config + auth provider
168
+ setSettingsEndpointConfig(config, authProvider);
159
169
  }
160
170
  /**
161
171
  * Get user preferences from the backend
@@ -189,8 +199,9 @@ export class SettingsService {
189
199
  * Create a settings service instance
190
200
  *
191
201
  * @param config - Endpoint configuration
202
+ * @param authProvider - Optional auth provider applied to settings requests
192
203
  * @returns SettingsService instance
193
204
  */
194
- export function createSettingsService(config) {
195
- return new SettingsService(config);
205
+ export function createSettingsService(config, authProvider) {
206
+ return new SettingsService(config, authProvider);
196
207
  }
@@ -40,6 +40,17 @@ export declare class ApiContext {
40
40
  * Discards any previously built client so the next access rebuilds it.
41
41
  */
42
42
  configure(config: EndpointConfig, authProvider?: AuthProvider): void;
43
+ /**
44
+ * Swap the auth provider at runtime — e.g. on login or logout — without
45
+ * reconfiguring endpoints or remounting. Propagates to the live client so
46
+ * in-flight components keep working against the same instance.
47
+ *
48
+ * Per-instance services read `fd.api.authProvider` per request, so they pick
49
+ * up the new provider on their next call automatically.
50
+ *
51
+ * @param authProvider - The new authentication provider
52
+ */
53
+ setAuthProvider(authProvider: AuthProvider): void;
43
54
  /** Whether {@link configure} has been called with a usable config. */
44
55
  isConfigured(): boolean;
45
56
  }
@@ -58,6 +58,21 @@ export class ApiContext {
58
58
  // Drop the cached client so it picks up the new config/auth on next access.
59
59
  this.#client = null;
60
60
  }
61
+ /**
62
+ * Swap the auth provider at runtime — e.g. on login or logout — without
63
+ * reconfiguring endpoints or remounting. Propagates to the live client so
64
+ * in-flight components keep working against the same instance.
65
+ *
66
+ * Per-instance services read `fd.api.authProvider` per request, so they pick
67
+ * up the new provider on their next call automatically.
68
+ *
69
+ * @param authProvider - The new authentication provider
70
+ */
71
+ setAuthProvider(authProvider) {
72
+ this.#authProvider = authProvider;
73
+ // Keep the cached client (and its in-flight consumers) in sync.
74
+ this.#client?.setAuthProvider(authProvider);
75
+ }
61
76
  /** Whether {@link configure} has been called with a usable config. */
62
77
  isConfigured() {
63
78
  return Boolean(this.#config);
@@ -28,7 +28,6 @@ export class CategoriesStore {
28
28
  #categories = $state([...DEFAULT_CATEGORIES]);
29
29
  /** Derived lookup map: category name → CategoryDefinition. */
30
30
  #categoryMap = $derived((() => {
31
- // eslint-disable-next-line svelte/prefer-svelte-reactivity -- rebuilt whole inside $derived, never mutated afterwards
32
31
  const map = new Map();
33
32
  for (const cat of this.#categories) {
34
33
  map.set(cat.name, cat);
@@ -109,7 +109,6 @@ export class PlaygroundStore {
109
109
  // acknowledged optimistic write, overwritten by the next server response.
110
110
  #isExecuting = $derived(this.#currentSession?.status === 'running');
111
111
  /** Cleanups for active subscribeToSessionStatus effect roots. */
112
- // eslint-disable-next-line svelte/prefer-svelte-reactivity -- non-reactive bookkeeping registry; nothing renders from it
113
112
  #statusSubscriptions = new Set();
114
113
  /** Bound mutation facade — see {@link PlaygroundStoreActions}. */
115
114
  actions;
@@ -354,7 +353,6 @@ export class PlaygroundStore {
354
353
  if (!this.#currentSession)
355
354
  return;
356
355
  const executions = [...(this.#currentSession.executions ?? [])];
357
- // eslint-disable-next-line svelte/prefer-svelte-reactivity -- transient local for dedup within this call
358
356
  const seenIds = new Set(executions.map((e) => e.id));
359
357
  let added = false;
360
358
  let gainedMainRun = false;
@@ -400,7 +400,8 @@ export async function mountWorkflowEditor(container, options = {}) {
400
400
  target: container,
401
401
  props: {
402
402
  instance: fd,
403
- endpointConfig: config
403
+ endpointConfig: config,
404
+ authProvider
404
405
  }
405
406
  });
406
407
  // Create the mounted app interface (simpler version)
@@ -2,7 +2,9 @@
2
2
  * Authentication Provider Types for FlowDrop
3
3
  *
4
4
  * Provides interfaces and implementations for authentication in FlowDrop.
5
- * AuthProvider is passed at mount time and cannot be changed without remounting.
5
+ * An AuthProvider is supplied at mount time (or via `fd.api.configure`) and can
6
+ * be swapped at runtime with `fd.api.setAuthProvider(...)` — e.g. on login or
7
+ * logout — without remounting.
6
8
  *
7
9
  * @module types/auth
8
10
  */
@@ -15,8 +17,7 @@
15
17
  * @example
16
18
  * ```typescript
17
19
  * const authProvider: AuthProvider = {
18
- * getAuthHeaders: async () => ({ Authorization: "Bearer token123" }),
19
- * isAuthenticated: () => true
20
+ * getAuthHeaders: async () => ({ Authorization: "Bearer token123" })
20
21
  * };
21
22
  * ```
22
23
  */
@@ -30,15 +31,6 @@ export interface AuthProvider {
30
31
  * @returns Promise resolving to a record of header name-value pairs
31
32
  */
32
33
  getAuthHeaders(): Promise<Record<string, string>>;
33
- /**
34
- * Check if currently authenticated
35
- *
36
- * Used to determine if API requests should be attempted.
37
- * Should return synchronously for performance.
38
- *
39
- * @returns true if authenticated, false otherwise
40
- */
41
- isAuthenticated(): boolean;
42
34
  /**
43
35
  * Called when API returns 401 Unauthorized
44
36
  *
@@ -68,6 +60,11 @@ export interface StaticAuthConfig {
68
60
  token?: string;
69
61
  /** API key (used when type is "api_key") */
70
62
  apiKey?: string;
63
+ /**
64
+ * Header name to carry the API key (used when type is "api_key").
65
+ * Defaults to `"X-API-Key"`.
66
+ */
67
+ apiKeyHeader?: string;
71
68
  /** Custom headers (used when type is "custom") */
72
69
  headers?: Record<string, string>;
73
70
  }
@@ -119,28 +116,6 @@ export declare class StaticAuthProvider implements AuthProvider {
119
116
  * @returns Promise resolving to authentication headers
120
117
  */
121
118
  getAuthHeaders(): Promise<Record<string, string>>;
122
- /**
123
- * Check if authenticated
124
- *
125
- * Returns true if any auth headers are configured.
126
- *
127
- * @returns true if headers are configured
128
- */
129
- isAuthenticated(): boolean;
130
- /**
131
- * Handle unauthorized response
132
- *
133
- * Static provider cannot refresh tokens, so always returns false.
134
- *
135
- * @returns Promise resolving to false (cannot refresh)
136
- */
137
- onUnauthorized(): Promise<boolean>;
138
- /**
139
- * Handle forbidden response
140
- *
141
- * Static provider has no special handling for 403.
142
- */
143
- onForbidden(): Promise<void>;
144
119
  }
145
120
  /**
146
121
  * Configuration for callback-based authentication
@@ -213,15 +188,6 @@ export declare class CallbackAuthProvider implements AuthProvider {
213
188
  * @returns Promise resolving to authentication headers
214
189
  */
215
190
  getAuthHeaders(): Promise<Record<string, string>>;
216
- /**
217
- * Check if authenticated
218
- *
219
- * For callback-based auth, we assume authenticated if getToken exists.
220
- * The actual token validity is checked when making requests.
221
- *
222
- * @returns true (assumes authenticated, actual check happens on request)
223
- */
224
- isAuthenticated(): boolean;
225
191
  /**
226
192
  * Handle unauthorized response
227
193
  *
@@ -252,12 +218,4 @@ export declare class NoAuthProvider implements AuthProvider {
252
218
  * @returns Promise resolving to empty object
253
219
  */
254
220
  getAuthHeaders(): Promise<Record<string, string>>;
255
- /**
256
- * Check if authenticated
257
- *
258
- * Always returns false (no auth configured).
259
- *
260
- * @returns false
261
- */
262
- isAuthenticated(): boolean;
263
221
  }
@@ -2,7 +2,9 @@
2
2
  * Authentication Provider Types for FlowDrop
3
3
  *
4
4
  * Provides interfaces and implementations for authentication in FlowDrop.
5
- * AuthProvider is passed at mount time and cannot be changed without remounting.
5
+ * An AuthProvider is supplied at mount time (or via `fd.api.configure`) and can
6
+ * be swapped at runtime with `fd.api.setAuthProvider(...)` — e.g. on login or
7
+ * logout — without remounting.
6
8
  *
7
9
  * @module types/auth
8
10
  */
@@ -55,7 +57,7 @@ export class StaticAuthProvider {
55
57
  break;
56
58
  case 'api_key':
57
59
  if (config.apiKey) {
58
- this.headers['X-API-Key'] = config.apiKey;
60
+ this.headers[config.apiKeyHeader ?? 'X-API-Key'] = config.apiKey;
59
61
  }
60
62
  break;
61
63
  case 'custom':
@@ -79,35 +81,6 @@ export class StaticAuthProvider {
79
81
  async getAuthHeaders() {
80
82
  return this.headers;
81
83
  }
82
- /**
83
- * Check if authenticated
84
- *
85
- * Returns true if any auth headers are configured.
86
- *
87
- * @returns true if headers are configured
88
- */
89
- isAuthenticated() {
90
- return Object.keys(this.headers).length > 0;
91
- }
92
- /**
93
- * Handle unauthorized response
94
- *
95
- * Static provider cannot refresh tokens, so always returns false.
96
- *
97
- * @returns Promise resolving to false (cannot refresh)
98
- */
99
- async onUnauthorized() {
100
- // Static provider cannot refresh tokens
101
- return false;
102
- }
103
- /**
104
- * Handle forbidden response
105
- *
106
- * Static provider has no special handling for 403.
107
- */
108
- async onForbidden() {
109
- // No special handling for static provider
110
- }
111
84
  }
112
85
  /**
113
86
  * Callback-based authentication provider
@@ -162,19 +135,6 @@ export class CallbackAuthProvider {
162
135
  }
163
136
  return {};
164
137
  }
165
- /**
166
- * Check if authenticated
167
- *
168
- * For callback-based auth, we assume authenticated if getToken exists.
169
- * The actual token validity is checked when making requests.
170
- *
171
- * @returns true (assumes authenticated, actual check happens on request)
172
- */
173
- isAuthenticated() {
174
- // For callback-based auth, we assume authenticated if getToken exists
175
- // The actual token validity is checked when making requests
176
- return true;
177
- }
178
138
  /**
179
139
  * Handle unauthorized response
180
140
  *
@@ -216,14 +176,4 @@ export class NoAuthProvider {
216
176
  async getAuthHeaders() {
217
177
  return {};
218
178
  }
219
- /**
220
- * Check if authenticated
221
- *
222
- * Always returns false (no auth configured).
223
- *
224
- * @returns false
225
- */
226
- isAuthenticated() {
227
- return false;
228
- }
229
179
  }
@@ -2,9 +2,9 @@
2
2
  * Core types for the Workflow Library
3
3
  */
4
4
  import type { Component } from 'svelte';
5
- import type { Node, Edge, XYPosition } from '@xyflow/svelte';
6
- import { ConnectionLineType } from '@xyflow/svelte';
5
+ import type { Node, Edge, XYPosition, ConnectionLineType } from '@xyflow/svelte';
7
6
  import type { EndpointConfig } from '../config/endpoints.js';
7
+ import type { AuthProvider } from './auth.js';
8
8
  /**
9
9
  * Built-in node categories that ship with FlowDrop.
10
10
  * These categories have predefined icons, colors, and display names.
@@ -1231,6 +1231,8 @@ export interface PipelineViewProps {
1231
1231
  pipelineId: string | null;
1232
1232
  workflow: Workflow;
1233
1233
  endpointConfig: EndpointConfig;
1234
+ /** Auth provider for building authenticated API clients in custom views. */
1235
+ authProvider?: AuthProvider;
1234
1236
  refreshTrigger?: number;
1235
1237
  }
1236
1238
  /** A custom view injected into the PipelinePanel view switcher. */
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Core types for the Workflow Library
3
3
  */
4
- import { ConnectionLineType } from '@xyflow/svelte';
5
4
  /**
6
5
  * Default workflow format used when none is specified.
7
6
  */
@@ -5,10 +5,14 @@
5
5
  * visual styling to workflow edges based on source port data types.
6
6
  * Used by both the visual editor and the command DSL system.
7
7
  */
8
- import { MarkerType } from '@xyflow/svelte';
9
8
  import { extractPortId } from '../utils/handleIds.js';
10
9
  import { isLoopbackEdge } from '../utils/connections.js';
11
10
  import { EDGE_MARKER_SIZES } from '../config/constants.js';
11
+ // xyflow's `MarkerType.ArrowClosed`, inlined as its literal value. This module
12
+ // is shared with the headless command DSL (reachable from `@flowdrop/flowdrop/core`),
13
+ // so it must NOT carry a runtime import of @xyflow/svelte — a type-only import
14
+ // plus this constant keeps the marker type-safe without the dependency.
15
+ const ARROW_CLOSED_MARKER = 'arrowclosed';
12
16
  /**
13
17
  * Check if a port ID matches a dynamic branch in a Gateway node.
14
18
  * Gateway nodes store branches in config.branches array.
@@ -101,7 +105,7 @@ export function applyConnectionStyling(edge, sourceNode, targetNode) {
101
105
  'stroke: var(--fd-edge-loopback); stroke-dasharray: var(--fd-edge-loopback-dasharray); stroke-width: var(--fd-edge-loopback-width); opacity: var(--fd-edge-loopback-opacity);';
102
106
  edge.class = 'flowdrop--edge--loopback';
103
107
  edge.markerEnd = {
104
- type: MarkerType.ArrowClosed,
108
+ type: ARROW_CLOSED_MARKER,
105
109
  ...EDGE_MARKER_SIZES.loopback,
106
110
  color: 'var(--fd-edge-loopback)'
107
111
  };
@@ -110,7 +114,7 @@ export function applyConnectionStyling(edge, sourceNode, targetNode) {
110
114
  edge.style = 'stroke: var(--fd-edge-trigger); stroke-width: var(--fd-edge-trigger-width);';
111
115
  edge.class = 'flowdrop--edge--trigger';
112
116
  edge.markerEnd = {
113
- type: MarkerType.ArrowClosed,
117
+ type: ARROW_CLOSED_MARKER,
114
118
  ...EDGE_MARKER_SIZES.trigger,
115
119
  color: 'var(--fd-edge-trigger)'
116
120
  };
@@ -119,7 +123,7 @@ export function applyConnectionStyling(edge, sourceNode, targetNode) {
119
123
  edge.style = 'stroke: var(--fd-edge-tool); stroke-dasharray: 5 3;';
120
124
  edge.class = 'flowdrop--edge--tool';
121
125
  edge.markerEnd = {
122
- type: MarkerType.ArrowClosed,
126
+ type: ARROW_CLOSED_MARKER,
123
127
  ...EDGE_MARKER_SIZES.tool,
124
128
  color: 'var(--fd-edge-tool)'
125
129
  };
@@ -129,7 +133,7 @@ export function applyConnectionStyling(edge, sourceNode, targetNode) {
129
133
  edge.style = 'stroke: var(--fd-edge-data);';
130
134
  edge.class = 'flowdrop--edge--data';
131
135
  edge.markerEnd = {
132
- type: MarkerType.ArrowClosed,
136
+ type: ARROW_CLOSED_MARKER,
133
137
  ...EDGE_MARKER_SIZES.data,
134
138
  color: 'var(--fd-edge-data)'
135
139
  };
@@ -1,25 +1,46 @@
1
1
  /**
2
- * Fetch authentication utilities
2
+ * Authenticated fetch
3
3
  *
4
- * Shared logic for building HTTP headers with authentication.
5
- * Used by components that make authenticated API requests (e.g., FormAutocomplete).
4
+ * The single fetch path for FlowDrop's per-instance services. It builds request
5
+ * headers (static endpoint headers + the {@link AuthProvider}'s headers + any
6
+ * caller headers) and uniformly applies the auth lifecycle: on `401` it invokes
7
+ * `onUnauthorized()` and — if that reports a successful refresh — retries the
8
+ * request once with freshly fetched headers; on `403` it invokes
9
+ * `onForbidden()`. Callers receive the raw {@link Response} and keep their own
10
+ * status/parse handling.
11
+ *
12
+ * Routing every service through this helper means a configured `AuthProvider`
13
+ * authenticates *and* refreshes consistently — matching the behaviour the typed
14
+ * workflow/node API gets from {@link EnhancedFlowDropApiClient}.
6
15
  *
7
16
  * @module utils/fetchWithAuth
8
17
  */
18
+ import type { EndpointConfig } from '../config/endpoints.js';
9
19
  import type { AuthProvider } from '../types/auth.js';
10
20
  /**
11
- * Build fetch headers with optional authentication
12
- *
13
- * Constructs standard JSON request headers and merges in authentication
14
- * headers from the provided AuthProvider, if available.
21
+ * Options describing how to authenticate and where the base headers come from.
15
22
  *
16
- * @param authProvider - Optional auth provider to get headers from
17
- * @returns Promise resolving to a complete headers object
23
+ * Provide `config` + `endpointKey` to source static endpoint headers (the usual
24
+ * case for endpoint-keyed services), or `baseHeaders` for ad-hoc requests that
25
+ * are not tied to a configured endpoint (e.g. a user-configured autocomplete
26
+ * URL). When neither is given the request carries only JSON defaults plus auth.
27
+ */
28
+ export interface AuthenticatedFetchOptions {
29
+ /** Auth provider supplying `Authorization` etc. and 401/403 lifecycle hooks. */
30
+ authProvider?: AuthProvider;
31
+ /** Endpoint configuration, used with `endpointKey` to look up static headers. */
32
+ config?: EndpointConfig;
33
+ /** Endpoint key identifying which static headers to apply. */
34
+ endpointKey?: string;
35
+ /** Base headers for requests not tied to a configured endpoint. */
36
+ baseHeaders?: Record<string, string>;
37
+ }
38
+ /**
39
+ * Perform an authenticated fetch with consistent 401/403 handling.
18
40
  *
19
- * @example
20
- * ```typescript
21
- * const headers = await buildFetchHeaders(authProvider);
22
- * const response = await fetch(url, { headers });
23
- * ```
41
+ * @param url - The fully-resolved request URL
42
+ * @param init - Standard fetch options (method, body, signal, headers, …)
43
+ * @param opts - Auth provider and header source
44
+ * @returns The raw {@link Response}; callers handle `.ok`/parsing themselves
24
45
  */
25
- export declare function buildFetchHeaders(authProvider?: AuthProvider): Promise<Record<string, string>>;
46
+ export declare function authenticatedFetch(url: string, init?: RequestInit, opts?: AuthenticatedFetchOptions): Promise<Response>;
@@ -1,34 +1,64 @@
1
1
  /**
2
- * Fetch authentication utilities
2
+ * Authenticated fetch
3
3
  *
4
- * Shared logic for building HTTP headers with authentication.
5
- * Used by components that make authenticated API requests (e.g., FormAutocomplete).
4
+ * The single fetch path for FlowDrop's per-instance services. It builds request
5
+ * headers (static endpoint headers + the {@link AuthProvider}'s headers + any
6
+ * caller headers) and uniformly applies the auth lifecycle: on `401` it invokes
7
+ * `onUnauthorized()` and — if that reports a successful refresh — retries the
8
+ * request once with freshly fetched headers; on `403` it invokes
9
+ * `onForbidden()`. Callers receive the raw {@link Response} and keep their own
10
+ * status/parse handling.
11
+ *
12
+ * Routing every service through this helper means a configured `AuthProvider`
13
+ * authenticates *and* refreshes consistently — matching the behaviour the typed
14
+ * workflow/node API gets from {@link EnhancedFlowDropApiClient}.
6
15
  *
7
16
  * @module utils/fetchWithAuth
8
17
  */
18
+ import { getEndpointHeaders } from '../config/endpoints.js';
19
+ const DEFAULT_HEADERS = {
20
+ Accept: 'application/json',
21
+ 'Content-Type': 'application/json'
22
+ };
9
23
  /**
10
- * Build fetch headers with optional authentication
11
- *
12
- * Constructs standard JSON request headers and merges in authentication
13
- * headers from the provided AuthProvider, if available.
14
- *
15
- * @param authProvider - Optional auth provider to get headers from
16
- * @returns Promise resolving to a complete headers object
24
+ * Build the merged header set: base headers < auth headers < per-request headers.
17
25
  *
18
- * @example
19
- * ```typescript
20
- * const headers = await buildFetchHeaders(authProvider);
21
- * const response = await fetch(url, { headers });
22
- * ```
26
+ * Re-invoked for the retry so a refreshed token is picked up.
23
27
  */
24
- export async function buildFetchHeaders(authProvider) {
25
- const headers = {
26
- Accept: 'application/json',
27
- 'Content-Type': 'application/json'
28
- };
29
- if (authProvider) {
30
- const authHeaders = await authProvider.getAuthHeaders();
31
- Object.assign(headers, authHeaders);
28
+ async function buildHeaders(init, opts) {
29
+ const headers = opts.config && opts.endpointKey
30
+ ? getEndpointHeaders(opts.config, opts.endpointKey)
31
+ : { ...DEFAULT_HEADERS, ...opts.baseHeaders };
32
+ if (opts.authProvider) {
33
+ Object.assign(headers, await opts.authProvider.getAuthHeaders());
34
+ }
35
+ if (init.headers) {
36
+ Object.assign(headers, init.headers);
32
37
  }
33
38
  return headers;
34
39
  }
40
+ /**
41
+ * Perform an authenticated fetch with consistent 401/403 handling.
42
+ *
43
+ * @param url - The fully-resolved request URL
44
+ * @param init - Standard fetch options (method, body, signal, headers, …)
45
+ * @param opts - Auth provider and header source
46
+ * @returns The raw {@link Response}; callers handle `.ok`/parsing themselves
47
+ */
48
+ export async function authenticatedFetch(url, init = {}, opts = {}) {
49
+ const headers = await buildHeaders(init, opts);
50
+ let response = await fetch(url, { ...init, headers });
51
+ // 401 → let the provider refresh, then retry once with fresh headers.
52
+ if (response.status === 401 && opts.authProvider?.onUnauthorized) {
53
+ const refreshed = await opts.authProvider.onUnauthorized();
54
+ if (refreshed) {
55
+ const retryHeaders = await buildHeaders(init, opts);
56
+ response = await fetch(url, { ...init, headers: retryHeaders });
57
+ }
58
+ }
59
+ // 403 → notify the provider (e.g. surface a permission error).
60
+ if (response.status === 403 && opts.authProvider?.onForbidden) {
61
+ await opts.authProvider.onForbidden();
62
+ }
63
+ return response;
64
+ }
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Works with both built-in types and custom registered types.
11
11
  */
12
- import { resolveBuiltinAlias, isBuiltinType } from '../registry/builtinNodes.js';
12
+ import { resolveBuiltinAlias, isBuiltinType } from '../registry/builtinNodeTypes.js';
13
13
  /**
14
14
  * Display names for built-in node types.
15
15
  */
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": "2.0.0-beta.1",
6
+ "version": "2.0.0-beta.2",
7
7
  "author": "Shibin Das (D34dMan)",
8
8
  "bugs": {
9
9
  "url": "https://github.com/flowdrop-io/flowdrop/issues"
@@ -289,6 +289,7 @@
289
289
  "watch:build": "npm-watch build",
290
290
  "preview": "vite preview",
291
291
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
292
+ "check:bundle": "node scripts/check-bundle.mjs",
292
293
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
293
294
  "lint": "eslint . && prettier --check .",
294
295
  "test": "vitest run",