@burdenoff/fluidgrids-fe-sdk 2026.1031.4 → 2026.1031.6

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 (43) hide show
  1. package/dist/ExecutionDetailPage-GKX4OUUR.js +23 -0
  2. package/dist/{WorkflowsList-BZXBTGNL.js → ExecutionsListPage-ZZOUZQBS.js} +9 -8
  3. package/dist/{TemplateDetailPanel-MTKW3SPM.js → TemplateDetailPanel-WH74G7PF.js} +18 -7
  4. package/dist/{WorkflowBuilder-N3ZUYFRK.js → WorkflowBuilder-4QEJNKVE.js} +12 -11
  5. package/dist/{ExecutionsListPage-AYDGFU6W.js → WorkflowsList-4CTHUYM7.js} +9 -8
  6. package/dist/{chunk-VJWJGSRS.js → chunk-34HHBNZE.js} +1 -1
  7. package/dist/{chunk-P3TGCUXP.js → chunk-34XK5BC7.js} +5 -37
  8. package/dist/{chunk-JFQGODKK.js → chunk-35N3PEO6.js} +1 -1
  9. package/dist/{chunk-DHR2YURL.js → chunk-3KKGE7CE.js} +164 -9
  10. package/dist/{chunk-XX6D7IND.js → chunk-5XQMYHJ7.js} +2 -2
  11. package/dist/{chunk-OGYAC7QW.js → chunk-AHDYETEE.js} +1 -1
  12. package/dist/{chunk-WHPQRYZD.js → chunk-CAZHE2MQ.js} +6 -5
  13. package/dist/{chunk-GV5RJ6BU.js → chunk-E7AQAPZN.js} +33 -22
  14. package/dist/{chunk-6PGLJWIC.js → chunk-EPYTVCYH.js} +30 -188
  15. package/dist/{chunk-DJAGZUHW.js → chunk-FUYHCONW.js} +1257 -34
  16. package/dist/{chunk-ADTEMSJI.js → chunk-GMVXPC5N.js} +849 -4
  17. package/dist/{chunk-IGROOI7E.js → chunk-GREPXIXS.js} +2 -2
  18. package/dist/{chunk-PTROESUB.js → chunk-HCKYGGUU.js} +4 -4
  19. package/dist/{chunk-Z3FYBDE3.js → chunk-HQX7JZLY.js} +33 -1
  20. package/dist/{chunk-QKQOTEAB.js → chunk-I5BPMBIV.js} +1 -1
  21. package/dist/{chunk-R4Q37Q4X.js → chunk-IJBT25CS.js} +3 -3
  22. package/dist/{chunk-44KAAKFK.js → chunk-JUHSLK7C.js} +5 -5
  23. package/dist/{chunk-AW3UXMNI.js → chunk-KEYKB4E7.js} +12 -1
  24. package/dist/{chunk-P2VAHDGO.js → chunk-NLRQSHRS.js} +2 -2
  25. package/dist/{chunk-BHD4CDLL.js → chunk-P4R4ACCC.js} +1 -1
  26. package/dist/{chunk-CIR7WF4W.js → chunk-PP62ABIG.js} +1 -1
  27. package/dist/{chunk-Q3HKE627.js → chunk-RYUSWVIR.js} +3 -3
  28. package/dist/{chunk-INMYNHJL.js → chunk-TVOXRODW.js} +34 -2
  29. package/dist/{chunk-PKG6BCHB.js → chunk-VWFA3RQR.js} +2 -2
  30. package/dist/{chunk-475XP5J7.js → chunk-ZEHMJURX.js} +6 -5
  31. package/dist/chunk-ZUVFD5KV.js +186 -0
  32. package/dist/executions.js +13 -12
  33. package/dist/history.js +5 -4
  34. package/dist/index.d.ts +741 -4
  35. package/dist/index.js +45 -40
  36. package/dist/nodes.js +2 -2
  37. package/dist/observability.js +7 -6
  38. package/dist/settings.js +7 -6
  39. package/dist/templates.js +11 -10
  40. package/dist/workflow-builder.js +13 -12
  41. package/dist/workflows-list.js +9 -8
  42. package/package.json +1 -1
  43. package/dist/ExecutionDetailPage-WOOORXXU.js +0 -22
@@ -1,20 +1,22 @@
1
1
  import { deepClone, applyTemplateInputs } from './chunk-GEMNVFPK.js';
2
2
  import { templatePreview } from './chunk-B6CCE2RW.js';
3
3
  import { Input } from './chunk-IEVSFCFS.js';
4
- import { Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, DialogDescription as DialogDescription$1, DialogFooter } from './chunk-YXMFRQYV.js';
4
+ import { Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, DialogDescription as DialogDescription$1, DialogFooter as DialogFooter$1 } from './chunk-YXMFRQYV.js';
5
5
  import { Badge as Badge$1, Button } from './chunk-BQXVT32O.js';
6
- import { QuotaExhaustedDialog, useWorkflowCodeExport, orderedExportFiles, fileBaseName, downloadWorkflowCodeExport, isLosslessReport, sortFidelityItems, isLossyFidelityItem, lineOfNode } from './chunk-IGROOI7E.js';
7
- import { EntityTagPicker, useWorkflowProposal, WorkflowProposalCard, useRunHistory, RunHistoryView, SignalRunDialog } from './chunk-ADTEMSJI.js';
6
+ import { QuotaExhaustedDialog, useWorkflowCodeExport, orderedExportFiles, fileBaseName, downloadWorkflowCodeExport, isLosslessReport, sortFidelityItems, isLossyFidelityItem, lineOfNode } from './chunk-GREPXIXS.js';
7
+ import { emptyTestCaseDraft, EntityTagPicker, useWorkflowProposal, WorkflowProposalCard, useRunHistory, RunHistoryView, SignalRunDialog, readAssertRulesSetting, rowsFromRules, normaliseAssertRules, ruleFromRow, describeAssertRuleProblem, operatorSpec, ASSERT_OPERATORS, emptyAssertRuleRow, useWorkflowTests, pinnedOutputsFromCanvas, enabledCaseIds, isTestRunOpen, MAX_TEST_CASES, draftFromTestCase, testRunSummaryText, testStatusLabel, TEST_STATUS_TONE_CLASS, testStatusTone, MAX_CASE_NAME, nextSortOrder, draftToInput } from './chunk-GMVXPC5N.js';
8
+ import { buildNodeDisplayMap } from './chunk-TVOXRODW.js';
8
9
  import { AiCreditBalanceSlot } from './chunk-Y53CVZVV.js';
9
- import { useRestoreWorkflowVersion, useWorkflowVersions, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-PKG6BCHB.js';
10
- import { isAgentNodeType, AgentDefinitionSelectField } from './chunk-JFQGODKK.js';
10
+ import { useRestoreWorkflowVersion, useWorkflowVersions, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-VWFA3RQR.js';
11
+ import { isAgentNodeType, AgentDefinitionSelectField } from './chunk-35N3PEO6.js';
11
12
  import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
12
- import { leaseCallQueue, NODE_CONFIGS, createConfiguredNode, resolveOutputHandles, resolveInputHandles, isNodeConfigsInitialized, useFlowStore, getNodeDetail, useSaveStatusStore, isNodeTypeHiddenFromPalette, liveInternalOperations, resolveNodeOperation, readDisplayOptions, parseParameterValue, canAutosave, STICKY_NOTE_TYPE, isNodeTrigger, resolveUiSpec, RUNS_QUOTA_NAME, throwIfResultError, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, describeOperationError, ensureNodeConfigsLoaded, useEditLeaseStore, useEditLeaseLocked, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, usePaletteGates, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, BuilderReadOnlyProvider, refetchStoreNodes, AVAILABLE_NODES, EDIT_SESSION_ID, useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, useDurableFunctionNodesEnabled, CODE_WORKFLOW_NODE_TYPE, SdkPermissionButton, bannerOf, useSaveWorkflow, useBuilderReadOnly, fieldsFromValue, filterPaletteNodes, useFluidGridsAnalytics, sessionBeforeReload, forgetSessionForReload, releasesOnClose, editStateOf, workflowFingerprint, isLocked, buildStickyNode, useNodeRegistryRevision, fieldsFromIO, readCodeWorkflowState, useCodeWorkflowInterface, clearCodeWorkflowDefinitionCache, isParamVisible, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, shouldReleaseForIdle, tickAction, rememberSessionForReload, buildOperationSeed, useDurableParityEnabled, useConnections, ConnectionFormDialog, useFluidGridsFlag, FLUIDGRIDS_FLAGS, saveIndicatorOf, inAppLeaveTarget, declaredOutputFields, subscribeWorkflowSaved, pathToExpression, diagnosticsByPath, interfaceHandlers, declaredSignals, ExpressionEditor, toCodeMirrorDiagnostics, formatParameterValue, leaveNeedsConfirmation, phaseAfterAcquire, rememberOwnStatusChange, snapshotForSave, childRunIdOf, defaultSourcePath, projectLanguage, changedSourceFiles, isEditableSourcePath, sourceLanguageOf, interfaceFields, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, loadDefinitionJson, openRunQuotaUpgradePrompt, isTriggerOperation, canRenderOperationPanel, runPreparationOf, resolveOperationName, savesInPlace, AUTOSAVE_RETRY_DELAYS_MS, AUTOSAVE_DEBOUNCE_MS, rememberRevision, phaseAfterTakeOver } from './chunk-6PGLJWIC.js';
13
+ import { leaseCallQueue, NODE_CONFIGS, createConfiguredNode, resolveOutputHandles, resolveInputHandles, isNodeConfigsInitialized, useFlowStore, getNodeDetail, useSaveStatusStore, isNodeTypeHiddenFromPalette, liveInternalOperations, resolveNodeOperation, readDisplayOptions, parseParameterValue, canAutosave, STICKY_NOTE_TYPE, isNodeTrigger, resolveUiSpec, RUNS_QUOTA_NAME, throwIfResultError, BILLING_PLANS_PATH, emitUpgradeClicked, FluidGridsContext, emitWorkflowCreated, emitTemplateUsed, describeOperationError, ensureNodeConfigsLoaded, useEditLeaseStore, useEditLeaseLocked, registerWorkflowNodeTypes, emitNodeInstalled, useNodeExecution, usePaletteGates, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useIsValidConnection, BuilderReadOnlyProvider, refetchStoreNodes, AVAILABLE_NODES, EDIT_SESSION_ID, useInsertNode, NODE_TYPE_DRAG_DATA_FORMAT, NODE_OPERATION_DRAG_DATA_FORMAT, useDurableFunctionNodesEnabled, CODE_WORKFLOW_NODE_TYPE, bannerOf, useSaveWorkflow, useBuilderReadOnly, fieldsFromValue, filterPaletteNodes, useFluidGridsAnalytics, sessionBeforeReload, forgetSessionForReload, releasesOnClose, editStateOf, workflowFingerprint, isLocked, buildStickyNode, useNodeRegistryRevision, fieldsFromIO, readCodeWorkflowState, useCodeWorkflowInterface, clearCodeWorkflowDefinitionCache, isParamVisible, ConnectionSelector, structuredParameterType, nodeAccent, NodeTypeIcon, NODES_METADATA, shouldReleaseForIdle, tickAction, rememberSessionForReload, buildOperationSeed, useDurableParityEnabled, useConnections, ConnectionFormDialog, useWorkflowTestsEnabled, useFluidGridsFlag, FLUIDGRIDS_FLAGS, saveIndicatorOf, inAppLeaveTarget, declaredOutputFields, subscribeWorkflowSaved, pathToExpression, diagnosticsByPath, interfaceHandlers, declaredSignals, ExpressionEditor, toCodeMirrorDiagnostics, formatParameterValue, leaveNeedsConfirmation, phaseAfterAcquire, rememberOwnStatusChange, snapshotForSave, childRunIdOf, defaultSourcePath, projectLanguage, changedSourceFiles, isEditableSourcePath, sourceLanguageOf, interfaceFields, isImageIcon, NODES, synchronizeOperationUpdate, NodePropertyPanel, loadDefinitionJson, openRunQuotaUpgradePrompt, isTriggerOperation, canRenderOperationPanel, runPreparationOf, resolveOperationName, savesInPlace, AUTOSAVE_RETRY_DELAYS_MS, AUTOSAVE_DEBOUNCE_MS, rememberRevision, phaseAfterTakeOver } from './chunk-EPYTVCYH.js';
14
+ import { SdkPermissionButton, FLUIDGRIDS_PERMISSIONS, useFluidGridsPermission } from './chunk-ZUVFD5KV.js';
13
15
  import { cn } from './chunk-JL7CASN5.js';
14
16
  import { getGlobalStoreClient } from './chunk-PXIFUMLA.js';
15
17
  import { getFluidGridsConfig } from './chunk-6AMNEX2Y.js';
16
18
  import { GetMyNodeInstallationsDocument, BrowseWorkflowTemplatesDocument, InstallWorkflowNodeDocument, SubmitWorkflowTemplateDocument, MyWorkflowTemplateSubmissionsDocument, WithdrawWorkflowTemplateSubmissionDocument, InstallPurchasedNodeDocument, BrowseWorkflowNodesDocument, UninstallWorkflowNodeDocument, CreateStoreOrderDocument, GetNodeInstallableVersionsDocument, OrganizationPrivateNodesDocument, InstallOrganizationPrivateNodeDocument, UninstallOrganizationPrivateNodeDocument } from './chunk-G77IGC3Y.js';
17
- import { WorkspaceRunsQuotaDocument, WorkflowGenerationTemplatesDocument, ReleaseWorkflowEditLeaseDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, AcquireWorkflowEditLeaseDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, WorkflowAuthoringDocument, NodeStatesDocument, PreviewScheduleDocument, WorkflowEditLeaseDocument, WorkflowVersioningDocument, WorkflowVersionDrainageDocument, ActivateWorkflowVersionCheckedDocument, WorkflowDefinitionDocument, WorkflowCodeSourceDocument, WorkflowEditLeaseStatusDocument, BuildWorkflowCodeDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument, SetWorkflowRolloutDocument, DeleteWorkflowRolloutDocument, TakeOverWorkflowEditLeaseDocument } from './chunk-AW3UXMNI.js';
19
+ import { WorkspaceRunsQuotaDocument, WorkflowGenerationTemplatesDocument, ReleaseWorkflowEditLeaseDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, AcquireWorkflowEditLeaseDocument, WorkflowEnvironmentsDocument, WorkflowPromotionRequestsDocument, DeployWorkflowVersionToDevDocument, RequestWorkflowPromotionDocument, ApproveWorkflowPromotionDocument, RejectWorkflowPromotionDocument, CancelWorkflowPromotionDocument, UpdateWorkflowEnvironmentPolicyDocument, WorkflowGitRepositoryDocument, WorkflowGitPushesDocument, WorkflowGitCommitsDocument, LinkWorkflowGitRepositoryDocument, UnlinkWorkflowGitRepositoryDocument, PushWorkflowToGitDocument, WorkflowGitDiffDocument, RestoreWorkflowFromGitDocument, WorkflowAuthoringDocument, NodeStatesDocument, PreviewScheduleDocument, WorkflowEditLeaseDocument, WorkflowVersioningDocument, WorkflowVersionDrainageDocument, ActivateWorkflowVersionCheckedDocument, WorkflowDefinitionDocument, WorkflowCodeSourceDocument, WorkflowEditLeaseStatusDocument, BuildWorkflowCodeDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument, SetWorkflowRolloutDocument, DeleteWorkflowRolloutDocument, TakeOverWorkflowEditLeaseDocument } from './chunk-KEYKB4E7.js';
18
20
  import { useTr } from './chunk-UULVIT25.js';
19
21
  import { getFluidGridsClient, waitForFluidGridsClient, getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
20
22
  import { __export } from './chunk-62GWEYJ5.js';
@@ -24,13 +26,13 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
24
26
  import * as PopoverPrimitive from '@radix-ui/react-popover';
25
27
  import { gql } from '@apollo/client';
26
28
  import { cn as cn$1 } from '@burdenoff/fe-libs/utils';
27
- import { Card as Card$1, Badge, CardContent as CardContent$1, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Button as Button$1, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Popover as Popover$1, PopoverTrigger as PopoverTrigger$1, PopoverContent as PopoverContent$1, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, Label, Textarea, AlertDialogAction, TooltipProvider, Switch, Input as Input$1, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Skeleton, Tooltip, TooltipTrigger, TooltipContent, DropdownMenuLabel, DropdownMenuSeparator } from '@burdenoff/fe-libs/ui';
29
+ import { Card as Card$1, Badge, CardContent as CardContent$1, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Button as Button$1, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Popover as Popover$1, PopoverTrigger as PopoverTrigger$1, PopoverContent as PopoverContent$1, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, Label, Textarea, AlertDialogAction, TooltipProvider, Switch, Input as Input$1, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Skeleton, Tooltip, TooltipTrigger, TooltipContent, DialogFooter, DropdownMenuLabel, DropdownMenuSeparator } from '@burdenoff/fe-libs/ui';
28
30
  import { Link, useParams, useNavigate } from 'react-router';
29
31
  import { useEventBus } from '@burdenoff/fe-libs/shared/events';
30
32
  import { useQuery, useMutation } from '@apollo/client/react';
31
33
  import { gql as gql$1 } from '@apollo/client/core';
32
34
  import 'jszip';
33
- import { Sparkles, Star, Download, Eye, Maximize2, Undo2, Redo2, LayoutGrid, Keyboard, Plus, Trash2, GitBranch, Loader2, AlertCircle, Search, RefreshCw, History, GitCompareArrows, Play, RotateCcw, Layers, Upload, Rocket, ArrowRight, Check, X, Link2, Unlink, GitCompare, ExternalLink, GitCommitHorizontal, CircleHelp, CheckCircle2, Package, SlidersHorizontal, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, AlertTriangle } from 'lucide-react';
35
+ import { Sparkles, Star, Download, Eye, Maximize2, Undo2, Redo2, LayoutGrid, Keyboard, Plus, Trash2, GitBranch, Loader2, AlertCircle, Search, RefreshCw, History, GitCompareArrows, Play, RotateCcw, Layers, Upload, Rocket, ArrowRight, Check, X, Link2, Unlink, GitCompare, ExternalLink, GitCommitHorizontal, CircleHelp, CheckCircle2, Package, SlidersHorizontal, FlaskConical, Pencil, Info, Copy, BadgeCheck, DollarSign, Type, Code, Hash, List, Braces, Workflow, PlusCircle, ChevronDown, ChevronRight, AlertTriangle } from 'lucide-react';
34
36
  import { Icon } from '@iconify/react';
35
37
  import { create } from 'zustand';
36
38
  import { toast } from 'sonner';
@@ -1942,13 +1944,23 @@ function TemplateCard({
1942
1944
  template.tags.length - 2
1943
1945
  ] })
1944
1946
  ] }),
1945
- /* @__PURE__ */ jsxs(Button, { size: "sm", className: "w-full mt-2", onClick: handleUseTemplate, children: [
1946
- /* @__PURE__ */ jsx(Sparkles, { className: "size-3 mr-1" }),
1947
- tr(
1948
- "fluidgrids.builder.templateCard.useTemplateButton",
1949
- "Use Template"
1950
- )
1951
- ] })
1947
+ /* @__PURE__ */ jsx(
1948
+ SdkPermissionButton,
1949
+ {
1950
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE,
1951
+ deniedTooltip: tr(
1952
+ "fluidgrids.templates.useDeniedTooltip",
1953
+ "You don't have permission to create workflows from templates"
1954
+ ),
1955
+ children: /* @__PURE__ */ jsxs(Button, { size: "sm", className: "w-full mt-2", onClick: handleUseTemplate, children: [
1956
+ /* @__PURE__ */ jsx(Sparkles, { className: "size-3 mr-1" }),
1957
+ tr(
1958
+ "fluidgrids.builder.templateCard.useTemplateButton",
1959
+ "Use Template"
1960
+ )
1961
+ ] })
1962
+ }
1963
+ )
1952
1964
  ] })
1953
1965
  ]
1954
1966
  }
@@ -25503,7 +25515,7 @@ function TemplatesGrid({
25503
25515
  ] });
25504
25516
  }
25505
25517
  var LazyTemplateDetailPanel = lazy$1(
25506
- () => import('./TemplateDetailPanel-MTKW3SPM.js').then((m) => ({
25518
+ () => import('./TemplateDetailPanel-WH74G7PF.js').then((m) => ({
25507
25519
  default: m.TemplateDetailPanel
25508
25520
  }))
25509
25521
  );
@@ -27241,8 +27253,8 @@ var reducer = (state, action) => {
27241
27253
  if (toastId) {
27242
27254
  addToRemoveQueue(toastId);
27243
27255
  } else {
27244
- state.toasts.forEach((toast18) => {
27245
- addToRemoveQueue(toast18.id);
27256
+ state.toasts.forEach((toast19) => {
27257
+ addToRemoveQueue(toast19.id);
27246
27258
  });
27247
27259
  }
27248
27260
  return {
@@ -28618,7 +28630,7 @@ function VisualMapperContent({
28618
28630
  mapping,
28619
28631
  onMappingChange
28620
28632
  }) {
28621
- const { toast: toast18 } = useToast();
28633
+ const { toast: toast19 } = useToast();
28622
28634
  const tr = useTr();
28623
28635
  const reactFlowWrapper = useRef(null);
28624
28636
  const [selectedSourceNode, setSelectedSourceNode] = useState(null);
@@ -28638,7 +28650,7 @@ function VisualMapperContent({
28638
28650
  targetOperation
28639
28651
  );
28640
28652
  if (generatedNodes.length === 0) {
28641
- toast18({
28653
+ toast19({
28642
28654
  title: tr(
28643
28655
  "fluidgrids.builder.visualMapper.noFieldsFoundTitle",
28644
28656
  "No fields found"
@@ -28664,7 +28676,7 @@ function VisualMapperContent({
28664
28676
  targetNode,
28665
28677
  targetOperation,
28666
28678
  setNodes,
28667
- toast18,
28679
+ toast19,
28668
28680
  tr
28669
28681
  ]);
28670
28682
  useEffect(() => {
@@ -28771,7 +28783,7 @@ function VisualMapperContent({
28771
28783
  }
28772
28784
  onMappingChange(updatedMapping);
28773
28785
  if (isCompatible) {
28774
- toast18({
28786
+ toast19({
28775
28787
  title: tr(
28776
28788
  "fluidgrids.builder.visualMapper.mappingCreatedTitle",
28777
28789
  "Mapping created"
@@ -28779,7 +28791,7 @@ function VisualMapperContent({
28779
28791
  description: `Mapped ${sourcePath} (${sourceType}) to ${targetPath} (${targetType})`
28780
28792
  });
28781
28793
  } else {
28782
- toast18({
28794
+ toast19({
28783
28795
  title: tr(
28784
28796
  "fluidgrids.builder.visualMapper.typeMismatchTitle",
28785
28797
  "Type mismatch warning"
@@ -28789,7 +28801,7 @@ function VisualMapperContent({
28789
28801
  });
28790
28802
  }
28791
28803
  } else {
28792
- toast18({
28804
+ toast19({
28793
28805
  title: tr(
28794
28806
  "fluidgrids.builder.visualMapper.invalidMappingTitle",
28795
28807
  "Invalid mapping"
@@ -28802,7 +28814,7 @@ function VisualMapperContent({
28802
28814
  });
28803
28815
  }
28804
28816
  },
28805
- [nodes, mapping, onMappingChange, toast18, tr, setEdges]
28817
+ [nodes, mapping, onMappingChange, toast19, tr, setEdges]
28806
28818
  );
28807
28819
  const onEdgeDelete = useCallback(
28808
28820
  (edge) => {
@@ -28859,7 +28871,7 @@ function VisualMapperContent({
28859
28871
  const handleSaveMappingDialog = (expression, targetFieldName) => {
28860
28872
  const targetPath = targetFieldName || selectedTargetNode?.data?.path;
28861
28873
  if (!targetPath) {
28862
- toast18({
28874
+ toast19({
28863
28875
  title: tr(
28864
28876
  "fluidgrids.builder.visualMapper.noTargetFieldTitle",
28865
28877
  "No target field specified"
@@ -28891,7 +28903,7 @@ function VisualMapperContent({
28891
28903
  }
28892
28904
  onMappingChange(updatedMapping);
28893
28905
  const mappingType = detectMappingType(expression);
28894
- toast18({
28906
+ toast19({
28895
28907
  title: `${mappingType.charAt(0).toUpperCase() + mappingType.slice(1)} mapping created`,
28896
28908
  description: `${tr("fluidgrids.builder.visualMapper.appliedMappingTo", "Applied mapping to")} ${targetPath}`
28897
28909
  });
@@ -29190,7 +29202,7 @@ function MappingEditor({
29190
29202
  setEdges,
29191
29203
  edgeId
29192
29204
  }) {
29193
- const { toast: toast18 } = useToast();
29205
+ const { toast: toast19 } = useToast();
29194
29206
  const tr = useTr();
29195
29207
  const [mappingJson, setMappingJson] = useState({});
29196
29208
  const [jsonDataString, setJsonDataString] = useState(
@@ -29314,7 +29326,7 @@ function MappingEditor({
29314
29326
  const handleSaveMapping = () => {
29315
29327
  try {
29316
29328
  checkJsonValidity();
29317
- toast18({
29329
+ toast19({
29318
29330
  title: tr(
29319
29331
  "fluidgrids.builder.mappingEditor.mappingSavedTitle",
29320
29332
  "Mapping saved"
@@ -29325,7 +29337,7 @@ function MappingEditor({
29325
29337
  )
29326
29338
  });
29327
29339
  } catch (error62) {
29328
- toast18({
29340
+ toast19({
29329
29341
  title: tr(
29330
29342
  "fluidgrids.builder.mappingEditor.errorSavingTitle",
29331
29343
  "Error saving mapping"
@@ -29344,7 +29356,7 @@ function MappingEditor({
29344
29356
  const parsedData = checkJsonValidity();
29345
29357
  const result = await applyMapping(parsedData, sampleInput);
29346
29358
  setPreviewOutput(result);
29347
- toast18({
29359
+ toast19({
29348
29360
  title: tr(
29349
29361
  "fluidgrids.builder.mappingEditor.mappingTestedTitle",
29350
29362
  "Mapping tested"
@@ -29356,7 +29368,7 @@ function MappingEditor({
29356
29368
  });
29357
29369
  } catch (error62) {
29358
29370
  console.error("Test mapping error:", error62);
29359
- toast18({
29371
+ toast19({
29360
29372
  title: tr(
29361
29373
  "fluidgrids.builder.mappingEditor.mappingTestFailedTitle",
29362
29374
  "Mapping test failed"
@@ -38540,6 +38552,284 @@ function ScheduleRulesEditor({
38540
38552
  }
38541
38553
  );
38542
38554
  }
38555
+ var rowInput3 = "rounded-md border border-input bg-background px-2 py-1 text-xs text-foreground outline-none focus:border-primary";
38556
+ function serialiseRows(rows, previous) {
38557
+ const rules = rows.map(ruleFromRow);
38558
+ return Array.isArray(previous) ? rules : JSON.stringify(rules, null, 2);
38559
+ }
38560
+ function textOf(value) {
38561
+ if (typeof value === "string") return value;
38562
+ if (value === void 0 || value === null) return "";
38563
+ return JSON.stringify(value, null, 2);
38564
+ }
38565
+ function AssertRulesEditor({
38566
+ value,
38567
+ onChange,
38568
+ testIdPrefix = "ndv"
38569
+ }) {
38570
+ const tr = useTr();
38571
+ const initial = readAssertRulesSetting(value);
38572
+ const [rows, setRows] = useState(
38573
+ () => rowsFromRules(initial ?? [])
38574
+ );
38575
+ const [jsonView, setJsonView] = useState(initial === null);
38576
+ const [jsonText, setJsonText] = useState(() => textOf(value));
38577
+ const written = useRef(value);
38578
+ useEffect(() => {
38579
+ if (value === written.current) return;
38580
+ written.current = value;
38581
+ const parsed = readAssertRulesSetting(value);
38582
+ setJsonText(textOf(value));
38583
+ if (parsed === null) {
38584
+ setJsonView(true);
38585
+ } else {
38586
+ setRows(rowsFromRules(parsed));
38587
+ }
38588
+ }, [value]);
38589
+ const write = (next) => {
38590
+ written.current = next;
38591
+ onChange(next);
38592
+ };
38593
+ const setRowList = (next) => {
38594
+ setRows(next);
38595
+ const serialised = serialiseRows(next, value);
38596
+ setJsonText(textOf(serialised));
38597
+ write(serialised);
38598
+ };
38599
+ const setRow = (index, patch) => {
38600
+ const next = rows.slice();
38601
+ const current = next[index];
38602
+ if (!current) return;
38603
+ next[index] = { ...current, ...patch };
38604
+ setRowList(next);
38605
+ };
38606
+ const parsedJson = jsonView ? readAssertRulesSetting(jsonText) : null;
38607
+ const check2 = normaliseAssertRules(
38608
+ jsonView ? parsedJson ?? [] : rows.map(ruleFromRow)
38609
+ );
38610
+ const problem = jsonView && parsedJson === null ? {
38611
+ key: "fluidgrids.builder.assertRules.invalidJson",
38612
+ fallback: "The rules must be a JSON array."
38613
+ } : "problem" in check2 ? describeAssertRuleProblem(check2.problem) : null;
38614
+ const toggleView = () => {
38615
+ if (jsonView) {
38616
+ const parsed = readAssertRulesSetting(jsonText);
38617
+ if (parsed === null) return;
38618
+ setRows(rowsFromRules(parsed));
38619
+ setJsonView(false);
38620
+ } else {
38621
+ setJsonText(textOf(serialiseRows(rows, value)));
38622
+ setJsonView(true);
38623
+ }
38624
+ };
38625
+ return /* @__PURE__ */ jsxs(
38626
+ "div",
38627
+ {
38628
+ className: "flex flex-col gap-2",
38629
+ "data-testid": `${testIdPrefix}-assert-rules`,
38630
+ children: [
38631
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsx(
38632
+ "button",
38633
+ {
38634
+ type: "button",
38635
+ onClick: toggleView,
38636
+ disabled: jsonView && parsedJson === null,
38637
+ className: "text-[11px] text-primary hover:underline disabled:opacity-50",
38638
+ "data-testid": `${testIdPrefix}-assert-rules-view`,
38639
+ children: jsonView ? tr("fluidgrids.builder.assertRules.showRows", "Edit as rows") : tr("fluidgrids.builder.assertRules.showJson", "Edit as JSON")
38640
+ }
38641
+ ) }),
38642
+ jsonView ? /* @__PURE__ */ jsx(
38643
+ "textarea",
38644
+ {
38645
+ value: jsonText,
38646
+ rows: 8,
38647
+ spellCheck: false,
38648
+ "aria-label": tr(
38649
+ "fluidgrids.builder.assertRules.jsonLabel",
38650
+ "Rules (JSON array)"
38651
+ ),
38652
+ onChange: (event) => {
38653
+ setJsonText(event.target.value);
38654
+ write(
38655
+ Array.isArray(value) ? readAssertRulesSetting(event.target.value) ?? value : event.target.value
38656
+ );
38657
+ },
38658
+ className: cn$1(rowInput3, "w-full font-mono"),
38659
+ "data-testid": `${testIdPrefix}-assert-rules-json`
38660
+ }
38661
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
38662
+ rows.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
38663
+ "fluidgrids.builder.assertRules.empty",
38664
+ "Add at least one rule."
38665
+ ) }),
38666
+ rows.map((row, index) => {
38667
+ const operand = operatorSpec(row.operator)?.operand ?? "expected";
38668
+ return /* @__PURE__ */ jsxs(
38669
+ "div",
38670
+ {
38671
+ className: "rounded-md border border-border p-2",
38672
+ "data-testid": `${testIdPrefix}-assert-rule-${index}`,
38673
+ children: [
38674
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
38675
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-semibold text-muted-foreground", children: [
38676
+ index + 1,
38677
+ "."
38678
+ ] }),
38679
+ /* @__PURE__ */ jsx(
38680
+ "input",
38681
+ {
38682
+ type: "text",
38683
+ value: row.path,
38684
+ placeholder: tr(
38685
+ "fluidgrids.builder.assertRules.pathPlaceholder",
38686
+ "path, e.g. body.items.0.id (empty = whole input)"
38687
+ ),
38688
+ "aria-label": tr(
38689
+ "fluidgrids.builder.assertRules.path",
38690
+ "Path"
38691
+ ),
38692
+ onChange: (event) => setRow(index, { path: event.target.value }),
38693
+ className: cn$1(rowInput3, "min-w-0 flex-1 font-mono")
38694
+ }
38695
+ ),
38696
+ /* @__PURE__ */ jsx(
38697
+ "select",
38698
+ {
38699
+ value: row.operator,
38700
+ "aria-label": tr(
38701
+ "fluidgrids.builder.assertRules.operator",
38702
+ "Operator"
38703
+ ),
38704
+ onChange: (event) => setRow(index, { operator: event.target.value }),
38705
+ className: rowInput3,
38706
+ children: ASSERT_OPERATORS.map((spec) => /* @__PURE__ */ jsx("option", { value: spec.wire, children: tr(
38707
+ `fluidgrids.builder.assertRules.op.${spec.wire}`,
38708
+ spec.label
38709
+ ) }, spec.wire))
38710
+ }
38711
+ ),
38712
+ /* @__PURE__ */ jsx(
38713
+ "button",
38714
+ {
38715
+ type: "button",
38716
+ onClick: () => setRowList(rows.filter((_, i) => i !== index)),
38717
+ title: tr(
38718
+ "fluidgrids.builder.assertRules.remove",
38719
+ "Remove rule"
38720
+ ),
38721
+ className: "rounded p-0.5 text-muted-foreground hover:text-[var(--color-status-error-text)]",
38722
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
38723
+ }
38724
+ )
38725
+ ] }),
38726
+ operand === "expected" && /* @__PURE__ */ jsx(
38727
+ "input",
38728
+ {
38729
+ type: "text",
38730
+ value: row.expected,
38731
+ placeholder: row.operator === "matches" ? tr(
38732
+ "fluidgrids.builder.assertRules.regexPlaceholder",
38733
+ "regular expression, e.g. ^ord_"
38734
+ ) : tr(
38735
+ "fluidgrids.builder.assertRules.expectedPlaceholder",
38736
+ 'expected value as JSON, e.g. 42, "ok" or {"a": 1}'
38737
+ ),
38738
+ "aria-label": tr(
38739
+ "fluidgrids.builder.assertRules.expected",
38740
+ "Expected"
38741
+ ),
38742
+ onChange: (event) => setRow(index, { expected: event.target.value }),
38743
+ className: cn$1(rowInput3, "mt-1.5 w-full font-mono")
38744
+ }
38745
+ ),
38746
+ operand === "range" && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex gap-1.5", children: [
38747
+ /* @__PURE__ */ jsx(
38748
+ "input",
38749
+ {
38750
+ type: "text",
38751
+ inputMode: "decimal",
38752
+ value: row.min,
38753
+ placeholder: tr(
38754
+ "fluidgrids.builder.assertRules.min",
38755
+ "min"
38756
+ ),
38757
+ "aria-label": tr(
38758
+ "fluidgrids.builder.assertRules.min",
38759
+ "min"
38760
+ ),
38761
+ onChange: (event) => setRow(index, { min: event.target.value }),
38762
+ className: cn$1(rowInput3, "w-full")
38763
+ }
38764
+ ),
38765
+ /* @__PURE__ */ jsx(
38766
+ "input",
38767
+ {
38768
+ type: "text",
38769
+ inputMode: "decimal",
38770
+ value: row.max,
38771
+ placeholder: tr(
38772
+ "fluidgrids.builder.assertRules.max",
38773
+ "max"
38774
+ ),
38775
+ "aria-label": tr(
38776
+ "fluidgrids.builder.assertRules.max",
38777
+ "max"
38778
+ ),
38779
+ onChange: (event) => setRow(index, { max: event.target.value }),
38780
+ className: cn$1(rowInput3, "w-full")
38781
+ }
38782
+ )
38783
+ ] }),
38784
+ /* @__PURE__ */ jsx(
38785
+ "input",
38786
+ {
38787
+ type: "text",
38788
+ value: row.message,
38789
+ placeholder: tr(
38790
+ "fluidgrids.builder.assertRules.messagePlaceholder",
38791
+ "message when it does not hold (optional)"
38792
+ ),
38793
+ "aria-label": tr(
38794
+ "fluidgrids.builder.assertRules.message",
38795
+ "Message"
38796
+ ),
38797
+ onChange: (event) => setRow(index, { message: event.target.value }),
38798
+ className: cn$1(rowInput3, "mt-1.5 w-full")
38799
+ }
38800
+ )
38801
+ ]
38802
+ },
38803
+ index
38804
+ );
38805
+ }),
38806
+ /* @__PURE__ */ jsxs(
38807
+ "button",
38808
+ {
38809
+ type: "button",
38810
+ onClick: () => setRowList([...rows, emptyAssertRuleRow()]),
38811
+ className: "flex items-center gap-1 self-start text-xs text-primary hover:underline",
38812
+ "data-testid": `${testIdPrefix}-assert-rule-add`,
38813
+ children: [
38814
+ /* @__PURE__ */ jsx(Plus, { className: "size-3.5" }),
38815
+ tr("fluidgrids.builder.assertRules.add", "Add rule")
38816
+ ]
38817
+ }
38818
+ )
38819
+ ] }),
38820
+ problem && /* @__PURE__ */ jsx(
38821
+ "p",
38822
+ {
38823
+ className: "text-[11px] text-[var(--color-status-error-text)]",
38824
+ role: "alert",
38825
+ "data-testid": `${testIdPrefix}-assert-rules-problem`,
38826
+ children: tr(problem.key, problem.fallback)
38827
+ }
38828
+ )
38829
+ ]
38830
+ }
38831
+ );
38832
+ }
38543
38833
  function formatRunInZone(date5, timezone) {
38544
38834
  try {
38545
38835
  return new Intl.DateTimeFormat(void 0, {
@@ -39446,6 +39736,25 @@ function NodeParamsForm({
39446
39736
  )
39447
39737
  ] }, p.name);
39448
39738
  }
39739
+ if (nodeType === "internal:assert" && p.name === "rules") {
39740
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
39741
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
39742
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-foreground", children: tr(
39743
+ "fluidgrids.builder.nodeParamsForm.assertRulesLabel",
39744
+ "Rules"
39745
+ ) }),
39746
+ p.required && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" })
39747
+ ] }),
39748
+ /* @__PURE__ */ jsx(
39749
+ AssertRulesEditor,
39750
+ {
39751
+ value: argsState[p.name],
39752
+ onChange: (next) => writeFixed(p.name, next),
39753
+ testIdPrefix
39754
+ }
39755
+ )
39756
+ ] }, p.name);
39757
+ }
39449
39758
  if (p.type === "workflow" || nodeType === "internal:subworkflow" && p.name === "workflowKey") {
39450
39759
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
39451
39760
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
@@ -43437,7 +43746,7 @@ function TriggerDataDialog({
43437
43746
  "."
43438
43747
  ] })
43439
43748
  ] }),
43440
- /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2 sm:gap-0", children: [
43749
+ /* @__PURE__ */ jsxs(DialogFooter$1, { className: "gap-2 sm:gap-0", children: [
43441
43750
  /* @__PURE__ */ jsx(
43442
43751
  Button$1,
43443
43752
  {
@@ -47282,6 +47591,918 @@ function GitSyncPanel({
47282
47591
  }
47283
47592
  );
47284
47593
  }
47594
+ var JSON_FIELDS = [
47595
+ {
47596
+ field: "triggerData",
47597
+ key: "triggerData",
47598
+ label: "Trigger input",
47599
+ hint: "The JSON object the run starts with.",
47600
+ rows: 4
47601
+ },
47602
+ {
47603
+ field: "connectionMocks",
47604
+ key: "connectionMocks",
47605
+ label: "Connection mocks",
47606
+ hint: '[{ "nodeId": "...", "operation"?: "...", "response": { ... } }] or an "error": { "message": "..." } instead of "response". A mocked node never calls its connection.',
47607
+ rows: 6
47608
+ },
47609
+ {
47610
+ field: "pinnedOutputs",
47611
+ key: "pinnedOutputs",
47612
+ label: "Pinned outputs",
47613
+ hint: '{ "nodeId": { "data": { ... } } }: the node is skipped and returns this output.',
47614
+ rows: 5
47615
+ },
47616
+ {
47617
+ field: "expectations",
47618
+ key: "expectations",
47619
+ label: "Expectations",
47620
+ hint: '{ "nodeId": [{ "path": "total", "operator": "equals", "expected": 42 }] }. Operators: equals, notEquals, contains, notContains, matches, jsonSchema, between, itemCount, exists, notExists.',
47621
+ rows: 6
47622
+ }
47623
+ ];
47624
+ var fieldClass = (invalid) => cn$1(
47625
+ "font-mono text-xs",
47626
+ invalid && "border-[var(--color-status-error-border)]"
47627
+ );
47628
+ function WorkflowTestCaseDialog({
47629
+ open: open2,
47630
+ mode,
47631
+ initialDraft,
47632
+ nodes,
47633
+ canvasPinnedOutputs,
47634
+ onSave,
47635
+ onOpenChange
47636
+ }) {
47637
+ const tr = useTr();
47638
+ const [draft, setDraft] = useState(initialDraft);
47639
+ const [problem, setProblem] = useState(null);
47640
+ const [saving, setSaving] = useState(false);
47641
+ useEffect(() => {
47642
+ if (open2) {
47643
+ setDraft(initialDraft);
47644
+ setProblem(null);
47645
+ }
47646
+ }, [open2, initialDraft]);
47647
+ const update = (key, value) => {
47648
+ setDraft((current) => ({ ...current, [key]: value }));
47649
+ setProblem(
47650
+ (current) => current && current.field === key ? null : current
47651
+ );
47652
+ };
47653
+ const hasCanvasPins = Object.keys(canvasPinnedOutputs).length > 0;
47654
+ const handleSave = async () => {
47655
+ setSaving(true);
47656
+ try {
47657
+ const result = await onSave(draft);
47658
+ setProblem(result);
47659
+ } finally {
47660
+ setSaving(false);
47661
+ }
47662
+ };
47663
+ const problemFor = (field) => problem && problem.field === field ? /* @__PURE__ */ jsx(
47664
+ "p",
47665
+ {
47666
+ className: "text-xs text-[var(--color-status-error-text)]",
47667
+ role: "alert",
47668
+ "data-testid": `fluidgrids-test-case-error-${field}`,
47669
+ children: problem.raw ? problem.fallback : tr(problem.key, problem.fallback)
47670
+ }
47671
+ ) : null;
47672
+ return /* @__PURE__ */ jsx(Dialog, { open: open2, onOpenChange: (next) => !saving && onOpenChange(next), children: /* @__PURE__ */ jsxs(
47673
+ DialogContent,
47674
+ {
47675
+ className: "z-[120] max-h-[90vh] overflow-y-auto sm:max-w-2xl",
47676
+ "data-testid": "fluidgrids-test-case-dialog",
47677
+ children: [
47678
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
47679
+ /* @__PURE__ */ jsx(DialogTitle, { children: mode === "create" ? tr("fluidgrids.builder.workflowTests.newTitle", "New test case") : tr(
47680
+ "fluidgrids.builder.workflowTests.editTitle",
47681
+ "Edit test case"
47682
+ ) }),
47683
+ /* @__PURE__ */ jsx(DialogDescription, { children: tr(
47684
+ "fluidgrids.builder.workflowTests.editorDescription",
47685
+ "A test case runs the workflow in test mode with this input. Mocked and pinned nodes never reach a real connection, and the expectations are checked as each node completes."
47686
+ ) })
47687
+ ] }),
47688
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
47689
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
47690
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-test-case-name", className: "text-xs", children: tr("fluidgrids.builder.workflowTests.nameLabel", "Name") }),
47691
+ /* @__PURE__ */ jsx(
47692
+ Input$1,
47693
+ {
47694
+ id: "fg-test-case-name",
47695
+ value: draft.name,
47696
+ maxLength: MAX_CASE_NAME,
47697
+ onChange: (event) => update("name", event.target.value),
47698
+ "data-testid": "fluidgrids-test-case-name"
47699
+ }
47700
+ ),
47701
+ problemFor("name")
47702
+ ] }),
47703
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
47704
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-test-case-description", className: "text-xs", children: tr(
47705
+ "fluidgrids.builder.workflowTests.descriptionLabel",
47706
+ "Description (optional)"
47707
+ ) }),
47708
+ /* @__PURE__ */ jsx(
47709
+ Textarea,
47710
+ {
47711
+ id: "fg-test-case-description",
47712
+ value: draft.description,
47713
+ rows: 2,
47714
+ onChange: (event) => update("description", event.target.value)
47715
+ }
47716
+ )
47717
+ ] }),
47718
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
47719
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
47720
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-test-case-start", className: "text-xs", children: tr(
47721
+ "fluidgrids.builder.workflowTests.startNodeLabel",
47722
+ "Start on"
47723
+ ) }),
47724
+ /* @__PURE__ */ jsxs(
47725
+ "select",
47726
+ {
47727
+ id: "fg-test-case-start",
47728
+ value: draft.triggerNodeId,
47729
+ onChange: (event) => update("triggerNodeId", event.target.value),
47730
+ className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm text-foreground",
47731
+ children: [
47732
+ /* @__PURE__ */ jsx("option", { value: "", children: tr(
47733
+ "fluidgrids.builder.workflowTests.startNodeDefault",
47734
+ "The workflow's entry node"
47735
+ ) }),
47736
+ nodes.map((node2) => /* @__PURE__ */ jsx("option", { value: node2.id, children: node2.label }, node2.id))
47737
+ ]
47738
+ }
47739
+ )
47740
+ ] }),
47741
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
47742
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-test-case-expected", className: "text-xs", children: tr(
47743
+ "fluidgrids.builder.workflowTests.expectedStatusLabel",
47744
+ "The run should"
47745
+ ) }),
47746
+ /* @__PURE__ */ jsxs(
47747
+ "select",
47748
+ {
47749
+ id: "fg-test-case-expected",
47750
+ value: draft.expectedStatus,
47751
+ onChange: (event) => update(
47752
+ "expectedStatus",
47753
+ event.target.value === "FAILED" /* Failed */ ? "FAILED" /* Failed */ : "COMPLETED" /* Completed */
47754
+ ),
47755
+ className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm text-foreground",
47756
+ children: [
47757
+ /* @__PURE__ */ jsx("option", { value: "COMPLETED" /* Completed */, children: tr(
47758
+ "fluidgrids.builder.workflowTests.expectCompleted",
47759
+ "Complete"
47760
+ ) }),
47761
+ /* @__PURE__ */ jsx("option", { value: "FAILED" /* Failed */, children: tr("fluidgrids.builder.workflowTests.expectFailed", "Fail") })
47762
+ ]
47763
+ }
47764
+ )
47765
+ ] })
47766
+ ] }),
47767
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border border-border p-2.5", children: [
47768
+ /* @__PURE__ */ jsx(Label, { htmlFor: "fg-test-case-enabled", className: "text-xs", children: tr(
47769
+ "fluidgrids.builder.workflowTests.enabledLabel",
47770
+ "Run this case with Run all"
47771
+ ) }),
47772
+ /* @__PURE__ */ jsx(
47773
+ Switch,
47774
+ {
47775
+ id: "fg-test-case-enabled",
47776
+ checked: draft.enabled,
47777
+ onCheckedChange: (checked) => update("enabled", checked)
47778
+ }
47779
+ )
47780
+ ] }),
47781
+ JSON_FIELDS.map((item) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
47782
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
47783
+ /* @__PURE__ */ jsx(
47784
+ Label,
47785
+ {
47786
+ htmlFor: `fg-test-case-${item.field}`,
47787
+ className: "text-xs",
47788
+ children: tr(
47789
+ `fluidgrids.builder.workflowTests.${item.key}Label`,
47790
+ item.label
47791
+ )
47792
+ }
47793
+ ),
47794
+ item.field === "pinnedOutputs" && hasCanvasPins && /* @__PURE__ */ jsx(
47795
+ Button$1,
47796
+ {
47797
+ type: "button",
47798
+ variant: "ghost",
47799
+ size: "sm",
47800
+ className: "h-6 px-2 text-xs",
47801
+ onClick: () => update(
47802
+ "pinnedOutputs",
47803
+ JSON.stringify(canvasPinnedOutputs, null, 2)
47804
+ ),
47805
+ "data-testid": "fluidgrids-test-case-use-pins",
47806
+ children: tr(
47807
+ "fluidgrids.builder.workflowTests.useCanvasPins",
47808
+ "Use the canvas's pinned data"
47809
+ )
47810
+ }
47811
+ )
47812
+ ] }),
47813
+ /* @__PURE__ */ jsx(
47814
+ Textarea,
47815
+ {
47816
+ id: `fg-test-case-${item.field}`,
47817
+ value: draft[item.field],
47818
+ rows: item.rows,
47819
+ spellCheck: false,
47820
+ className: fieldClass(problem?.field === item.field),
47821
+ onChange: (event) => update(item.field, event.target.value),
47822
+ "data-testid": `fluidgrids-test-case-${item.field}`
47823
+ }
47824
+ ),
47825
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: tr(
47826
+ `fluidgrids.builder.workflowTests.${item.key}Hint`,
47827
+ item.hint
47828
+ ) }),
47829
+ problemFor(item.field)
47830
+ ] }, item.field)),
47831
+ problemFor("size")
47832
+ ] }),
47833
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
47834
+ /* @__PURE__ */ jsx(
47835
+ Button$1,
47836
+ {
47837
+ variant: "outline",
47838
+ disabled: saving,
47839
+ onClick: () => onOpenChange(false),
47840
+ children: tr("fluidgrids.builder.workflowTests.cancel", "Cancel")
47841
+ }
47842
+ ),
47843
+ /* @__PURE__ */ jsxs(
47844
+ Button$1,
47845
+ {
47846
+ disabled: saving,
47847
+ onClick: () => void handleSave(),
47848
+ "data-testid": "fluidgrids-test-case-save",
47849
+ children: [
47850
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 size-4 animate-spin" }),
47851
+ mode === "create" ? tr("fluidgrids.builder.workflowTests.create", "Save test case") : tr("fluidgrids.builder.workflowTests.save", "Save changes")
47852
+ ]
47853
+ }
47854
+ )
47855
+ ] })
47856
+ ]
47857
+ }
47858
+ ) });
47859
+ }
47860
+ function WorkflowTestStatusBadge({
47861
+ status,
47862
+ className
47863
+ }) {
47864
+ const tr = useTr();
47865
+ const label = testStatusLabel(status);
47866
+ return /* @__PURE__ */ jsx(
47867
+ Badge,
47868
+ {
47869
+ variant: "outline",
47870
+ className: cn$1(
47871
+ "h-5 shrink-0 px-1.5 py-0 text-[10px]",
47872
+ TEST_STATUS_TONE_CLASS[testStatusTone(status)],
47873
+ className
47874
+ ),
47875
+ "data-status": status,
47876
+ "data-testid": "fluidgrids-test-status",
47877
+ children: tr(label.key, label.fallback)
47878
+ }
47879
+ );
47880
+ }
47881
+ function WorkflowTestCaseResultItem({
47882
+ result,
47883
+ onOpenRun
47884
+ }) {
47885
+ const tr = useTr();
47886
+ return /* @__PURE__ */ jsxs(
47887
+ "li",
47888
+ {
47889
+ className: "rounded-md border border-border p-2",
47890
+ "data-testid": "fluidgrids-test-result",
47891
+ children: [
47892
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
47893
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-sm text-foreground", children: result.caseName }),
47894
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
47895
+ /* @__PURE__ */ jsx(WorkflowTestStatusBadge, { status: result.status }),
47896
+ result.runId && /* @__PURE__ */ jsxs(
47897
+ "button",
47898
+ {
47899
+ type: "button",
47900
+ className: "inline-flex items-center gap-0.5 text-xs text-primary hover:underline",
47901
+ onClick: () => result.runId && onOpenRun(result.runId),
47902
+ "data-testid": "fluidgrids-test-result-run",
47903
+ children: [
47904
+ tr("fluidgrids.builder.workflowTests.openRun", "Run"),
47905
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3" })
47906
+ ]
47907
+ }
47908
+ )
47909
+ ] })
47910
+ ] }),
47911
+ result.failures.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-1.5 space-y-1", children: result.failures.map((failure2, index) => /* @__PURE__ */ jsxs(
47912
+ "li",
47913
+ {
47914
+ className: "text-xs text-muted-foreground",
47915
+ "data-testid": "fluidgrids-test-failure",
47916
+ children: [
47917
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[var(--color-status-error-text)]", children: failure2.code }),
47918
+ failure2.nodeId && /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
47919
+ " \xB7 ",
47920
+ failure2.nodeId
47921
+ ] }),
47922
+ /* @__PURE__ */ jsx("span", { className: "block whitespace-pre-wrap break-words text-foreground", children: failure2.message })
47923
+ ]
47924
+ },
47925
+ `${failure2.code}-${failure2.nodeId ?? ""}-${index}`
47926
+ )) })
47927
+ ]
47928
+ }
47929
+ );
47930
+ }
47931
+ function WorkflowTestRunCard({
47932
+ run,
47933
+ onOpenRun
47934
+ }) {
47935
+ const tr = useTr();
47936
+ return /* @__PURE__ */ jsxs(
47937
+ "div",
47938
+ {
47939
+ className: "rounded-lg border border-border p-3",
47940
+ "data-testid": "fluidgrids-test-run",
47941
+ "data-status": run.status,
47942
+ children: [
47943
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
47944
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
47945
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-foreground", children: [
47946
+ tr("fluidgrids.builder.workflowTests.suiteRun", "Test run"),
47947
+ " ",
47948
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: run.version })
47949
+ ] }),
47950
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
47951
+ testRunSummaryText(run.summary),
47952
+ " \xB7 ",
47953
+ relativeTime(run.startedAt)
47954
+ ] })
47955
+ ] }),
47956
+ /* @__PURE__ */ jsx(WorkflowTestStatusBadge, { status: run.status })
47957
+ ] }),
47958
+ run.results.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-2 space-y-1.5", children: run.results.map((result) => /* @__PURE__ */ jsx(
47959
+ WorkflowTestCaseResultItem,
47960
+ {
47961
+ result,
47962
+ onOpenRun
47963
+ },
47964
+ result.caseId
47965
+ )) })
47966
+ ]
47967
+ }
47968
+ );
47969
+ }
47970
+ function WorkflowTestCaseRow({
47971
+ testCase,
47972
+ actions
47973
+ }) {
47974
+ const tr = useTr();
47975
+ return /* @__PURE__ */ jsxs(
47976
+ "li",
47977
+ {
47978
+ className: "rounded-lg border border-border p-3",
47979
+ "data-testid": "fluidgrids-test-case",
47980
+ children: [
47981
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
47982
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
47983
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: testCase.name }),
47984
+ testCase.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 line-clamp-2 text-xs text-muted-foreground", children: testCase.description }),
47985
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: [
47986
+ testCase.connectionMocks.length > 0 ? `${testCase.connectionMocks.length} ${tr("fluidgrids.builder.workflowTests.mocks", "mocked")}` : null,
47987
+ testCase.expectations.length > 0 ? `${testCase.expectations.length} ${tr("fluidgrids.builder.workflowTests.checked", "checked")}` : null,
47988
+ testCase.lastResult?.completedAt ? `${tr("fluidgrids.builder.workflowTests.lastRun", "last run")} ${relativeTime(testCase.lastResult.completedAt)}` : null
47989
+ ].filter(Boolean).join(" \xB7 ") })
47990
+ ] }),
47991
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
47992
+ !testCase.enabled && /* @__PURE__ */ jsx(
47993
+ Badge,
47994
+ {
47995
+ variant: "outline",
47996
+ className: "h-5 px-1.5 py-0 text-[10px] text-muted-foreground",
47997
+ children: tr("fluidgrids.builder.workflowTests.disabled", "Off")
47998
+ }
47999
+ ),
48000
+ testCase.lastResult ? /* @__PURE__ */ jsx(WorkflowTestStatusBadge, { status: testCase.lastResult.status }) : /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: tr("fluidgrids.builder.workflowTests.neverRun", "Not run yet") })
48001
+ ] })
48002
+ ] }),
48003
+ /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-1.5", children: actions })
48004
+ ]
48005
+ }
48006
+ );
48007
+ }
48008
+ var SHEET_TOOLTIP = "z-[130]";
48009
+ function WorkflowTestsPanel({
48010
+ className,
48011
+ compact = false
48012
+ }) {
48013
+ const enabled = useWorkflowTestsEnabled();
48014
+ if (!enabled) return null;
48015
+ return /* @__PURE__ */ jsx(WorkflowTestsSheet, { className, compact });
48016
+ }
48017
+ function WorkflowTestsSheet({
48018
+ className,
48019
+ compact = false
48020
+ }) {
48021
+ const tr = useTr();
48022
+ const params = useParams();
48023
+ const workflowKey = params?.id ?? "";
48024
+ const loadedVersion = useFlowStore((s) => s.workflow.version);
48025
+ const currentVersion = params?.version || loadedVersion || "";
48026
+ const guardedNavigate = useGuardedNavigate();
48027
+ const canvasNodes = useFlowStore((s) => s.workflow.nodes);
48028
+ const canvasPins = useFlowStore((s) => s.workflow.pinnedNodeData);
48029
+ const [open2, setOpen] = useState(false);
48030
+ const {
48031
+ cases,
48032
+ runs,
48033
+ activeRun,
48034
+ loading,
48035
+ error: error62,
48036
+ forbidden,
48037
+ refetch,
48038
+ createCase,
48039
+ updateCase,
48040
+ deleteCase,
48041
+ runTests
48042
+ } = useWorkflowTests(workflowKey, open2);
48043
+ useEffect(() => {
48044
+ if (open2) void refetch();
48045
+ }, [open2, refetch]);
48046
+ const { allowed: canEdit } = useFluidGridsPermission(
48047
+ FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE
48048
+ );
48049
+ const { allowed: canRun } = useFluidGridsPermission(
48050
+ FLUIDGRIDS_PERMISSIONS.WORKFLOW_RUN_EXECUTE
48051
+ );
48052
+ const [editor, setEditor] = useState(null);
48053
+ const [deleting, setDeleting] = useState(null);
48054
+ const [busy, setBusy] = useState(null);
48055
+ const nodeOptions = useMemo(() => {
48056
+ const labels = buildNodeDisplayMap({ data: { nodes: canvasNodes } });
48057
+ return canvasNodes.filter((node2) => typeof node2.id === "string" && node2.id !== "").map((node2) => ({
48058
+ id: node2.id,
48059
+ label: labels[node2.id]?.label ? `${labels[node2.id]?.label} (${node2.id})` : node2.id
48060
+ }));
48061
+ }, [canvasNodes]);
48062
+ const canvasPinnedOutputs = useMemo(
48063
+ () => pinnedOutputsFromCanvas(canvasPins),
48064
+ [canvasPins]
48065
+ );
48066
+ const runIds = enabledCaseIds(cases);
48067
+ const suiteOpen = !!activeRun && isTestRunOpen(activeRun.status);
48068
+ const atCap = cases.length >= MAX_TEST_CASES;
48069
+ const deniedEdit = tr(
48070
+ "fluidgrids.builder.workflowTests.deniedEdit",
48071
+ "You need edit access to this workflow to change its test cases"
48072
+ );
48073
+ const deniedRun = tr(
48074
+ "fluidgrids.builder.workflowTests.deniedRun",
48075
+ "You need permission to run this workflow to run its tests"
48076
+ );
48077
+ const openRun = (runId) => guardedNavigate(`../runs/${encodeURIComponent(runId)}`);
48078
+ const start = async (key, caseIds) => {
48079
+ setBusy(key);
48080
+ try {
48081
+ await runTests({
48082
+ version: currentVersion || void 0,
48083
+ ...caseIds ? { caseIds } : {}
48084
+ });
48085
+ } catch (err) {
48086
+ toast.error(
48087
+ tr(
48088
+ "fluidgrids.builder.workflowTests.runError",
48089
+ "The tests did not start"
48090
+ ),
48091
+ { description: err instanceof Error ? err.message : String(err) }
48092
+ );
48093
+ } finally {
48094
+ setBusy(null);
48095
+ }
48096
+ };
48097
+ const save = async (draft) => {
48098
+ if (!editor) return null;
48099
+ const sortOrder = editor.mode === "edit" ? editor.sortOrder : nextSortOrder(cases);
48100
+ const parsed = draftToInput(draft, sortOrder);
48101
+ if ("error" in parsed) return parsed.error;
48102
+ try {
48103
+ if (editor.mode === "edit") await updateCase(editor.id, parsed.input);
48104
+ else await createCase(parsed.input);
48105
+ toast.success(
48106
+ tr("fluidgrids.builder.workflowTests.saved", "Test case saved"),
48107
+ { dismissible: true }
48108
+ );
48109
+ setEditor(null);
48110
+ return null;
48111
+ } catch (err) {
48112
+ return {
48113
+ field: "size",
48114
+ key: "fluidgrids.builder.workflowTests.saveError",
48115
+ fallback: err instanceof Error ? err.message : String(err),
48116
+ raw: true
48117
+ };
48118
+ }
48119
+ };
48120
+ const confirmDelete = async () => {
48121
+ const target = deleting;
48122
+ setDeleting(null);
48123
+ if (!target) return;
48124
+ setBusy(`delete:${target.id}`);
48125
+ try {
48126
+ await deleteCase(target.id);
48127
+ toast.success(
48128
+ tr("fluidgrids.builder.workflowTests.deleted", "Test case deleted"),
48129
+ { dismissible: true }
48130
+ );
48131
+ } catch (err) {
48132
+ toast.error(
48133
+ tr(
48134
+ "fluidgrids.builder.workflowTests.deleteError",
48135
+ "Could not delete the test case"
48136
+ ),
48137
+ { description: err instanceof Error ? err.message : String(err) }
48138
+ );
48139
+ } finally {
48140
+ setBusy(null);
48141
+ }
48142
+ };
48143
+ const triggerLabel = tr("fluidgrids.builder.workflowTests.trigger", "Tests");
48144
+ return /* @__PURE__ */ jsxs(Sheet, { open: open2, onOpenChange: setOpen, children: [
48145
+ /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
48146
+ Button$1,
48147
+ {
48148
+ "data-tour": "fluidgrids-builder-tests",
48149
+ variant: "outline",
48150
+ size: compact ? "icon" : "sm",
48151
+ className: cn$1(!compact && "flex gap-1", className),
48152
+ "aria-label": triggerLabel,
48153
+ title: compact ? triggerLabel : void 0,
48154
+ disabled: !workflowKey,
48155
+ "data-testid": "fluidgrids-tests-trigger",
48156
+ children: [
48157
+ /* @__PURE__ */ jsx(FlaskConical, { className: "size-4" }),
48158
+ !compact && triggerLabel
48159
+ ]
48160
+ }
48161
+ ) }),
48162
+ /* @__PURE__ */ jsxs(
48163
+ SheetContent,
48164
+ {
48165
+ side: "right",
48166
+ className: "w-[380px] overflow-y-auto sm:w-[480px]",
48167
+ "data-testid": "fluidgrids-tests-panel",
48168
+ children: [
48169
+ /* @__PURE__ */ jsxs(SheetHeader, { children: [
48170
+ /* @__PURE__ */ jsxs(SheetTitle, { className: "flex items-center gap-2", children: [
48171
+ /* @__PURE__ */ jsx(FlaskConical, { className: "size-5" }),
48172
+ tr("fluidgrids.builder.workflowTests.title", "Test cases")
48173
+ ] }),
48174
+ /* @__PURE__ */ jsx(SheetDescription, { children: tr(
48175
+ "fluidgrids.builder.workflowTests.description",
48176
+ "Saved inputs, mocked connections and expected outputs for this workflow. Each case runs in test mode, so no real connection is called for a mocked node."
48177
+ ) })
48178
+ ] }),
48179
+ forbidden ? /* @__PURE__ */ jsx(
48180
+ "p",
48181
+ {
48182
+ className: "py-6 text-sm text-muted-foreground",
48183
+ "data-testid": "fluidgrids-tests-no-access",
48184
+ children: tr(
48185
+ "fluidgrids.builder.workflowTests.noAccess",
48186
+ "You don't have access to this workflow's test cases."
48187
+ )
48188
+ }
48189
+ ) : loading && cases.length === 0 && runs.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-8 text-sm text-muted-foreground", children: [
48190
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 size-4 animate-spin" }),
48191
+ tr("fluidgrids.builder.workflowTests.loading", "Loading...")
48192
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-5 pb-6", children: [
48193
+ error62 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-[var(--color-status-error-border)] p-2 text-xs text-[var(--color-status-error-text)]", children: [
48194
+ error62,
48195
+ " ",
48196
+ /* @__PURE__ */ jsx(
48197
+ "button",
48198
+ {
48199
+ type: "button",
48200
+ className: "underline",
48201
+ onClick: () => void refetch(),
48202
+ children: tr("fluidgrids.builder.workflowTests.retry", "Retry")
48203
+ }
48204
+ )
48205
+ ] }),
48206
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
48207
+ /* @__PURE__ */ jsx(
48208
+ SdkPermissionButton,
48209
+ {
48210
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_RUN_EXECUTE,
48211
+ deniedTooltip: deniedRun,
48212
+ deniedTooltipClassName: SHEET_TOOLTIP,
48213
+ children: /* @__PURE__ */ jsxs(
48214
+ Button$1,
48215
+ {
48216
+ size: "sm",
48217
+ className: "gap-1",
48218
+ disabled: busy !== null || suiteOpen || runIds.length === 0,
48219
+ onClick: () => void start("all"),
48220
+ "data-testid": "fluidgrids-tests-run-all",
48221
+ children: [
48222
+ busy === "all" || suiteOpen ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "size-4" }),
48223
+ tr("fluidgrids.builder.workflowTests.runAll", "Run all")
48224
+ ]
48225
+ }
48226
+ )
48227
+ }
48228
+ ),
48229
+ /* @__PURE__ */ jsx(
48230
+ SdkPermissionButton,
48231
+ {
48232
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE,
48233
+ deniedTooltip: deniedEdit,
48234
+ deniedTooltipClassName: SHEET_TOOLTIP,
48235
+ children: /* @__PURE__ */ jsxs(
48236
+ Button$1,
48237
+ {
48238
+ size: "sm",
48239
+ variant: "outline",
48240
+ className: "gap-1",
48241
+ disabled: busy !== null || atCap,
48242
+ title: atCap ? tr(
48243
+ "fluidgrids.builder.workflowTests.atCap",
48244
+ "A workflow can have at most 50 test cases"
48245
+ ) : void 0,
48246
+ onClick: () => setEditor({ mode: "create", draft: emptyTestCaseDraft() }),
48247
+ "data-testid": "fluidgrids-tests-new",
48248
+ children: [
48249
+ /* @__PURE__ */ jsx(Plus, { className: "size-4" }),
48250
+ tr(
48251
+ "fluidgrids.builder.workflowTests.newCase",
48252
+ "New test case"
48253
+ )
48254
+ ]
48255
+ }
48256
+ )
48257
+ }
48258
+ ),
48259
+ /* @__PURE__ */ jsx(
48260
+ Button$1,
48261
+ {
48262
+ size: "icon",
48263
+ variant: "ghost",
48264
+ className: "ml-auto size-8",
48265
+ disabled: loading,
48266
+ "aria-label": tr(
48267
+ "fluidgrids.builder.workflowTests.refresh",
48268
+ "Refresh"
48269
+ ),
48270
+ onClick: () => void refetch(),
48271
+ children: /* @__PURE__ */ jsx(
48272
+ RefreshCw,
48273
+ {
48274
+ className: cn$1("size-4", loading && "animate-spin")
48275
+ }
48276
+ )
48277
+ }
48278
+ )
48279
+ ] }),
48280
+ (!canEdit || !canRun) && /* @__PURE__ */ jsx(
48281
+ "p",
48282
+ {
48283
+ className: "text-[11px] text-muted-foreground",
48284
+ "data-testid": "fluidgrids-tests-read-only",
48285
+ children: !canEdit && !canRun ? tr(
48286
+ "fluidgrids.builder.workflowTests.readOnly",
48287
+ "You can view the test cases and their results, but not change or run them."
48288
+ ) : !canEdit ? tr(
48289
+ "fluidgrids.builder.workflowTests.runOnly",
48290
+ "You can run the test cases, but not change them."
48291
+ ) : tr(
48292
+ "fluidgrids.builder.workflowTests.editOnly",
48293
+ "You can change the test cases, but not run them."
48294
+ )
48295
+ }
48296
+ ),
48297
+ activeRun && /* @__PURE__ */ jsxs("section", { "aria-labelledby": "fg-tests-active", children: [
48298
+ /* @__PURE__ */ jsx(
48299
+ "h3",
48300
+ {
48301
+ id: "fg-tests-active",
48302
+ className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
48303
+ children: suiteOpen ? tr(
48304
+ "fluidgrids.builder.workflowTests.runningTitle",
48305
+ "Running"
48306
+ ) : tr(
48307
+ "fluidgrids.builder.workflowTests.latestTitle",
48308
+ "Latest run"
48309
+ )
48310
+ }
48311
+ ),
48312
+ /* @__PURE__ */ jsx(WorkflowTestRunCard, { run: activeRun, onOpenRun: openRun })
48313
+ ] }),
48314
+ /* @__PURE__ */ jsxs("section", { "aria-labelledby": "fg-tests-cases", children: [
48315
+ /* @__PURE__ */ jsxs(
48316
+ "h3",
48317
+ {
48318
+ id: "fg-tests-cases",
48319
+ className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
48320
+ children: [
48321
+ tr("fluidgrids.builder.workflowTests.casesTitle", "Cases"),
48322
+ " (",
48323
+ cases.length,
48324
+ ")"
48325
+ ]
48326
+ }
48327
+ ),
48328
+ cases.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
48329
+ "fluidgrids.builder.workflowTests.empty",
48330
+ "No test cases yet. Create one here, or open a finished run and choose Save as test case."
48331
+ ) }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: cases.map((testCase) => /* @__PURE__ */ jsx(
48332
+ WorkflowTestCaseRow,
48333
+ {
48334
+ testCase,
48335
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
48336
+ /* @__PURE__ */ jsx(
48337
+ SdkPermissionButton,
48338
+ {
48339
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_RUN_EXECUTE,
48340
+ deniedTooltip: deniedRun,
48341
+ deniedTooltipClassName: SHEET_TOOLTIP,
48342
+ children: /* @__PURE__ */ jsxs(
48343
+ Button$1,
48344
+ {
48345
+ size: "sm",
48346
+ variant: "secondary",
48347
+ className: "h-7 gap-1 text-xs",
48348
+ disabled: busy !== null || suiteOpen,
48349
+ onClick: () => void start(`run:${testCase.id}`, [testCase.id]),
48350
+ "data-testid": "fluidgrids-test-case-run",
48351
+ children: [
48352
+ busy === `run:${testCase.id}` ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "size-3.5" }),
48353
+ tr(
48354
+ "fluidgrids.builder.workflowTests.runOne",
48355
+ "Run"
48356
+ )
48357
+ ]
48358
+ }
48359
+ )
48360
+ }
48361
+ ),
48362
+ /* @__PURE__ */ jsx(
48363
+ SdkPermissionButton,
48364
+ {
48365
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE,
48366
+ deniedTooltip: deniedEdit,
48367
+ deniedTooltipClassName: SHEET_TOOLTIP,
48368
+ children: /* @__PURE__ */ jsxs(
48369
+ Button$1,
48370
+ {
48371
+ size: "sm",
48372
+ variant: "outline",
48373
+ className: "h-7 gap-1 text-xs",
48374
+ disabled: busy !== null,
48375
+ onClick: () => setEditor({
48376
+ mode: "edit",
48377
+ id: testCase.id,
48378
+ sortOrder: testCase.sortOrder,
48379
+ draft: draftFromTestCase(testCase)
48380
+ }),
48381
+ "data-testid": "fluidgrids-test-case-edit",
48382
+ children: [
48383
+ /* @__PURE__ */ jsx(Pencil, { className: "size-3.5" }),
48384
+ tr(
48385
+ "fluidgrids.builder.workflowTests.edit",
48386
+ "Edit"
48387
+ )
48388
+ ]
48389
+ }
48390
+ )
48391
+ }
48392
+ ),
48393
+ /* @__PURE__ */ jsx(
48394
+ SdkPermissionButton,
48395
+ {
48396
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE,
48397
+ deniedTooltip: deniedEdit,
48398
+ deniedTooltipClassName: SHEET_TOOLTIP,
48399
+ children: /* @__PURE__ */ jsxs(
48400
+ Button$1,
48401
+ {
48402
+ size: "sm",
48403
+ variant: "ghost",
48404
+ className: "h-7 gap-1 text-xs",
48405
+ disabled: busy !== null,
48406
+ onClick: () => setDeleting(testCase),
48407
+ "data-testid": "fluidgrids-test-case-delete",
48408
+ children: [
48409
+ busy === `delete:${testCase.id}` ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" }),
48410
+ tr(
48411
+ "fluidgrids.builder.workflowTests.delete",
48412
+ "Delete"
48413
+ )
48414
+ ]
48415
+ }
48416
+ )
48417
+ }
48418
+ )
48419
+ ] })
48420
+ },
48421
+ testCase.id
48422
+ )) })
48423
+ ] }),
48424
+ runs.length > 1 && /* @__PURE__ */ jsxs("section", { "aria-labelledby": "fg-tests-history", children: [
48425
+ /* @__PURE__ */ jsx(
48426
+ "h3",
48427
+ {
48428
+ id: "fg-tests-history",
48429
+ className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
48430
+ children: tr(
48431
+ "fluidgrids.builder.workflowTests.historyTitle",
48432
+ "Earlier runs"
48433
+ )
48434
+ }
48435
+ ),
48436
+ /* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: runs.filter((run) => run.id !== activeRun?.id).map((run) => /* @__PURE__ */ jsxs(
48437
+ "li",
48438
+ {
48439
+ className: "flex items-center justify-between gap-2 text-xs",
48440
+ "data-testid": "fluidgrids-test-run-history",
48441
+ children: [
48442
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-muted-foreground", children: [
48443
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: run.version }),
48444
+ " \xB7",
48445
+ " ",
48446
+ run.summary.passed,
48447
+ "/",
48448
+ run.summary.total,
48449
+ " \xB7",
48450
+ " ",
48451
+ relativeTime(run.startedAt)
48452
+ ] }),
48453
+ /* @__PURE__ */ jsx(WorkflowTestStatusBadge, { status: run.status })
48454
+ ]
48455
+ },
48456
+ run.id
48457
+ )) })
48458
+ ] })
48459
+ ] })
48460
+ ]
48461
+ }
48462
+ ),
48463
+ /* @__PURE__ */ jsx(
48464
+ WorkflowTestCaseDialog,
48465
+ {
48466
+ open: editor !== null,
48467
+ mode: editor?.mode ?? "create",
48468
+ initialDraft: editor?.draft ?? EMPTY_DRAFT,
48469
+ nodes: nodeOptions,
48470
+ canvasPinnedOutputs,
48471
+ onSave: save,
48472
+ onOpenChange: (next) => !next && setEditor(null)
48473
+ }
48474
+ ),
48475
+ /* @__PURE__ */ jsx(
48476
+ AlertDialog,
48477
+ {
48478
+ open: deleting !== null,
48479
+ onOpenChange: (next) => !next && setDeleting(null),
48480
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, { className: "z-[120]", children: [
48481
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
48482
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: tr(
48483
+ "fluidgrids.builder.workflowTests.deleteTitle",
48484
+ "Delete this test case?"
48485
+ ) }),
48486
+ /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [
48487
+ deleting?.name,
48488
+ ".",
48489
+ " ",
48490
+ tr(
48491
+ "fluidgrids.builder.workflowTests.deleteBody",
48492
+ "Earlier test runs keep their results."
48493
+ )
48494
+ ] })
48495
+ ] }),
48496
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
48497
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: tr("fluidgrids.builder.workflowTests.cancel", "Cancel") }),
48498
+ /* @__PURE__ */ jsx(AlertDialogAction, { onClick: () => void confirmDelete(), children: tr("fluidgrids.builder.workflowTests.delete", "Delete") })
48499
+ ] })
48500
+ ] })
48501
+ }
48502
+ )
48503
+ ] });
48504
+ }
48505
+ var EMPTY_DRAFT = emptyTestCaseDraft();
47285
48506
  var NO_TEMPLATE = "__none__";
47286
48507
  function AiDraftPanel({
47287
48508
  className,
@@ -48320,6 +49541,7 @@ function PanelIcons() {
48320
49541
  /* @__PURE__ */ jsx(AiDraftPanel, { compact: true }),
48321
49542
  /* @__PURE__ */ jsx(EnvironmentsPanel, { compact: true }),
48322
49543
  /* @__PURE__ */ jsx(GitSyncPanel, { compact: true }),
49544
+ /* @__PURE__ */ jsx(WorkflowTestsPanel, { compact: true }),
48323
49545
  /* @__PURE__ */ jsx(CodeViewPanel, { compact: true })
48324
49546
  ] });
48325
49547
  }
@@ -48338,6 +49560,7 @@ function HeaderActions({ tier }) {
48338
49560
  /* @__PURE__ */ jsx(VersionHistoryPanel, { compact: true }),
48339
49561
  /* @__PURE__ */ jsx(EnvironmentsPanel, { compact: true }),
48340
49562
  /* @__PURE__ */ jsx(GitSyncPanel, { compact: true }),
49563
+ /* @__PURE__ */ jsx(WorkflowTestsPanel, { compact: true }),
48341
49564
  /* @__PURE__ */ jsx(CodeViewPanel, { compact: true }),
48342
49565
  /* @__PURE__ */ jsx(GuideIcon, {}),
48343
49566
  /* @__PURE__ */ jsx(Divider, {}),