@burdenoff/fluidgrids-fe-sdk 2026.1111.16 → 2026.1111.17

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 (112) hide show
  1. package/README.md +41 -0
  2. package/dist/AiStudioPage-kJHSsIcj.js +774 -0
  3. package/dist/ApprovalInboxPage-dbBDSJ-f.js +558 -0
  4. package/dist/DataSinkDashboardPage-1h2QsMzH.js +270 -0
  5. package/dist/DataTablesPage-BZy2z0LB.js +1690 -0
  6. package/dist/EntityTagPicker-BqRNsLCQ.js +135 -0
  7. package/dist/{ExecutionDetailPage-D-0_POAf.js → ExecutionDetailPage-BdcZmLT4.js} +799 -4420
  8. package/dist/ExecutionDetailPage-DvQ667OE.d.ts +10 -0
  9. package/dist/ExecutionsListPage-BCUJoAIE.js +2742 -0
  10. package/dist/{FailureTriagePanel-BHiduL7A.js → FailureTriagePanel-B9hDQymi.js} +4 -97
  11. package/dist/{FluidGridsProvider-DmvFADFa.js → FluidGridsProvider-dvZSNtSW.js} +10 -350
  12. package/dist/HistoryPage-BLoRpJ1R.d.ts +6 -0
  13. package/dist/{HistoryPage-BbUgxx1I.js → HistoryPage-U0tqupBN.js} +6 -4
  14. package/dist/HumanRequestCard-D-ooVPDn.js +439 -0
  15. package/dist/{InsightsPage-s6f5bU-8.js → InsightsPage-CALo16eB.js} +17 -328
  16. package/dist/ManagedAiPolicyAuthority-BxwJ3ptd.js +40 -0
  17. package/dist/{ManagedAiPolicyAuthority-CGux_DaV.d.ts → ManagedAiPolicyAuthority-DM3t3wNr.d.ts} +1 -5
  18. package/dist/ManagedAiPolicyAuthorityContext-CNkxIOZ1.js +10 -0
  19. package/dist/{MissingNodesInstallModal-B-9gHy2K.js → MissingNodesInstallModal-CMybsI8h.js} +3 -2
  20. package/dist/{NodesPage-ClvhUfbh.js → NodesPage-Bf5PhfBK.js} +7 -3
  21. package/dist/NodesPage-DEbnz9iU.d.ts +6 -0
  22. package/dist/{PublishCustomNodeAction-D2F7oN0S.js → PublishCustomNodeAction-5XXnSA3H.js} +3 -45
  23. package/dist/QuotaExhaustedDialog-7IFSaLyi.js +54 -0
  24. package/dist/RunnerStatusPill-Ctg4TJcP.js +187 -0
  25. package/dist/RunnersPage-CLCOCclc.js +467 -0
  26. package/dist/{ManagedAiPolicyAuthority-DsSaz-vg.js → SettingsPage-CgkRlCM5.js} +19 -50
  27. package/dist/SettingsPage-DMHQFj5-.d.ts +9 -0
  28. package/dist/TagFilterSelect-DCBOH6Ou.js +41 -0
  29. package/dist/TaskQueuesPage-9VgSnLdL.js +951 -0
  30. package/dist/TemplateCard-DsC0KNH1.js +1416 -0
  31. package/dist/{TemplateConfigurationModal-D_Mvgxqa.js → TemplateConfigurationModal-BhGWIzpm.js} +3 -2
  32. package/dist/{TemplateDetailPanel-DfFfUcOe.js → TemplateDetailPanel-D4cUVr-m.js} +3 -3
  33. package/dist/TemplatePreview-GthzCOSo.js +231 -0
  34. package/dist/TemplatesPage-BG3H-6v6.d.ts +10 -0
  35. package/dist/{TemplatesPage-Cku13bav.js → TemplatesPage-CsDNT54N.js} +11 -228
  36. package/dist/VariablesPage-67WR_lVE.js +949 -0
  37. package/dist/{VersionDiffDialog-Dq2xlPFI.js → VersionDiffDialog-DSN0mU0f.js} +2 -1
  38. package/dist/WorkflowBuilder-DEi_9YOb.js +555 -0
  39. package/dist/{WorkflowSearchRedirect-CaoOOpEl.js → WorkflowBuilder-iPqXgbYr.js} +19 -585
  40. package/dist/{WorkflowProposalCard-8Vr56kQa.js → WorkflowProposalCard-qrEeNyYG.js} +4 -405
  41. package/dist/WorkflowSearchRedirect-DwDNZ_q5.js +36 -0
  42. package/dist/{WorkflowShareDialog-6RRDe-_v.js → WorkflowShareDialog-B_gn968c.js} +6 -52
  43. package/dist/{WorkflowsList-B2j1Pehb.js → WorkflowsList-CA20dVIl.js} +11 -6
  44. package/dist/WorkflowsList-CcNDQge5.d.ts +6 -0
  45. package/dist/agent-form-Bfvm32j1.js +83 -0
  46. package/dist/{agentTools-DJ5o2ekg.js → agentTools-CkRJB7r5.js} +3 -2
  47. package/dist/{ai-usage-D5p7ZeRR.js → ai-usage-glCwHyQz.js} +1 -1
  48. package/dist/api-BrCHB5oA.js +224 -0
  49. package/dist/api-C8e0iV-Q.js +55 -0
  50. package/dist/button-DG7y_FVJ.js +48 -0
  51. package/dist/{canvasNoteTools-D3zUdEeg.js → canvasNoteTools-CQIc7Oxg.js} +2 -2
  52. package/dist/client-C9XSzSXr.js +1147 -0
  53. package/dist/{codec-client-DstqyzTN.js → codec-client-CJ95Rmqo.js} +1 -1
  54. package/dist/constants-B2lhWNoN.js +202 -0
  55. package/dist/constants-XdvUxeWK.d.ts +162 -0
  56. package/dist/dialog-Cjb4lB4D.js +105 -0
  57. package/dist/executions.d.ts +1 -1
  58. package/dist/executions.js +2 -1
  59. package/dist/feature-flags-DM5myWaD.js +296 -0
  60. package/dist/flags.d.ts +3 -0
  61. package/dist/flags.js +4 -0
  62. package/dist/{flow-builder-DEzT8TQu.js → flow-builder-y2Y1iG4Y.js} +70 -1731
  63. package/dist/{node-endpoint-configs-DHFGA5MQ.js → graphql-operation-types-kX-MMjgW.js} +1 -1194
  64. package/dist/{graphql-types-XcDLtFwU.js → graphql-types-CDKVWH2R.js} +2 -202
  65. package/dist/history.d.ts +1 -1
  66. package/dist/history.js +1 -1
  67. package/dist/index-72XFgvbn.d.ts +132 -0
  68. package/dist/index-BcdtKBM-.d.ts +56 -0
  69. package/dist/{index-B3BWqafN.d.ts → index-DjiFgEQl.d.ts} +5 -131
  70. package/dist/{index-DRf25-D3.d.ts → index-W2o__Nm6.d.ts} +32 -214
  71. package/dist/index.d.ts +13 -9
  72. package/dist/index.js +50 -5300
  73. package/dist/{node-detail-view-LNqeIqN-.js → node-detail-view-B91kXzIy.js} +11 -8
  74. package/dist/node-endpoint-configs-B5bjNMDW.js +37 -0
  75. package/dist/node-registry-refresh-Dy0uRg9f.js +84 -0
  76. package/dist/nodes.d.ts +1 -1
  77. package/dist/nodes.js +1 -1
  78. package/dist/observability.d.ts +1 -1
  79. package/dist/observability.js +2 -1
  80. package/dist/{quick-config-panel-D3ogsaFL.js → quick-config-panel-DggPl1vY.js} +2 -2
  81. package/dist/rolldown-runtime-hDbaFkMy.js +18 -0
  82. package/dist/run-display-BycKYSU0.js +46 -0
  83. package/dist/run-list-links-ByYC0OlV.js +98 -0
  84. package/dist/settings.d.ts +2 -1
  85. package/dist/settings.js +2 -1
  86. package/dist/shared-CMfn30xT.js +196 -0
  87. package/dist/templates.d.ts +1 -1
  88. package/dist/templates.js +2 -1
  89. package/dist/theme-provider-B3mIuz2d.js +11 -0
  90. package/dist/{useAgentDefinitions-NFtLytDR.js → useAgentDefinitions-Dpz0HDTq.js} +2 -1
  91. package/dist/useDurableParity-CzUQMFJ3.js +415 -0
  92. package/dist/useEntityTags-DhnsZcTl.js +236 -0
  93. package/dist/{useExecutions-DAzeWHS6.js → useExecutions-9aLuOt1c.js} +4 -3
  94. package/dist/{useObservability-COCZ0hUS.js → useObservability-CfEw3ZFD.js} +2 -2
  95. package/dist/{useRunHistory-CsYUbouO.js → useRunHistory-B1xJDwl1.js} +4 -3
  96. package/dist/{useWorkflows-w4Dx2fF2.js → useWorkflows-BlVVu8NM.js} +3 -2
  97. package/dist/utils-uaL5UVxb.js +65 -0
  98. package/dist/workflow-builder.d.ts +1 -1
  99. package/dist/workflow-builder.js +4 -2
  100. package/dist/workflow-host.d.ts +6 -0
  101. package/dist/workflow-host.js +9 -0
  102. package/dist/workflow-pages.d.ts +55 -0
  103. package/dist/workflow-pages.js +113 -0
  104. package/dist/workflows-list.d.ts +1 -1
  105. package/dist/workflows-list.js +1 -1
  106. package/package.json +13 -1
  107. package/dist/ExecutionDetailPage-MvsnOnE1.d.ts +0 -4
  108. package/dist/HistoryPage-BqUizaFv.d.ts +0 -3
  109. package/dist/NodesPage-C97-9K2Y.d.ts +0 -3
  110. package/dist/TemplatesPage-D0qnXzHh.d.ts +0 -4
  111. package/dist/WorkflowsList-0d5GSKdh.d.ts +0 -3
  112. package/dist/agent-form-BvMWhQ42.js +0 -496
@@ -1,27 +1,38 @@
1
1
  "use client";
2
+ import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
2
3
  import { A as nodeAccent, B as NODE_TYPE_DRAG_DATA_FORMAT, G as isTriggerOperation, H as useFlowStore, M as isImageIcon, T as resolveInputHandles, U as useIsValidConnection, V as STICKY_NOTE_TYPE, g as toCodeMirrorDiagnostics, j as NodeTypeIcon, r as CODE_WORKFLOW_NODE_TYPE, z as NODE_OPERATION_DRAG_DATA_FORMAT } from "./node-operation-selection--oy-yEq_.js";
3
- import { $ as isStringOptionList, $t as inAppLeaveTarget, A as StoreNodeProductsByTypeDocument, At as useSaveStatusStore, B as useRunGuard, Bn as useWorkflowTestsEnabled, Bt as shouldReleaseForIdle, C as GetMyNodeInstallationsDocument, Ct as useSaveWorkflow, D as InstallWorkflowNodeDocument, Dn as FLUIDGRIDS_FLAGS, E as InstallPurchasedNodeDocument, F as getNodeMetadata, Fn as useFluidGridsFlag, Ft as leaseCallQueue, G as nodeSubtitle, Gn as replaceAdapterConnections, Gt as sessionBeforeReload, H as asRecord, Hn as isOptionalAdapter, Ht as EDIT_SESSION_ID, I as buildOperationSeed, It as leaseCallSequence, J as paramsFromOperation, Jn as BuilderReadOnlyProvider, Jt as AUTOSAVE_DEBOUNCE_MS, K as nodeTitle, Kn as getNodeDetail, Kt as rememberOwnStatusChange, Ln as usePaletteGates, Lt as phaseAfterAcquire, M as UninstallOrganizationPrivateNodeDocument, Mt as useEditLeaseStore, N as UninstallWorkflowNodeDocument, Nn as useDurableFunctionNodesEnabled, Nt as bannerOf, Ot as refusalOfQuota, Pn as useDurableParityEnabled, Pt as isLocked, R as useInsertNode, Rn as usePhaseCFlags, Rt as phaseAfterTakeOver, S as CreateStoreOrderDocument, St as useFlowValidator, T as InstallOrganizationPrivateNodeDocument, U as coerceNumber, Un as matchesAdapterProvider, Ut as forgetSessionForReload, V as workflowParameterIssues, Vn as connectionsForAdapter, Vt as tickAction, Wn as newConnectionProviderKey, Wt as rememberSessionForReload, X as useNodeConfig, Xt as canAutosave, Yn as useBuilderReadOnly, Yt as AUTOSAVE_RETRY_DELAYS_MS, Zn as getGlobalStoreClient, _t as leavingLosesChanges, a as NODES_METADATA, at as casesFromRules, b as BrowseWorkflowNodesDocument, bt as watchLeavingLosesChanges, c as ensureInstalledNodeTypesLoaded, cn as parseParameterValue, d as ensureRegistryNodeTypesLoaded, dt as useGuardedNavigate, en as resettledBaseline, et as optionListText, f as isNodeConfigsInitialized, fn as isParamVisible, ft as useManualSave, g as registerWorkflowNodeTypes, gn as useNodeRegistryRevision, gt as editingLeaseLocked, hn as useCanvasEdges, ht as editStateFor, i as AVAILABLE_NODES, it as SwitchRulesEditor, jt as useEditLeaseLocked, k as OrganizationPrivateNodesDocument, kt as saveIndicatorOf, l as ensureNodeConfigsLoaded, ln as structuredParameterType, lt as boundFieldsOf, m as refetchStoreNodes, mn as openRunQuotaUpgradePrompt, mt as departingSave, nn as workflowFingerprint, nt as resolveLoadOptionsSpec, o as NODE_CONFIGS, ot as FieldTree, p as onNodeTypesUpdate, pn as useFluidGridsAnalytics, pt as autosaveAllowedFor, q as optionText, qn as buildStickyNode, qt as rememberRevision, rn as useNodeExecution, rt as ConditionTreeBuilder, s as NODE_TYPES, sn as formatParameterValue, st as parseMappingValue, t as FluidGridsContext, tn as saveFailureActionOf, tt as optionListValues, ut as missingRequiredParameters, v as scheduleNodePaletteLoad, vn as loadDefinitionJson, vt as savedEverythingBeforeLeaving, w as GetNodeInstallableVersionsDocument, x as BrowseWorkflowTemplatesDocument, xt as isFlowComplete, yt as touchesSavedFields, z as MissingParametersDialog, zt as releasesOnClose } from "./FluidGridsProvider-DmvFADFa.js";
4
+ import { $ as isStringOptionList, $t as inAppLeaveTarget, An as newConnectionProviderKey, At as useSaveStatusStore, B as useRunGuard, Bt as shouldReleaseForIdle, Ct as useSaveWorkflow, D as InstallWorkflowNodeDocument, Dn as connectionsForAdapter, E as InstallPurchasedNodeDocument, F as getNodeMetadata, Fn as useBuilderReadOnly, Ft as leaseCallQueue, G as nodeSubtitle, Gt as sessionBeforeReload, H as asRecord, Ht as EDIT_SESSION_ID, I as buildOperationSeed, It as leaseCallSequence, J as paramsFromOperation, Jt as AUTOSAVE_DEBOUNCE_MS, K as nodeTitle, Kt as rememberOwnStatusChange, Lt as phaseAfterAcquire, M as UninstallOrganizationPrivateNodeDocument, Mn as getNodeDetail, Mt as useEditLeaseStore, N as UninstallWorkflowNodeDocument, Nn as buildStickyNode, Nt as bannerOf, On as isOptionalAdapter, Ot as refusalOfQuota, Pn as BuilderReadOnlyProvider, Pt as isLocked, R as useInsertNode, Rt as phaseAfterTakeOver, S as CreateStoreOrderDocument, St as useFlowValidator, T as InstallOrganizationPrivateNodeDocument, U as coerceNumber, Ut as forgetSessionForReload, V as workflowParameterIssues, Vt as tickAction, Wt as rememberSessionForReload, X as useNodeConfig, Xt as canAutosave, Yt as AUTOSAVE_RETRY_DELAYS_MS, _t as leavingLosesChanges, a as NODES_METADATA, at as casesFromRules, b as BrowseWorkflowNodesDocument, bt as watchLeavingLosesChanges, cn as parseParameterValue, dt as useGuardedNavigate, en as resettledBaseline, et as optionListText, f as isNodeConfigsInitialized, fn as isParamVisible, ft as useManualSave, g as registerWorkflowNodeTypes, gn as useNodeRegistryRevision, gt as editingLeaseLocked, hn as useCanvasEdges, ht as editStateFor, i as AVAILABLE_NODES, it as SwitchRulesEditor, jn as replaceAdapterConnections, jt as useEditLeaseLocked, k as OrganizationPrivateNodesDocument, kn as matchesAdapterProvider, kt as saveIndicatorOf, l as ensureNodeConfigsLoaded, ln as structuredParameterType, lt as boundFieldsOf, m as refetchStoreNodes, mn as openRunQuotaUpgradePrompt, mt as departingSave, nn as workflowFingerprint, nt as resolveLoadOptionsSpec, o as NODE_CONFIGS, ot as FieldTree, p as onNodeTypesUpdate, pn as useFluidGridsAnalytics, pt as autosaveAllowedFor, q as optionText, qt as rememberRevision, rn as useNodeExecution, rt as ConditionTreeBuilder, s as NODE_TYPES, sn as formatParameterValue, st as parseMappingValue, t as FluidGridsContext, tn as saveFailureActionOf, tt as optionListValues, ut as missingRequiredParameters, v as scheduleNodePaletteLoad, vn as loadDefinitionJson, vt as savedEverythingBeforeLeaving, w as GetNodeInstallableVersionsDocument, xt as isFlowComplete, yt as touchesSavedFields, z as MissingParametersDialog, zt as releasesOnClose } from "./FluidGridsProvider-dvZSNtSW.js";
4
5
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
5
- import { Bi as describeOperationError, Br as WorkflowGitRepositoryDocument, Ct as LinkWorkflowGitRepositoryDocument, Dr as WorkflowDefinitionDocument, Ei as WorkspaceRunsQuotaDocument, Ht as PreviewScheduleDocument, Ir as WorkflowGenerationTemplatesDocument, J as DeleteWorkflowRolloutDocument, Kt as PushWorkflowToGitDocument, Li as emitFluidGridsEvent, Lr as WorkflowGitCommitsDocument, Mt as NodeSchemaIntrospectionDocument, Ni as waitForFluidGridsClient, Nr as WorkflowEnvironmentsDocument, Oi as getFluidGridsClient, Pt as NodeUsageStatusDocument, Qt as RemoveNodeFromWorkflowsDocument, Rr as WorkflowGitDiffDocument, Si as WorkflowVersioningDocument, Ui as throwIfResultError, Vn as TakeOverWorkflowEditLeaseDocument, Wr as WorkflowPromotionRequestsDocument, Xn as UnlinkWorkflowGitRepositoryDocument, Yt as RejectWorkflowPromotionDocument, Z as DeployWorkflowVersionToDevDocument, Zt as ReleaseWorkflowEditLeaseDocument, cr as UpdateWorkflowEnvironmentPolicyDocument, i as ActivateWorkflowVersionCheckedDocument, jn as SetWorkflowRolloutDocument, jr as WorkflowEditLeaseDocument, ki as getFluidGridsClientSafe, mr as WebhookTestListenerDocument, n as AcquireWorkflowEditLeaseDocument, nn as RequestWorkflowPromotionDocument, oa as __exportAll, on as RestoreWorkflowFromGitDocument, qi as getFluidGridsConfig, u as ApproveWorkflowPromotionDocument, wr as WorkflowCallInputsDocument, wt as ListenForWebhookTestEventDocument, x as CancelWorkflowPromotionDocument, xi as WorkflowVersionDrainageDocument, yr as WorkflowAuthoringDocument, zn as StopWebhookTestListenerDocument, zr as WorkflowGitPushesDocument } from "./node-endpoint-configs-DHFGA5MQ.js";
6
- import { D as describeSchedule, H as pauseSchedule, _ as useResource, c as FieldError, d as NativeSelect, et as triggerSchedule, nt as unpauseSchedule, p as StatusPill, r as describeRule, s as isAgentNodeType, v as backfillSchedule } from "./agent-form-BvMWhQ42.js";
7
- import { CF as WorkflowExportFormat, Eo as CodeReplaceStatus, PD as ScheduleType, QF as WorkflowTestExpectedStatus, SF as WorkflowEnvironment, VF as WorkflowScheduleOverlapPolicy, hI as FLUIDGRIDS_PERMISSIONS, vI as SdkPermissionButton, xI as useFluidGridsPermission, xu as DrainageStatus, yI as SdkPermissionGate, yP as VersioningBehavior } from "./graphql-types-XcDLtFwU.js";
8
- import { a as useConnections, r as useAgentsUsingConnection, t as useAgentDefinitionList, wt as useRunFromNodeEligibilityQuery } from "./useAgentDefinitions-NFtLytDR.js";
9
- import { a as emitTemplateUsed, f as BILLING_PLANS_PATH, g as collectExtensions, m as RUNS_QUOTA_NAME, o as emitUpgradeClicked, s as emitWorkflowCreated, t as emitNodeInstalled, y as isQuotaExhaustedFailure } from "./fluidgridsEvents-CNdcrUNN.js";
10
- import { F as normaliseAssertRules, I as operatorSpec, L as readAssertRulesSetting, M as ASSERT_OPERATORS, N as describeAssertRuleProblem, P as emptyAssertRuleRow, R as rowsFromRules, _ as buildNodeDisplayMap, a as TEST_STATUS_TONE_CLASS, b as EntityTagPicker, c as draftToInput, d as isTestRunOpen, f as nextSortOrder, g as testStatusTone, h as testStatusLabel, i as useWorkflowTests, l as emptyTestCaseDraft, m as testRunSummaryText, n as useWorkflowProposal, p as pinnedOutputsFromCanvas, s as draftFromTestCase, t as WorkflowProposalCard, u as enabledCaseIds, z as ruleFromRow } from "./WorkflowProposalCard-8Vr56kQa.js";
6
+ import { c as useDurableParityEnabled, d as usePaletteGates, f as usePhaseCFlags, l as useFluidGridsFlag, m as useWorkflowTestsEnabled, s as useDurableFunctionNodesEnabled, t as FLUIDGRIDS_FLAGS } from "./feature-flags-DM5myWaD.js";
7
+ import { _ as throwIfResultError, d as emitFluidGridsEvent, m as describeOperationError, n as getFluidGridsClient, r as getFluidGridsClientSafe, s as waitForFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
8
+ import { L as triggerSchedule, T as pauseSchedule, t as backfillSchedule, u as describeSchedule, z as unpauseSchedule } from "./api-BrCHB5oA.js";
9
+ import { i as NativeSelect, o as StatusPill, t as FieldError, u as useResource } from "./shared-CMfn30xT.js";
10
+ import { CF as WorkflowExportFormat, Eo as CodeReplaceStatus, PD as ScheduleType, QF as WorkflowTestExpectedStatus, SF as WorkflowEnvironment, VF as WorkflowScheduleOverlapPolicy, xu as DrainageStatus, yP as VersioningBehavior } from "./graphql-types-CDKVWH2R.js";
11
+ import { a as SdkPermissionGate, i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
12
+ import { a as useConnections, r as useAgentsUsingConnection, t as useAgentDefinitionList, wt as useRunFromNodeEligibilityQuery } from "./useAgentDefinitions-Dpz0HDTq.js";
13
+ import { r as describeRule, s as isAgentNodeType } from "./agent-form-Bfvm32j1.js";
14
+ import { An as SetWorkflowRolloutDocument, Ar as WorkflowEditLeaseDocument, Bn as TakeOverWorkflowEditLeaseDocument, Cr as WorkflowCallInputsDocument, Ct as ListenForWebhookTestEventDocument, Er as WorkflowDefinitionDocument, Fr as WorkflowGenerationTemplatesDocument, Gt as PushWorkflowToGitDocument, Ir as WorkflowGitCommitsDocument, Jt as RejectWorkflowPromotionDocument, Lr as WorkflowGitDiffDocument, Mr as WorkflowEnvironmentsDocument, Nt as NodeUsageStatusDocument, Rn as StopWebhookTestListenerDocument, Rr as WorkflowGitPushesDocument, St as LinkWorkflowGitRepositoryDocument, Ti as WorkspaceRunsQuotaDocument, Ur as WorkflowPromotionRequestsDocument, Vt as PreviewScheduleDocument, X as DeployWorkflowVersionToDevDocument, Xt as ReleaseWorkflowEditLeaseDocument, Yn as UnlinkWorkflowGitRepositoryDocument, Zt as RemoveNodeFromWorkflowsDocument, an as RestoreWorkflowFromGitDocument, b as CancelWorkflowPromotionDocument, bi as WorkflowVersionDrainageDocument, jt as NodeSchemaIntrospectionDocument, l as ApproveWorkflowPromotionDocument, pr as WebhookTestListenerDocument, q as DeleteWorkflowRolloutDocument, r as ActivateWorkflowVersionCheckedDocument, sr as UpdateWorkflowEnvironmentPolicyDocument, t as AcquireWorkflowEditLeaseDocument, tn as RequestWorkflowPromotionDocument, vr as WorkflowAuthoringDocument, xi as WorkflowVersioningDocument, zr as WorkflowGitRepositoryDocument } from "./graphql-operation-types-kX-MMjgW.js";
15
+ import { f as BILLING_PLANS_PATH, g as collectExtensions, m as RUNS_QUOTA_NAME, o as emitUpgradeClicked, t as emitNodeInstalled } from "./fluidgridsEvents-CNdcrUNN.js";
16
+ import { C as rowsFromRules, S as readAssertRulesSetting, _ as ASSERT_OPERATORS, a as TEST_STATUS_TONE_CLASS, b as normaliseAssertRules, c as draftToInput, d as isTestRunOpen, f as nextSortOrder, g as testStatusTone, h as testStatusLabel, i as useWorkflowTests, l as emptyTestCaseDraft, m as testRunSummaryText, n as useWorkflowProposal, p as pinnedOutputsFromCanvas, s as draftFromTestCase, t as WorkflowProposalCard, u as enabledCaseIds, v as describeAssertRuleProblem, w as ruleFromRow, x as operatorSpec, y as emptyAssertRuleRow } from "./WorkflowProposalCard-qrEeNyYG.js";
11
17
  import { t as cn$1 } from "./cn-QqidFIgr.js";
12
- import { i as Button$2, t as PublishCustomNodeAction } from "./PublishCustomNodeAction-D2F7oN0S.js";
18
+ import { _ as storeNodeInstallVersion, a as useTemplateImportFlow, d as filterStoreNodesForPalette, f as getStoreNodeAvailability, g as storeNodeDisplayVersion, h as isStoreNodeUpdateAvailable, k as filterPaletteNodes, m as getStoreNodeType, p as getStoreNodeIcon, t as TemplateCard, u as useWorkflowTemplates, v as useInstalledNodes, y as useGlobalStoreClient } from "./TemplateCard-DsC0KNH1.js";
19
+ import { a as useRunners, t as RunnerStatusPill } from "./RunnerStatusPill-Ctg4TJcP.js";
20
+ import { a as DialogFooter$1, d as Input$1, i as DialogDescription$1, l as DialogTitle$1, o as DialogHeader$1, r as DialogContent$1, t as Dialog$1 } from "./dialog-Cjb4lB4D.js";
21
+ import { t as Button$2 } from "./button-DG7y_FVJ.js";
22
+ import { t as PublishCustomNodeAction } from "./PublishCustomNodeAction-5XXnSA3H.js";
13
23
  import { n as useAgentMarksStore } from "./marksStore-DGCXjTmZ.js";
14
- import { O as CreateWorkflowRefusalDialog, b as lineOfNode, h as fileBaseName, i as useWorkflowCodeExport, n as downloadWorkflowCodeExport, t as WorkflowShareDialog, v as isLosslessReport, w as sortFidelityItems, x as orderedExportFiles, y as isLossyFidelityItem } from "./WorkflowShareDialog-6RRDe-_v.js";
24
+ import { O as CreateWorkflowRefusalDialog, b as lineOfNode, h as fileBaseName, i as useWorkflowCodeExport, n as downloadWorkflowCodeExport, t as WorkflowShareDialog, v as isLosslessReport, w as sortFidelityItems, x as orderedExportFiles, y as isLossyFidelityItem } from "./WorkflowShareDialog-B_gn968c.js";
25
+ import { t as EntityTagPicker } from "./EntityTagPicker-BqRNsLCQ.js";
15
26
  import { t as FluidGridsDocsLink } from "./FluidGridsDocsLink-JxkBpOeb.js";
16
- import { a as useDeactivateWorkflow, d as useRestoreWorkflowVersion, o as useDeleteWorkflow, t as useActivateWorkflow, v as useWorkflowVersions } from "./useWorkflows-w4Dx2fF2.js";
17
- import { n as VersionLifecycleBar, r as countLifecycle, t as VersionDiffDialog } from "./VersionDiffDialog-Dq2xlPFI.js";
27
+ import { a as useDeactivateWorkflow, d as useRestoreWorkflowVersion, o as useDeleteWorkflow, t as useActivateWorkflow, v as useWorkflowVersions } from "./useWorkflows-BlVVu8NM.js";
28
+ import { n as VersionLifecycleBar, r as countLifecycle, t as VersionDiffDialog } from "./VersionDiffDialog-DSN0mU0f.js";
29
+ import { t as buildNodeDisplayMap } from "./run-display-BycKYSU0.js";
18
30
  import { n as useRequestedProposal } from "./useRequestedProposal-B7uPXfZB.js";
19
31
  import { t as AiCreditBalanceSlot } from "./aiCreditsBridge-BuE92tKo.js";
20
32
  import * as React$1 from "react";
21
- import React, { Suspense, createContext, forwardRef, lazy, memo, useCallback, useContext, useDeferredValue, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
33
+ import { Suspense, createContext, lazy, memo, useCallback, useContext, useDeferredValue, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
22
34
  import { Link, useNavigate, useParams } from "react-router";
23
35
  import { useMutation, useQuery } from "@apollo/client/react";
24
- import { gql } from "@apollo/client/core";
25
36
  import { create } from "zustand";
26
37
  import { Background, Controls, EdgeLabelRenderer, Handle, MarkerType, MiniMap, NodeToolbar, Panel, Position, ReactFlow, ReactFlowProvider, ViewportPortal, addEdge, getBezierPath, useEdgesState, useInternalNode, useNodes, useNodesState, useOnSelectionChange, useReactFlow, useStore } from "@xyflow/react";
27
38
  import { Icon } from "@iconify/react";
@@ -32,15 +43,14 @@ import { produce } from "immer";
32
43
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
33
44
  import { toast } from "sonner";
34
45
  import { edgeColor, nodeCategoryColor } from "@burdenoff/fe-libs/shared/utils";
35
- import { gql as gql$1 } from "@apollo/client";
46
+ import { gql } from "@apollo/client";
36
47
  import { shallow, useShallow } from "zustand/shallow";
37
48
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Popover, PopoverContent, PopoverTrigger, ResponsiveDialog, ResponsiveDialogContent, ResponsiveDialogDescription, ResponsiveDialogFooter, ResponsiveDialogHeader, ResponsiveDialogTitle, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster as Toaster$1, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@burdenoff/fe-libs/ui";
38
49
  import { useEventBus } from "@burdenoff/fe-libs/shared/events";
39
50
  import { Button as Button$1 } from "@burdenoff/fe-libs/button";
40
51
  import { ThemeProvider } from "@burdenoff/fe-libs/shared/providers/shell";
41
52
  import "@xyflow/react/dist/style.css";
42
- import { cva } from "class-variance-authority";
43
- import { AlertCircle, AlertTriangle, ArrowRight, BadgeCheck, Braces, Check, CheckCircle2, ChevronDown, ChevronRight, CircleHelp, Code, Copy, DollarSign, Download, ExternalLink, Eye, FlaskConical, GitBranch, GitCommitHorizontal, GitCompare, GitCompareArrows, Hash, History, Info, Keyboard, Layers, LayoutGrid, Link2, List, Loader2, Maximize2, Package, Pencil, Play, Plus, PlusCircle, Redo2, RefreshCw, Rocket, RotateCcw, Search, Share2, SlidersHorizontal, Sparkles, Star, Trash2, Type, Undo2, Unlink, Upload, Workflow, X, XIcon } from "lucide-react";
53
+ import { AlertCircle, AlertTriangle, ArrowRight, BadgeCheck, Braces, Check, CheckCircle2, ChevronDown, ChevronRight, CircleHelp, Code, Copy, DollarSign, Download, ExternalLink, Eye, FlaskConical, GitBranch, GitCommitHorizontal, GitCompare, GitCompareArrows, Hash, History, Info, Keyboard, Layers, LayoutGrid, Link2, List, Loader2, Maximize2, Package, Pencil, Play, Plus, PlusCircle, Redo2, RefreshCw, Rocket, RotateCcw, Search, Share2, SlidersHorizontal, Sparkles, Star, Trash2, Type, Undo2, Unlink, Upload, Workflow, X } from "lucide-react";
44
54
  import { ConnectionDialog, ProviderIcon } from "@burdenoff/fe-libs/connections";
45
55
  import { isEmpty } from "radash";
46
56
  import { formatDistanceToNow } from "date-fns";
@@ -49,7 +59,6 @@ import { lintGutter, setDiagnostics } from "@codemirror/lint";
49
59
  import cronstrue from "cronstrue";
50
60
  import { digest } from "ohash";
51
61
  import * as PopoverPrimitive from "@radix-ui/react-popover";
52
- import * as DialogPrimitive from "@radix-ui/react-dialog";
53
62
  import { animate, easeOut } from "popmotion";
54
63
  import { createPortal } from "react-dom";
55
64
  import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLUnionType, buildSchema } from "graphql";
@@ -57,134 +66,6 @@ import { useAgentTools } from "@burdenoff/fe-libs/shared/agent-tools";
57
66
  import { useUnsavedChanges } from "@burdenoff/fe-libs/shared/hooks";
58
67
  import { buildActorFallback, useActorProfiles } from "@burdenoff/fe-libs/shared";
59
68
 
60
- // src/feature-flags/durable-nodes.ts
61
- /**
62
- * Palette gating for the durable-function nodes (BOFF-9905).
63
- *
64
- * wspace-workflow-svc only seeds the `internal:code-workflow` and
65
- * `internal:wait-for-signal` manifests while `DURABLE_NODE_MANIFESTS_ENABLED`
66
- * is set, and refuses durable work while the `fluidgrids.durable-functions`
67
- * flag is off for the workspace. Once the manifests are seeded for every
68
- * workspace, the builder still has to hide the two nodes from the palettes of
69
- * workspaces where the flag is off. This module is that client-side filter; it
70
- * mirrors the server-side gate and is never the gate itself.
71
- *
72
- * Hiding only affects what can be picked from a palette. A workflow that
73
- * already contains one of these nodes keeps rendering and running unchanged.
74
- */
75
- /** Node types offered only while durable functions are enabled. */
76
- const DURABLE_FUNCTION_NODE_TYPES = /* @__PURE__ */ new Set(["internal:code-workflow", "internal:wait-for-signal"]);
77
- /**
78
- * The run-control nodes of Phase C (BOFF-9906, DC15). wspace-workflow-svc
79
- * seeds their manifests only while `RUN_CONTROL_NODE_MANIFESTS_ENABLED` is
80
- * set, which is turned on after this filter ships; the builder then still
81
- * hides each one where its flag is off: Signal Run needs run control, Set
82
- * Search Attributes needs search attributes.
83
- */
84
- const RUN_CONTROL_NODE_TYPES = /* @__PURE__ */ new Set(["internal:signal-run", "internal:set-search-attributes"]);
85
- /**
86
- * The Assert node of the workflow test suites (BOFF-9960). wspace-workflow-svc
87
- * seeds its manifest for every workspace, so the builder hides it wherever the
88
- * `fluidgrids.workflow-tests` flag is off.
89
- */
90
- const WORKFLOW_TEST_NODE_TYPES = /* @__PURE__ */ new Set(["internal:assert"]);
91
- /** Whether a workflow node type is one of the Phase C run-control nodes. */
92
- function isRunControlNodeType(nodeType) {
93
- return !!nodeType && RUN_CONTROL_NODE_TYPES.has(nodeType);
94
- }
95
- /** Normalises the boolean (Phase B) or object form of the palette gates. */
96
- function paletteGatesOf(gates) {
97
- if (typeof gates === "boolean") return {
98
- durableFunctions: gates,
99
- runControl: false,
100
- searchAttributes: false,
101
- workflowTests: false
102
- };
103
- return {
104
- durableFunctions: gates?.durableFunctions === true,
105
- runControl: gates?.runControl === true,
106
- searchAttributes: gates?.searchAttributes === true,
107
- workflowTests: gates?.workflowTests === true
108
- };
109
- }
110
- function hiddenBy(nodeType, gates) {
111
- if (DURABLE_FUNCTION_NODE_TYPES.has(nodeType)) return !gates.durableFunctions;
112
- if (nodeType === "internal:signal-run") return !gates.runControl;
113
- if (nodeType === "internal:set-search-attributes") return !gates.searchAttributes;
114
- if (WORKFLOW_TEST_NODE_TYPES.has(nodeType)) return !gates.workflowTests;
115
- return false;
116
- }
117
- /** Whether a workflow node type is one of the durable-function nodes. */
118
- function isDurableFunctionNodeType(nodeType) {
119
- return !!nodeType && DURABLE_FUNCTION_NODE_TYPES.has(nodeType);
120
- }
121
- /**
122
- * Whether a node type must be left out of the builder palettes. Fails closed:
123
- * a family the caller does not say is enabled is not.
124
- */
125
- function isNodeTypeHiddenFromPalette(nodeType, gates = false) {
126
- return !!nodeType && hiddenBy(nodeType, paletteGatesOf(gates));
127
- }
128
- /**
129
- * Drops the palette entries that must stay hidden. Returns the input array
130
- * itself when nothing is hidden, so the canvas palettes keep the exact list
131
- * (and references) they had before this filter existed.
132
- */
133
- function filterPaletteNodes(nodes, gates = false) {
134
- const resolved = paletteGatesOf(gates);
135
- if (!nodes.some((node) => hiddenBy(node.type, resolved))) return nodes;
136
- return nodes.filter((node) => !hiddenBy(node.type, resolved));
137
- }
138
-
139
- // src/ui/input.tsx
140
- const Input$1 = forwardRef(({ className, type, ...props }, ref) => {
141
- return /* @__PURE__ */ jsx("input", {
142
- type,
143
- className: cn$1("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className),
144
- ref,
145
- ...props
146
- });
147
- });
148
- Input$1.displayName = "Input";
149
-
150
- // src/ui/card.tsx
151
- const Card$1 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", {
152
- ref,
153
- className: cn$1("rounded-lg border bg-card text-card-foreground shadow-sm transition-all duration-200 ease-out hover:shadow-md", className),
154
- ...props
155
- }));
156
- Card$1.displayName = "Card";
157
- const CardHeader$1 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", {
158
- ref,
159
- className: cn$1("flex flex-col space-y-1.5 p-6", className),
160
- ...props
161
- }));
162
- CardHeader$1.displayName = "CardHeader";
163
- const CardTitle$1 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", {
164
- ref,
165
- className: cn$1("text-2xl font-semibold leading-none tracking-tight", className),
166
- ...props
167
- }));
168
- CardTitle$1.displayName = "CardTitle";
169
- const CardDescription$1 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", {
170
- ref,
171
- className: cn$1("text-sm text-muted-foreground", className),
172
- ...props
173
- }));
174
- CardDescription$1.displayName = "CardDescription";
175
- const CardContent$1 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", {
176
- ref,
177
- className: cn$1("p-6 pt-0", className),
178
- ...props
179
- }));
180
- CardContent$1.displayName = "CardContent";
181
- const CardFooter = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", {
182
- ref,
183
- className: cn$1("flex items-center p-6 pt-0", className),
184
- ...props
185
- }));
186
- CardFooter.displayName = "CardFooter";
187
-
188
69
  // src/components/shared/FluidGridsMark.tsx
189
70
  /**
190
71
  * FluidGridsMark — the small "Powered by FluidGrids" mark that every workflow
@@ -2030,7 +1911,7 @@ function WebhookTestListenerControls({ nodeId }) {
2030
1911
  }
2031
1912
 
2032
1913
  // src/builder/components/ndv/workflow-picker-field.tsx
2033
- const LIST_WORKFLOWS_QUERY = gql$1`
1914
+ const LIST_WORKFLOWS_QUERY = gql`
2034
1915
  query ListWorkflowDefinitionsForPicker($limit: Int, $offset: Int) {
2035
1916
  workflowDefinitions(input: { limit: $limit, offset: $offset }) {
2036
1917
  items {
@@ -2464,7 +2345,7 @@ function InputField({ field, mapping, inputMapping, onChange, testId }) {
2464
2345
  }
2465
2346
 
2466
2347
  // src/hooks/load-node-options-request.ts
2467
- const LOAD_NODE_OPTIONS = gql$1`
2348
+ const LOAD_NODE_OPTIONS = gql`
2468
2349
  query LoadNodeOptions(
2469
2350
  $nodeType: String!
2470
2351
  $nodeVersion: String!
@@ -3546,7 +3427,7 @@ function defaultScheduleRule() {
3546
3427
  function isScheduleRuleField(value) {
3547
3428
  return typeof value === "string" && SCHEDULE_RULE_FIELDS.some((field) => field === value);
3548
3429
  }
3549
- function isRecord$6(value) {
3430
+ function isRecord$5(value) {
3550
3431
  return typeof value === "object" && value !== null && !Array.isArray(value);
3551
3432
  }
3552
3433
  function toNumber(value) {
@@ -3645,10 +3526,10 @@ function rulesFromLegacyState(state) {
3645
3526
  * array and the legacy fields, and always yields at least one rule.
3646
3527
  */
3647
3528
  function normalizeScheduleState(state) {
3648
- const source = isRecord$6(state) ? state : {};
3529
+ const source = isRecord$5(state) ? state : {};
3649
3530
  let rules;
3650
3531
  if (Array.isArray(source.rules)) {
3651
- rules = source.rules.filter(isRecord$6).map(normalizeScheduleRule);
3532
+ rules = source.rules.filter(isRecord$5).map(normalizeScheduleRule);
3652
3533
  if (rules.length === 0) rules = [defaultScheduleRule()];
3653
3534
  } else rules = rulesFromLegacyState(source);
3654
3535
  const timezone = toTrimmedString(source.timezone) || "UTC";
@@ -3663,7 +3544,7 @@ function normalizeScheduleState(state) {
3663
3544
  }
3664
3545
  /** Whether the saved state predates `rules[]` (so the editor is showing a conversion). */
3665
3546
  function isLegacyScheduleState(state) {
3666
- if (!isRecord$6(state) || Array.isArray(state.rules)) return false;
3547
+ if (!isRecord$5(state) || Array.isArray(state.rules)) return false;
3667
3548
  return state.mode !== void 0 || toTrimmedString(state.cronExpression) !== "" || toNumber(state.interval) !== void 0;
3668
3549
  }
3669
3550
  function inRange(value, min, max) {
@@ -4313,7 +4194,7 @@ const TOOL_APPROVAL_RULES = [
4313
4194
  function isRule(value) {
4314
4195
  return typeof value === "string" && TOOL_APPROVAL_RULES.includes(value);
4315
4196
  }
4316
- function isRecord$5(value) {
4197
+ function isRecord$4(value) {
4317
4198
  return typeof value === "object" && value !== null && !Array.isArray(value);
4318
4199
  }
4319
4200
  function parsed(value) {
@@ -4327,11 +4208,11 @@ function parsed(value) {
4327
4208
  /** The rows of a stored setting; rows with an unknown rule read as `ask`. */
4328
4209
  function readToolApprovalRows(value) {
4329
4210
  const raw = parsed(value);
4330
- if (Array.isArray(raw)) return raw.filter(isRecord$5).map((row) => ({
4211
+ if (Array.isArray(raw)) return raw.filter(isRecord$4).map((row) => ({
4331
4212
  toolName: typeof row.toolName === "string" ? row.toolName : "",
4332
4213
  rule: isRule(row.rule) ? row.rule : "ask"
4333
4214
  }));
4334
- if (isRecord$5(raw)) return Object.entries(raw).map(([toolName, rule]) => ({
4215
+ if (isRecord$4(raw)) return Object.entries(raw).map(([toolName, rule]) => ({
4335
4216
  toolName,
4336
4217
  rule: isRule(rule) ? rule : "ask"
4337
4218
  }));
@@ -4348,9 +4229,9 @@ function connectedToolNames(agentId, nodes, edges) {
4348
4229
  for (const edge of edges) {
4349
4230
  if (edge.source !== agentId || edge.sourceHandle !== "Tool") continue;
4350
4231
  const node = edge.target ? byId.get(edge.target) : void 0;
4351
- const args = isRecord$5(node?.data) ? node.data.args : void 0;
4352
- const state = isRecord$5(args) ? args.state : void 0;
4353
- const name = isRecord$5(state) ? state.toolName : void 0;
4232
+ const args = isRecord$4(node?.data) ? node.data.args : void 0;
4233
+ const state = isRecord$4(args) ? args.state : void 0;
4234
+ const name = isRecord$4(state) ? state.toolName : void 0;
4354
4235
  if (typeof name === "string" && name.trim()) names.add(name.trim());
4355
4236
  }
4356
4237
  return [...names];
@@ -4923,7 +4804,7 @@ function TimezoneSelect({ value, onChange, className, testId }) {
4923
4804
  /** Builder field types the trigger manifests give the two picker fields. */
4924
4805
  const PLATFORM_EVENT_MODULE_FIELD = "platform-event-module";
4925
4806
  const PLATFORM_EVENT_RECORD_TYPE_FIELD = "platform-event-record-type";
4926
- const PLATFORM_EVENT_SOURCES_QUERY = gql$1`
4807
+ const PLATFORM_EVENT_SOURCES_QUERY = gql`
4927
4808
  query PlatformEventSources {
4928
4809
  platformEventSources {
4929
4810
  module
@@ -4937,17 +4818,17 @@ const PLATFORM_EVENT_SOURCES_QUERY = gql$1`
4937
4818
  /** The list hardly changes; one read per few minutes is plenty. */
4938
4819
  const CACHE_TTL_MS$1 = 18e4;
4939
4820
  const caches$1 = /* @__PURE__ */ new WeakMap();
4940
- function isRecord$4(value) {
4821
+ function isRecord$3(value) {
4941
4822
  return typeof value === "object" && value !== null && !Array.isArray(value);
4942
4823
  }
4943
4824
  /** The well-formed sources of a `platformEventSources` response; anything else is dropped. */
4944
4825
  function readPlatformEventSources(data) {
4945
- const list = isRecord$4(data) ? data.platformEventSources : void 0;
4826
+ const list = isRecord$3(data) ? data.platformEventSources : void 0;
4946
4827
  if (!Array.isArray(list)) return [];
4947
4828
  const sources = [];
4948
4829
  for (const item of list) {
4949
- if (!isRecord$4(item) || typeof item.module !== "string") continue;
4950
- const recordTypes = Array.isArray(item.recordTypes) ? item.recordTypes.flatMap((type) => isRecord$4(type) && typeof type.name === "string" ? [{
4830
+ if (!isRecord$3(item) || typeof item.module !== "string") continue;
4831
+ const recordTypes = Array.isArray(item.recordTypes) ? item.recordTypes.flatMap((type) => isRecord$3(type) && typeof type.name === "string" ? [{
4951
4832
  name: type.name,
4952
4833
  actions: Array.isArray(type.actions) ? type.actions.filter((action) => typeof action === "string") : []
4953
4834
  }] : []) : [];
@@ -5196,7 +5077,7 @@ function PlatformEventSourceSelect({ kind, value, module = "", required = false,
5196
5077
  // src/builder/components/ndv/timecampus-time-triggers.ts
5197
5078
  /** Builder field type the TimeCampus Event manifest gives its `triggerIds` field. */
5198
5079
  const TIMECAMPUS_TIME_TRIGGER_FIELD = "timecampus-time-trigger";
5199
- const MY_TIME_TRIGGERS_QUERY = gql$1`
5080
+ const MY_TIME_TRIGGERS_QUERY = gql`
5200
5081
  query FluidGridsTimeCampusMyTimeTriggers {
5201
5082
  timeCampusMyTimeTriggers {
5202
5083
  id
@@ -5214,16 +5095,16 @@ const MY_TIME_TRIGGERS_QUERY = gql$1`
5214
5095
  */
5215
5096
  const CACHE_TTL_MS = 3e4;
5216
5097
  const caches = /* @__PURE__ */ new WeakMap();
5217
- function isRecord$3(value) {
5098
+ function isRecord$2(value) {
5218
5099
  return typeof value === "object" && value !== null && !Array.isArray(value);
5219
5100
  }
5220
5101
  /** The well-formed triggers of a `timeCampusMyTimeTriggers` response, sorted by name. */
5221
5102
  function readTimeTriggers(data) {
5222
- const list = isRecord$3(data) ? data.timeCampusMyTimeTriggers : void 0;
5103
+ const list = isRecord$2(data) ? data.timeCampusMyTimeTriggers : void 0;
5223
5104
  if (!Array.isArray(list)) return [];
5224
5105
  const triggers = [];
5225
5106
  for (const item of list) {
5226
- if (!isRecord$3(item) || typeof item.id !== "string" || !item.id) continue;
5107
+ if (!isRecord$2(item) || typeof item.id !== "string" || !item.id) continue;
5227
5108
  triggers.push({
5228
5109
  id: item.id,
5229
5110
  name: typeof item.name === "string" && item.name.trim() ? item.name.trim() : item.id,
@@ -5537,24 +5418,6 @@ function TimeCampusTriggerSelect({ value, onChange, className, testId, label, in
5537
5418
  });
5538
5419
  }
5539
5420
 
5540
- // src/hooks/store/useGlobalStoreClient.ts
5541
- /**
5542
- * Hook to get the Global Store Apollo client
5543
- *
5544
- * Priority:
5545
- * 1. FluidGridsProvider context (if available)
5546
- * 2. Module-level singleton set via setGlobalStoreClient()
5547
- *
5548
- * @throws Error if no global client is available from either source
5549
- */
5550
- function useGlobalStoreClient() {
5551
- const context = useContext(FluidGridsContext);
5552
- if (context?.globalClient) return context.globalClient;
5553
- const singletonClient = getGlobalStoreClient();
5554
- if (singletonClient) return singletonClient;
5555
- throw new Error("useGlobalStoreClient: No global client available. Either wrap your component tree with <FluidGridsProvider globalClient={...}> or call setGlobalStoreClient(client) before rendering.");
5556
- }
5557
-
5558
5421
  // src/modules/channels/hooks/useChannels.ts
5559
5422
  /**
5560
5423
  * Custom hooks for fetching channels from global-channel-svc.
@@ -5563,7 +5426,7 @@ function useGlobalStoreClient() {
5563
5426
  * Channels represent configured connections to communication platforms
5564
5427
  * (Slack, Teams, Discord, Email, SMS, etc.).
5565
5428
  */
5566
- const CHANNEL_SUMMARY_FIELDS = gql$1`
5429
+ const CHANNEL_SUMMARY_FIELDS = gql`
5567
5430
  fragment ChannelSummaryFields on Channel {
5568
5431
  id
5569
5432
  name
@@ -5577,7 +5440,7 @@ const CHANNEL_SUMMARY_FIELDS = gql$1`
5577
5440
  createdAt
5578
5441
  }
5579
5442
  `;
5580
- const CHANNEL_DETAIL_FIELDS = gql$1`
5443
+ const CHANNEL_DETAIL_FIELDS = gql`
5581
5444
  fragment ChannelDetailFields on Channel {
5582
5445
  id
5583
5446
  name
@@ -5599,7 +5462,7 @@ const CHANNEL_DETAIL_FIELDS = gql$1`
5599
5462
  updatedAt
5600
5463
  }
5601
5464
  `;
5602
- const GET_CHANNELS = gql$1`
5465
+ const GET_CHANNELS = gql`
5603
5466
  ${CHANNEL_SUMMARY_FIELDS}
5604
5467
  query GetChannels(
5605
5468
  $filter: ChannelFilterInput
@@ -5614,7 +5477,7 @@ const GET_CHANNELS = gql$1`
5614
5477
  }
5615
5478
  }
5616
5479
  `;
5617
- const GET_CHANNEL = gql$1`
5480
+ const GET_CHANNEL = gql`
5618
5481
  ${CHANNEL_DETAIL_FIELDS}
5619
5482
  query GetChannel($id: ID!) {
5620
5483
  getChannel(id: $id) {
@@ -5863,7 +5726,7 @@ function channelAuthUrl(providerKey, returnTo, nodeId) {
5863
5726
  * panel: pick a communication channel, see its status, start the platform's
5864
5727
  * OAuth flow, and link an integration connection to the channel.
5865
5728
  */
5866
- const LINK_INTEGRATION_MUTATION = gql$1`
5729
+ const LINK_INTEGRATION_MUTATION = gql`
5867
5730
  mutation LinkIntegrationConnection(
5868
5731
  $integrationConnectionId: ID!
5869
5732
  $channelId: ID!
@@ -8048,184 +7911,6 @@ function NodeParamsForm({ config, nodeType, testIdPrefix = "ndv" }) {
8048
7911
  ] });
8049
7912
  }
8050
7913
 
8051
- // src/modules/runners/hooks/useRunners.ts
8052
- /**
8053
- * Self-hosted runners (wspace-sandbox-svc): list, add, rotate token, remove.
8054
- *
8055
- * A runner is a program the customer installs on their own machine; workflow
8056
- * nodes whose "Run on" names it execute there, in a local sandbox. Mirrors the
8057
- * connections/variables hook pattern (shared Apollo client, no generated hooks).
8058
- */
8059
- const RUNNER_FIELDS = gql$1`
8060
- fragment SandboxRunnerFields on SandboxRunner {
8061
- id
8062
- name
8063
- status
8064
- hostname
8065
- os
8066
- arch
8067
- driver
8068
- version
8069
- capacity
8070
- privateNetwork {
8071
- target
8072
- ports
8073
- nodes
8074
- }
8075
- tokenPrefix
8076
- lastSeenAt
8077
- connectedAt
8078
- createdBy
8079
- createdAt
8080
- }
8081
- `;
8082
- const LIST_RUNNERS = gql$1`
8083
- ${RUNNER_FIELDS}
8084
- query SandboxRunners {
8085
- sandboxRunners(context: {}) {
8086
- ...SandboxRunnerFields
8087
- }
8088
- }
8089
- `;
8090
- const CREATE_RUNNER = gql$1`
8091
- ${RUNNER_FIELDS}
8092
- mutation CreateSandboxRunner($name: String!) {
8093
- createSandboxRunner(input: { name: $name, context: {} }) {
8094
- token
8095
- bridgeUrl
8096
- runner {
8097
- ...SandboxRunnerFields
8098
- }
8099
- }
8100
- }
8101
- `;
8102
- const ROTATE_RUNNER_TOKEN = gql$1`
8103
- ${RUNNER_FIELDS}
8104
- mutation RotateSandboxRunnerToken($id: ID!) {
8105
- rotateSandboxRunnerToken(id: $id, context: {}) {
8106
- token
8107
- bridgeUrl
8108
- runner {
8109
- ...SandboxRunnerFields
8110
- }
8111
- }
8112
- }
8113
- `;
8114
- const DELETE_RUNNER = gql$1`
8115
- mutation DeleteSandboxRunner($id: ID!) {
8116
- deleteSandboxRunner(id: $id, context: {})
8117
- }
8118
- `;
8119
- /** Where the installer is published (fluidgrids-runner release workflow). */
8120
- const RUNNER_INSTALL_SCRIPT_URL = "https://www.fluidgrids.ai/downloads/runner/install.sh";
8121
- /**
8122
- * The one-line install command shown after creating a runner. It carries NO
8123
- * token: the installer asks for it (input hidden), so the token never lands in
8124
- * shell history or the machine's process list.
8125
- */
8126
- function runnerInstallCommand(bridgeUrl) {
8127
- const base = `curl -fsSL ${RUNNER_INSTALL_SCRIPT_URL} | sh`;
8128
- return bridgeUrl && bridgeUrl !== "wss://graphqlworkspaces.burdenoff.com/runner-bridge" ? `${base} -s -- --url ${bridgeUrl}` : base;
8129
- }
8130
- /** The runner's built-in default; omitted from the command when it matches. */
8131
- const DEFAULT_BRIDGE_URL = "wss://graphqlworkspaces.burdenoff.com/runner-bridge";
8132
- /**
8133
- * Runners in the current workspace. Polls while mounted so ONLINE/OFFLINE
8134
- * stays current (a runner heartbeats every 20 s).
8135
- */
8136
- function useRunners(options) {
8137
- const [runners, setRunners] = useState([]);
8138
- const [loading, setLoading] = useState(!options?.skip);
8139
- const [error, setError] = useState(null);
8140
- const mounted = useRef(true);
8141
- const fetchRunners = useCallback(async () => {
8142
- if (options?.skip) return;
8143
- try {
8144
- const result = await getFluidGridsClient().query({
8145
- query: LIST_RUNNERS,
8146
- fetchPolicy: "network-only"
8147
- });
8148
- if (!mounted.current) return;
8149
- setRunners(result.data?.sandboxRunners ?? []);
8150
- setError(null);
8151
- } catch (err) {
8152
- if (mounted.current) setError(err);
8153
- } finally {
8154
- if (mounted.current) setLoading(false);
8155
- }
8156
- }, [options?.skip]);
8157
- useEffect(() => {
8158
- mounted.current = true;
8159
- fetchRunners();
8160
- const pollMs = options?.pollMs ?? 1e4;
8161
- const timer = pollMs > 0 ? setInterval(fetchRunners, pollMs) : null;
8162
- return () => {
8163
- mounted.current = false;
8164
- if (timer) clearInterval(timer);
8165
- };
8166
- }, [fetchRunners, options?.pollMs]);
8167
- return {
8168
- runners,
8169
- loading,
8170
- error,
8171
- refetch: fetchRunners
8172
- };
8173
- }
8174
- function useRunnerActions() {
8175
- const [busy, setBusy] = useState(false);
8176
- const run = useCallback(async (fn) => {
8177
- setBusy(true);
8178
- try {
8179
- return await fn();
8180
- } finally {
8181
- setBusy(false);
8182
- }
8183
- }, []);
8184
- return {
8185
- createRunner: useCallback((name) => run(async () => {
8186
- const result = throwIfResultError(await getFluidGridsClient().mutate({
8187
- mutation: CREATE_RUNNER,
8188
- variables: { name }
8189
- }));
8190
- if (!result.data?.createSandboxRunner) throw new Error("No runner returned");
8191
- return result.data.createSandboxRunner;
8192
- }), [run]),
8193
- rotateToken: useCallback((id) => run(async () => {
8194
- const result = throwIfResultError(await getFluidGridsClient().mutate({
8195
- mutation: ROTATE_RUNNER_TOKEN,
8196
- variables: { id }
8197
- }));
8198
- if (!result.data?.rotateSandboxRunnerToken) throw new Error("No runner returned");
8199
- return result.data.rotateSandboxRunnerToken;
8200
- }), [run]),
8201
- deleteRunner: useCallback((id) => run(async () => {
8202
- return throwIfResultError(await getFluidGridsClient().mutate({
8203
- mutation: DELETE_RUNNER,
8204
- variables: { id }
8205
- })).data?.deleteSandboxRunner === true;
8206
- }), [run]),
8207
- busy
8208
- };
8209
- }
8210
-
8211
- // src/modules/runners/components/RunnerStatusPill.tsx
8212
- const STYLES = {
8213
- ONLINE: "border-status-success-border bg-status-success-bg-subtle text-status-success-text",
8214
- OFFLINE: "border-border-default bg-bg-sunken text-text-muted",
8215
- NEVER_CONNECTED: "border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text"
8216
- };
8217
- /** Online / Offline / Not connected yet. */
8218
- function RunnerStatusPill({ status, className }) {
8219
- const tr = useTr();
8220
- const label = status === "ONLINE" ? tr("fluidgrids.runners.online", "Online") : status === "OFFLINE" ? tr("fluidgrids.runners.offline", "Offline") : tr("fluidgrids.runners.neverConnected", "Not connected yet");
8221
- return /* @__PURE__ */ jsx("span", {
8222
- className: cn("inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium", STYLES[status], className),
8223
- "data-testid": "runner-status",
8224
- "data-status": status,
8225
- children: label
8226
- });
8227
- }
8228
-
8229
7914
  // src/modules/runners/components/RunOnSection.tsx
8230
7915
  /**
8231
7916
  * NDV Settings → "Run on": FluidGrids Cloud (default) or one of the
@@ -9330,45 +9015,6 @@ function useStoreNodes(options = {}) {
9330
9015
  };
9331
9016
  }
9332
9017
 
9333
- // src/hooks/store/useInstalledNodes.ts
9334
- /**
9335
- * Hook to get installed nodes for the current workspace
9336
- *
9337
- * Fetches installations from the Store service to show "Already Installed" status
9338
- * in the Store popup and display installed count.
9339
- */
9340
- function useInstalledNodes() {
9341
- const client = useGlobalStoreClient();
9342
- const workspaceId = useContext(FluidGridsContext)?.workspaceId ?? getFluidGridsConfig().workspaceId;
9343
- const { data, loading, error, refetch } = useQuery(GetMyNodeInstallationsDocument, {
9344
- client,
9345
- variables: {
9346
- workspaceId: workspaceId || "",
9347
- limit: 100,
9348
- offset: 0
9349
- },
9350
- skip: !workspaceId,
9351
- fetchPolicy: "cache-and-network"
9352
- });
9353
- const installations = data?.myStoreAppInstallations?.items || [];
9354
- const activeInstallations = installations.filter((item) => item.status === "ACTIVE");
9355
- return {
9356
- installations,
9357
- installedAppIds: new Set(activeInstallations.map((item) => item.productId)),
9358
- installationsMap: new Map(activeInstallations.map((item) => [item.productId, {
9359
- installedVersion: item.version ?? void 0,
9360
- installationId: item.id,
9361
- installedAt: item.installedAt
9362
- }])),
9363
- deprecatedInstallations: activeInstallations.filter((item) => item.productStatus === "UNLISTED"),
9364
- installedNodeTypes: /* @__PURE__ */ new Set(),
9365
- totalCount: data?.myStoreAppInstallations?.total || 0,
9366
- loading,
9367
- error,
9368
- refetch
9369
- };
9370
- }
9371
-
9372
9018
  // src/hooks/store/useRefreshAvailableNodes.ts
9373
9019
  /**
9374
9020
  * Hook to refresh the available nodes list after installing/uninstalling nodes.
@@ -9395,138 +9041,6 @@ function useRefreshAvailableNodes() {
9395
9041
  }, []) };
9396
9042
  }
9397
9043
 
9398
- // src/builder/components/flow-builder/components/store/store-node-availability.ts
9399
- /**
9400
- * Operations a user may actually pick for a node type: adapter operations
9401
- * (supplyModel/supplyTool/…) and `internal` ones (the AI Agent's planStep) are
9402
- * executor plumbing, chosen automatically, and must not count as user choices.
9403
- */
9404
- function userSelectableOperationCount(nodeType) {
9405
- if (!nodeType) return 0;
9406
- return (NODE_CONFIGS.find((c) => c.type === nodeType)?.operations ?? []).filter((op) => !op.isAdapterOperation && !op.internal).length;
9407
- }
9408
- /**
9409
- * Resolve the workflow node type for a store node.
9410
- *
9411
- * Installed nodes can fall back to the historical slug-based type when older
9412
- * manifests did not yet include `manifest.nodeType`.
9413
- */
9414
- function getStoreNodeType(node, isInstalled) {
9415
- return node.manifest?.nodeType ?? (isInstalled ? `store/${node.slug}` : void 0);
9416
- }
9417
- /**
9418
- * A node is considered prebuilt when it is already present in the workflow
9419
- * registry/available nodes list even though the workspace does not have a store
9420
- * installation record for it.
9421
- */
9422
- function getStoreNodeAvailability(node, isInstalled, options = {}) {
9423
- const nodeType = getStoreNodeType(node, isInstalled);
9424
- if (isNodeTypeHiddenFromPalette(nodeType, options.durableFunctionsEnabled)) return {
9425
- nodeType,
9426
- isPrebuilt: false,
9427
- isDraggable: false,
9428
- isHidden: true
9429
- };
9430
- const isInternalBuiltin = !!nodeType && nodeType.startsWith("internal:");
9431
- const isRegistryBacked = !!nodeType && AVAILABLE_NODES.some((availableNode) => availableNode.type === nodeType && (availableNode.publishType === "BUILTIN" || availableNode.publishType === void 0));
9432
- const isPrebuilt = !isInstalled && (isInternalBuiltin || isRegistryBacked);
9433
- return {
9434
- nodeType,
9435
- isPrebuilt,
9436
- isDraggable: !!nodeType && (isInstalled || isPrebuilt) && userSelectableOperationCount(nodeType) <= 1,
9437
- isHidden: false
9438
- };
9439
- }
9440
- /**
9441
- * The icon a store listing shows.
9442
- *
9443
- * Listings for the built-in nodes are seeded without an icon, so every one of
9444
- * them rendered as a letter tile in the Node Store while the canvas showed the
9445
- * node's real icon (BOFF-7651). The registry already carries that icon from
9446
- * the node's manifest, so a listing without its own falls back to it. A
9447
- * listing's own icon always wins; `null` means "no icon anywhere", and the
9448
- * caller draws its letter tile.
9449
- */
9450
- function getStoreNodeIcon(listingIcon, nodeType) {
9451
- if (listingIcon) return listingIcon;
9452
- if (!nodeType) return null;
9453
- return AVAILABLE_NODES.find((availableNode) => availableNode.type === nodeType)?.icon || null;
9454
- }
9455
- /**
9456
- * The version to show for a store node: the node's own version from its
9457
- * manifest (`nodeVersion`, then `identity.version`), falling back to the
9458
- * product's `manifestVersion`. The product field is usually the manifest
9459
- * *format* version ("1.0"), so showing it read as "v1.0" for every node
9460
- * (BOFF-11456).
9461
- */
9462
- function storeNodeDisplayVersion(manifest, manifestVersion) {
9463
- const record = typeof manifest === "string" ? parseManifest(manifest) : manifest;
9464
- if (typeof record === "object" && record !== null) {
9465
- const { nodeVersion, identity } = record;
9466
- if (typeof nodeVersion === "string" && nodeVersion.trim()) return nodeVersion.trim();
9467
- const identityVersion = identity?.version;
9468
- if (typeof identityVersion === "string" && identityVersion.trim()) return identityVersion.trim();
9469
- }
9470
- return manifestVersion?.trim() || void 0;
9471
- }
9472
- const MAJOR_MINOR_ONLY = /^\d+\.\d+$/;
9473
- /**
9474
- * Node versions are full semver: node images are tagged `major.minor.patch`
9475
- * and the sandbox picks one by the exact version a workflow node pins. A bare
9476
- * `major.minor` (the manifest format version, "1.0") therefore means
9477
- * `major.minor.0` (BOFF-11496). Other values come back trimmed.
9478
- */
9479
- function canonicalNodeVersion(version) {
9480
- const trimmed = version.trim();
9481
- return MAJOR_MINOR_ONLY.test(trimmed) ? `${trimmed}.0` : trimmed;
9482
- }
9483
- /**
9484
- * The version a store node installs as: its own version from the manifest,
9485
- * canonical, `1.0.0` when the product names none. Installs used to send the
9486
- * product's `manifestVersion` ("1.0"), which no node image carries
9487
- * (BOFF-11496).
9488
- */
9489
- function storeNodeInstallVersion(manifest, manifestVersion) {
9490
- return canonicalNodeVersion(storeNodeDisplayVersion(manifest, manifestVersion) ?? "1.0.0");
9491
- }
9492
- /**
9493
- * Whether an installed store node has a newer version: the installation's
9494
- * recorded version against the node's own current version, both canonical, so
9495
- * an installation recorded as "1.0" is not "outdated" against 1.0.0
9496
- * (BOFF-11496).
9497
- */
9498
- function isStoreNodeUpdateAvailable(installedVersion, manifest, manifestVersion) {
9499
- if (!installedVersion?.trim()) return false;
9500
- const latest = storeNodeDisplayVersion(manifest, manifestVersion);
9501
- if (!latest) return false;
9502
- return canonicalNodeVersion(installedVersion) !== canonicalNodeVersion(latest);
9503
- }
9504
- function parseManifest(text) {
9505
- try {
9506
- return JSON.parse(text);
9507
- } catch {
9508
- return;
9509
- }
9510
- }
9511
- /**
9512
- * Whether a store product must be left out of the builder's store listings
9513
- * because it is a gated node (durable-function or run-control) whose flag is
9514
- * off.
9515
- * Fails closed when `durableFunctionsEnabled` is omitted.
9516
- */
9517
- function isStoreNodeHiddenFromPalette(node, durableFunctionsEnabled = false) {
9518
- return isNodeTypeHiddenFromPalette(getStoreNodeType(node, false), durableFunctionsEnabled);
9519
- }
9520
- /**
9521
- * Drops the store products that must stay hidden from the builder. Returns
9522
- * the input array itself when nothing is hidden.
9523
- */
9524
- function filterStoreNodesForPalette(nodes, durableFunctionsEnabled = false) {
9525
- const hidden = (node) => isStoreNodeHiddenFromPalette(node, durableFunctionsEnabled);
9526
- if (!nodes.some(hidden)) return nodes;
9527
- return nodes.filter((node) => !hidden(node));
9528
- }
9529
-
9530
9044
  // src/hooks/store/useInstallNode.ts
9531
9045
  /** The configured JSON scalar contract for an installation manifest is an object. */
9532
9046
  function isManifestRecord(value) {
@@ -9803,77 +9317,6 @@ const useRemoveNodeFromWorkflows = () => {
9803
9317
  }, []) };
9804
9318
  };
9805
9319
 
9806
- // src/hooks/store/useWorkflowTemplates.ts
9807
- /**
9808
- * Hook to browse workflow templates from the Store
9809
- *
9810
- * Fetches WORKFLOW type products from the global store service
9811
- * with rich preview data (screenshots, videos, etc.)
9812
- *
9813
- * @param searchTerm - Optional search query
9814
- * @param limit - Number of templates per page (default: 20)
9815
- * @param enabled - Whether to enable the query (default: true). Set to false to skip the query.
9816
- * @param category - Optional store category to filter by (exact match)
9817
- */
9818
- function useWorkflowTemplates(searchTerm, limit = 20, enabled = true, category) {
9819
- const client = useGlobalStoreClient();
9820
- const context = useContext(FluidGridsContext);
9821
- const workspaceId = context?.workspaceId ?? getFluidGridsConfig().workspaceId;
9822
- const organizationId = context?.organizationId;
9823
- const contextHeaders = useMemo(() => {
9824
- const headers = {};
9825
- if (organizationId) headers["x-organization-id"] = organizationId;
9826
- if (workspaceId) headers["x-workspace-id"] = workspaceId;
9827
- return headers;
9828
- }, [organizationId, workspaceId]);
9829
- const buildVariables = (offset) => ({ input: {
9830
- filter: {
9831
- type: "WORKFLOW",
9832
- status: "PUBLISHED",
9833
- search: searchTerm,
9834
- ...category ? { category } : {}
9835
- },
9836
- pagination: {
9837
- limit,
9838
- offset
9839
- },
9840
- sortBy: searchTerm ? "RELEVANCE" : "NEWEST"
9841
- } });
9842
- const { data, loading, error, fetchMore, refetch } = useQuery(BrowseWorkflowTemplatesDocument, {
9843
- client,
9844
- variables: buildVariables(0),
9845
- fetchPolicy: "cache-and-network",
9846
- skip: !enabled,
9847
- context: Object.keys(contextHeaders).length > 0 ? { headers: contextHeaders } : void 0
9848
- });
9849
- const templates = data?.browseStore?.products || [];
9850
- const totalCount = data?.browseStore?.totalCount || 0;
9851
- const hasMore = data?.browseStore?.hasMore || false;
9852
- const loadMore = () => {
9853
- if (!hasMore || loading) return;
9854
- return fetchMore({
9855
- variables: buildVariables(templates.length),
9856
- context: Object.keys(contextHeaders).length > 0 ? { headers: contextHeaders } : void 0,
9857
- updateQuery: (prev, { fetchMoreResult }) => {
9858
- if (!fetchMoreResult) return prev;
9859
- return { browseStore: {
9860
- ...fetchMoreResult.browseStore,
9861
- products: [...prev.browseStore?.products || [], ...fetchMoreResult.browseStore?.products || []]
9862
- } };
9863
- }
9864
- });
9865
- };
9866
- return {
9867
- templates,
9868
- totalCount,
9869
- hasMore,
9870
- loading,
9871
- error,
9872
- loadMore,
9873
- refetch
9874
- };
9875
- }
9876
-
9877
9320
  // src/hooks/store/useCreateOrder.ts
9878
9321
  /**
9879
9322
  * Hook to create a store order for paid products
@@ -9915,829 +9358,21 @@ function useCreateOrder() {
9915
9358
  };
9916
9359
  }
9917
9360
 
9918
- // src/hooks/store/useTemplateDetail.ts
9919
- const GET_WORKFLOW_TEMPLATE_DETAIL = gql`
9920
- query GetWorkflowTemplateDetail($id: ID!) {
9921
- product(id: $id) {
9922
- id
9923
- name
9924
- slug
9925
- type
9926
- description
9927
- longDescription
9928
- icon
9929
- screenshots
9930
- videoUrls
9931
- rating
9932
- reviewCount
9933
- downloads
9934
- viewCount
9935
- price
9936
- pricingModel
9937
- currency
9938
- featured
9939
- content
9940
- manifest
9941
- manifestVersion
9942
- authorName
9943
- license
9944
- category
9945
- tags
9946
- compatibleProducts
9947
- publisher {
9948
- id
9949
- name
9950
- email
9951
- websiteUrl
9952
- logoUrl
9953
- isVerified
9954
- }
9955
- createdAt
9956
- updatedAt
9957
- publishedAt
9958
- }
9959
- }
9960
- `;
9361
+ // src/hooks/store/useOrganizationPrivateNodes.ts
9362
+ function errorMessage(error, fallback) {
9363
+ return error instanceof Error && error.message ? error.message : fallback;
9364
+ }
9961
9365
  /**
9962
- * Hook to lazily fetch full details of a workflow template from the store.
9963
- *
9964
- * Unlike `useWorkflowTemplates` which fetches browse-level metadata,
9965
- * this hook fetches the full product detail including the `content` field
9966
- * which contains the workflow definitionJson.
9366
+ * The workspace-private custom nodes of the current workspace's organization
9367
+ * (BOFF-9766), with install, update and uninstall.
9967
9368
  *
9968
- * Uses a manual query (not useQuery) to only fetch when explicitly requested.
9369
+ * These nodes skip the public store: devportal's `organizationPrivateNodes`
9370
+ * lists them, `installApp` installs a chosen version (which also registers
9371
+ * it in the workspace's builder; installing a newer version over an active
9372
+ * install updates it), and `uninstallApp` removes it. After every change the
9373
+ * builder's node palette is refreshed so the node appears or disappears.
9969
9374
  */
9970
- function useTemplateDetail() {
9971
- const client = useGlobalStoreClient();
9972
- const [loading, setLoading] = useState(false);
9973
- const [error, setError] = useState(null);
9974
- const context = useContext(FluidGridsContext);
9975
- const workspaceId = context?.workspaceId ?? getFluidGridsConfig().workspaceId;
9976
- const organizationId = context?.organizationId;
9977
- const contextHeaders = useMemo(() => {
9978
- const headers = {};
9979
- if (organizationId) headers["x-organization-id"] = organizationId;
9980
- if (workspaceId) headers["x-workspace-id"] = workspaceId;
9981
- return headers;
9982
- }, [organizationId, workspaceId]);
9983
- return {
9984
- fetchTemplateDetail: useCallback(async (templateId) => {
9985
- setLoading(true);
9986
- setError(null);
9987
- try {
9988
- const result = await client.query({
9989
- query: GET_WORKFLOW_TEMPLATE_DETAIL,
9990
- variables: { id: templateId },
9991
- fetchPolicy: "network-only",
9992
- context: Object.keys(contextHeaders).length > 0 ? { headers: contextHeaders } : void 0
9993
- });
9994
- if (result.error) throw result.error;
9995
- return result.data?.product ?? null;
9996
- } catch (err) {
9997
- const fetchError = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to fetch template detail");
9998
- setError(fetchError);
9999
- console.error("[useTemplateDetail] Error:", err);
10000
- return null;
10001
- } finally {
10002
- setLoading(false);
10003
- }
10004
- }, [client, contextHeaders]),
10005
- loading,
10006
- error
10007
- };
10008
- }
10009
-
10010
- // src/utils/template-input-applicator.ts
10011
- /**
10012
- * Template Input Applicator
10013
- *
10014
- * Walks a workflow definitionJson and replaces ${variableName} placeholders
10015
- * with user-provided values. Used during template import to apply
10016
- * user-configurable inputs before creating the workflow.
10017
- */
10018
- /**
10019
- * Deep-clone an object using structured clone.
10020
- * Falls back to JSON parse/stringify for environments without structuredClone.
10021
- */
10022
- function deepClone(obj) {
10023
- if (typeof structuredClone === "function") return structuredClone(obj);
10024
- return JSON.parse(JSON.stringify(obj));
10025
- }
10026
- /**
10027
- * Apply template input values to a definitionJson by replacing
10028
- * ${variableName} placeholders in string values throughout the object tree.
10029
- *
10030
- * @param definitionJson - The workflow definition JSON to process
10031
- * @param inputs - Map of variable names to user-provided values
10032
- * @returns A new definitionJson with placeholders replaced
10033
- *
10034
- * @example
10035
- * ```ts
10036
- * const definition = {
10037
- * nodes: [{
10038
- * data: { url: "${slack_webhook_url}", message: "Hello ${name}" }
10039
- * }]
10040
- * };
10041
- * const result = applyTemplateInputs(definition, {
10042
- * slack_webhook_url: "https://hooks.slack.com/...",
10043
- * name: "World"
10044
- * });
10045
- * // result.nodes[0].data.url === "https://hooks.slack.com/..."
10046
- * // result.nodes[0].data.message === "Hello World"
10047
- * ```
10048
- */
10049
- function applyTemplateInputs(definitionJson, inputs) {
10050
- if (!inputs || Object.keys(inputs).length === 0) return definitionJson;
10051
- return walkAndReplace(deepClone(definitionJson), inputs);
10052
- }
10053
- /**
10054
- * Recursively walk an object/array and replace ${variableName} placeholders
10055
- * in string values.
10056
- */
10057
- function walkAndReplace(obj, inputs) {
10058
- if (typeof obj === "string") return replaceVariables(obj, inputs);
10059
- if (Array.isArray(obj)) return obj.map((item) => walkAndReplace(item, inputs));
10060
- if (obj !== null && typeof obj === "object") {
10061
- const result = {};
10062
- for (const [key, value] of Object.entries(obj)) result[key] = walkAndReplace(value, inputs);
10063
- return result;
10064
- }
10065
- return obj;
10066
- }
10067
- /**
10068
- * Replace ${variableName} patterns in a string with provided values.
10069
- *
10070
- * Supports:
10071
- * - ${variableName} — standard replacement
10072
- * - ${variableName || 'default'} — fallback to default if variable not provided
10073
- */
10074
- function replaceVariables(str, inputs) {
10075
- return str.replace(/\$\{([^}]+)\}/g, (_match, expression) => {
10076
- const trimmed = expression.trim();
10077
- const fallbackMatch = trimmed.match(/^(\w+)\s*\|\|\s*['"]?(.+?)['"]?$/);
10078
- if (fallbackMatch) {
10079
- const varName = fallbackMatch[1];
10080
- const fallback = fallbackMatch[2];
10081
- if (varName in inputs) return String(inputs[varName]);
10082
- return fallback;
10083
- }
10084
- if (trimmed in inputs) return String(inputs[trimmed]);
10085
- return _match;
10086
- });
10087
- }
10088
-
10089
- // src/utils/template-import-flow.ts
10090
- function isRecord$2(value) {
10091
- return typeof value === "object" && value !== null && !Array.isArray(value);
10092
- }
10093
- function graphOf(value) {
10094
- if (!Array.isArray(value.nodes)) return null;
10095
- return {
10096
- nodes: value.nodes,
10097
- edges: Array.isArray(value.edges) ? value.edges : [],
10098
- name: typeof value.name === "string" ? value.name : void 0
10099
- };
10100
- }
10101
- /**
10102
- * The canvas of a WORKFLOW product that keeps it in its manifest instead of
10103
- * `content.definitionJson`: either a flat `{ nodes, edges }` manifest, or the
10104
- * Store Listing JSON editor's `manifest.jsonContent` string. Null when the
10105
- * manifest holds neither.
10106
- */
10107
- function manifestTemplateGraph(manifest) {
10108
- if (!isRecord$2(manifest)) return null;
10109
- const direct = graphOf(manifest);
10110
- if (direct) return direct;
10111
- if (typeof manifest.jsonContent !== "string") return null;
10112
- try {
10113
- const parsed = JSON.parse(manifest.jsonContent);
10114
- return isRecord$2(parsed) ? graphOf(parsed) : null;
10115
- } catch {
10116
- return null;
10117
- }
10118
- }
10119
- /**
10120
- * The template content a product carries: `content` when it holds a
10121
- * `definitionJson`, otherwise a content built from the manifest's canvas,
10122
- * with its required nodes worked out by `requiredNodeTypesOf` (a failed
10123
- * lookup leaves them empty, which skips the node check). Null when the
10124
- * product has no workflow at all.
10125
- */
10126
- async function resolveTemplateContent(product, requiredNodeTypesOf) {
10127
- const content = product.content;
10128
- if (content?.definitionJson) return content;
10129
- const graph = manifestTemplateGraph(product.manifest);
10130
- if (!graph) return null;
10131
- let requiredNodeTypes = [];
10132
- try {
10133
- requiredNodeTypes = await requiredNodeTypesOf(graph.nodes);
10134
- } catch {
10135
- requiredNodeTypes = [];
10136
- }
10137
- return {
10138
- definitionJson: {
10139
- nodes: graph.nodes,
10140
- edges: graph.edges
10141
- },
10142
- requiredNodeTypes,
10143
- defaultWorkflowName: graph.name ?? product.name
10144
- };
10145
- }
10146
- /**
10147
- * The canvas to create a copy from. Some templates store a whole API
10148
- * response as their definition (`{ data: { workflowDefinition: {
10149
- * definitionJson } } }`); the canvas is the inner `definitionJson`.
10150
- */
10151
- function unwrapTemplateDefinition(definitionJson) {
10152
- const data = definitionJson.data;
10153
- if (!isRecord$2(data)) return definitionJson;
10154
- const nested = isRecord$2(data.workflowDefinition) ? data.workflowDefinition.definitionJson : void 0;
10155
- return isRecord$2(nested) ? nested : definitionJson;
10156
- }
10157
- /**
10158
- * Whether "Use this template" must stop at the configuration dialog before
10159
- * creating the copy: the template asks for inputs or connections, or one of
10160
- * its nodes is not installed (free to install, paid, or not offered by the
10161
- * store at all).
10162
- */
10163
- function needsTemplateConfiguration(content, nodeCheck) {
10164
- return (content.requiredInputs?.length ?? 0) > 0 || (content.requiredCredentialTypes?.length ?? 0) > 0 || nodeCheck.missing.length > 0 || nodeCheck.missingPaid.length > 0 || (nodeCheck.unavailable?.length ?? 0) > 0;
10165
- }
10166
- /** The free nodes to install before the copy is created. */
10167
- function nodesToInstall(nodeCheck) {
10168
- return (nodeCheck?.missing ?? []).filter((node) => !!node.storeProductId);
10169
- }
10170
- /**
10171
- * Install nodes one at a time, in order, reporting progress before each.
10172
- * One at a time on purpose: the dialog shows "installing i of n", and every
10173
- * install writes the same workspace's installation records in the store.
10174
- * A failed install is reported to `onFailure` and the rest still run, so the
10175
- * copy can be created; the user installs the node later from the Node Store.
10176
- * Resolves to the node types that installed.
10177
- */
10178
- async function installNodesInOrder(nodes, install, onProgress, onFailure) {
10179
- const installed = [];
10180
- await nodes.reduce((previous, node, index) => previous.then(async () => {
10181
- onProgress({
10182
- current: index + 1,
10183
- total: nodes.length,
10184
- nodeName: node.displayName || node.nodeType
10185
- });
10186
- try {
10187
- await install(node);
10188
- installed.push(node.nodeType);
10189
- } catch (error) {
10190
- onFailure(node, error);
10191
- }
10192
- }), Promise.resolve());
10193
- return installed;
10194
- }
10195
-
10196
- // src/hooks/store/useImportWorkflowTemplate.ts
10197
- const IMPORT_WORKFLOW_TEMPLATE = gql`
10198
- mutation ImportWorkflowTemplate($input: ImportWorkflowTemplateInput!) {
10199
- importWorkflowTemplate(input: $input) {
10200
- success
10201
- message
10202
- workflow {
10203
- id
10204
- workflowKey
10205
- version
10206
- name
10207
- description
10208
- status
10209
- definitionJson
10210
- tags
10211
- category
10212
- icon
10213
- createdAt
10214
- }
10215
- }
10216
- }
10217
- `;
10218
- const RECORD_DOWNLOAD = gql`
10219
- mutation RecordDownload($productId: String!) {
10220
- recordDownload(productId: $productId) {
10221
- id
10222
- downloadedAt
10223
- }
10224
- }
10225
- `;
10226
- const WORKFLOW_DEFINITIONS = gql`
10227
- query WorkflowDefinitions($input: ListWorkflowDefinitionsInput!) {
10228
- workflowDefinitions(input: $input) {
10229
- items {
10230
- id
10231
- workflowKey
10232
- name
10233
- version
10234
- status
10235
- }
10236
- total
10237
- hasMore
10238
- }
10239
- }
10240
- `;
10241
- /**
10242
- * Hook to orchestrate importing a workflow template from the store.
10243
- *
10244
- * It fetches template detail, parses content and creates the copy:
10245
- * 1. Fetch full template detail from global store (lazy load content)
10246
- * 2. Apply user-provided template inputs to definitionJson
10247
- * 3. Create workflow via importWorkflowTemplate mutation (workspace gateway)
10248
- * 4. Record the download and the installation in the store
10249
- *
10250
- * The node check, the configuration dialog and installing missing nodes are
10251
- * done by `useTemplateImportFlow`, which every "Use this template" button
10252
- * goes through (BOFF-11153).
10253
- */
10254
- function useImportWorkflowTemplate() {
10255
- const [importing, setImporting] = useState(false);
10256
- const [error, setError] = useState(null);
10257
- const { fetchTemplateDetail, loading: fetchingDetail } = useTemplateDetail();
10258
- const globalClient = useGlobalStoreClient();
10259
- const context = useContext(FluidGridsContext);
10260
- const workspaceId = context?.workspaceId ?? getFluidGridsConfig().workspaceId;
10261
- const bus = useEventBus();
10262
- const getTemplateDetail = useCallback(async (templateId) => {
10263
- return fetchTemplateDetail(templateId);
10264
- }, [fetchTemplateDetail]);
10265
- /**
10266
- * Parse the template content from a StoreProduct.
10267
- * Returns null if the product doesn't have valid workflow template content.
10268
- */
10269
- const parseTemplateContent = useCallback((template) => {
10270
- try {
10271
- const content = template.content;
10272
- if (!content?.definitionJson) {
10273
- console.warn("[useImportWorkflowTemplate] Template has no definitionJson in content:", template.id);
10274
- return null;
10275
- }
10276
- return content;
10277
- } catch {
10278
- console.error("[useImportWorkflowTemplate] Failed to parse template content:", template.id);
10279
- return null;
10280
- }
10281
- }, []);
10282
- return {
10283
- /** Fetch full template content from store (lazy load) */
10284
- getTemplateDetail,
10285
- /** Parse WorkflowTemplateContent from a StoreProduct */
10286
- parseTemplateContent,
10287
- /** Full import: apply inputs -> create workflow -> record analytics */
10288
- importTemplate: useCallback(async (template, options) => {
10289
- setImporting(true);
10290
- setError(null);
10291
- try {
10292
- const content = options?.content ?? parseTemplateContent(template);
10293
- if (!content) throw new Error("Template does not contain valid workflow definition data");
10294
- let definitionJson = unwrapTemplateDefinition(deepClone(content.definitionJson));
10295
- if (options?.templateInputs) definitionJson = applyTemplateInputs(definitionJson, options.templateInputs);
10296
- const config = getFluidGridsConfig();
10297
- const importContext = {
10298
- ...options?.context || {},
10299
- ...config.productId ? { productId: config.productId } : {},
10300
- ...config.productName ? { productName: config.productName } : {}
10301
- };
10302
- const client = getFluidGridsClient();
10303
- const importResult = throwIfResultError(await client.mutate({
10304
- mutation: IMPORT_WORKFLOW_TEMPLATE,
10305
- variables: { input: {
10306
- definitionJson,
10307
- name: options?.name || content.defaultWorkflowName || template.name,
10308
- description: options?.description || template.description,
10309
- sourceTemplateId: template.id,
10310
- sourceTemplateName: template.name,
10311
- tags: content.defaultTags || template.tags || [],
10312
- category: content.defaultCategory || template.category || void 0,
10313
- icon: template.icon || void 0,
10314
- projectId: options?.projectId,
10315
- context: importContext
10316
- } },
10317
- refetchQueries: [WORKFLOW_DEFINITIONS]
10318
- })).data?.importWorkflowTemplate;
10319
- if (!importResult?.success || !importResult.workflow) throw new Error(importResult?.message || "Failed to create workflow from template");
10320
- try {
10321
- throwIfResultError(await globalClient.mutate({
10322
- mutation: RECORD_DOWNLOAD,
10323
- variables: { productId: template.id }
10324
- }));
10325
- } catch {
10326
- console.warn("[useImportWorkflowTemplate] Failed to record store download");
10327
- }
10328
- try {
10329
- throwIfResultError(await globalClient.mutate({
10330
- mutation: InstallWorkflowNodeDocument,
10331
- variables: { input: {
10332
- productId: template.id,
10333
- workspaceId,
10334
- organizationId: context?.tenantId ?? getFluidGridsConfig().tenantId,
10335
- version: template.manifestVersion || "1.0.0",
10336
- manifest: template.manifest,
10337
- metadata: {
10338
- type: "WORKFLOW",
10339
- sourceTemplateId: template.id,
10340
- sourceTemplateName: template.name,
10341
- importedWorkflowKey: importResult.workflow.workflowKey,
10342
- importedWorkflowVersion: importResult.workflow.version
10343
- }
10344
- } }
10345
- }));
10346
- } catch {
10347
- console.warn("[useImportWorkflowTemplate] Failed to record app installation");
10348
- }
10349
- emitWorkflowCreated(bus, {
10350
- workspaceId,
10351
- workflowId: importResult.workflow.id,
10352
- source: "template",
10353
- definition: definitionJson
10354
- });
10355
- emitTemplateUsed(bus, {
10356
- workspaceId,
10357
- templateId: template.id,
10358
- category: content.defaultCategory || template.category
10359
- });
10360
- return {
10361
- success: true,
10362
- workflow: importResult.workflow,
10363
- message: importResult.message || "Template imported successfully",
10364
- installedNodes: [],
10365
- requiredCredentials: content.requiredCredentialTypes || []
10366
- };
10367
- } catch (err) {
10368
- const importError = err instanceof Error ? err : /* @__PURE__ */ new Error("Template import failed");
10369
- setError(importError);
10370
- console.error("[useImportWorkflowTemplate] Import failed:", err);
10371
- return {
10372
- success: false,
10373
- workflow: null,
10374
- message: importError.message,
10375
- installedNodes: [],
10376
- requiredCredentials: [],
10377
- error: importError
10378
- };
10379
- } finally {
10380
- setImporting(false);
10381
- }
10382
- }, [
10383
- parseTemplateContent,
10384
- globalClient,
10385
- workspaceId,
10386
- context?.tenantId,
10387
- bus
10388
- ]),
10389
- /** Whether we're fetching template detail */
10390
- fetchingDetail,
10391
- /** Whether we're importing */
10392
- importing,
10393
- /** Last error */
10394
- error
10395
- };
10396
- }
10397
-
10398
- // src/utils/workflow-node-check.ts
10399
- /**
10400
- * Workflow node dependency checking.
10401
- *
10402
- * Shared logic for detecting node types used by a workflow that are not
10403
- * installed in the current workspace, resolving them to store products, and
10404
- * classifying them as installed / missing (free installable) / missingPaid /
10405
- * unavailable (not offered by the store, so nothing can install them).
10406
- *
10407
- * Used by:
10408
- * - Template import flow (desktop-sidebar-fragment)
10409
- * - Shared-workflow copy flow (WorkflowItem)
10410
- * - AI-assistant-created workflow open flow (WorkflowItem)
10411
- */
10412
- /**
10413
- * Canvas-only node types: they live on the React Flow canvas but are never
10414
- * executed, never registered in NODE_CONFIGS and never published to the store.
10415
- *
10416
- * They must be excluded from the dependency check. A sticky note reaching it
10417
- * was reported as `sticky-note v1.0 — Not installed` in the Missing Nodes
10418
- * dialog on every open of an affected workflow, and because no store product
10419
- * backs it the Install action could only ever be a no-op (BOFF-7267).
10420
- *
10421
- * Kept as a literal set rather than importing STICKY_NOTE_TYPE from the
10422
- * builder so this module stays free of builder-component imports.
10423
- */
10424
- const CANVAS_ONLY_NODE_TYPES = /* @__PURE__ */ new Set(["sticky-note"]);
10425
- /**
10426
- * Resolve the set of unique executable node types used by a workflow.
10427
- *
10428
- * Canvas-only annotations (see CANVAS_ONLY_NODE_TYPES) are skipped: they carry
10429
- * no dependency, so including them produces a permanently unsatisfiable
10430
- * "missing node".
10431
- */
10432
- function getWorkflowNodeTypes(nodes) {
10433
- return [...new Set(nodes.map((n) => n.type).filter((t) => Boolean(t)).filter((t) => !CANVAS_ONLY_NODE_TYPES.has(t)))];
10434
- }
10435
- /**
10436
- * Node types per store lookup: global-store-svc answers for at most 100 at a
10437
- * time. A workflow misses far fewer; more are asked in parallel requests.
10438
- */
10439
- const STORE_NODE_LOOKUP_BATCH_SIZE = 100;
10440
- /** The store context of FluidGrids node products, as the Node Store browses them. */
10441
- const STORE_NODE_PRODUCT_CONTEXT = "fluidgrids";
10442
- /**
10443
- * The store products that offer any of `nodeTypes`, at most
10444
- * `STORE_NODE_LOOKUP_BATCH_SIZE` types per request, the requests in parallel.
10445
- * Blank and repeated types are asked once.
10446
- */
10447
- async function fetchStoreNodeProducts(nodeTypes, fetchProducts) {
10448
- const wanted = [...new Set(nodeTypes.filter(Boolean))];
10449
- const batches = [];
10450
- for (let i = 0; i < wanted.length; i += 100) batches.push(wanted.slice(i, i + 100));
10451
- return (await Promise.all(batches.map(fetchProducts))).flatMap((products) => [...products ?? []]);
10452
- }
10453
- function publishedAtMs(product) {
10454
- const ms = product.publishedAt ? Date.parse(product.publishedAt) : NaN;
10455
- return Number.isNaN(ms) ? Number.NEGATIVE_INFINITY : ms;
10456
- }
10457
- /**
10458
- * Order two store products that offer the same nodeType; a negative result
10459
- * means `a` is the better match.
10460
- *
10461
- * 1. A product linked to a devportal application (`productId` set) wins. Only
10462
- * those have an installable published version; the unlinked rows are
10463
- * orphaned seed rows such as `app-gmail-reader-001` that cannot install.
10464
- * 2. Then more downloads, 3. then the most recently published, 4. then the
10465
- * product id, so the choice never depends on the order the store returned.
10466
- */
10467
- function compareStoreNodeCandidates(a, b) {
10468
- const aLinked = Boolean(a.productId);
10469
- if (aLinked !== Boolean(b.productId)) return aLinked ? -1 : 1;
10470
- const downloads = (b.downloads ?? 0) - (a.downloads ?? 0);
10471
- if (downloads !== 0) return downloads;
10472
- const aPublished = publishedAtMs(a);
10473
- const bPublished = publishedAtMs(b);
10474
- if (aPublished !== bPublished) return aPublished > bPublished ? -1 : 1;
10475
- if (a.id === b.id) return 0;
10476
- return a.id < b.id ? -1 : 1;
10477
- }
10478
- /**
10479
- * Map nodeType -> best store product (see compareStoreNodeCandidates).
10480
- * Pure function, independent of the order of `products`.
10481
- */
10482
- function buildStoreNodeTypeMap(products) {
10483
- const nodeTypeToProduct = /* @__PURE__ */ new Map();
10484
- for (const product of products) {
10485
- const nodeType = product.manifest?.nodeType;
10486
- if (typeof nodeType !== "string" || !nodeType) continue;
10487
- const current = nodeTypeToProduct.get(nodeType);
10488
- if (!current || compareStoreNodeCandidates(product, current) < 0) nodeTypeToProduct.set(nodeType, product);
10489
- }
10490
- return nodeTypeToProduct;
10491
- }
10492
- /**
10493
- * Ask the global store which products offer `nodeTypes` and map each type to the
10494
- * best one (see compareStoreNodeCandidates). Only the asked types travel: the
10495
- * check used to page through the whole node catalog for them (BOFF-11754).
10496
- */
10497
- async function fetchStoreNodeTypeMap(nodeTypes) {
10498
- const storeClient = getGlobalStoreClient();
10499
- if (!storeClient || nodeTypes.length === 0) return /* @__PURE__ */ new Map();
10500
- return buildStoreNodeTypeMap(await fetchStoreNodeProducts(nodeTypes, async (batch) => {
10501
- return (await storeClient.query({
10502
- query: StoreNodeProductsByTypeDocument,
10503
- variables: {
10504
- nodeTypes: batch,
10505
- compatibleWith: STORE_NODE_PRODUCT_CONTEXT
10506
- },
10507
- fetchPolicy: "cache-first"
10508
- })).data?.storeNodeProductsByType;
10509
- }));
10510
- }
10511
- /**
10512
- * One required-node entry of a template (BOFF-11496): a type the registry
10513
- * already has is BUILTIN at its registry version; any other type is a STORE
10514
- * node at the node's own version from its store product. Versions are
10515
- * canonical, never the product's manifest format version ("1.0").
10516
- */
10517
- function requiredNodeEntry(type, registered, product) {
10518
- if (registered) return {
10519
- nodeType: type,
10520
- nodeVersion: canonicalNodeVersion(registered.version || "1.0.0"),
10521
- publishType: "BUILTIN",
10522
- displayName: type
10523
- };
10524
- return {
10525
- nodeType: type,
10526
- nodeVersion: storeNodeInstallVersion(product?.manifest, product?.manifestVersion),
10527
- publishType: "STORE",
10528
- storeProductId: product?.id,
10529
- displayName: product?.name ?? type,
10530
- requiresPurchase: product !== void 0 && product.pricingModel !== "FREE"
10531
- };
10532
- }
10533
- /**
10534
- * Build the list of required node types for a workflow.
10535
- *
10536
- * - BUILTIN / already-installed types are marked BUILTIN.
10537
- * - Missing types are looked up in the store; if found, they are marked STORE
10538
- * with storeProductId and requiresPurchase.
10539
- * - Missing types not found in the store are still reported as STORE without
10540
- * a storeProductId so the UI can show "not installed".
10541
- */
10542
- async function buildRequiredNodeTypes(nodes) {
10543
- const workflowNodeTypes = getWorkflowNodeTypes(nodes);
10544
- if (workflowNodeTypes.length === 0) return [];
10545
- const isRegistered = (type) => NODE_CONFIGS.some((c) => c.type === type);
10546
- await ensureRegistryNodeTypesLoaded(workflowNodeTypes);
10547
- const missingTypes = workflowNodeTypes.filter((type) => !isRegistered(type));
10548
- let nodeTypeToProduct = /* @__PURE__ */ new Map();
10549
- if (missingTypes.length > 0) [, nodeTypeToProduct] = await Promise.all([ensureInstalledNodeTypesLoaded(missingTypes), fetchStoreNodeTypeMap(missingTypes)]);
10550
- return workflowNodeTypes.map((type) => {
10551
- const registered = NODE_CONFIGS.find((c) => c.type === type);
10552
- return requiredNodeEntry(type, registered ? { version: registered.defaultData.version } : void 0, nodeTypeToProduct.get(type));
10553
- });
10554
- }
10555
- /**
10556
- * Classify required nodes against the set of installed store app IDs.
10557
- *
10558
- * Pure function — easy to unit test.
10559
- */
10560
- function buildNodeCheck(requiredNodeTypes, installedAppIds) {
10561
- const installed = requiredNodeTypes.filter((n) => n.publishType === "BUILTIN" || n.storeProductId && installedAppIds.has(n.storeProductId));
10562
- const notInstalled = requiredNodeTypes.filter((n) => n.publishType !== "BUILTIN" && (!n.storeProductId || !installedAppIds.has(n.storeProductId)));
10563
- const unavailable = notInstalled.filter((n) => !n.storeProductId);
10564
- const installable = notInstalled.filter((n) => !!n.storeProductId);
10565
- const missing = installable.filter((n) => !n.requiresPurchase);
10566
- const missingPaid = installable.filter((n) => !!n.requiresPurchase);
10567
- return {
10568
- installed,
10569
- missing,
10570
- missingPaid,
10571
- unavailable,
10572
- canImport: missingPaid.length === 0
10573
- };
10574
- }
10575
-
10576
- // src/hooks/store/useTemplateImportFlow.tsx
10577
- const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-D_Mvgxqa.js").then((m) => ({ default: m.TemplateConfigurationModal })));
10578
- /**
10579
- * "Use this template", the same everywhere it is offered: the builder's
10580
- * template panel, the Templates page and the template preview page
10581
- * (BOFF-11153). Before this, only the builder panel checked the template's
10582
- * nodes and asked for its inputs; the two pages created the copy directly.
10583
- *
10584
- * 1. Load the template's full detail and resolve its content.
10585
- * 2. Check its required nodes against the workspace's installed nodes.
10586
- * 3. When it asks for inputs or connections, or a node is not installed,
10587
- * show the configuration dialog; otherwise go straight on.
10588
- * 4. Install the missing free nodes, create the copy with the user's inputs,
10589
- * and open it (`onImported`).
10590
- */
10591
- function useTemplateImportFlow({ onImported, onQuotaExhausted }) {
10592
- const tr = useTr();
10593
- const { bus, workspaceId } = useFluidGridsAnalytics();
10594
- const { installedAppIds } = useInstalledNodes();
10595
- const { getTemplateDetail, importTemplate } = useImportWorkflowTemplate();
10596
- const [pending, setPending] = useState(null);
10597
- const [busy, setBusy] = useState(false);
10598
- const [installProgress, setInstallProgress] = useState(null);
10599
- const installMissingNodes = useCallback(async (nodeCheck) => {
10600
- const nodes = nodesToInstall(nodeCheck);
10601
- const storeClient = getGlobalStoreClient();
10602
- if (nodes.length === 0 || !storeClient) return;
10603
- const config = getFluidGridsConfig();
10604
- await installNodesInOrder(nodes, async (node) => {
10605
- throwIfResultError(await storeClient.mutate({
10606
- mutation: InstallWorkflowNodeDocument,
10607
- variables: { input: {
10608
- productId: node.storeProductId ?? "",
10609
- workspaceId: config.workspaceId,
10610
- organizationId: config.tenantId,
10611
- version: node.nodeVersion,
10612
- manifest: null
10613
- } }
10614
- }));
10615
- emitNodeInstalled(bus, {
10616
- workspaceId,
10617
- nodeType: node.nodeType,
10618
- source: "store"
10619
- });
10620
- }, setInstallProgress, (node, err) => console.warn(`[useTemplateImportFlow] Failed to install node ${node.nodeType}:`, err));
10621
- setInstallProgress(null);
10622
- }, [bus, workspaceId]);
10623
- const createCopy = useCallback(async (template, content, nodeCheck, overrides) => {
10624
- setBusy(true);
10625
- try {
10626
- await installMissingNodes(nodeCheck);
10627
- toast.loading(tr("fluidgrids.builder.desktopSidebar.creatingWorkflowToast", "Creating workflow..."), { id: "template-import" });
10628
- const result = await importTemplate(template, {
10629
- content,
10630
- name: overrides?.name,
10631
- templateInputs: overrides?.templateInputs
10632
- });
10633
- toast.dismiss("template-import");
10634
- if (result.success && result.workflow) {
10635
- toast.success(tr("fluidgrids.builder.desktopSidebar.workflowCreatedToast", "Workflow created from template!"), { description: `"${result.workflow.name}" is ready to edit` });
10636
- if ((content.requiredCredentialTypes?.length ?? 0) > 0) toast.info(tr("fluidgrids.builder.desktopSidebar.credentialsNeededToast", "Credentials needed"), {
10637
- description: `Configure ${content.requiredCredentialTypes?.join(", ")} in the Credentials page`,
10638
- duration: 6e3
10639
- });
10640
- onImported(result.workflow);
10641
- return true;
10642
- }
10643
- if (onQuotaExhausted && isQuotaExhaustedFailure(result.error?.cause ?? result.error)) {
10644
- onQuotaExhausted();
10645
- return false;
10646
- }
10647
- toast.error(tr("fluidgrids.builder.desktopSidebar.importTemplateErrorToast", "Failed to import template"), { description: result.message || tr("fluidgrids.builder.desktopSidebar.importTemplateErrorFallback", "Unknown error during import") });
10648
- return false;
10649
- } catch (err) {
10650
- toast.dismiss("template-import");
10651
- toast.error(tr("fluidgrids.builder.desktopSidebar.importFailedToast", "Import failed"), { description: err instanceof Error ? err.message : tr("fluidgrids.builder.desktopSidebar.importFailedFallback", "Unknown error") });
10652
- return false;
10653
- } finally {
10654
- setInstallProgress(null);
10655
- setBusy(false);
10656
- }
10657
- }, [
10658
- importTemplate,
10659
- installMissingNodes,
10660
- onImported,
10661
- onQuotaExhausted,
10662
- tr
10663
- ]);
10664
- const start = useCallback(async (template) => {
10665
- setBusy(true);
10666
- toast.loading(tr("fluidgrids.builder.desktopSidebar.loadingTemplateToast", "Loading template..."), { id: "template-load" });
10667
- try {
10668
- const full = await getTemplateDetail(template.id);
10669
- toast.dismiss("template-load");
10670
- if (!full) {
10671
- toast.error(tr("fluidgrids.builder.desktopSidebar.templateDetailsErrorToast", "Failed to load template details"));
10672
- return;
10673
- }
10674
- const content = await resolveTemplateContent(full, buildRequiredNodeTypes);
10675
- if (!content) {
10676
- toast.error(tr("fluidgrids.builder.desktopSidebar.invalidTemplateDataToast", "Template does not contain valid workflow data"));
10677
- return;
10678
- }
10679
- const nodeCheck = buildNodeCheck(content.requiredNodeTypes ?? [], installedAppIds);
10680
- if (needsTemplateConfiguration(content, nodeCheck)) {
10681
- setPending({
10682
- template: full,
10683
- content,
10684
- nodeCheck
10685
- });
10686
- return;
10687
- }
10688
- await createCopy(full, content, nodeCheck);
10689
- } catch (err) {
10690
- toast.dismiss("template-load");
10691
- toast.error(tr("fluidgrids.builder.desktopSidebar.genericErrorToast", "Something went wrong"), { description: err instanceof Error ? err.message : tr("fluidgrids.builder.desktopSidebar.genericErrorFallback", "Failed to process template") });
10692
- } finally {
10693
- setBusy(false);
10694
- }
10695
- }, [
10696
- createCopy,
10697
- getTemplateDetail,
10698
- installedAppIds,
10699
- tr
10700
- ]);
10701
- const confirm = useCallback(async (config) => {
10702
- if (!pending?.nodeCheck.canImport) return;
10703
- if (await createCopy(pending.template, pending.content, pending.nodeCheck, config)) setPending(null);
10704
- }, [createCopy, pending]);
10705
- return {
10706
- start,
10707
- dialog: pending ? /* @__PURE__ */ jsx(Suspense, {
10708
- fallback: null,
10709
- children: /* @__PURE__ */ jsx(LazyTemplateConfigurationModal, {
10710
- open: true,
10711
- onOpenChange: (open) => {
10712
- if (!open && !busy) setPending(null);
10713
- },
10714
- template: pending.template,
10715
- content: pending.content,
10716
- nodeCheckResult: pending.nodeCheck,
10717
- onConfirm: confirm,
10718
- importing: busy,
10719
- installProgress
10720
- })
10721
- }) : null,
10722
- busy
10723
- };
10724
- }
10725
-
10726
- // src/hooks/store/useOrganizationPrivateNodes.ts
10727
- function errorMessage(error, fallback) {
10728
- return error instanceof Error && error.message ? error.message : fallback;
10729
- }
10730
- /**
10731
- * The workspace-private custom nodes of the current workspace's organization
10732
- * (BOFF-9766), with install, update and uninstall.
10733
- *
10734
- * These nodes skip the public store: devportal's `organizationPrivateNodes`
10735
- * lists them, `installApp` installs a chosen version (which also registers
10736
- * it in the workspace's builder; installing a newer version over an active
10737
- * install updates it), and `uninstallApp` removes it. After every change the
10738
- * builder's node palette is refreshed so the node appears or disappears.
10739
- */
10740
- function useOrganizationPrivateNodes() {
9375
+ function useOrganizationPrivateNodes() {
10741
9376
  const client = useGlobalStoreClient();
10742
9377
  const workspaceId = useContext(FluidGridsContext)?.workspaceId ?? getFluidGridsConfig().workspaceId;
10743
9378
  const { refreshAvailableNodes } = useRefreshAvailableNodes();
@@ -12404,302 +11039,6 @@ function SidebarButtonItem({ children, className, active, ...props }) {
12404
11039
  });
12405
11040
  }
12406
11041
 
12407
- // src/ui/dialog.tsx
12408
- function Dialog$1({ ...props }) {
12409
- return /* @__PURE__ */ jsx(DialogPrimitive.Root, {
12410
- "data-slot": "dialog",
12411
- ...props
12412
- });
12413
- }
12414
- function DialogTrigger$1({ ...props }) {
12415
- return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, {
12416
- "data-slot": "dialog-trigger",
12417
- ...props
12418
- });
12419
- }
12420
- function DialogPortal({ ...props }) {
12421
- return /* @__PURE__ */ jsx(DialogPrimitive.Portal, {
12422
- "data-slot": "dialog-portal",
12423
- ...props
12424
- });
12425
- }
12426
- function DialogClose({ ...props }) {
12427
- return /* @__PURE__ */ jsx(DialogPrimitive.Close, {
12428
- "data-slot": "dialog-close",
12429
- ...props
12430
- });
12431
- }
12432
- function DialogOverlay({ className, ...props }) {
12433
- return /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
12434
- "data-slot": "dialog-overlay",
12435
- className: cn$1("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-bg-overlay", className),
12436
- ...props
12437
- });
12438
- }
12439
- function DialogContent$1({ className, children, showCloseButton = true, size = "default", ...props }) {
12440
- const tr = useTr();
12441
- return /* @__PURE__ */ jsxs(DialogPortal, {
12442
- "data-slot": "dialog-portal",
12443
- children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
12444
- "data-slot": "dialog-content",
12445
- className: cn$1("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none", {
12446
- default: "sm:max-w-lg",
12447
- lg: "sm:max-w-3xl",
12448
- xl: "sm:max-w-[1400px] w-[90vw]",
12449
- full: "sm:max-w-[95vw] w-[95vw]"
12450
- }[size], className),
12451
- ...props,
12452
- children: [children, showCloseButton && /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
12453
- "data-slot": "dialog-close",
12454
- className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none",
12455
- children: [/* @__PURE__ */ jsx(XIcon, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
12456
- className: "sr-only",
12457
- children: tr("fluidgrids.ui.dialog.closeLabel", "Close")
12458
- })]
12459
- })]
12460
- })]
12461
- });
12462
- }
12463
- function DialogHeader$1({ className, ...props }) {
12464
- return /* @__PURE__ */ jsx("div", {
12465
- "data-slot": "dialog-header",
12466
- className: cn$1("flex flex-col gap-2 text-center sm:text-left", className),
12467
- ...props
12468
- });
12469
- }
12470
- function DialogFooter$1({ className, ...props }) {
12471
- return /* @__PURE__ */ jsx("div", {
12472
- "data-slot": "dialog-footer",
12473
- className: cn$1("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
12474
- ...props
12475
- });
12476
- }
12477
- function DialogTitle$1({ className, ...props }) {
12478
- return /* @__PURE__ */ jsx(DialogPrimitive.Title, {
12479
- "data-slot": "dialog-title",
12480
- className: cn$1("text-lg leading-none font-semibold", className),
12481
- ...props
12482
- });
12483
- }
12484
- function DialogDescription$1({ className, ...props }) {
12485
- return /* @__PURE__ */ jsx(DialogPrimitive.Description, {
12486
- "data-slot": "dialog-description",
12487
- className: cn$1("text-muted-foreground text-sm", className),
12488
- ...props
12489
- });
12490
- }
12491
-
12492
- // src/ui/badge.tsx
12493
- const badgeVariants = cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
12494
- variants: { variant: {
12495
- default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
12496
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
12497
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
12498
- outline: "text-foreground",
12499
- success: "border-transparent bg-status-success-bg-subtle text-status-success-text",
12500
- warning: "border-transparent bg-status-warning-bg-subtle text-status-warning-text",
12501
- info: "border-transparent bg-status-info-bg-subtle text-status-info-text"
12502
- } },
12503
- defaultVariants: { variant: "default" }
12504
- });
12505
- function Badge$1({ className, variant, ...props }) {
12506
- return /* @__PURE__ */ jsx("div", {
12507
- className: cn$1(badgeVariants({ variant }), className),
12508
- ...props
12509
- });
12510
- }
12511
-
12512
- // src/utils/template-preview.ts
12513
- const ICONIFY_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:[-_][a-z0-9]+)*$/i;
12514
- /** True when the value is an Iconify `prefix:name` icon, not a URL. */
12515
- function isIconifyName(value) {
12516
- return ICONIFY_NAME.test(value) && !/^(https?|data|blob):/i.test(value);
12517
- }
12518
- /**
12519
- * The preview for a template: its first screenshot, else its icon, which is
12520
- * shown as an image only when it is not an Iconify name.
12521
- */
12522
- function templatePreview(template) {
12523
- const screenshot = template.screenshots?.find((shot) => typeof shot === "string" && shot.trim() !== "");
12524
- if (screenshot) return {
12525
- kind: "image",
12526
- src: screenshot
12527
- };
12528
- const icon = template.icon?.trim();
12529
- if (!icon) return { kind: "none" };
12530
- return isIconifyName(icon) ? {
12531
- kind: "icon",
12532
- name: icon
12533
- } : {
12534
- kind: "image",
12535
- src: icon
12536
- };
12537
- }
12538
-
12539
- // src/builder/components/flow-builder/components/templates/TemplateCard.tsx
12540
- function formatNumber(count) {
12541
- if (count >= 1e6) return `${(count / 1e6).toFixed(1)}M`;
12542
- if (count >= 1e3) return `${(count / 1e3).toFixed(1)}k`;
12543
- return count.toString();
12544
- }
12545
- /**
12546
- * Template card component inspired by ComfyUI's template showcase
12547
- * Features:
12548
- * - Large preview image/screenshot
12549
- * - Template name and description
12550
- * - Author info
12551
- * - Stats (downloads, views, rating)
12552
- * - Featured badge
12553
- * - Category/tags
12554
- */
12555
- function TemplateCard({ template, onClick, onUseTemplate }) {
12556
- const tr = useTr();
12557
- const [imageError, setImageError] = React.useState(false);
12558
- const preview = templatePreview(template);
12559
- const previewImage = preview.kind === "image" ? preview.src : void 0;
12560
- const hasPreview = Boolean(previewImage) && !imageError;
12561
- const handleUseTemplate = (e) => {
12562
- e.stopPropagation();
12563
- onUseTemplate?.(template);
12564
- };
12565
- const handleImageError = () => {
12566
- setImageError(true);
12567
- };
12568
- return /* @__PURE__ */ jsxs(Card$1, {
12569
- className: cn$1("group overflow-hidden transition-all duration-300 cursor-pointer", "hover:shadow-xl hover:scale-[1.02] border-2 hover:border-primary/50"),
12570
- onClick,
12571
- children: [/* @__PURE__ */ jsxs("div", {
12572
- className: "relative aspect-video w-full overflow-hidden bg-gradient-to-br from-primary/5 to-primary/10",
12573
- children: [
12574
- hasPreview ? /* @__PURE__ */ jsx("img", {
12575
- src: previewImage,
12576
- alt: template.name,
12577
- onError: handleImageError,
12578
- className: "w-full h-full object-cover group-hover:scale-110 transition-transform duration-300"
12579
- }) : /* @__PURE__ */ jsx("div", {
12580
- className: "w-full h-full flex items-center justify-center",
12581
- children: preview.kind === "icon" ? /* @__PURE__ */ jsx(Icon, {
12582
- icon: preview.name,
12583
- width: 64,
12584
- height: 64,
12585
- className: "text-primary/60",
12586
- "aria-hidden": "true"
12587
- }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-16 text-primary/30" })
12588
- }),
12589
- template.featured && /* @__PURE__ */ jsx("div", {
12590
- className: "absolute top-2 right-2",
12591
- children: /* @__PURE__ */ jsxs(Badge$1, {
12592
- variant: "default",
12593
- className: "bg-status-warning-bg-subtle text-status-warning-text border border-status-warning-border shadow-lg",
12594
- children: [/* @__PURE__ */ jsx(Star, { className: "size-3 mr-1 fill-current" }), tr("fluidgrids.builder.templateCard.featuredBadge", "Featured")]
12595
- })
12596
- }),
12597
- /* @__PURE__ */ jsx("div", {
12598
- className: "absolute top-2 left-2",
12599
- children: template.pricingModel === "FREE" ? /* @__PURE__ */ jsx(Badge$1, {
12600
- variant: "secondary",
12601
- className: "bg-status-success-bg text-action-primary-text border-0",
12602
- children: tr("fluidgrids.builder.templateCard.freeBadge", "Free")
12603
- }) : /* @__PURE__ */ jsxs(Badge$1, {
12604
- variant: "default",
12605
- className: "bg-status-info-bg text-action-primary-text border-0",
12606
- children: ["$", template.price?.toString() || "0"]
12607
- })
12608
- }),
12609
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-bg-overlay via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" })
12610
- ]
12611
- }), /* @__PURE__ */ jsxs("div", {
12612
- className: "p-4 space-y-3",
12613
- children: [
12614
- /* @__PURE__ */ jsx("h3", {
12615
- className: "font-semibold text-base line-clamp-1 group-hover:text-primary transition-colors",
12616
- children: template.name
12617
- }),
12618
- /* @__PURE__ */ jsx("p", {
12619
- className: "text-sm text-muted-foreground line-clamp-2 min-h-[2.5rem]",
12620
- children: template.description || tr("fluidgrids.builder.templateCard.noDescription", "No description available")
12621
- }),
12622
- /* @__PURE__ */ jsxs("div", {
12623
- className: "flex items-center gap-2 text-xs text-muted-foreground",
12624
- children: [/* @__PURE__ */ jsx("span", {
12625
- className: "font-medium",
12626
- children: template.authorName || template.publisher?.name || tr("fluidgrids.builder.templateCard.unknownAuthor", "Unknown")
12627
- }), template.publisher?.isVerified && /* @__PURE__ */ jsx(Badge$1, {
12628
- variant: "secondary",
12629
- className: "text-xs px-1.5 py-0",
12630
- children: "✓"
12631
- })]
12632
- }),
12633
- /* @__PURE__ */ jsxs("div", {
12634
- className: "flex items-center justify-between text-xs text-muted-foreground pt-2 border-t",
12635
- children: [/* @__PURE__ */ jsx("div", {
12636
- className: "flex items-center gap-3",
12637
- children: template.rating && /* @__PURE__ */ jsxs("div", {
12638
- className: "flex items-center gap-1",
12639
- children: [
12640
- /* @__PURE__ */ jsx(Star, { className: "size-3 fill-status-warning-bg text-status-warning-text" }),
12641
- /* @__PURE__ */ jsx("span", {
12642
- className: "font-medium",
12643
- children: template.rating.toFixed(1)
12644
- }),
12645
- /* @__PURE__ */ jsxs("span", {
12646
- className: "text-muted-foreground/60",
12647
- children: [
12648
- "(",
12649
- template.reviewCount || 0,
12650
- ")"
12651
- ]
12652
- })
12653
- ]
12654
- })
12655
- }), /* @__PURE__ */ jsxs("div", {
12656
- className: "flex items-center gap-3",
12657
- children: [/* @__PURE__ */ jsxs("div", {
12658
- className: "flex items-center gap-1",
12659
- title: tr("fluidgrids.builder.templateCard.downloadsTooltip", "Downloads"),
12660
- children: [/* @__PURE__ */ jsx(Download, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: formatNumber(template.downloads || 0) })]
12661
- }), /* @__PURE__ */ jsxs("div", {
12662
- className: "flex items-center gap-1",
12663
- title: tr("fluidgrids.builder.templateCard.viewsTooltip", "Views"),
12664
- children: [/* @__PURE__ */ jsx(Eye, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: formatNumber(template.viewCount || 0) })]
12665
- })]
12666
- })]
12667
- }),
12668
- (template.category || template.tags?.length > 0) && /* @__PURE__ */ jsxs("div", {
12669
- className: "flex flex-wrap gap-1",
12670
- children: [
12671
- template.category && /* @__PURE__ */ jsx(Badge$1, {
12672
- variant: "outline",
12673
- className: "text-xs",
12674
- children: template.category
12675
- }),
12676
- template.tags?.slice(0, 2).map((tag) => /* @__PURE__ */ jsx(Badge$1, {
12677
- variant: "outline",
12678
- className: "text-xs",
12679
- children: tag
12680
- }, tag)),
12681
- template.tags?.length > 2 && /* @__PURE__ */ jsxs(Badge$1, {
12682
- variant: "outline",
12683
- className: "text-xs",
12684
- children: ["+", template.tags.length - 2]
12685
- })
12686
- ]
12687
- }),
12688
- /* @__PURE__ */ jsx(SdkPermissionButton, {
12689
- permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE,
12690
- deniedTooltip: tr("fluidgrids.templates.useDeniedTooltip", "You don't have permission to create workflows from templates"),
12691
- children: /* @__PURE__ */ jsxs(Button$2, {
12692
- size: "sm",
12693
- className: "w-full mt-2",
12694
- onClick: handleUseTemplate,
12695
- children: [/* @__PURE__ */ jsx(Sparkles, { className: "size-3 mr-1" }), tr("fluidgrids.builder.templateCard.useTemplateButton", "Use Template")]
12696
- })
12697
- })
12698
- ]
12699
- })]
12700
- });
12701
- }
12702
-
12703
11042
  // src/builder/components/flow-builder/components/templates/TemplatesGrid.tsx
12704
11043
  /**
12705
11044
  * Grid layout for displaying workflow templates
@@ -12751,7 +11090,7 @@ function TemplatesGrid({ templates, loading, hasMore, onLoadMore, onTemplateClic
12751
11090
  }
12752
11091
 
12753
11092
  // src/builder/components/flow-builder/components/templates/TemplatesModal.tsx
12754
- const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-DfFfUcOe.js").then((m) => ({ default: m.TemplateDetailPanel })));
11093
+ const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-D4cUVr-m.js").then((m) => ({ default: m.TemplateDetailPanel })));
12755
11094
  /**
12756
11095
  * Modal for browsing and selecting workflow templates
12757
11096
  * Features:
@@ -27452,8 +25791,8 @@ function EditLeaseBanner() {
27452
25791
  }
27453
25792
 
27454
25793
  // src/builder/components/flow-builder/flow-builder.tsx
27455
- const LazyNodeDetailView = lazy(() => import("./node-detail-view-LNqeIqN-.js").then((m) => ({ default: m.NodeDetailView })));
27456
- const LazyQuickConfigPanel = lazy(() => import("./quick-config-panel-D3ogsaFL.js").then((m) => ({ default: m.QuickConfigPanel })));
25794
+ const LazyNodeDetailView = lazy(() => import("./node-detail-view-B91kXzIy.js").then((m) => ({ default: m.NodeDetailView })));
25795
+ const LazyQuickConfigPanel = lazy(() => import("./quick-config-panel-DggPl1vY.js").then((m) => ({ default: m.QuickConfigPanel })));
27457
25796
  const edgeTypes = { deletable: CustomDeletableEdge };
27458
25797
  const edgeOptions = {
27459
25798
  type: "deletable",
@@ -27513,7 +25852,7 @@ const FlowBuilder = ({ readOnly = false }) => {
27513
25852
  const [agentTools, setAgentTools] = useState([]);
27514
25853
  useEffect(() => {
27515
25854
  let cancelled = false;
27516
- import("./agentTools-DJ5o2ekg.js").then(({ createBuilderAgentTools }) => {
25855
+ import("./agentTools-CkRJB7r5.js").then(({ createBuilderAgentTools }) => {
27517
25856
  if (cancelled) return;
27518
25857
  setAgentTools(createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current }));
27519
25858
  }).catch((error) => {
@@ -27568,7 +25907,7 @@ const FlowBuilder = ({ readOnly = false }) => {
27568
25907
  const [noteTools, setNoteTools] = useState([]);
27569
25908
  useEffect(() => {
27570
25909
  let cancelled = false;
27571
- import("./canvasNoteTools-D3zUdEeg.js").then(({ createCanvasNoteTools }) => {
25910
+ import("./canvasNoteTools-CQIc7Oxg.js").then(({ createCanvasNoteTools }) => {
27572
25911
  if (cancelled) return;
27573
25912
  setNoteTools(createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
27574
25913
  nodes: [{ id: nodeId }],
@@ -27876,4 +26215,4 @@ const FlowBuilderPage = ({ workflow, readOnly = false }) => {
27876
26215
  ] });
27877
26216
  };
27878
26217
 
27879
- export { Popover$1 as $, DialogPortal as A, useInstalledNodes as B, Dialog$1 as C, WORKFLOW_TEST_NODE_TYPES as Ct, DialogFooter$1 as D, isRunControlNodeType as Dt, DialogDescription$1 as E, isNodeTypeHiddenFromPalette as Et, buildNodeCheck as F, RunnerStatusPill as G, NodeEditorTabBody as H, buildRequiredNodeTypes as I, useRunnerActions as J, RUNNER_INSTALL_SCRIPT_URL as K, getWorkflowNodeTypes as L, DialogTrigger$1 as M, computeAttachPosition as N, DialogHeader$1 as O, useTemplateImportFlow as P, NDV_Z_INDEX as Q, useImportWorkflowTemplate as R, badgeVariants as S, RUN_CONTROL_NODE_TYPES as St, DialogContent$1 as T, isDurableFunctionNodeType as Tt, NodeSettingsPanel as U, NodeExecutionActions as V, RunOnSection as W, NodeParamsForm as X, useRunners as Y, useGlobalStoreClient as Z, JsonView as _, CardFooter as _t, useAiWorkflowTemplates as a, ReadOnlyFieldset as at, templatePreview as b, Input$1 as bt, RunsUsageMeter as c, SchedulePanel as ct, readRunsQuota as d, NodeInputFields as dt, PopoverContent$1 as et, runsQuotaLevel as f, useNodeSelectedEvent as ft, notesKeyOf as g, CardDescription$1 as gt, useWorkflowReloadStore as h, CardContent$1 as ht, enrichDraftNodes as i, ConnectionFormDialog as it, DialogTitle$1 as j, DialogOverlay as k, RunsUsageMeterView as l, NodeEditorTabs as lt, releaseLeaseKeptForReload as m, Card$1 as mt, AI_DRAFT_MAX_PROMPT_LENGTH as n, CodeEditorField as nt, BUILDER_FULLSCREEN_ATTRIBUTE as o, useNodeEditorTab as ot, summarizeRunsQuota as p, FluidGridsMark as pt, runnerInstallCommand as q, draftToCanvas as r, ConnectionSelector as rt, BUILDER_FULLSCREEN_EVENT as s, SCHEDULE_NODE_TYPE as st, FlowBuilderPage as t, PopoverTrigger$1 as tt, useRunsQuota as u, TabButton as ut, TableView as v, CardHeader$1 as vt, DialogClose as w, filterPaletteNodes as wt, Badge$1 as x, DURABLE_FUNCTION_NODE_TYPES as xt, TemplateCard as y, CardTitle$1 as yt, useWorkflowTemplates as z };
26218
+ export { ConnectionSelector as A, FluidGridsMark as B, RunOnSection as C, PopoverContent$1 as D, Popover$1 as E, SchedulePanel as F, NodeEditorTabs as I, TabButton as L, ReadOnlyFieldset as M, useNodeEditorTab as N, PopoverTrigger$1 as O, SCHEDULE_NODE_TYPE as P, NodeInputFields as R, NodeSettingsPanel as S, NDV_Z_INDEX as T, JsonView as _, useAiWorkflowTemplates as a, NodeExecutionActions as b, RunsUsageMeter as c, readRunsQuota as d, runsQuotaLevel as f, notesKeyOf as g, useWorkflowReloadStore as h, enrichDraftNodes as i, ConnectionFormDialog as j, CodeEditorField as k, RunsUsageMeterView as l, releaseLeaseKeptForReload as m, AI_DRAFT_MAX_PROMPT_LENGTH as n, BUILDER_FULLSCREEN_ATTRIBUTE as o, summarizeRunsQuota as p, draftToCanvas as r, BUILDER_FULLSCREEN_EVENT as s, FlowBuilderPage as t, useRunsQuota as u, TableView as v, NodeParamsForm as w, NodeEditorTabBody as x, computeAttachPosition as y, useNodeSelectedEvent as z };