@burdenoff/fluidgrids-fe-sdk 2026.1017.2 → 2026.1019.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/{ExecutionDetailPage-5ZLI6YZP.js → ExecutionDetailPage-TWLLKOCJ.js} +3 -3
  2. package/dist/{ExecutionsListPage-4OKTQUB7.js → ExecutionsListPage-NZZFAU5B.js} +4 -4
  3. package/dist/{WorkflowBuilder-3AZ5D3L7.js → WorkflowBuilder-SVW4D5OV.js} +5 -5
  4. package/dist/{WorkflowsList-FESCHECO.js → WorkflowsList-LDIKEXC3.js} +4 -4
  5. package/dist/{chunk-DIQQNS6K.js → chunk-3ZQVSPPA.js} +2 -2
  6. package/dist/{chunk-5KAMCVQN.js → chunk-7JDPYTJI.js} +3 -3
  7. package/dist/{chunk-Y33ZGP5F.js → chunk-7S5WTQ7X.js} +2 -2
  8. package/dist/{chunk-UNRFUBE5.js → chunk-BEOGOFNQ.js} +2 -2
  9. package/dist/{chunk-PYQHV2NT.js → chunk-BXRTBIUQ.js} +15 -1
  10. package/dist/{chunk-MESOVECH.js → chunk-EPYVZPGU.js} +1 -1
  11. package/dist/{chunk-OBH5MEIP.js → chunk-GSUWFVSF.js} +460 -68
  12. package/dist/{chunk-JPH6XZ5V.js → chunk-HY4XW2FZ.js} +1 -1
  13. package/dist/{chunk-NFBNWXJH.js → chunk-L2UWPFJM.js} +4 -1
  14. package/dist/{chunk-XZO64J5O.js → chunk-L323V5C2.js} +11 -3
  15. package/dist/{chunk-UUELRAQ2.js → chunk-O6E247J6.js} +1 -1
  16. package/dist/{chunk-P6EUVE3U.js → chunk-WQ7CETWI.js} +1 -1
  17. package/dist/{chunk-7ZVWGAVI.js → chunk-YA73BHRQ.js} +2 -2
  18. package/dist/{chunk-XL3QSK42.js → chunk-YDKAMLUD.js} +3 -3
  19. package/dist/executions.js +5 -5
  20. package/dist/history.js +3 -3
  21. package/dist/index.d.ts +101 -3
  22. package/dist/index.js +20 -20
  23. package/dist/nodes.js +2 -2
  24. package/dist/settings.js +3 -3
  25. package/dist/templates.js +5 -5
  26. package/dist/{workflow-builder-CBLLeFzM.d.ts → workflow-builder-Uhk2DIB6.d.ts} +12 -0
  27. package/dist/workflow-builder.d.ts +1 -1
  28. package/dist/workflow-builder.js +6 -6
  29. package/dist/workflows-list.js +4 -4
  30. package/package.json +1 -1
@@ -7,10 +7,10 @@ import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, re
7
7
  import { GetMyNodeInstallationsDocument, BrowseWorkflowTemplatesDocument, InstallWorkflowNodeDocument, InstallPurchasedNodeDocument, BrowseWorkflowNodesDocument, UninstallWorkflowNodeDocument, CreateStoreOrderDocument, GetNodeInstallableVersionsDocument } from './chunk-WBH2XEWO.js';
8
8
  import { SdkPermissionGate, FLUIDGRIDS_PERMISSIONS, SdkPermissionButton } from './chunk-ONVGFVNH.js';
9
9
  import { cn as cn$1 } from './chunk-JL7CASN5.js';
10
- import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-UUELRAQ2.js';
10
+ import { useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeleteWorkflow, useLazyWorkflowDefinition, useDeactivateWorkflow } from './chunk-O6E247J6.js';
11
11
  import { config, getFluidGridsConfig, validateTrustedOrigin, resetFluidGridsExecutionState, setFluidGridsConfig, resetFluidGridsConfig } from './chunk-UZG3ZIQM.js';
12
- import { useFlowStore, isNodeTrigger, useIsValidConnection, isTriggerOperation } from './chunk-PYQHV2NT.js';
13
- import { NodeEndpointConfigsDocument, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowRunDocument, FluidgridsEntityTagsDocument, AvailableFluidgridsTagsDocument, SearchFluidgridsTagsDocument, TagFluidgridsEntityDocument, UntagFluidgridsEntityDocument, ReplaceFluidgridsEntityTagsDocument, CreateFluidgridsTagDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-NFBNWXJH.js';
12
+ import { useFlowStore, isNodeTrigger, useIsValidConnection, isTriggerOperation } from './chunk-BXRTBIUQ.js';
13
+ import { NodeEndpointConfigsDocument, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowRunDocument, FluidgridsEntityTagsDocument, AvailableFluidgridsTagsDocument, SearchFluidgridsTagsDocument, TagFluidgridsEntityDocument, UntagFluidgridsEntityDocument, ReplaceFluidgridsEntityTagsDocument, CreateFluidgridsTagDocument, NodeSchemaIntrospectionDocument, useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, useRunFromNodeEligibilityQuery, EventRegistrationsDocument, WebhookTestListenerDocument, StopWebhookTestListenerDocument, ListenForWebhookTestEventDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-L2UWPFJM.js';
14
14
  import { throwIfResultError, describeOperationError } from './chunk-CFCUM4CN.js';
15
15
  import { useTr, FluidGridsTranslationProvider, ENGLISH_FALLBACK_TRANSLATOR } from './chunk-UULVIT25.js';
16
16
  import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-NTWG3CJX.js';
@@ -6235,6 +6235,29 @@ function useNodeExecution() {
6235
6235
  completeExecution
6236
6236
  ]
6237
6237
  );
6238
+ const followRun = useCallback(
6239
+ (runId, triggerNodeId) => {
6240
+ cleanup();
6241
+ clearLogs();
6242
+ setNodeStatus(triggerNodeId, "running");
6243
+ startExecution(triggerNodeId, runId, {
6244
+ workflowKey: workflow.workflowKey || workflow.id,
6245
+ version: workflow.version,
6246
+ sourceMode: "TEST"
6247
+ });
6248
+ pollRunUpdates(runId);
6249
+ },
6250
+ [
6251
+ cleanup,
6252
+ clearLogs,
6253
+ setNodeStatus,
6254
+ startExecution,
6255
+ workflow.workflowKey,
6256
+ workflow.id,
6257
+ workflow.version,
6258
+ pollRunUpdates
6259
+ ]
6260
+ );
6238
6261
  const executeFromNode = useCallback(
6239
6262
  async (sourceRunId, nodeId) => {
6240
6263
  cleanup();
@@ -6372,6 +6395,7 @@ function useNodeExecution() {
6372
6395
  executeStep,
6373
6396
  executeFullWorkflow,
6374
6397
  executeFromNode,
6398
+ followRun,
6375
6399
  loadExecution,
6376
6400
  cleanup,
6377
6401
  findTriggerNode
@@ -6825,28 +6849,35 @@ function RunOnSection({
6825
6849
  children: /* @__PURE__ */ jsx(SelectValue, {})
6826
6850
  }
6827
6851
  ),
6828
- /* @__PURE__ */ jsxs(SelectContent, { children: [
6829
- /* @__PURE__ */ jsx(SelectItem, { value: CLOUD, "data-testid": "run-on-cloud", children: tr(
6830
- "fluidgrids.builder.nodeSettings.runOnCloud",
6831
- "FluidGrids Cloud (default)"
6832
- ) }),
6833
- runners.map((runner) => /* @__PURE__ */ jsxs(
6834
- SelectItem,
6835
- {
6836
- value: runner.id,
6837
- "data-testid": "run-on-runner-option",
6838
- children: [
6839
- runner.name,
6840
- runner.status !== "ONLINE" ? ` (${runner.status === "OFFLINE" ? tr("fluidgrids.runners.offline", "Offline") : tr("fluidgrids.runners.neverConnected", "Not connected yet")})` : ""
6841
- ]
6842
- },
6843
- runner.id
6844
- )),
6845
- value && !loading && !selected && /* @__PURE__ */ jsx(SelectItem, { value: value.runnerId, disabled: true, children: tr(
6846
- "fluidgrids.builder.nodeSettings.runOnMissing",
6847
- "Removed runner"
6848
- ) })
6849
- ] })
6852
+ /* @__PURE__ */ jsxs(
6853
+ SelectContent,
6854
+ {
6855
+ style: { zIndex: 1100 },
6856
+ "data-testid": "ndv-settings-run-on-options",
6857
+ children: [
6858
+ /* @__PURE__ */ jsx(SelectItem, { value: CLOUD, "data-testid": "run-on-cloud", children: tr(
6859
+ "fluidgrids.builder.nodeSettings.runOnCloud",
6860
+ "FluidGrids Cloud (default)"
6861
+ ) }),
6862
+ runners.map((runner) => /* @__PURE__ */ jsxs(
6863
+ SelectItem,
6864
+ {
6865
+ value: runner.id,
6866
+ "data-testid": "run-on-runner-option",
6867
+ children: [
6868
+ runner.name,
6869
+ runner.status !== "ONLINE" ? ` (${runner.status === "OFFLINE" ? tr("fluidgrids.runners.offline", "Offline") : tr("fluidgrids.runners.neverConnected", "Not connected yet")})` : ""
6870
+ ]
6871
+ },
6872
+ runner.id
6873
+ )),
6874
+ value && !loading && !selected && /* @__PURE__ */ jsx(SelectItem, { value: value.runnerId, disabled: true, children: tr(
6875
+ "fluidgrids.builder.nodeSettings.runOnMissing",
6876
+ "Removed runner"
6877
+ ) })
6878
+ ]
6879
+ }
6880
+ )
6850
6881
  ]
6851
6882
  }
6852
6883
  ),
@@ -21641,69 +21672,405 @@ function WaitResumeUrlField() {
21641
21672
  ) })
21642
21673
  ] });
21643
21674
  }
21675
+ var POLL_INTERVAL_MS = 2e3;
21676
+ function useWebhookTestListener(target, onReceived) {
21677
+ const [listener, setListener] = useState(
21678
+ null
21679
+ );
21680
+ const [busy, setBusy] = useState(false);
21681
+ const [error, setError] = useState(null);
21682
+ const pollTimer = useRef(null);
21683
+ const openId = useRef(null);
21684
+ const generation = useRef(0);
21685
+ const onReceivedRef = useRef(onReceived);
21686
+ onReceivedRef.current = onReceived;
21687
+ const workflowKey = target?.workflowKey;
21688
+ const version = target?.version;
21689
+ const triggerNodeId = target?.triggerNodeId;
21690
+ const clearPoll = useCallback(() => {
21691
+ if (pollTimer.current) {
21692
+ clearTimeout(pollTimer.current);
21693
+ pollTimer.current = null;
21694
+ }
21695
+ }, []);
21696
+ const poll = useCallback(
21697
+ (id) => {
21698
+ clearPoll();
21699
+ pollTimer.current = setTimeout(async () => {
21700
+ const client = getFluidGridsClientSafe();
21701
+ if (!client || openId.current !== id) return;
21702
+ try {
21703
+ const result = await client.query({
21704
+ query: WebhookTestListenerDocument,
21705
+ variables: { id },
21706
+ fetchPolicy: "network-only"
21707
+ });
21708
+ const latest = result.data?.webhookTestListener;
21709
+ if (openId.current !== id) return;
21710
+ if (!latest) {
21711
+ openId.current = null;
21712
+ setListener(null);
21713
+ return;
21714
+ }
21715
+ const status = latest.status;
21716
+ setListener(
21717
+ (current) => current && current.id === id ? {
21718
+ ...current,
21719
+ status,
21720
+ runId: latest.runId ?? null,
21721
+ error: latest.error ?? null
21722
+ } : current
21723
+ );
21724
+ if (status === "LISTENING") {
21725
+ poll(id);
21726
+ return;
21727
+ }
21728
+ openId.current = null;
21729
+ if (status === "RECEIVED" && latest.runId) {
21730
+ onReceivedRef.current(latest.runId);
21731
+ }
21732
+ } catch {
21733
+ if (openId.current === id) poll(id);
21734
+ }
21735
+ }, POLL_INTERVAL_MS);
21736
+ },
21737
+ [clearPoll]
21738
+ );
21739
+ const stopAbandoned = useCallback((id) => {
21740
+ void getFluidGridsClientSafe()?.mutate({ mutation: StopWebhookTestListenerDocument, variables: { id } }).catch(() => void 0);
21741
+ }, []);
21742
+ const stopOpenListener = useCallback(() => {
21743
+ const id = openId.current;
21744
+ openId.current = null;
21745
+ clearPoll();
21746
+ if (id) stopAbandoned(id);
21747
+ }, [clearPoll, stopAbandoned]);
21748
+ const listen = useCallback(async () => {
21749
+ const client = getFluidGridsClientSafe();
21750
+ if (!client || !workflowKey || !version || !triggerNodeId) return;
21751
+ stopOpenListener();
21752
+ const armedFor = generation.current;
21753
+ setBusy(true);
21754
+ setError(null);
21755
+ try {
21756
+ const result = await client.mutate({
21757
+ mutation: ListenForWebhookTestEventDocument,
21758
+ variables: { input: { workflowKey, version, triggerNodeId } }
21759
+ });
21760
+ const armed = result.data?.listenForWebhookTestEvent;
21761
+ if (!armed) throw new Error("The test listener could not be started");
21762
+ if (generation.current !== armedFor) {
21763
+ stopAbandoned(armed.id);
21764
+ return;
21765
+ }
21766
+ openId.current = armed.id;
21767
+ setListener({
21768
+ id: armed.id,
21769
+ status: armed.status,
21770
+ testUrl: armed.testUrl ?? null,
21771
+ expiresAt: armed.expiresAt,
21772
+ runId: armed.runId ?? null,
21773
+ error: armed.error ?? null
21774
+ });
21775
+ poll(armed.id);
21776
+ } catch (err) {
21777
+ if (generation.current === armedFor)
21778
+ setError(describeOperationError(err));
21779
+ } finally {
21780
+ if (generation.current === armedFor) setBusy(false);
21781
+ }
21782
+ }, [
21783
+ workflowKey,
21784
+ version,
21785
+ triggerNodeId,
21786
+ poll,
21787
+ stopOpenListener,
21788
+ stopAbandoned
21789
+ ]);
21790
+ const stop = useCallback(async () => {
21791
+ const id = openId.current;
21792
+ const client = getFluidGridsClientSafe();
21793
+ if (!id || !client) return;
21794
+ clearPoll();
21795
+ const stoppedFor = generation.current;
21796
+ setBusy(true);
21797
+ try {
21798
+ const result = await client.mutate({ mutation: StopWebhookTestListenerDocument, variables: { id } });
21799
+ const stopped = result.data?.stopWebhookTestListener;
21800
+ if (generation.current !== stoppedFor) return;
21801
+ if (!stopped) throw new Error("The test listener could not be stopped");
21802
+ const closedHere = openId.current === id;
21803
+ if (closedHere) openId.current = null;
21804
+ const status = stopped.status;
21805
+ setListener(
21806
+ (current) => current && current.id === id ? {
21807
+ ...current,
21808
+ status,
21809
+ runId: stopped.runId ?? null,
21810
+ error: stopped.error ?? null
21811
+ } : current
21812
+ );
21813
+ if (closedHere && status === "RECEIVED" && stopped.runId) {
21814
+ onReceivedRef.current(stopped.runId);
21815
+ }
21816
+ } catch (err) {
21817
+ if (generation.current !== stoppedFor) return;
21818
+ setError(describeOperationError(err));
21819
+ if (openId.current === id) poll(id);
21820
+ } finally {
21821
+ if (generation.current === stoppedFor) setBusy(false);
21822
+ }
21823
+ }, [clearPoll, poll]);
21824
+ useEffect(() => {
21825
+ setListener(null);
21826
+ setError(null);
21827
+ setBusy(false);
21828
+ return () => {
21829
+ generation.current += 1;
21830
+ stopOpenListener();
21831
+ };
21832
+ }, [workflowKey, version, triggerNodeId, stopOpenListener]);
21833
+ return { listener, busy, error, listen, stop };
21834
+ }
21835
+ var BuilderReadOnlyContext = createContext(false);
21836
+ var BuilderReadOnlyProvider = BuilderReadOnlyContext.Provider;
21837
+ function useBuilderReadOnly() {
21838
+ return useContext(BuilderReadOnlyContext);
21839
+ }
21644
21840
  var SECTION_LABEL2 = "text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground";
21645
- function WebhookUrlField({ webhookUrl, loading }) {
21841
+ function CopyableUrl({
21842
+ url,
21843
+ copyAriaLabel,
21844
+ testId
21845
+ }) {
21646
21846
  const tr = useTr();
21647
21847
  const [copied, setCopied] = useState(false);
21648
21848
  const handleCopy = async () => {
21649
- if (!webhookUrl) return;
21650
21849
  try {
21651
- await navigator.clipboard.writeText(webhookUrl);
21850
+ await navigator.clipboard.writeText(url);
21652
21851
  setCopied(true);
21653
21852
  setTimeout(() => setCopied(false), 1500);
21654
21853
  } catch {
21655
21854
  }
21656
21855
  };
21856
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-2", children: [
21857
+ /* @__PURE__ */ jsx(
21858
+ "input",
21859
+ {
21860
+ readOnly: true,
21861
+ value: url,
21862
+ "data-testid": testId,
21863
+ onFocus: (e) => e.currentTarget.select(),
21864
+ className: "min-w-0 flex-1 rounded-md border border-border bg-muted/40 px-3 py-2 font-mono text-xs text-foreground outline-none focus:border-primary"
21865
+ }
21866
+ ),
21867
+ /* @__PURE__ */ jsxs(
21868
+ "button",
21869
+ {
21870
+ type: "button",
21871
+ onClick: handleCopy,
21872
+ className: cn(
21873
+ "inline-flex shrink-0 items-center gap-1.5 rounded-md border border-border px-3 py-2 text-xs font-medium transition-colors",
21874
+ copied ? "border-primary bg-primary/10 text-primary" : "bg-background text-foreground hover:bg-muted"
21875
+ ),
21876
+ "aria-label": copyAriaLabel,
21877
+ children: [
21878
+ /* @__PURE__ */ jsx(
21879
+ Icon,
21880
+ {
21881
+ icon: copied ? "mdi:check" : "mdi:content-copy",
21882
+ className: "h-4 w-4"
21883
+ }
21884
+ ),
21885
+ copied ? tr("fluidgrids.builder.webhookUrlField.copied", "Copied") : tr("fluidgrids.builder.webhookUrlField.copy", "Copy")
21886
+ ]
21887
+ }
21888
+ )
21889
+ ] });
21890
+ }
21891
+ function WebhookUrlField({ webhookUrl, loading }) {
21892
+ const tr = useTr();
21893
+ const isActiveVersion = useFlowStore((s) => s.workflow.status === "ACTIVE");
21657
21894
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", "data-testid": "ndv-webhook-url", children: [
21658
- /* @__PURE__ */ jsx("div", { className: SECTION_LABEL2, children: tr("fluidgrids.builder.webhookUrlField.label", "Webhook URL") }),
21895
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
21896
+ /* @__PURE__ */ jsx("div", { className: SECTION_LABEL2, children: tr(
21897
+ "fluidgrids.builder.webhookUrlField.productionLabel",
21898
+ "Production URL"
21899
+ ) }),
21900
+ webhookUrl && isActiveVersion && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-medium text-primary", children: tr("fluidgrids.builder.webhookUrlField.live", "Live") })
21901
+ ] }),
21659
21902
  webhookUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
21660
- /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-2", children: [
21661
- /* @__PURE__ */ jsx(
21662
- "input",
21663
- {
21664
- readOnly: true,
21665
- value: webhookUrl,
21666
- onFocus: (e) => e.currentTarget.select(),
21667
- className: "min-w-0 flex-1 rounded-md border border-border bg-muted/40 px-3 py-2 font-mono text-xs text-foreground outline-none focus:border-primary"
21668
- }
21669
- ),
21903
+ /* @__PURE__ */ jsx(
21904
+ CopyableUrl,
21905
+ {
21906
+ url: webhookUrl,
21907
+ testId: "ndv-webhook-production-url",
21908
+ copyAriaLabel: tr(
21909
+ "fluidgrids.builder.webhookUrlField.copyAriaLabel",
21910
+ "Copy webhook URL"
21911
+ )
21912
+ }
21913
+ ),
21914
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: isActiveVersion ? tr(
21915
+ "fluidgrids.builder.webhookUrlField.activeHint",
21916
+ "Requests to this URL run this version of the workflow."
21917
+ ) : tr(
21918
+ "fluidgrids.builder.webhookUrlField.inactiveHint",
21919
+ "Requests to this URL run the workflow's active version only. Activate this version to make it live, or use a test URL to try it."
21920
+ ) })
21921
+ ] }) : /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: loading ? tr(
21922
+ "fluidgrids.builder.webhookUrlField.loading",
21923
+ "Loading webhook URL\u2026"
21924
+ ) : tr(
21925
+ "fluidgrids.builder.webhookUrlField.unsaved",
21926
+ "Save the workflow to generate its webhook URL."
21927
+ ) })
21928
+ ] });
21929
+ }
21930
+ function useSecondsLeft(expiresAt) {
21931
+ const [now, setNow] = useState(() => Date.now());
21932
+ useEffect(() => {
21933
+ if (!expiresAt) return;
21934
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
21935
+ return () => clearInterval(timer);
21936
+ }, [expiresAt]);
21937
+ if (!expiresAt) return 0;
21938
+ return Math.max(0, Math.ceil((Date.parse(expiresAt) - now) / 1e3));
21939
+ }
21940
+ function WebhookTestListenerField({
21941
+ nodeId
21942
+ }) {
21943
+ const readOnly = useBuilderReadOnly();
21944
+ return readOnly ? null : /* @__PURE__ */ jsx(WebhookTestListenerControls, { nodeId });
21945
+ }
21946
+ function WebhookTestListenerControls({
21947
+ nodeId
21948
+ }) {
21949
+ const tr = useTr();
21950
+ const workflowKey = useFlowStore((s) => s.workflow.workflowKey);
21951
+ const version = useFlowStore((s) => s.workflow.version);
21952
+ const requestFollowRun = useFlowStore(
21953
+ (s) => s.actions.execution.requestFollowRun
21954
+ );
21955
+ const saved = !!workflowKey && !!version && !!nodeId;
21956
+ const target = saved ? { workflowKey, version, triggerNodeId: nodeId } : null;
21957
+ const { listener, busy, error, listen, stop } = useWebhookTestListener(
21958
+ target,
21959
+ (runId) => {
21960
+ if (nodeId) requestFollowRun(runId, nodeId);
21961
+ }
21962
+ );
21963
+ const listening = listener?.status === "LISTENING";
21964
+ const secondsLeft = useSecondsLeft(listening ? listener.expiresAt : null);
21965
+ const outcome = (() => {
21966
+ switch (listener?.status) {
21967
+ case "RECEIVED":
21968
+ return tr(
21969
+ "fluidgrids.builder.webhookTestListener.received",
21970
+ "Test event received \u2014 the run is shown on the canvas."
21971
+ );
21972
+ case "EXPIRED":
21973
+ return tr(
21974
+ "fluidgrids.builder.webhookTestListener.expired",
21975
+ "Stopped listening: no test event arrived in time."
21976
+ );
21977
+ case "STOPPED":
21978
+ return tr(
21979
+ "fluidgrids.builder.webhookTestListener.stopped",
21980
+ "Stopped listening."
21981
+ );
21982
+ case "FAILED":
21983
+ return listener.error ?? tr(
21984
+ "fluidgrids.builder.webhookTestListener.failed",
21985
+ "The test event arrived but the run could not be started."
21986
+ );
21987
+ default:
21988
+ return null;
21989
+ }
21990
+ })();
21991
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", "data-testid": "ndv-webhook-test", children: [
21992
+ /* @__PURE__ */ jsx("div", { className: SECTION_LABEL2, children: tr("fluidgrids.builder.webhookTestListener.label", "Test URL") }),
21993
+ !saved ? /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: tr(
21994
+ "fluidgrids.builder.webhookTestListener.unsaved",
21995
+ "Save the workflow to test this webhook."
21996
+ ) }) : listening && listener.testUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
21997
+ /* @__PURE__ */ jsx(
21998
+ CopyableUrl,
21999
+ {
22000
+ url: listener.testUrl,
22001
+ testId: "ndv-webhook-test-url",
22002
+ copyAriaLabel: tr(
22003
+ "fluidgrids.builder.webhookTestListener.copyAriaLabel",
22004
+ "Copy test URL"
22005
+ )
22006
+ }
22007
+ ),
22008
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
21670
22009
  /* @__PURE__ */ jsxs(
21671
- "button",
22010
+ "p",
21672
22011
  {
21673
- type: "button",
21674
- onClick: handleCopy,
21675
- className: cn(
21676
- "inline-flex shrink-0 items-center gap-1.5 rounded-md border border-border px-3 py-2 text-xs font-medium transition-colors",
21677
- copied ? "border-primary bg-primary/10 text-primary" : "bg-background text-foreground hover:bg-muted"
21678
- ),
21679
- "aria-label": tr(
21680
- "fluidgrids.builder.webhookUrlField.copyAriaLabel",
21681
- "Copy webhook URL"
21682
- ),
22012
+ className: "flex items-center gap-1.5 text-[11px] text-muted-foreground",
22013
+ "data-testid": "ndv-webhook-test-status",
21683
22014
  children: [
21684
22015
  /* @__PURE__ */ jsx(
21685
22016
  Icon,
21686
22017
  {
21687
- icon: copied ? "mdi:check" : "mdi:content-copy",
21688
- className: "h-4 w-4"
22018
+ icon: "mdi:access-point",
22019
+ className: "h-3.5 w-3.5 text-primary"
21689
22020
  }
21690
22021
  ),
21691
- copied ? tr("fluidgrids.builder.webhookUrlField.copied", "Copied") : tr("fluidgrids.builder.webhookUrlField.copy", "Copy")
22022
+ tr(
22023
+ "fluidgrids.builder.webhookTestListener.listening",
22024
+ "Listening for one test event to version {{version}} \u2014 {{seconds}}s left",
22025
+ { version, seconds: secondsLeft }
22026
+ )
21692
22027
  ]
21693
22028
  }
22029
+ ),
22030
+ /* @__PURE__ */ jsx(
22031
+ "button",
22032
+ {
22033
+ type: "button",
22034
+ onClick: () => void stop(),
22035
+ disabled: busy,
22036
+ "data-testid": "ndv-webhook-test-stop",
22037
+ className: "shrink-0 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:opacity-50",
22038
+ children: tr("fluidgrids.builder.webhookTestListener.stop", "Stop")
22039
+ }
21694
22040
  )
21695
- ] }),
21696
- /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: tr(
21697
- "fluidgrids.builder.webhookUrlField.hint",
21698
- "Send HTTP requests to this URL to trigger the workflow."
21699
- ) })
21700
- ] }) : /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: loading ? tr(
21701
- "fluidgrids.builder.webhookUrlField.loading",
21702
- "Loading webhook URL\u2026"
21703
- ) : tr(
21704
- "fluidgrids.builder.webhookUrlField.unsaved",
21705
- "Save the workflow to generate its webhook URL."
21706
- ) })
22041
+ ] })
22042
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
22043
+ /* @__PURE__ */ jsxs(
22044
+ "button",
22045
+ {
22046
+ type: "button",
22047
+ onClick: () => void listen(),
22048
+ disabled: busy,
22049
+ "data-testid": "ndv-webhook-test-listen",
22050
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-2 text-xs font-medium text-foreground hover:bg-muted disabled:opacity-50",
22051
+ children: [
22052
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:access-point", className: "h-4 w-4" }),
22053
+ tr(
22054
+ "fluidgrids.builder.webhookTestListener.listen",
22055
+ "Listen for test event"
22056
+ )
22057
+ ]
22058
+ }
22059
+ ),
22060
+ /* @__PURE__ */ jsx(
22061
+ "p",
22062
+ {
22063
+ className: "text-[11px] text-muted-foreground",
22064
+ "data-testid": "ndv-webhook-test-status",
22065
+ children: outcome ?? tr(
22066
+ "fluidgrids.builder.webhookTestListener.hint",
22067
+ "Creates a one-time URL for the saved version {{version}}. Unsaved changes are not included.",
22068
+ { version }
22069
+ )
22070
+ }
22071
+ )
22072
+ ] }),
22073
+ error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
21707
22074
  ] });
21708
22075
  }
21709
22076
  var LIST_WORKFLOWS_QUERY = gql`
@@ -23006,7 +23373,16 @@ function NodeParamsForm({
23006
23373
  return "1.0.0";
23007
23374
  })();
23008
23375
  return /* @__PURE__ */ jsxs(Fragment, { children: [
23009
- isWebhookTrigger && /* @__PURE__ */ jsx(WebhookUrlField, { webhookUrl, loading: webhookUrlLoading }),
23376
+ isWebhookTrigger && /* @__PURE__ */ jsxs(Fragment, { children: [
23377
+ /* @__PURE__ */ jsx(
23378
+ WebhookUrlField,
23379
+ {
23380
+ webhookUrl,
23381
+ loading: webhookUrlLoading
23382
+ }
23383
+ ),
23384
+ /* @__PURE__ */ jsx(WebhookTestListenerField, { nodeId: config2.node?.id })
23385
+ ] }),
23010
23386
  isWebhookWait && /* @__PURE__ */ jsx(WaitResumeUrlField, {}),
23011
23387
  connectionAdapters.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", "data-testid": `${testIdPrefix}-connections`, children: [
23012
23388
  /* @__PURE__ */ jsx("div", { className: SECTION_LABEL3, children: tr(
@@ -24803,6 +25179,19 @@ function EmptyState({
24803
25179
  /* @__PURE__ */ jsx("div", { className: "max-w-[14rem] text-xs text-muted-foreground", children: hint })
24804
25180
  ] });
24805
25181
  }
25182
+ function WebhookRunFollower() {
25183
+ const followRequest = useFlowStore((s) => s.execution.followRequest);
25184
+ const clearFollowRequest = useFlowStore(
25185
+ (s) => s.actions.execution.clearFollowRequest
25186
+ );
25187
+ const { followRun } = useNodeExecution();
25188
+ useEffect(() => {
25189
+ if (!followRequest) return;
25190
+ clearFollowRequest();
25191
+ followRun(followRequest.runId, followRequest.triggerNodeId);
25192
+ }, [followRequest, clearFollowRequest, followRun]);
25193
+ return null;
25194
+ }
24806
25195
  var PANEL_WIDTH = 380;
24807
25196
  var PANEL_EST_HEIGHT = 480;
24808
25197
  var MAPPER_WIDTH = 260;
@@ -26358,8 +26747,11 @@ var FlowBuilder = ({ readOnly = false }) => {
26358
26747
  ),
26359
26748
  /* @__PURE__ */ jsx(ChatPanel, {}),
26360
26749
  /* @__PURE__ */ jsx(LogsPanel, {}),
26361
- /* @__PURE__ */ jsx(NodeDetailView, {}),
26362
- /* @__PURE__ */ jsx(QuickConfigPanel, {}),
26750
+ /* @__PURE__ */ jsxs(BuilderReadOnlyProvider, { value: isReadOnly, children: [
26751
+ /* @__PURE__ */ jsx(NodeDetailView, {}),
26752
+ /* @__PURE__ */ jsx(QuickConfigPanel, {})
26753
+ ] }),
26754
+ !isReadOnly && /* @__PURE__ */ jsx(WebhookRunFollower, {}),
26363
26755
  /* @__PURE__ */ jsx(
26364
26756
  "div",
26365
26757
  {
@@ -1,4 +1,4 @@
1
- import { NodeEndpointConfigsDocument, NodeEndpointConfigDocument, NodeManifestDocument } from './chunk-NFBNWXJH.js';
1
+ import { NodeEndpointConfigsDocument, NodeEndpointConfigDocument, NodeManifestDocument } from './chunk-L2UWPFJM.js';
2
2
  import { useTr } from './chunk-UULVIT25.js';
3
3
  import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
4
4
  import { useState, useCallback, useEffect, useMemo } from 'react';