@burdenoff/fluidgrids-fe-sdk 2026.1023.2 → 2026.1023.4

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.
@@ -1,4 +1,4 @@
1
- export { ExecutionDetailPage } from './chunk-MP7MRTB4.js';
1
+ export { ExecutionDetailPage } from './chunk-VMVY65DC.js';
2
2
  import './chunk-XCQROWIZ.js';
3
3
  import './chunk-LLPLOWMA.js';
4
4
  import './chunk-QBFAJNUC.js';
@@ -11,9 +11,25 @@ import { Tabs, TabsList, TabsTrigger, TabsContent, Input, Switch, Select, Select
11
11
  import { toast } from 'sonner';
12
12
  import { jsxs, jsx } from 'react/jsx-runtime';
13
13
 
14
+ // src/modules/settings/utils/error-trigger.ts
15
+ var ERROR_TRIGGER_NODE_TYPE = "internal:error-trigger";
16
+ function asRecord(value) {
17
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
18
+ }
19
+ function hasErrorTriggerNode(definitionJson) {
20
+ const definition = asRecord(definitionJson);
21
+ const dataNodes = asRecord(definition?.data)?.nodes;
22
+ const nodes = Array.isArray(dataNodes) ? dataNodes : definition?.nodes;
23
+ return Array.isArray(nodes) && nodes.some((node) => asRecord(node)?.type === ERROR_TRIGGER_NODE_TYPE);
24
+ }
25
+
26
+ // src/modules/settings/utils/optional-positive-int.ts
27
+ function parseOptionalPositiveInt(value) {
28
+ if (value.trim() === "") return void 0;
29
+ const parsed = Number.parseInt(value, 10);
30
+ return Number.isFinite(parsed) ? Math.max(1, parsed) : void 0;
31
+ }
14
32
  var DEFAULT_EXECUTION_SETTINGS = {
15
- timeout: 300,
16
- maxParallelism: 5,
17
33
  onError: "stop"
18
34
  };
19
35
  var DEFAULT_SETTINGS = {
@@ -183,6 +199,15 @@ function ExecutionTab({ workflowKey }) {
183
199
  }, [workflow?.definitionJson]);
184
200
  const [form, setForm] = useState(persistedConfig);
185
201
  useEffect(() => setForm(persistedConfig), [persistedConfig]);
202
+ const selectedErrorWorkflowKey = form.errorWorkflowKey;
203
+ const { data: errorWorkflowData } = useWorkflowDefinitionQuery({
204
+ variables: { workflowKey: selectedErrorWorkflowKey ?? "" },
205
+ skip: !selectedErrorWorkflowKey
206
+ });
207
+ const selectedErrorWorkflow = errorWorkflowData?.workflowDefinition;
208
+ const errorWorkflowLacksTrigger = Boolean(
209
+ selectedErrorWorkflowKey && selectedErrorWorkflow && !hasErrorTriggerNode(selectedErrorWorkflow.definitionJson)
210
+ );
186
211
  const handleSave = useCallback(async () => {
187
212
  if (!workflow?.id) return;
188
213
  try {
@@ -267,7 +292,7 @@ function ExecutionTab({ workflowKey }) {
267
292
  ),
268
293
  description: tr(
269
294
  "fluidgrids.settings.execution.defaultTimeoutDescription",
270
- "Maximum time this workflow's execution can run (in seconds)"
295
+ "Stop a run that keeps running longer than this. Time a run spends paused (a Wait over a minute, an approval, a person's answer) does not count. Leave empty for no limit."
271
296
  ),
272
297
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
273
298
  /* @__PURE__ */ jsx(
@@ -275,10 +300,18 @@ function ExecutionTab({ workflowKey }) {
275
300
  {
276
301
  type: "number",
277
302
  min: 1,
278
- value: form.timeout ?? DEFAULT_EXECUTION_SETTINGS.timeout,
303
+ value: form.timeout ?? "",
304
+ placeholder: tr(
305
+ "fluidgrids.settings.execution.timeoutPlaceholder",
306
+ "No timeout"
307
+ ),
308
+ "aria-label": tr(
309
+ "fluidgrids.settings.execution.defaultTimeoutLabel",
310
+ "Timeout"
311
+ ),
279
312
  onChange: (e) => setForm((f) => ({
280
313
  ...f,
281
- timeout: Math.max(1, parseInt(e.target.value, 10) || 1)
314
+ timeout: parseOptionalPositiveInt(e.target.value)
282
315
  })),
283
316
  disabled: loading,
284
317
  className: "w-[100px] text-right"
@@ -295,25 +328,33 @@ function ExecutionTab({ workflowKey }) {
295
328
  SettingRow,
296
329
  {
297
330
  label: tr(
298
- "fluidgrids.settings.execution.concurrencyLimitLabel",
299
- "Concurrency Limit"
331
+ "fluidgrids.settings.execution.maxParallelLabel",
332
+ "Max parallel executions"
300
333
  ),
301
334
  description: tr(
302
- "fluidgrids.settings.execution.concurrencyLimitDescription",
303
- "Maximum number of this workflow's executions running simultaneously"
335
+ "fluidgrids.settings.execution.maxParallelDescription",
336
+ "How many production runs of this workflow may run at once. Extra runs wait in a queue and start in order; manual test runs are never held back. Leave empty for no limit."
304
337
  ),
305
338
  children: /* @__PURE__ */ jsx(
306
339
  Input,
307
340
  {
308
341
  type: "number",
309
342
  min: 1,
310
- value: form.maxParallelism ?? DEFAULT_EXECUTION_SETTINGS.maxParallelism,
343
+ value: form.maxParallelism ?? "",
344
+ placeholder: tr(
345
+ "fluidgrids.settings.execution.maxParallelPlaceholder",
346
+ "No limit"
347
+ ),
348
+ "aria-label": tr(
349
+ "fluidgrids.settings.execution.maxParallelLabel",
350
+ "Max parallel executions"
351
+ ),
311
352
  onChange: (e) => setForm((f) => ({
312
353
  ...f,
313
- maxParallelism: Math.max(1, parseInt(e.target.value, 10) || 1)
354
+ maxParallelism: parseOptionalPositiveInt(e.target.value)
314
355
  })),
315
356
  disabled: loading,
316
- className: "w-[80px] text-right"
357
+ className: "w-[100px] text-right"
317
358
  }
318
359
  )
319
360
  }
@@ -357,34 +398,50 @@ function ExecutionTab({ workflowKey }) {
357
398
  ),
358
399
  description: tr(
359
400
  "fluidgrids.settings.execution.errorWorkflowDescription",
360
- "Workflow key to run when this workflow's execution fails (optional)"
401
+ "Runs when a production run of this workflow fails, starting at that workflow's Error Trigger (optional)"
361
402
  ),
362
- children: /* @__PURE__ */ jsxs(
363
- Select,
364
- {
365
- value: form.errorWorkflowKey ?? "__none__",
366
- onValueChange: (val) => setForm((f) => ({
367
- ...f,
368
- errorWorkflowKey: val === "__none__" ? void 0 : val
369
- })),
370
- disabled: loading || otherWorkflowsLoading,
371
- children: [
372
- /* @__PURE__ */ jsx(SelectTrigger, { className: "w-[220px] bg-background-secondary border-border-secondary", children: /* @__PURE__ */ jsx(
373
- SelectValue,
374
- {
375
- placeholder: tr(
376
- "fluidgrids.settings.execution.errorWorkflowPlaceholder",
403
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
404
+ /* @__PURE__ */ jsxs(
405
+ Select,
406
+ {
407
+ value: form.errorWorkflowKey ?? "__none__",
408
+ onValueChange: (val) => setForm((f) => ({
409
+ ...f,
410
+ errorWorkflowKey: val === "__none__" ? void 0 : val
411
+ })),
412
+ disabled: loading || otherWorkflowsLoading,
413
+ children: [
414
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "w-[220px] bg-background-secondary border-border-secondary", children: /* @__PURE__ */ jsx(
415
+ SelectValue,
416
+ {
417
+ placeholder: tr(
418
+ "fluidgrids.settings.execution.errorWorkflowPlaceholder",
419
+ "None"
420
+ )
421
+ }
422
+ ) }),
423
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
424
+ /* @__PURE__ */ jsx(SelectItem, { value: "__none__", children: tr(
425
+ "fluidgrids.settings.execution.errorWorkflowNone",
377
426
  "None"
378
- )
379
- }
380
- ) }),
381
- /* @__PURE__ */ jsxs(SelectContent, { children: [
382
- /* @__PURE__ */ jsx(SelectItem, { value: "__none__", children: tr("fluidgrids.settings.execution.errorWorkflowNone", "None") }),
383
- errorWorkflowOptions.map((w) => /* @__PURE__ */ jsx(SelectItem, { value: w.workflowKey, children: w.name }, w.workflowKey))
384
- ] })
385
- ]
386
- }
387
- )
427
+ ) }),
428
+ errorWorkflowOptions.map((w) => /* @__PURE__ */ jsx(SelectItem, { value: w.workflowKey, children: w.name }, w.workflowKey))
429
+ ] })
430
+ ]
431
+ }
432
+ ),
433
+ errorWorkflowLacksTrigger && /* @__PURE__ */ jsx(
434
+ "p",
435
+ {
436
+ className: "max-w-[260px] text-right text-xs text-status-warning-text",
437
+ "data-testid": "error-workflow-no-trigger",
438
+ children: tr(
439
+ "fluidgrids.settings.execution.errorWorkflowNoTrigger",
440
+ "This workflow has no Error Trigger, so it will not start. Add an Error Trigger node to it."
441
+ )
442
+ }
443
+ )
444
+ ] })
388
445
  }
389
446
  ),
390
447
  /* @__PURE__ */ jsx(
@@ -674,7 +674,7 @@ function RunHumanRequests({ requests }) {
674
674
  children: [
675
675
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-text-primary", children: [
676
676
  /* @__PURE__ */ jsx(Icon, { icon: "mdi:account-clock-outline", width: 18 }),
677
- tr("fluidgrids.humanResponse.runHeading", "Waiting on people")
677
+ requests.some((request) => request.status === "PENDING") ? tr("fluidgrids.humanResponse.runHeading", "Waiting on people") : tr("fluidgrids.humanResponse.runHeadingDone", "People asked")
678
678
  ] }),
679
679
  requests.map((request) => /* @__PURE__ */ jsx(RunHumanRequestRow, { request }, request.id))
680
680
  ]
@@ -723,6 +723,57 @@ function RunHumanRequestRow({ request }) {
723
723
  }
724
724
  );
725
725
  }
726
+
727
+ // src/modules/executions/utils/run-queue-marker.ts
728
+ function isRecord(value) {
729
+ return typeof value === "object" && value !== null && !Array.isArray(value);
730
+ }
731
+ function readRunQueueMarker(context) {
732
+ if (!isRecord(context)) return null;
733
+ const marker = context.concurrency;
734
+ if (!isRecord(marker)) return null;
735
+ const { queuedAt, checkedAt, limit, active, position } = marker;
736
+ if (typeof queuedAt !== "string" || typeof checkedAt !== "string" || typeof limit !== "number" || typeof active !== "number" || typeof position !== "number") {
737
+ return null;
738
+ }
739
+ return { queuedAt, checkedAt, limit, active, position };
740
+ }
741
+ function RunQueuedBanner({
742
+ status,
743
+ context
744
+ }) {
745
+ const tr = useTr();
746
+ if (status !== "PENDING") return null;
747
+ const marker = readRunQueueMarker(context);
748
+ if (!marker) return null;
749
+ return /* @__PURE__ */ jsxs(
750
+ "div",
751
+ {
752
+ className: "mb-6 rounded-lg border border-border-secondary bg-background-secondary p-4",
753
+ "data-testid": "run-queued-banner",
754
+ children: [
755
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-text-primary", children: [
756
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:tray-full", width: 18 }),
757
+ tr("fluidgrids.executions.queued.heading", "Queued")
758
+ ] }),
759
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-secondary", children: tr(
760
+ "fluidgrids.executions.queued.body",
761
+ "This workflow allows {{limit}} runs at a time and {{active}} are in progress. This run is number {{position}} in line and starts automatically when a slot frees up.",
762
+ {
763
+ limit: String(marker.limit),
764
+ active: String(marker.active),
765
+ position: String(marker.position)
766
+ }
767
+ ) }),
768
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-text-tertiary", children: tr(
769
+ "fluidgrids.executions.queued.since",
770
+ "Queued since {{time}} \xB7 change the limit in the workflow's Settings (Max parallel executions)",
771
+ { time: new Date(marker.queuedAt).toLocaleString() }
772
+ ) })
773
+ ]
774
+ }
775
+ );
776
+ }
726
777
  function ExecutionDetailPage() {
727
778
  const tr = useTr();
728
779
  const { runId } = useParams();
@@ -1311,6 +1362,7 @@ function ExecutionDetailPage() {
1311
1362
  ] }),
1312
1363
  /* @__PURE__ */ jsx("p", { className: "text-[var(--color-status-error-text)]", children: execution.errorMessage })
1313
1364
  ] }),
1365
+ /* @__PURE__ */ jsx(RunQueuedBanner, { status: execution.status, context: execution.context }),
1314
1366
  /* @__PURE__ */ jsx(RunHumanRequests, { requests: humanRequests }),
1315
1367
  /* @__PURE__ */ jsx("div", { className: "border-b border-border-secondary mb-6", children: /* @__PURE__ */ jsx("nav", { className: "flex gap-4 -mb-px", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
1316
1368
  "button",
@@ -1,6 +1,6 @@
1
1
  import './chunk-AD3BXTJS.js';
2
2
  export { ExecutionsListPage } from './chunk-OKWH2CHF.js';
3
- export { ExecutionDetailPage } from './chunk-MP7MRTB4.js';
3
+ export { ExecutionDetailPage } from './chunk-VMVY65DC.js';
4
4
  import './chunk-XCQROWIZ.js';
5
5
  import './chunk-LLPLOWMA.js';
6
6
  import './chunk-QBFAJNUC.js';
package/dist/index.js CHANGED
@@ -5,14 +5,14 @@ export { FirstWorkflowChecklist, FirstWorkflowChecklistView, WorkflowsList } fro
5
5
  export { GUIDED_FIRST_WORKFLOW_NAV, GUIDED_FIRST_WORKFLOW_STATE, firstWorkflowProgress, isGuidedTourIntent, pickGuidedTour } from './chunk-YGBDZ424.js';
6
6
  import './chunk-AD3BXTJS.js';
7
7
  export { ExecutionsListPage } from './chunk-OKWH2CHF.js';
8
- import { usePendingHumanRequests, HumanRequestCard } from './chunk-MP7MRTB4.js';
9
- export { ExecutionDetailPage, HumanRequestCard, RunHumanRequests, usePendingHumanRequests, useRespondToHumanRequest, useRunHumanRequests } from './chunk-MP7MRTB4.js';
8
+ import { usePendingHumanRequests, HumanRequestCard } from './chunk-VMVY65DC.js';
9
+ export { ExecutionDetailPage, HumanRequestCard, RunHumanRequests, usePendingHumanRequests, useRespondToHumanRequest, useRunHumanRequests } from './chunk-VMVY65DC.js';
10
10
  import { TagFilterSelect } from './chunk-XCQROWIZ.js';
11
11
  import './chunk-LLPLOWMA.js';
12
12
  export { HistoryPage } from './chunk-UKMXGBU3.js';
13
13
  export { NodesPage, fetchAllNodesWithManifests, useLazyNodeManifest, useNodeEndpoint, useNodeEndpoints, useNodeManifest } from './chunk-HAYSY6TZ.js';
14
14
  export { DataSinkDashboardPage, ErrorAnalyticsPage, InsightsPage } from './chunk-KAOLPDNX.js';
15
- export { SettingsPage } from './chunk-YTLPI7UV.js';
15
+ export { SettingsPage } from './chunk-EB344COE.js';
16
16
  export { FLUIDGRIDS_FLAGS, useDataSinkData, useDataSinks, useFluidGridsFlag, useInsightsData, useLogStreamDestinationActions, useLogStreamDestinations, useRunReplayActions } from './chunk-2QTKDR23.js';
17
17
  import './chunk-QBFAJNUC.js';
18
18
  import { ThemeProvider, extractTemplateGraph } from './chunk-3BK65TDZ.js';
@@ -81,7 +81,7 @@ var ExecutionsListPage2 = lazy(
81
81
  }))
82
82
  );
83
83
  var ExecutionDetailPage2 = lazy(
84
- () => import('./ExecutionDetailPage-K3MPJLTL.js').then((m) => ({
84
+ () => import('./ExecutionDetailPage-FDX242B5.js').then((m) => ({
85
85
  default: m.ExecutionDetailPage
86
86
  }))
87
87
  );
package/dist/settings.js CHANGED
@@ -1,4 +1,4 @@
1
- export { SettingsPage } from './chunk-YTLPI7UV.js';
1
+ export { SettingsPage } from './chunk-EB344COE.js';
2
2
  import './chunk-2QTKDR23.js';
3
3
  import './chunk-QBFAJNUC.js';
4
4
  import './chunk-4VJ3MDOV.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fluidgrids-fe-sdk",
3
- "version": "2026.1023.2",
3
+ "version": "2026.1023.4",
4
4
  "description": "FluidGrids Workflow Builder SDK - Embeddable visual workflow automation",
5
5
  "type": "module",
6
6
  "imports": {