@burdenoff/fluidgrids-fe-sdk 2026.1107.1 → 2026.1108.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 (64) hide show
  1. package/dist/{ExecutionDetailPage-TQST7JKC.js → ExecutionDetailPage-PY5OE2RJ.js} +16 -15
  2. package/dist/{ExecutionsListPage-QQKEGXSC.js → ExecutionsListPage-LKNYPXNZ.js} +12 -11
  3. package/dist/{MissingNodesInstallModal-OSAJLIBE.js → MissingNodesInstallModal-Q6OPULJU.js} +2 -1
  4. package/dist/{TemplateConfigurationModal-SILENYXX.js → TemplateConfigurationModal-PWCDDPKT.js} +2 -1
  5. package/dist/{TemplateDetailPanel-7MPY5MYT.js → TemplateDetailPanel-5WY75DVB.js} +3 -2
  6. package/dist/WorkflowBuilder-YFPBMOWZ.js +37 -0
  7. package/dist/{WorkflowsList-2WX7N526.js → WorkflowsList-TETNEMYP.js} +11 -11
  8. package/dist/{agentTools-MEEGOAE2.js → agentTools-BW2XMHRV.js} +7 -7
  9. package/dist/{chunk-KE677WYY.js → chunk-2CXUTQUH.js} +1 -1
  10. package/dist/{chunk-H4ERTSMX.js → chunk-2VSYOGNI.js} +1948 -35
  11. package/dist/{chunk-NZMUKAXG.js → chunk-54NXUOHI.js} +1 -1
  12. package/dist/{chunk-E3PNEIBJ.js → chunk-62RJORF7.js} +5 -5
  13. package/dist/{chunk-4F54UT62.js → chunk-66AKFXP7.js} +21 -18
  14. package/dist/{chunk-MESGVHUU.js → chunk-7SSMRID2.js} +5 -5
  15. package/dist/{chunk-752BT4WM.js → chunk-7XW6BC4B.js} +1 -1
  16. package/dist/{chunk-5375NUIO.js → chunk-AR22A74Y.js} +11 -0
  17. package/dist/{chunk-5KK3ALQ4.js → chunk-D5Y3DHGJ.js} +1 -1
  18. package/dist/{chunk-NSDBXKIC.js → chunk-DG4UO5OY.js} +5 -5
  19. package/dist/{chunk-NMYENCP2.js → chunk-E7KIH3CF.js} +1 -1
  20. package/dist/{chunk-HUCRE45R.js → chunk-GI6BQIQA.js} +127 -42
  21. package/dist/{chunk-Z3ABLERJ.js → chunk-GOZR72NK.js} +64 -1
  22. package/dist/{chunk-KHS2DZHH.js → chunk-HICQ4J4W.js} +1 -1
  23. package/dist/chunk-HPNZ3PYU.js +1448 -0
  24. package/dist/{chunk-RI4NFNDX.js → chunk-HQSSW7K3.js} +8 -8
  25. package/dist/{chunk-CTKWMPQQ.js → chunk-HSOSMPRC.js} +3 -1
  26. package/dist/{chunk-JRSCUUBT.js → chunk-IPJV2IOH.js} +1 -1
  27. package/dist/{chunk-PYSEOWLW.js → chunk-IWLNBDR2.js} +5 -5
  28. package/dist/{chunk-MYAFMXFI.js → chunk-IZOPKR64.js} +3 -3
  29. package/dist/{chunk-ZWDKN6PU.js → chunk-JRR4VKYD.js} +95 -6
  30. package/dist/{chunk-TDFC5U66.js → chunk-JSJSBB76.js} +2 -2
  31. package/dist/{chunk-V2K4HJY4.js → chunk-KJB3WZVD.js} +2 -2
  32. package/dist/{chunk-OHKSDHNC.js → chunk-L7PHZ7RY.js} +2 -2
  33. package/dist/{chunk-JOOGRWYY.js → chunk-LOMLLWHD.js} +705 -12
  34. package/dist/{chunk-ZTZNQ5GF.js → chunk-MYPB2KTJ.js} +13 -6
  35. package/dist/{chunk-HY3XCZLB.js → chunk-N2QXEV5Y.js} +8 -8
  36. package/dist/{chunk-EXJW7EKR.js → chunk-OJHBCOBA.js} +164 -1
  37. package/dist/{chunk-BLLBUHVF.js → chunk-POYMF7L6.js} +1 -1
  38. package/dist/{chunk-BQXVT32O.js → chunk-QJAZXCFQ.js} +1 -23
  39. package/dist/{chunk-GA3V3X7Q.js → chunk-RNYFPG7R.js} +4 -4
  40. package/dist/chunk-TFCIZFDM.js +28 -0
  41. package/dist/{chunk-PASKZCII.js → chunk-TN3NGTF7.js} +3 -3
  42. package/dist/{chunk-KXCJPWYE.js → chunk-VH3EET6H.js} +2 -2
  43. package/dist/{chunk-UZJKX5FN.js → chunk-X3F5IQBP.js} +5 -3
  44. package/dist/chunk-XRGAH6IQ.js +110 -0
  45. package/dist/chunk-YASVILWQ.js +398 -0
  46. package/dist/{chunk-WBVBRXDI.js → chunk-ZHXJYOOG.js} +2115 -25
  47. package/dist/executions.js +17 -16
  48. package/dist/history.js +9 -9
  49. package/dist/index.d.ts +15474 -238
  50. package/dist/index.js +1064 -58
  51. package/dist/{node-detail-view-AQCGO4XQ.js → node-detail-view-BGWKE6KW.js} +490 -22
  52. package/dist/nodes.js +5 -2
  53. package/dist/observability.js +9 -9
  54. package/dist/{quick-config-panel-G3T4JCF6.js → quick-config-panel-67BU4I23.js} +9 -9
  55. package/dist/settings.js +11 -9
  56. package/dist/templates.js +19 -17
  57. package/dist/{workflow-builder-C261exmL.d.ts → workflow-builder-D7eMRluc.d.ts} +3 -1
  58. package/dist/workflow-builder.d.ts +1 -1
  59. package/dist/workflow-builder.js +21 -19
  60. package/dist/workflow-preview.js +2 -2
  61. package/dist/workflows-list.js +11 -11
  62. package/package.json +2 -2
  63. package/dist/WorkflowBuilder-R7VIOVRO.js +0 -35
  64. package/dist/chunk-VKC3ICRA.js +0 -529
@@ -1,37 +1,39 @@
1
- import { NDV_Z_INDEX, NodeExecutionActions, ReadOnlyFieldset, NodeParamsForm } from './chunk-MESGVHUU.js';
2
- import { useNodeConfig, latestNodeLog, nodeTitle, nodeSubtitle, FieldTree } from './chunk-MYAFMXFI.js';
1
+ import { NDV_Z_INDEX, NodeExecutionActions, ReadOnlyFieldset, NodeParamsForm } from './chunk-7SSMRID2.js';
2
+ import { useNodeConfig, latestNodeLog, nodeTitle, nodeSubtitle, FieldTree } from './chunk-IZOPKR64.js';
3
3
  import { CodeEditorField } from './chunk-KZCNJAMK.js';
4
4
  import { RunOnSection } from './chunk-U7UNWR3D.js';
5
5
  import './chunk-GTEBQQ7J.js';
6
- import { useRunHistory, RunHistoryView, SignalRunDialog } from './chunk-ZTZNQ5GF.js';
6
+ import { useRunHistory, RunHistoryView, SignalRunDialog } from './chunk-MYPB2KTJ.js';
7
7
  import { agentTracePath } from './chunk-NZAYYUQ4.js';
8
- import './chunk-5KK3ALQ4.js';
8
+ import './chunk-D5Y3DHGJ.js';
9
+ import { useResource, describeSchedule, NativeSelect, StatusPill, FieldError, fetchCodeBundleSdk, triggerSchedule, pauseSchedule, unpauseSchedule, backfillSchedule } from './chunk-YASVILWQ.js';
9
10
  import { isAgentNodeType } from './chunk-WZ3EJVS6.js';
10
- import { TableView, JsonView } from './chunk-JRSCUUBT.js';
11
+ import { TableView, JsonView } from './chunk-IPJV2IOH.js';
11
12
  import './chunk-JDLQ6QIB.js';
12
- import './chunk-H4ERTSMX.js';
13
- import { useDurableFunctionNodesEnabled, useGuardedNavigate, fieldsFromValue, useNodeExecution, getNodeDetail, useCodeWorkflowInterface, clearCodeWorkflowDefinitionCache, declaredSignals } from './chunk-HUCRE45R.js';
14
- import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-5375NUIO.js';
15
- import './chunk-JL7CASN5.js';
13
+ import './chunk-2VSYOGNI.js';
14
+ import { usePhaseCFlags, useDurableFunctionNodesEnabled, useGuardedNavigate, fieldsFromValue, useNodeExecution, getNodeDetail, useCodeWorkflowInterface, clearCodeWorkflowDefinitionCache, declaredSignals } from './chunk-GI6BQIQA.js';
15
+ import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-AR22A74Y.js';
16
16
  import './chunk-SDZEAIIE.js';
17
- import { useFlowStore, CODE_WORKFLOW_NODE_TYPE, readCodeWorkflowState, diagnosticsByPath, interfaceHandlers, childRunIdOf, defaultSourcePath, projectLanguage, changedSourceFiles, isEditableSourcePath, sourceLanguageOf, interfaceFields } from './chunk-UZJKX5FN.js';
17
+ import { useFlowStore, CODE_WORKFLOW_NODE_TYPE, readCodeWorkflowState, diagnosticsByPath, interfaceHandlers, childRunIdOf, defaultSourcePath, projectLanguage, changedSourceFiles, isEditableSourcePath, sourceLanguageOf, interfaceFields } from './chunk-X3F5IQBP.js';
18
18
  import './chunk-Y7FKJJMF.js';
19
19
  import './chunk-TYI2XOSI.js';
20
20
  import './chunk-G77IGC3Y.js';
21
- import { useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, NodeStatesDocument, FileDownloadUrlDocument, WorkflowCodeSourceDocument, WorkflowEditLeaseStatusDocument, BuildWorkflowCodeDocument } from './chunk-Z3ABLERJ.js';
21
+ import { useWorkflowPinnedDataQuery, usePinNodeDataMutation, useUnpinNodeDataMutation, NodeStatesDocument, FileDownloadUrlDocument, WorkflowCodeSourceDocument, WorkflowEditLeaseStatusDocument, BuildWorkflowCodeDocument } from './chunk-GOZR72NK.js';
22
+ import './chunk-JL7CASN5.js';
22
23
  import { useTr } from './chunk-UULVIT25.js';
23
24
  import { getFluidGridsClientSafe, getFluidGridsClient } from './chunk-PDODDDHY.js';
24
- import { throwIfResultError, describeOperationError } from './chunk-JKLZ6YZ3.js';
25
+ import { describeOperationError, throwIfResultError } from './chunk-JKLZ6YZ3.js';
25
26
  import './chunk-UEG2WC3G.js';
26
27
  import * as React from 'react';
27
28
  import { useState, useMemo, useRef, useEffect, useCallback } from 'react';
28
29
  import { Icon } from '@iconify/react';
29
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Skeleton } from '@burdenoff/fe-libs/ui';
30
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Label, Input, Switch, Skeleton } from '@burdenoff/fe-libs/ui';
30
31
  import { cn } from '@burdenoff/fe-libs/utils';
31
32
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
32
33
  import { FileCode, FileSpreadsheet, FileText, FileImage, File, Download } from 'lucide-react';
33
- import { useNavigate } from 'react-router';
34
34
  import { Button } from '@burdenoff/fe-libs/button';
35
+ import { toast } from 'sonner';
36
+ import { useNavigate } from 'react-router';
35
37
 
36
38
  var SECTION_LABEL = "text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground";
37
39
  var inputCx = "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm outline-none transition-colors focus:border-primary disabled:opacity-50";
@@ -880,6 +882,407 @@ function useMediaQuery(query) {
880
882
  }, [query]);
881
883
  return matches;
882
884
  }
885
+ var SCHEDULE_NODE_TYPE = "internal:schedule";
886
+ var OVERLAP_POLICIES = [
887
+ "SKIP" /* Skip */,
888
+ "BUFFER_ONE" /* BufferOne */,
889
+ "BUFFER_ALL" /* BufferAll */,
890
+ "CANCEL_OTHER" /* CancelOther */,
891
+ "TERMINATE_OTHER" /* TerminateOther */,
892
+ "ALLOW_ALL" /* AllowAll */
893
+ ];
894
+ var MAX_BACKFILL = 1e3;
895
+ function overlapPolicyOf(value) {
896
+ return OVERLAP_POLICIES.find((policy) => policy === value) ?? null;
897
+ }
898
+ function secondsOf(value) {
899
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0)
900
+ return value;
901
+ if (typeof value !== "string") return null;
902
+ const match = /^(\d+)\s*(s|m|h|d)?$/.exec(value.trim());
903
+ if (!match) return null;
904
+ const unit = { s: 1, m: 60, h: 3600, d: 86400 }[match[2] ?? "s"] ?? 1;
905
+ return Number(match[1]) * unit;
906
+ }
907
+ function SchedulePanel({
908
+ state,
909
+ writeField,
910
+ workflowKey,
911
+ triggerNodeId,
912
+ canExecute,
913
+ canUpdate
914
+ }) {
915
+ const tr = useTr();
916
+ const policy = overlapPolicyOf(state.overlapPolicy);
917
+ const catchup = secondsOf(state.catchupWindow);
918
+ const limited = typeof state.limitedActions === "number" ? state.limitedActions : null;
919
+ const describe = useResource(
920
+ workflowKey ? `schedule:${workflowKey}:${triggerNodeId}` : null,
921
+ !!workflowKey,
922
+ () => describeSchedule(workflowKey ?? "", triggerNodeId)
923
+ );
924
+ const [note, setNote] = useState("");
925
+ const [from, setFrom] = useState("");
926
+ const [to, setTo] = useState("");
927
+ const [busy, setBusy] = useState(null);
928
+ const [error, setError] = useState(null);
929
+ const act = async (name, run) => {
930
+ setBusy(name);
931
+ setError(null);
932
+ try {
933
+ await run();
934
+ } catch (err) {
935
+ setError(describeOperationError(err));
936
+ } finally {
937
+ setBusy(null);
938
+ }
939
+ };
940
+ const live = describe.data;
941
+ const backfillFrom = from ? new Date(from) : null;
942
+ const backfillTo = to ? new Date(to) : null;
943
+ const backfillValid = !!backfillFrom && !!backfillTo && !Number.isNaN(backfillFrom.getTime()) && !Number.isNaN(backfillTo.getTime()) && backfillFrom < backfillTo && backfillTo.getTime() <= Date.now();
944
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5 p-4", "data-testid": "schedule-panel", children: [
945
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
946
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-foreground", children: tr("fluidgrids.builder.schedule.policy", "Policy") }),
947
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
948
+ /* @__PURE__ */ jsx(Label, { htmlFor: "schedule-overlap", children: tr(
949
+ "fluidgrids.builder.schedule.overlap",
950
+ "When the previous run is still open"
951
+ ) }),
952
+ /* @__PURE__ */ jsxs(
953
+ NativeSelect,
954
+ {
955
+ id: "schedule-overlap",
956
+ value: policy ?? "",
957
+ disabled: !canUpdate,
958
+ onChange: (value) => writeField("overlapPolicy", overlapPolicyOf(value) ?? void 0),
959
+ testId: "schedule-overlap",
960
+ children: [
961
+ /* @__PURE__ */ jsx("option", { value: "", children: tr(
962
+ "fluidgrids.builder.schedule.overlapLegacy",
963
+ "Start every run (no policy)"
964
+ ) }),
965
+ /* @__PURE__ */ jsx("option", { value: "SKIP" /* Skip */, children: tr("fluidgrids.builder.schedule.skip", "Skip the fire") }),
966
+ /* @__PURE__ */ jsx("option", { value: "BUFFER_ONE" /* BufferOne */, children: tr(
967
+ "fluidgrids.builder.schedule.bufferOne",
968
+ "Run once after it ends"
969
+ ) }),
970
+ /* @__PURE__ */ jsx("option", { value: "BUFFER_ALL" /* BufferAll */, children: tr(
971
+ "fluidgrids.builder.schedule.bufferAll",
972
+ "Run every missed fire after it ends"
973
+ ) }),
974
+ /* @__PURE__ */ jsx("option", { value: "CANCEL_OTHER" /* CancelOther */, children: tr(
975
+ "fluidgrids.builder.schedule.cancelOther",
976
+ "Cancel it and start"
977
+ ) }),
978
+ /* @__PURE__ */ jsx("option", { value: "TERMINATE_OTHER" /* TerminateOther */, children: tr(
979
+ "fluidgrids.builder.schedule.terminateOther",
980
+ "Terminate it and start"
981
+ ) }),
982
+ /* @__PURE__ */ jsx("option", { value: "ALLOW_ALL" /* AllowAll */, children: tr("fluidgrids.builder.schedule.allowAll", "Start anyway") })
983
+ ]
984
+ }
985
+ ),
986
+ !policy && canUpdate && /* @__PURE__ */ jsx(
987
+ Button,
988
+ {
989
+ size: "sm",
990
+ variant: "link",
991
+ className: "h-auto p-0 text-xs",
992
+ onClick: () => writeField("overlapPolicy", "SKIP" /* Skip */),
993
+ "data-testid": "schedule-use-policy",
994
+ children: tr(
995
+ "fluidgrids.builder.schedule.usePolicy",
996
+ "Use a policy (skip while a run is open)"
997
+ )
998
+ }
999
+ )
1000
+ ] }),
1001
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
1002
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1003
+ /* @__PURE__ */ jsx(Label, { htmlFor: "schedule-catchup", children: tr(
1004
+ "fluidgrids.builder.schedule.catchup",
1005
+ "Catch-up window (seconds)"
1006
+ ) }),
1007
+ /* @__PURE__ */ jsx(
1008
+ Input,
1009
+ {
1010
+ id: "schedule-catchup",
1011
+ inputMode: "numeric",
1012
+ disabled: !canUpdate,
1013
+ value: catchup === null ? "" : String(catchup),
1014
+ onChange: (event) => {
1015
+ const value = event.target.value.trim();
1016
+ writeField(
1017
+ "catchupWindow",
1018
+ value === "" ? void 0 : /^\d+$/.test(value) ? Math.min(Number(value), 604800) : state.catchupWindow
1019
+ );
1020
+ },
1021
+ "data-testid": "schedule-catchup"
1022
+ }
1023
+ )
1024
+ ] }),
1025
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1026
+ /* @__PURE__ */ jsx(Label, { htmlFor: "schedule-limited", children: tr(
1027
+ "fluidgrids.builder.schedule.limited",
1028
+ "Stop after this many runs (optional)"
1029
+ ) }),
1030
+ /* @__PURE__ */ jsx(
1031
+ Input,
1032
+ {
1033
+ id: "schedule-limited",
1034
+ inputMode: "numeric",
1035
+ disabled: !canUpdate,
1036
+ value: limited === null ? "" : String(limited),
1037
+ onChange: (event) => {
1038
+ const value = event.target.value.trim();
1039
+ writeField(
1040
+ "limitedActions",
1041
+ value === "" ? void 0 : /^\d+$/.test(value) ? Number(value) : state.limitedActions
1042
+ );
1043
+ },
1044
+ "data-testid": "schedule-limited"
1045
+ }
1046
+ )
1047
+ ] })
1048
+ ] }),
1049
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-3 text-sm text-foreground", children: [
1050
+ tr(
1051
+ "fluidgrids.builder.schedule.pauseOnFailure",
1052
+ "Pause the schedule when a run it started fails"
1053
+ ),
1054
+ /* @__PURE__ */ jsx(
1055
+ Switch,
1056
+ {
1057
+ checked: state.pauseOnFailure === true,
1058
+ disabled: !canUpdate,
1059
+ onCheckedChange: (checked) => writeField("pauseOnFailure", checked ? true : void 0),
1060
+ "data-testid": "schedule-pause-on-failure"
1061
+ }
1062
+ )
1063
+ ] }),
1064
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
1065
+ "fluidgrids.builder.schedule.saveHint",
1066
+ "Policy changes apply when the workflow is saved and activated."
1067
+ ) })
1068
+ ] }),
1069
+ !workflowKey ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: tr(
1070
+ "fluidgrids.builder.schedule.unsaved",
1071
+ "Save the workflow to see and control its schedule."
1072
+ ) }) : /* @__PURE__ */ jsxs("section", { className: "space-y-3", "data-testid": "schedule-live", children: [
1073
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
1074
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-foreground", children: tr("fluidgrids.builder.schedule.live", "Schedule") }),
1075
+ live && /* @__PURE__ */ jsx(
1076
+ StatusPill,
1077
+ {
1078
+ tone: live.state.paused ? "warning" : "success",
1079
+ testId: "schedule-state",
1080
+ children: live.state.paused ? tr("fluidgrids.builder.schedule.paused", "Paused") : tr("fluidgrids.builder.schedule.active", "Active")
1081
+ }
1082
+ ),
1083
+ /* @__PURE__ */ jsx(
1084
+ Button,
1085
+ {
1086
+ size: "sm",
1087
+ variant: "ghost",
1088
+ className: "ml-auto",
1089
+ onClick: () => void describe.refetch(),
1090
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:refresh", width: 14 })
1091
+ }
1092
+ )
1093
+ ] }),
1094
+ /* @__PURE__ */ jsx(
1095
+ FieldError,
1096
+ {
1097
+ message: describe.error,
1098
+ testId: "schedule-describe-error"
1099
+ }
1100
+ ),
1101
+ live?.state.paused && live.state.note && /* @__PURE__ */ jsx("p", { className: "text-sm text-foreground", "data-testid": "schedule-note", children: live.state.note }),
1102
+ live && /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
1103
+ /* @__PURE__ */ jsxs("div", { children: [
1104
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: tr("fluidgrids.builder.schedule.next", "Next fire times") }),
1105
+ /* @__PURE__ */ jsxs(
1106
+ "ul",
1107
+ {
1108
+ className: "space-y-0.5 font-mono text-xs text-foreground",
1109
+ "data-testid": "schedule-next",
1110
+ children: [
1111
+ live.nextFireTimes.slice(0, 5).map((time) => /* @__PURE__ */ jsx("li", { children: new Date(time).toLocaleString() }, time)),
1112
+ live.nextFireTimes.length === 0 && /* @__PURE__ */ jsx("li", { children: "\u2014" })
1113
+ ]
1114
+ }
1115
+ )
1116
+ ] }),
1117
+ /* @__PURE__ */ jsxs("div", { children: [
1118
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: tr("fluidgrids.builder.schedule.recent", "Recent actions") }),
1119
+ /* @__PURE__ */ jsxs(
1120
+ "ul",
1121
+ {
1122
+ className: "space-y-0.5 text-xs text-foreground",
1123
+ "data-testid": "schedule-recent",
1124
+ children: [
1125
+ live.recentActions.slice(0, 5).map((tick) => /* @__PURE__ */ jsxs("li", { children: [
1126
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: new Date(tick.dueAt).toLocaleString() }),
1127
+ " ",
1128
+ "\xB7 ",
1129
+ tick.kind,
1130
+ " \xB7 ",
1131
+ tick.outcome
1132
+ ] }, tick.id)),
1133
+ live.recentActions.length === 0 && /* @__PURE__ */ jsx("li", { children: "\u2014" })
1134
+ ]
1135
+ }
1136
+ )
1137
+ ] })
1138
+ ] }),
1139
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
1140
+ canExecute && /* @__PURE__ */ jsxs(
1141
+ Button,
1142
+ {
1143
+ size: "sm",
1144
+ variant: "outline",
1145
+ disabled: busy !== null,
1146
+ onClick: () => void act("trigger", async () => {
1147
+ const result = await triggerSchedule({
1148
+ workflowKey,
1149
+ triggerNodeId
1150
+ });
1151
+ toast.success(
1152
+ result.runId ? tr(
1153
+ "fluidgrids.builder.schedule.triggered",
1154
+ "Run started"
1155
+ ) : tr(
1156
+ "fluidgrids.builder.schedule.triggeredOutcome",
1157
+ "Tick recorded: {{outcome}}",
1158
+ { outcome: result.tick.outcome }
1159
+ )
1160
+ );
1161
+ await describe.refetch();
1162
+ }),
1163
+ "data-testid": "schedule-trigger",
1164
+ children: [
1165
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:play", className: "mr-1", width: 14 }),
1166
+ tr("fluidgrids.builder.schedule.trigger", "Trigger now")
1167
+ ]
1168
+ }
1169
+ ),
1170
+ canUpdate && live && !live.state.paused && /* @__PURE__ */ jsxs(
1171
+ Button,
1172
+ {
1173
+ size: "sm",
1174
+ variant: "outline",
1175
+ disabled: busy !== null,
1176
+ onClick: () => void act("pause", async () => {
1177
+ describe.set(
1178
+ await pauseSchedule({ workflowKey, triggerNodeId, note })
1179
+ );
1180
+ setNote("");
1181
+ }),
1182
+ "data-testid": "schedule-pause",
1183
+ children: [
1184
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:pause", className: "mr-1", width: 14 }),
1185
+ tr("fluidgrids.builder.schedule.pause", "Pause")
1186
+ ]
1187
+ }
1188
+ ),
1189
+ canUpdate && live?.state.paused && /* @__PURE__ */ jsxs(
1190
+ Button,
1191
+ {
1192
+ size: "sm",
1193
+ variant: "outline",
1194
+ disabled: busy !== null,
1195
+ onClick: () => void act(
1196
+ "unpause",
1197
+ async () => describe.set(
1198
+ await unpauseSchedule({ workflowKey, triggerNodeId })
1199
+ )
1200
+ ),
1201
+ "data-testid": "schedule-unpause",
1202
+ children: [
1203
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:play-pause", className: "mr-1", width: 14 }),
1204
+ tr("fluidgrids.builder.schedule.unpause", "Unpause")
1205
+ ]
1206
+ }
1207
+ )
1208
+ ] }),
1209
+ canUpdate && live && !live.state.paused && /* @__PURE__ */ jsx(
1210
+ Input,
1211
+ {
1212
+ value: note,
1213
+ maxLength: 1e3,
1214
+ placeholder: tr(
1215
+ "fluidgrids.builder.schedule.notePlaceholder",
1216
+ "Note for the pause (optional)"
1217
+ ),
1218
+ onChange: (event) => setNote(event.target.value),
1219
+ "data-testid": "schedule-pause-note"
1220
+ }
1221
+ ),
1222
+ canExecute && /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-border p-3", children: [
1223
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: tr("fluidgrids.builder.schedule.backfill", "Backfill") }),
1224
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [
1225
+ /* @__PURE__ */ jsx(
1226
+ Input,
1227
+ {
1228
+ type: "datetime-local",
1229
+ value: from,
1230
+ onChange: (event) => setFrom(event.target.value),
1231
+ "aria-label": tr("fluidgrids.builder.schedule.from", "From"),
1232
+ "data-testid": "schedule-backfill-from"
1233
+ }
1234
+ ),
1235
+ /* @__PURE__ */ jsx(
1236
+ Input,
1237
+ {
1238
+ type: "datetime-local",
1239
+ value: to,
1240
+ onChange: (event) => setTo(event.target.value),
1241
+ "aria-label": tr("fluidgrids.builder.schedule.to", "To"),
1242
+ "data-testid": "schedule-backfill-to"
1243
+ }
1244
+ )
1245
+ ] }),
1246
+ /* @__PURE__ */ jsx(
1247
+ Button,
1248
+ {
1249
+ size: "sm",
1250
+ variant: "outline",
1251
+ disabled: !backfillValid || busy !== null,
1252
+ onClick: () => void act("backfill", async () => {
1253
+ if (!backfillFrom || !backfillTo) return;
1254
+ const result = await backfillSchedule({
1255
+ workflowKey,
1256
+ triggerNodeId,
1257
+ from: backfillFrom.toISOString(),
1258
+ to: backfillTo.toISOString()
1259
+ });
1260
+ toast.success(
1261
+ tr(
1262
+ "fluidgrids.builder.schedule.backfilled",
1263
+ "{{queued}} of {{fireTimes}} fire times queued",
1264
+ { queued: result.queued, fireTimes: result.fireTimes }
1265
+ )
1266
+ );
1267
+ await describe.refetch();
1268
+ }),
1269
+ "data-testid": "schedule-backfill",
1270
+ children: tr(
1271
+ "fluidgrids.builder.schedule.runBackfill",
1272
+ "Run the fire times in this range"
1273
+ )
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: tr(
1277
+ "fluidgrids.builder.schedule.backfillHint",
1278
+ "At most {{max}} past fire times; each started run counts toward the monthly runs.",
1279
+ { max: MAX_BACKFILL }
1280
+ ) })
1281
+ ] }),
1282
+ /* @__PURE__ */ jsx(FieldError, { message: error, testId: "schedule-error" })
1283
+ ] })
1284
+ ] });
1285
+ }
883
1286
  function entryOf(definitionJson) {
884
1287
  if (typeof definitionJson !== "object" || definitionJson === null || Array.isArray(definitionJson))
885
1288
  return void 0;
@@ -937,7 +1340,8 @@ function useWorkflowCodeEditor({
937
1340
  version,
938
1341
  enabled,
939
1342
  canEdit,
940
- onSaved
1343
+ onSaved,
1344
+ pythonEnabled = false
941
1345
  }) {
942
1346
  const [loading, setLoading] = useState(false);
943
1347
  const [loadError, setLoadError] = useState(null);
@@ -970,7 +1374,9 @@ function useWorkflowCodeEditor({
970
1374
  )
971
1375
  );
972
1376
  setSelectedPath(
973
- loaded ? defaultSourcePath(loaded.files, loaded.entry) : void 0
1377
+ loaded ? defaultSourcePath(loaded.files, loaded.entry, {
1378
+ python: pythonEnabled && projectLanguage(loaded.files) === "python"
1379
+ }) : void 0
974
1380
  );
975
1381
  setLastBuild(null);
976
1382
  setBuildError(null);
@@ -980,15 +1386,28 @@ function useWorkflowCodeEditor({
980
1386
  } finally {
981
1387
  if (id === request.current) setLoading(false);
982
1388
  }
983
- }, [enabled, workflowKey, version]);
1389
+ }, [enabled, workflowKey, version, pythonEnabled]);
984
1390
  useEffect(() => {
985
1391
  void reload();
986
1392
  }, [reload]);
1393
+ const [baseSdk, setBaseSdk] = useState(null);
1394
+ useEffect(() => {
1395
+ setBaseSdk(null);
1396
+ if (!pythonEnabled || !enabled || !workflowKey || !version) return;
1397
+ let cancelled = false;
1398
+ fetchCodeBundleSdk(workflowKey, version).then((bundle) => {
1399
+ if (!cancelled) setBaseSdk(bundle?.sdkVersion ?? null);
1400
+ }).catch(() => void 0);
1401
+ return () => {
1402
+ cancelled = true;
1403
+ };
1404
+ }, [pythonEnabled, enabled, workflowKey, version]);
987
1405
  const language = useMemo(
988
1406
  () => projectLanguage(source?.files ?? []),
989
1407
  [source]
990
1408
  );
991
- const readOnlyReason = language === "python" ? "python" : !canEdit ? "permission" : leaseBlocksEditing(lease) ? "lease" : null;
1409
+ const pythonEditable = pythonEnabled && language === "python";
1410
+ const readOnlyReason = language === "python" && !pythonEnabled ? "python" : !canEdit ? "permission" : leaseBlocksEditing(lease) ? "lease" : null;
992
1411
  const changed = useMemo(
993
1412
  () => changedSourceFiles(source?.files ?? [], contents),
994
1413
  [source, contents]
@@ -1054,7 +1473,8 @@ function useWorkflowCodeEditor({
1054
1473
  language,
1055
1474
  lease,
1056
1475
  readOnlyReason,
1057
- isEditable: (path) => !readOnlyReason && isEditableSourcePath(path),
1476
+ rebuildSdk: baseSdk && lastBuild?.sdkVersion && baseSdk !== lastBuild.sdkVersion ? { from: baseSdk, to: lastBuild.sdkVersion } : null,
1477
+ isEditable: (path) => !readOnlyReason && isEditableSourcePath(path, { python: pythonEditable }),
1058
1478
  building,
1059
1479
  buildError,
1060
1480
  lastBuild,
@@ -1145,11 +1565,13 @@ function CodeWorkflowSourceEditor({
1145
1565
  const tr = useTr();
1146
1566
  const navigate = useNavigate();
1147
1567
  const [description, setDescription] = useState("");
1568
+ const { pythonDurableFunctions } = usePhaseCFlags();
1148
1569
  const editor = useWorkflowCodeEditor({
1149
1570
  workflowKey,
1150
1571
  version,
1151
1572
  enabled,
1152
1573
  canEdit,
1574
+ pythonEnabled: pythonDurableFunctions,
1153
1575
  onSaved: (savedVersion) => {
1154
1576
  onSaved?.(savedVersion);
1155
1577
  navigate(
@@ -1230,6 +1652,18 @@ function CodeWorkflowSourceEditor({
1230
1652
  children: readOnlyNote
1231
1653
  }
1232
1654
  ),
1655
+ editor.rebuildSdk && /* @__PURE__ */ jsx(
1656
+ "p",
1657
+ {
1658
+ className: "rounded-md border border-border bg-muted px-3 py-2 text-xs text-muted-foreground",
1659
+ "data-testid": "code-workflow-rebuild-sdk",
1660
+ children: tr(
1661
+ "fluidgrids.builder.codeWorkflow.source.rebuildSdk",
1662
+ "Saving will rebuild with SDK {{to}}; this version was built with {{from}}.",
1663
+ { to: editor.rebuildSdk.to, from: editor.rebuildSdk.from }
1664
+ )
1665
+ }
1666
+ ),
1233
1667
  /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 gap-3", children: [
1234
1668
  /* @__PURE__ */ jsx(
1235
1669
  "nav",
@@ -1685,8 +2119,14 @@ function NodeDetailView() {
1685
2119
  inputData,
1686
2120
  outputFields
1687
2121
  } = config;
1688
- const [tab, setTab] = useState(
1689
- "parameters"
2122
+ const [tab, setTab] = useState("parameters");
2123
+ const { schedulePolicies } = usePhaseCFlags();
2124
+ const isSchedule = schedulePolicies && node?.type === SCHEDULE_NODE_TYPE;
2125
+ const { allowed: canExecuteRuns } = useFluidGridsPermission(
2126
+ FLUIDGRIDS_PERMISSIONS.WORKFLOW_RUN_EXECUTE
2127
+ );
2128
+ const { allowed: canUpdateWorkflow } = useFluidGridsPermission(
2129
+ FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE
1690
2130
  );
1691
2131
  const durableEnabled = useDurableFunctionNodesEnabled();
1692
2132
  const isCodeWorkflow = durableEnabled && node?.type === CODE_WORKFLOW_NODE_TYPE;
@@ -2066,6 +2506,17 @@ function NodeDetailView() {
2066
2506
  }
2067
2507
  )
2068
2508
  ] }),
2509
+ isSchedule && /* @__PURE__ */ jsx(
2510
+ TabButton,
2511
+ {
2512
+ active: tab === "schedule",
2513
+ onClick: () => setTab("schedule"),
2514
+ children: tr(
2515
+ "fluidgrids.builder.nodeDetailView.tabSchedule",
2516
+ "Schedule"
2517
+ )
2518
+ }
2519
+ ),
2069
2520
  /* @__PURE__ */ jsx(
2070
2521
  TabButton,
2071
2522
  {
@@ -2093,7 +2544,24 @@ function NodeDetailView() {
2093
2544
  }
2094
2545
  )
2095
2546
  }
2096
- ) : tab === "parameters" ? /* @__PURE__ */ jsx(
2547
+ ) : isSchedule && ndv && tab === "schedule" ? /* @__PURE__ */ jsx(
2548
+ "div",
2549
+ {
2550
+ className: "flex min-h-0 flex-1 flex-col overflow-auto",
2551
+ "data-testid": "ndv-schedule",
2552
+ children: /* @__PURE__ */ jsx(
2553
+ SchedulePanel,
2554
+ {
2555
+ state: config.argsState,
2556
+ writeField: config.writeFixed,
2557
+ workflowKey: workflowDbId && workflowKey ? workflowKey : null,
2558
+ triggerNodeId: ndv.nodeId,
2559
+ canExecute: canExecuteRuns,
2560
+ canUpdate: canUpdateWorkflow
2561
+ }
2562
+ )
2563
+ }
2564
+ ) : tab === "parameters" || tab === "schedule" ? /* @__PURE__ */ jsx(
2097
2565
  "div",
2098
2566
  {
2099
2567
  className: "min-h-0 flex-1 space-y-4 overflow-auto p-4",
package/dist/nodes.js CHANGED
@@ -1,5 +1,8 @@
1
- export { NodesPage } from './chunk-CTKWMPQQ.js';
2
- import './chunk-Z3ABLERJ.js';
1
+ export { NodesPage } from './chunk-HSOSMPRC.js';
2
+ import './chunk-XRGAH6IQ.js';
3
+ import './chunk-QJAZXCFQ.js';
4
+ import './chunk-GOZR72NK.js';
5
+ import './chunk-JL7CASN5.js';
3
6
  import './chunk-UULVIT25.js';
4
7
  import './chunk-PDODDDHY.js';
5
8
  import './chunk-JKLZ6YZ3.js';
@@ -1,19 +1,19 @@
1
- export { DataSinkDashboardPage, ErrorAnalyticsPage, InsightsPage } from './chunk-NSDBXKIC.js';
2
- import './chunk-NZMUKAXG.js';
1
+ export { DataSinkDashboardPage, ErrorAnalyticsPage, InsightsPage } from './chunk-DG4UO5OY.js';
2
+ import './chunk-54NXUOHI.js';
3
3
  import './chunk-NZAYYUQ4.js';
4
- import './chunk-BLLBUHVF.js';
4
+ import './chunk-POYMF7L6.js';
5
5
  import './chunk-5ERAJC2I.js';
6
6
  import './chunk-GLYINMOR.js';
7
- import './chunk-H4ERTSMX.js';
8
- import './chunk-HUCRE45R.js';
9
- import './chunk-5375NUIO.js';
10
- import './chunk-JL7CASN5.js';
7
+ import './chunk-2VSYOGNI.js';
8
+ import './chunk-GI6BQIQA.js';
9
+ import './chunk-AR22A74Y.js';
11
10
  import './chunk-SDZEAIIE.js';
12
- import './chunk-UZJKX5FN.js';
11
+ import './chunk-X3F5IQBP.js';
13
12
  import './chunk-Y7FKJJMF.js';
14
13
  import './chunk-TYI2XOSI.js';
15
14
  import './chunk-G77IGC3Y.js';
16
- import './chunk-Z3ABLERJ.js';
15
+ import './chunk-GOZR72NK.js';
16
+ import './chunk-JL7CASN5.js';
17
17
  import './chunk-UULVIT25.js';
18
18
  import './chunk-PDODDDHY.js';
19
19
  import './chunk-JKLZ6YZ3.js';