@oomol-lab/open-flow 0.1.0-beta.76 → 0.1.0-beta.77

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 (71) hide show
  1. package/README.md +1 -0
  2. package/dist/browser/{ContentIcon-Cm1hbVzW.js → ContentIcon-VZQO8VZU.js} +8 -5
  3. package/dist/browser/{FlowCanvas-NzKtla_c.js → FlowCanvas-BNGDMaWr.js} +15 -3
  4. package/dist/browser/{createResourceDialog-BgdedQqu.js → createResourceDialog-BmtakfVg.js} +63 -57
  5. package/dist/browser/{dialog-aSElFFw2.js → dialog-BcFBfRB1.js} +84 -15
  6. package/dist/browser/{exportSvg-Dcz4HTo_.js → exportSvg-CG8hEUNk.js} +2 -2
  7. package/dist/browser/{flowChanges-NZyS7HSp.js → flowChanges-BGo_4Ypo.js} +5849 -10882
  8. package/dist/browser/{flowWorkspace-Bl0SDehy.js → flowWorkspace-BFHGev7s.js} +1025 -2607
  9. package/dist/browser/preview.css +112 -5
  10. package/dist/browser/preview.js +1630 -9295
  11. package/dist/browser/theme.css +4 -2
  12. package/dist/browser/{tooltip-3XMK9jkr.js → tooltip-BAWSUexx.js} +1 -1
  13. package/dist/browser/{typeScriptSession-C9sBGOKZ.js → typeScriptSession-CRoezJPN.js} +1 -1
  14. package/dist/browser/ui.css +111 -4
  15. package/dist/browser/ui.js +3 -2
  16. package/dist/browser/workbench.css +9906 -9778
  17. package/dist/browser/workbench.d.ts +6 -2
  18. package/dist/browser/workbench.js +838 -499
  19. package/dist/browser/{workbenchSelect-DBGP7Y4Z.js → workbenchSelect-CjK-xdm3.js} +2 -2
  20. package/dist/command/index.js +48 -48
  21. package/dist/common/authoring.d.ts +4 -4
  22. package/dist/common/authoringExamples.d.ts +3 -2
  23. package/dist/common/authoringSchema.d.ts +23 -4
  24. package/dist/common/control-api-conformance.js +162 -15
  25. package/dist/common/control-api-errors.d.ts +12 -0
  26. package/dist/common/control-api.d.ts +35 -2
  27. package/dist/common/control-api.js +187 -37
  28. package/dist/common/control-requests.d.ts +22 -3
  29. package/dist/common/control-requests.js +152 -14
  30. package/dist/common/engine-contract.d.ts +3 -2
  31. package/dist/common/flow-authoring.js +55 -7
  32. package/dist/common/flow-change-schema.d.ts +1 -1
  33. package/dist/common/flow-change.d.ts +2 -2
  34. package/dist/common/flow-change.js +56 -8
  35. package/dist/common/flow-encoding.js +55 -7
  36. package/dist/common/flow-semantics.js +91 -7
  37. package/dist/common/flow-state.d.ts +123 -0
  38. package/dist/common/flow-state.js +5852 -0
  39. package/dist/common/mcp.d.ts +96 -3
  40. package/dist/common/mcp.js +165 -13
  41. package/dist/common/run-events.js +1 -0
  42. package/dist/common/runtime-contract.d.ts +14 -0
  43. package/dist/common/runtime-contract.js +118 -5
  44. package/dist/common/scheduler-node-execution.d.ts +1 -0
  45. package/dist/common/scheduler.d.ts +1 -1
  46. package/dist/common/scheduler.js +52 -3
  47. package/dist/common/state-conformance.d.ts +16 -0
  48. package/dist/common/tags.d.ts +4 -0
  49. package/dist/index.d.ts +11 -0
  50. package/package.json +5 -1
  51. package/src/control/common/api.ts +84 -11
  52. package/src/control/common/authoring.ts +6 -1
  53. package/src/control/common/authoringExamples.ts +8 -0
  54. package/src/control/common/authoringSchema.ts +18 -12
  55. package/src/control/common/errors.ts +6 -0
  56. package/src/control/common/flowDecoders.ts +1 -0
  57. package/src/control/common/mcp.ts +16 -0
  58. package/src/control/common/requests.ts +10 -2
  59. package/src/control/common/tags.ts +20 -0
  60. package/src/execution/common/events.ts +3 -0
  61. package/src/execution/common/node-execution.ts +3 -0
  62. package/src/execution/common/scheduler.ts +2 -0
  63. package/src/flow/common/change.ts +2 -1
  64. package/src/flow/common/changeSchema.ts +11 -1
  65. package/src/flow/common/errorHandling.ts +1 -1
  66. package/src/flow/common/semantics.ts +20 -0
  67. package/src/runtime/common/engineContract.ts +6 -3
  68. package/src/runtime/common/runtime.ts +116 -0
  69. package/src/state/common/conformance.ts +86 -0
  70. package/src/state/common/state.ts +169 -0
  71. package/src/types/index.ts +11 -0
@@ -1,10 +1,11 @@
1
- import { C as DropdownMenuContent, N as DropdownMenuTrigger, T as DropdownMenuItem, d as PopoverContent, g as PopoverTrigger, h as PopoverTitle, it as providerIconAppearance, k as DropdownMenuSeparator, st as IconThemeContext, u as Popover, x as DropdownMenu } from "./ContentIcon-Cm1hbVzW.js";
1
+ import { C as DropdownMenuContent, N as DropdownMenuTrigger, T as DropdownMenuItem, d as PopoverContent, g as PopoverTrigger, h as PopoverTitle, it as providerIconAppearance, k as DropdownMenuSeparator, st as IconThemeContext, u as Popover, x as DropdownMenu } from "./ContentIcon-VZQO8VZU.js";
2
2
  import { a as derive, i as compute, l as val } from "./dist-bO8nvHFH.js";
3
3
  import { c as useLang, l as useTranslate, o as I18nProvider, u as useVal } from "./dist-BFmpkX4P.js";
4
- import { br as cn, i as TooltipTrigger, n as TooltipContent, t as Tooltip, vr as Button } from "./tooltip-3XMK9jkr.js";
5
- import { $t as Input, C as upgradeManualTrigger, M as mapConditionSources, N as nodeInputMappings, Nt as dequal, S as resourceNameIssue, _ as applyFlowChanges, a as DialogFooter, d as Field, g as FlowChangeError, h as FieldLabel, i as DialogDescription, l as Icon, m as FieldGroup, o as DialogHeader, p as FieldError, r as DialogContent, s as DialogTitle, t as Dialog, u as Spinner, v as isTriggerNode, w as upgradeRevisionContent } from "./dialog-aSElFFw2.js";
6
- import { $ as connectorActionPorts, At as setConnectorConnection, Bn as invalidResponse, Bt as InputGroupAddon, C as updateValue, Ct as setComment, Dt as repairNodeNames, Et as setNodePositions, Ft as dequal$1, Gn as controlErrorCode, Gt as EmptyHeader, Hn as record, Ht as Empty, In as workflowCandidate, It as WorkflowExportMenuItem, J as scopedValue, Jt as Badge, K as combineSources, Kt as EmptyMedia, Lt as IdTooltip, Mt as setServiceConnections, Nt as updateStartInputDefs, Ot as resetInputValues, Pt as updateTriggerSchedule, Q as resourceValue, Qt as createI18n, Rt as followWorkbenchLink, S as updateTriggerConfig, Sn as workflowNameFromFile, St as restoreCanvasPresentation, Tt as setNodeContentHidden, U as addNodeIntent, Un as string, Ut as EmptyContent, Vn as jsonValue, Vt as InputGroupInput, W as deriveAddNodeOptions, Wn as ApiError, Wt as EmptyDescription, Y as CacheResource, Yt as EventSourcesPage, Z as resourceData, _ as updateOutputPorts, _n as connection, _t as connectionCatalog, a as nameCreatedNodes, at as RunStore, b as updateTaskAdditionalInputs, bn as parseWorkflowFile, bt as flowPresentation, c as setInputVariable, ct as revisionView, d as updateCondition, dt as deriveInspectorDiagnostics, et as designerGraph, f as updateHttpParameters, ft as diagnosticItems, g as updateNodeSettings, gt as actionWithConnections, h as updateNodeName, hn as triggerOutputDefinitions, i as deleteSelection, in as allConnectorConnectionsQuery, it as providerIcon, jt as setInputSources, kt as setCodeActions, l as setTriggerConnection, lt as flowResourceReferences, m as updateNodeIcon, mn as sampleErrorOutputs, nn as WorkbenchClient, o as selectWebhookResponse, on as decodeTriggerCatalog, p as updateNodeDescription, pn as normalizeTriggerOutputs, qt as EmptyTitle, r as applyFlowChanges$1, rn as randomId, s as setInputValue, t as addNode, tn as errorNotice, u as setTriggerEventSource, ut as Latest, v as updateResolution, vn as connectorActionMetadata, vt as canvasPresentationChange, w as updateWebhook, wt as setFlowViewport, x as updateTaskPorts, xn as workflowFileName, xt as removeComments, y as updateTask, yn as encodeWorkflowFile, yt as commentIds, zt as InputGroup } from "./flowChanges-NZyS7HSp.js";
4
+ import { i as TooltipTrigger, n as TooltipContent, t as Tooltip, xr as cn, yr as Button } from "./tooltip-BAWSUexx.js";
5
+ import { C as upgradeManualTrigger, It as dequal, M as mapConditionSources, N as nodeInputMappings, S as resourceNameIssue, _ as applyFlowChanges, a as DialogFooter, d as Field, g as FlowChangeError, h as FieldLabel, i as DialogDescription, l as Icon, m as FieldGroup, nn as Input, o as DialogHeader, p as FieldError, r as DialogContent, s as DialogTitle, t as Dialog, u as Spinner, v as isTriggerNode, w as upgradeRevisionContent } from "./dialog-BcFBfRB1.js";
6
+ import { $ as connectorActionPorts, At as setConnectorConnection, Bn as triggerOutputDefinitions, Bt as FlowTagChips, C as updateValue, Ct as setComment, Dn as normalizeFlowQuery, Dt as repairNodeNames, En as WorkbenchClient, Et as setNodePositions, Ft as dequal$1, Gn as parseWorkflowFile, Hn as connection, It as WorkflowExportMenuItem, J as scopedValue, K as combineSources, Kn as workflowFileName, Lt as IdTooltip, Mt as setServiceConnections, Nt as updateStartInputDefs, On as randomId, Ot as resetInputValues, Pt as updateTriggerSchedule, Q as resourceValue, Rn as normalizeTriggerOutputs, S as updateTriggerConfig, Sn as createI18n, St as restoreCanvasPresentation, Tn as errorNotice, Tt as setNodeContentHidden, U as addNodeIntent, Un as connectorActionMetadata, Vt as TagPicker, W as deriveAddNodeOptions, Wn as encodeWorkflowFile, Y as CacheResource, Z as resourceData, _ as updateOutputPorts, _n as EmptyTitle, _t as connectionCatalog, a as nameCreatedNodes, at as RunStore, b as updateTaskAdditionalInputs, bt as flowPresentation, c as setInputVariable, cn as followWorkbenchLink, ct as revisionView, d as updateCondition, dn as InputGroupInput, dr as invalidResponse, dt as deriveInspectorDiagnostics, et as designerGraph, f as updateHttpParameters, fn as Empty, fr as jsonValue, ft as diagnosticItems, g as updateNodeSettings, gn as EmptyMedia, gr as controlErrorCode, gt as actionWithConnections, h as updateNodeName, hn as EmptyHeader, hr as ApiError, i as deleteSelection, it as providerIcon, jn as decodeTriggerCatalog, jt as setInputSources, kn as allConnectorConnectionsQuery, kt as setCodeActions, l as setTriggerConnection, ln as InputGroup, lt as flowResourceReferences, m as updateNodeIcon, mn as EmptyDescription, mr as string, o as selectWebhookResponse, p as updateNodeDescription, pn as EmptyContent, pr as record, qn as workflowNameFromFile, r as applyFlowChanges$1, s as setInputValue, sn as useFlowQuery, sr as workflowCandidate, t as addNode, u as setTriggerEventSource, un as InputGroupAddon, ut as Latest, v as updateResolution, vn as Badge, vt as canvasPresentationChange, w as updateWebhook, wt as setFlowViewport, x as updateTaskPorts, xt as removeComments, y as updateTask, yn as EventSourcesPage, yt as commentIds, zn as sampleErrorOutputs, zt as FlowSettingsDialog } from "./flowChanges-BGo_4Ypo.js";
7
7
  import { a as uiLanguages, i as uiLanguageNames, n as isUiLanguage, r as resolveUiLanguage, t as importedWorkflowName } from "./languages-gYzoQqCi.js";
8
+ import { i as objectValue, n as isJsonValue } from "./value-BbFQZZ_V.js";
8
9
  import { Suspense, forwardRef, lazy, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
9
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
11
  import * as Cause from "effect/Cause";
@@ -97,11 +98,11 @@ var NavigationStore = class {
97
98
  this.#syncing = false;
98
99
  this.#write(view, false, { runSource });
99
100
  }
100
- async importFlow(file, name, key, importer) {
101
- return await this.#openNewFlow(() => this.#store.importFlow(file, name, key, importer));
101
+ async importFlow(file, name, key, importer, tagIds) {
102
+ return await this.#openNewFlow(() => this.#store.importFlow(file, name, key, importer, tagIds));
102
103
  }
103
- async createFlow(name, create) {
104
- return await this.#openNewFlow(() => this.#store.createFlow(name, create));
104
+ async createFlow(name, create, tagIds) {
105
+ return await this.#openNewFlow(() => this.#store.createFlow(name, create, tagIds));
105
106
  }
106
107
  async #openNewFlow(create) {
107
108
  const change = ++this.#change;
@@ -516,7 +517,7 @@ function FlowDeletionDialog({ flow, workspace, container, busy, finalFocus, onCl
516
517
  }
517
518
  //#endregion
518
519
  //#region src/workbench/browser/runtime/shell/resourceBrowser.tsx
519
- var CreateResourceDialog = lazy(() => import("./createResourceDialog-BgdedQqu.js"));
520
+ var CreateResourceDialog = lazy(() => import("./createResourceDialog-BmtakfVg.js"));
520
521
  var flowIdTooltipAlignOffset = 44;
521
522
  var renamePopoverAlignOffset = -12;
522
523
  function compactFlowId(flowId) {
@@ -533,7 +534,7 @@ function formatUpdatedAt(updatedAt, locale) {
533
534
  year: date.getFullYear() == current.getFullYear() ? void 0 : "numeric"
534
535
  });
535
536
  }
536
- function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
537
+ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store, onTagFilter, onChanged, settingsFinalFocus }) {
537
538
  const [root, setRoot] = useState(null);
538
539
  const renameAnchor = useRef(null);
539
540
  const actionsButton = useRef(null);
@@ -551,6 +552,7 @@ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
551
552
  try {
552
553
  if (action == "publish") await store.workspace.publishFlow(flow);
553
554
  else await store.workspace.setFlowEnabled(flow, action);
555
+ onChanged();
554
556
  } finally {
555
557
  setPending(void 0);
556
558
  }
@@ -559,10 +561,16 @@ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
559
561
  async function rename(event) {
560
562
  event.preventDefault();
561
563
  const nextName = name.trim();
562
- if (resourceNameIssue(nextName) == null && await store.workspace.renameFlow(flow.flowId, nextName)) setMode("idle");
564
+ if (resourceNameIssue(nextName) == null && await store.workspace.renameFlow(flow, nextName)) {
565
+ setMode("idle");
566
+ onChanged();
567
+ }
563
568
  }
564
569
  async function remove() {
565
- if (await store.workspace.deleteFlow(flow.flowId)) setMode("idle");
570
+ if (await store.workspace.deleteFlow(flow)) {
571
+ setMode("idle");
572
+ onChanged();
573
+ }
566
574
  }
567
575
  return /* @__PURE__ */ jsxs("div", {
568
576
  className: "resource-item-row",
@@ -577,9 +585,9 @@ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
577
585
  "aria-hidden": "true",
578
586
  className: "resource-flow-icon",
579
587
  children: /* @__PURE__ */ jsx("i", { className: "i-lucide-light:workflow" })
580
- }), /* @__PURE__ */ jsx("span", {
588
+ }), /* @__PURE__ */ jsxs("span", {
581
589
  className: "resource-primary-copy",
582
- children: /* @__PURE__ */ jsxs("span", {
590
+ children: [/* @__PURE__ */ jsxs("span", {
583
591
  className: "resource-primary-heading",
584
592
  children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
585
593
  render: /* @__PURE__ */ jsx("a", {
@@ -675,7 +683,12 @@ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
675
683
  })]
676
684
  })
677
685
  })]
678
- })
686
+ }), /* @__PURE__ */ jsx(FlowTagChips, {
687
+ container: root,
688
+ ids: flow.tagIds ?? [],
689
+ store,
690
+ onFilter: onTagFilter
691
+ })]
679
692
  })]
680
693
  }),
681
694
  /* @__PURE__ */ jsx("span", {
@@ -775,6 +788,14 @@ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
775
788
  className: "i-lucide-light:text-cursor-input size-4 shrink-0"
776
789
  }), t("common.rename")]
777
790
  }),
791
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
792
+ disabled: flow.status != "active" || busy != null,
793
+ onClick: () => setMode("settings"),
794
+ children: [/* @__PURE__ */ jsx("i", {
795
+ "aria-hidden": "true",
796
+ className: "i-lucide-light:settings size-4 shrink-0"
797
+ }), t("flowSettings.title")]
798
+ }),
778
799
  /* @__PURE__ */ jsxs(DropdownMenuItem, {
779
800
  disabled: flow.status != "active" || busy != null || pending != null || flow.live != null && !changed,
780
801
  onClick: () => void update("publish"),
@@ -825,6 +846,16 @@ function FlowItem({ busy, flow, href, hrefForFlow, onSelect, store }) {
825
846
  ]
826
847
  })] })]
827
848
  }),
849
+ mode == "settings" && /* @__PURE__ */ jsx(FlowSettingsDialog, {
850
+ store,
851
+ flow,
852
+ container: root,
853
+ hrefForFlow,
854
+ onSelectFlow: onSelect,
855
+ onSaved: onChanged,
856
+ finalFocus: () => actionsButton.current ?? settingsFinalFocus(),
857
+ onClose: () => setMode("idle")
858
+ }),
828
859
  mode == "delete" && /* @__PURE__ */ jsx(FlowDeletionDialog, {
829
860
  flow,
830
861
  workspace: store.workspace,
@@ -867,22 +898,39 @@ function FlowSkeleton() {
867
898
  var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, hrefForFlow, initializing = false, onCreateFlow, onImportFlow, onSelectFlow, store, language }, ref) {
868
899
  const t = useTranslate();
869
900
  const busy = useVal(store.workspace.$.busy);
870
- const loadFailed = useVal(store.workspace.$.flowLoadFailed);
871
- const loadMoreFailed = useVal(store.workspace.$.flowLoadMoreFailed);
872
- const flowLoading = useVal(store.workspace.$.flowLoading);
901
+ const [filter, setFilter] = useState("");
902
+ const [tagIds, setTagIds] = useState([]);
903
+ const [untagged, setUntagged] = useState(false);
904
+ const { catalog, flows, total, failed: loadFailed, loadMoreFailed, loading: flowLoading, loadingMore, nextCursor, refreshing } = useFlowQuery(store, {
905
+ query: filter,
906
+ tagIds,
907
+ untagged
908
+ });
873
909
  const loading = initializing || flowLoading;
874
- const loadingMore = useVal(store.workspace.$.flowLoadingMore);
875
- const nextCursor = useVal(store.workspace.$.flowNextCursor);
876
- const refreshing = useVal(store.workspace.$.flowRefreshing);
877
- const flows = useVal(store.workspace.$.flows);
878
- const total = useVal(store.workspace.$.flowTotal);
910
+ const tagState = useVal(store.tags.state);
911
+ const [root, setRoot] = useState(null);
912
+ const tagsContext = {
913
+ store,
914
+ container: root,
915
+ hrefForFlow,
916
+ onSelectFlow
917
+ };
918
+ const hasFilters = filter.trim().length > 0 || tagIds.length > 0 || untagged;
919
+ function clearFilters() {
920
+ setFilter("");
921
+ setTagIds([]);
922
+ setUntagged(false);
923
+ }
924
+ useEffect(() => {
925
+ store.tags.start();
926
+ }, [store]);
879
927
  const [creation$] = useState(() => val(void 0));
880
928
  const creation = useVal(creation$);
881
929
  const fileInput = useRef(null);
882
- const [filter, setFilter] = useState("");
930
+ const searchInput = useRef(null);
883
931
  const [name, setName] = useState("");
884
- const normalized = filter.trim().toLocaleLowerCase();
885
- const visible = flows.filter((flow) => flow.name.toLocaleLowerCase().includes(normalized));
932
+ const [creationTags, setCreationTags] = useState([]);
933
+ const [creatingTag, setCreatingTag] = useState(false);
886
934
  const command = useRef();
887
935
  useImperativeHandle(ref, () => ({ openImport: (input) => command.current?.(input) ?? "unavailable" }), []);
888
936
  useLayoutEffect(() => {
@@ -903,6 +951,8 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
903
951
  });
904
952
  return "invalid-file";
905
953
  }
954
+ setCreationTags([]);
955
+ setCreatingTag(false);
906
956
  setName(file.name ?? (input.fileName == null ? importedWorkflowName(language) : workflowNameFromFile(input.fileName)));
907
957
  creation$.set({
908
958
  kind: "import",
@@ -915,20 +965,23 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
915
965
  function openCreate() {
916
966
  if (creation$.value != null) return;
917
967
  setName("");
968
+ setCreationTags([]);
969
+ setCreatingTag(false);
918
970
  creation$.set({ kind: "create" });
919
971
  }
920
972
  async function create(event) {
921
973
  event.preventDefault();
922
974
  const request = creation$.value;
923
975
  const nextName = name.trim();
924
- if (request == null || resourceNameIssue(nextName) != null) return;
925
- if (!(request.kind == "create" ? await onCreateFlow(nextName) : await onImportFlow?.(request.file, nextName, request.key)) || command.current == null || creation$.value !== request) return;
976
+ if (request == null || creatingTag || resourceNameIssue(nextName) != null) return;
977
+ if (!(request.kind == "create" ? await onCreateFlow(nextName, creationTags) : await onImportFlow?.(request.file, nextName, request.key, creationTags)) || command.current == null || creation$.value !== request) return;
926
978
  creation$.set(void 0);
927
979
  setName("");
928
980
  }
929
981
  return /* @__PURE__ */ jsx("main", {
930
982
  className: "resource-browser",
931
983
  "data-tooltip-portal": true,
984
+ ref: setRoot,
932
985
  children: /* @__PURE__ */ jsxs("div", {
933
986
  className: cn("resource-page", {
934
987
  "resource-page-full": catalogWidth == null || catalogWidth == "full",
@@ -951,7 +1004,7 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
951
1004
  children: [/* @__PURE__ */ jsx("h2", {
952
1005
  id: "flow-list-title",
953
1006
  children: t("resource.flows")
954
- }), !loading && /* @__PURE__ */ jsx("span", { children: t("resource.flowCount", { count: total ?? flows.length }) })]
1007
+ }), !loading && total != null && /* @__PURE__ */ jsx("span", { children: t("resource.flowCount", { count: total }) })]
955
1008
  }), /* @__PURE__ */ jsxs("div", {
956
1009
  className: "resource-list-actions",
957
1010
  children: [
@@ -961,6 +1014,7 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
961
1014
  name: "search",
962
1015
  size: 17
963
1016
  }) }), /* @__PURE__ */ jsx(InputGroupInput, {
1017
+ ref: searchInput,
964
1018
  autoComplete: "off",
965
1019
  "aria-label": t("resource.searchByName"),
966
1020
  name: "flow-search",
@@ -969,6 +1023,13 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
969
1023
  value: filter
970
1024
  })]
971
1025
  }),
1026
+ /* @__PURE__ */ jsx(TagPicker, {
1027
+ ...tagsContext,
1028
+ value: tagIds,
1029
+ onChange: setTagIds,
1030
+ untagged,
1031
+ onUntaggedChange: setUntagged
1032
+ }),
972
1033
  onImportFlow != null && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Input, {
973
1034
  ref: fileInput,
974
1035
  className: "hidden",
@@ -1007,6 +1068,42 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
1007
1068
  ]
1008
1069
  })]
1009
1070
  }),
1071
+ (tagIds.length > 0 || untagged) && /* @__PURE__ */ jsxs("div", {
1072
+ className: "flex items-center gap-2 border-t border-border px-4 py-3",
1073
+ children: [/* @__PURE__ */ jsx("i", {
1074
+ "aria-hidden": "true",
1075
+ className: "i-lucide-light:funnel size-4 shrink-0 text-muted-foreground"
1076
+ }), /* @__PURE__ */ jsxs("div", {
1077
+ className: "flex min-w-0 flex-wrap items-center gap-2",
1078
+ children: [
1079
+ tagState.tags.filter((tag) => tagIds.includes(tag.tagId)).map((tag) => /* @__PURE__ */ jsxs(Button, {
1080
+ variant: "secondary",
1081
+ size: "sm",
1082
+ "aria-label": t("tags.remove", { name: tag.name }),
1083
+ onClick: () => setTagIds(tagIds.filter((id) => id != tag.tagId)),
1084
+ children: [tag.name, /* @__PURE__ */ jsx("i", {
1085
+ "aria-hidden": "true",
1086
+ className: "i-lucide-light:x size-3"
1087
+ })]
1088
+ }, tag.tagId)),
1089
+ untagged && /* @__PURE__ */ jsxs(Button, {
1090
+ variant: "secondary",
1091
+ size: "sm",
1092
+ onClick: () => setUntagged(false),
1093
+ children: [t("tags.untagged"), /* @__PURE__ */ jsx("i", {
1094
+ "aria-hidden": "true",
1095
+ className: "i-lucide-light:x size-3"
1096
+ })]
1097
+ }),
1098
+ /* @__PURE__ */ jsx(Button, {
1099
+ variant: "ghost",
1100
+ size: "sm",
1101
+ onClick: clearFilters,
1102
+ children: t("tags.clear")
1103
+ })
1104
+ ]
1105
+ })]
1106
+ }),
1010
1107
  /* @__PURE__ */ jsx("div", {
1011
1108
  className: "resource-table-scroll",
1012
1109
  children: /* @__PURE__ */ jsxs("div", {
@@ -1049,32 +1146,43 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
1049
1146
  /* @__PURE__ */ jsx(EmptyTitle, { children: t("resource.flowsLoadFailed") }),
1050
1147
  /* @__PURE__ */ jsx(EmptyDescription, { children: t("resource.flowsLoadFailedDescription") })
1051
1148
  ] }), /* @__PURE__ */ jsx(EmptyContent, { children: /* @__PURE__ */ jsx(Button, {
1052
- onClick: () => void store.retryFlows(),
1149
+ onClick: () => void catalog?.reload(),
1053
1150
  variant: "outline",
1054
1151
  children: t("empty.retry")
1055
1152
  }) })]
1056
- }) : visible.length == 0 ? /* @__PURE__ */ jsxs(Empty, {
1153
+ }) : flows.length == 0 ? /* @__PURE__ */ jsxs(Empty, {
1057
1154
  className: "min-h-64",
1058
- children: [/* @__PURE__ */ jsxs(EmptyHeader, { children: [
1059
- /* @__PURE__ */ jsx(EmptyMedia, {
1060
- variant: "icon",
1061
- children: /* @__PURE__ */ jsx(Icon, {
1062
- name: "flow",
1063
- size: 20
1064
- })
1065
- }),
1066
- /* @__PURE__ */ jsx(EmptyTitle, { children: t(normalized.length == 0 ? "resource.noFlows" : "resource.noMatchingFlows") }),
1067
- /* @__PURE__ */ jsx(EmptyDescription, { children: t(normalized.length == 0 ? "resource.noFlowsDescription" : "resource.noMatchingDescription") })
1068
- ] }), normalized.length == 0 && /* @__PURE__ */ jsx(EmptyContent, { children: /* @__PURE__ */ jsxs(Button, {
1069
- onClick: openCreate,
1070
- variant: "outline",
1071
- children: [/* @__PURE__ */ jsx(Icon, {
1072
- "data-icon": "inline-start",
1073
- name: "plus"
1074
- }), t("resource.newFlow")]
1075
- }) })]
1076
- }) : visible.map((flow) => /* @__PURE__ */ jsx(FlowItem, {
1155
+ children: [
1156
+ /* @__PURE__ */ jsxs(EmptyHeader, { children: [
1157
+ /* @__PURE__ */ jsx(EmptyMedia, {
1158
+ variant: "icon",
1159
+ children: /* @__PURE__ */ jsx(Icon, {
1160
+ name: "flow",
1161
+ size: 20
1162
+ })
1163
+ }),
1164
+ /* @__PURE__ */ jsx(EmptyTitle, { children: t(!hasFilters ? "resource.noFlows" : "resource.noMatchingFlows") }),
1165
+ /* @__PURE__ */ jsx(EmptyDescription, { children: t(!hasFilters ? "resource.noFlowsDescription" : "resource.noMatchingDescription") })
1166
+ ] }),
1167
+ hasFilters && /* @__PURE__ */ jsx(EmptyContent, { children: /* @__PURE__ */ jsx(Button, {
1168
+ variant: "outline",
1169
+ onClick: clearFilters,
1170
+ children: t("tags.clear")
1171
+ }) }),
1172
+ !hasFilters && /* @__PURE__ */ jsx(EmptyContent, { children: /* @__PURE__ */ jsxs(Button, {
1173
+ onClick: openCreate,
1174
+ variant: "outline",
1175
+ children: [/* @__PURE__ */ jsx(Icon, {
1176
+ "data-icon": "inline-start",
1177
+ name: "plus"
1178
+ }), t("resource.newFlow")]
1179
+ }) })
1180
+ ]
1181
+ }) : flows.map((flow) => /* @__PURE__ */ jsx(FlowItem, {
1077
1182
  busy,
1183
+ settingsFinalFocus: () => searchInput.current,
1184
+ onChanged: () => void catalog?.reload(),
1185
+ onTagFilter: (id) => setTagIds((current) => current.includes(id) ? current : [...current, id]),
1078
1186
  flow,
1079
1187
  href: hrefForFlow(flow),
1080
1188
  hrefForFlow,
@@ -1088,7 +1196,7 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
1088
1196
  className: "resource-list-footer",
1089
1197
  children: /* @__PURE__ */ jsx(Button, {
1090
1198
  disabled: loadingMore || refreshing,
1091
- onClick: () => void store.workspace.loadMoreFlows(),
1199
+ onClick: () => void catalog?.loadMore(),
1092
1200
  variant: "outline",
1093
1201
  children: t(loadingMore ? "resource.loadingMore" : loadMoreFailed ? "resource.retryLoadMore" : "resource.loadMore")
1094
1202
  })
@@ -1098,7 +1206,15 @@ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisa
1098
1206
  creation != null && /* @__PURE__ */ jsx(Suspense, {
1099
1207
  fallback: null,
1100
1208
  children: /* @__PURE__ */ jsx(CreateResourceDialog, {
1101
- disabled: createFlowDisabled,
1209
+ disabled: createFlowDisabled || creatingTag,
1210
+ tagField: (container) => /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: t("tags.title") }), /* @__PURE__ */ jsx(TagPicker, {
1211
+ ...tagsContext,
1212
+ onPendingChange: setCreatingTag,
1213
+ dialogContainer: root,
1214
+ container,
1215
+ value: creationTags,
1216
+ onChange: setCreationTags
1217
+ })] }),
1102
1218
  field: createFlowField,
1103
1219
  id: "flow-name",
1104
1220
  issue: resourceNameIssue(name),
@@ -1789,6 +1905,55 @@ var PublicationStore = class {
1789
1905
  }
1790
1906
  };
1791
1907
  //#endregion
1908
+ //#region src/workbench/browser/runtime/runs/runInputStorage.ts
1909
+ var databaseName = "open-flow:run-inputs";
1910
+ var storeName = "inputs";
1911
+ function scopeKey(scope) {
1912
+ return JSON.stringify([
1913
+ scope.environment ?? "production",
1914
+ scope.sessionId,
1915
+ scope.connectorOwnerId ?? null
1916
+ ]);
1917
+ }
1918
+ /** Local authoring data has an independent lifetime from fetched catalog responses. */
1919
+ async function transact(mode, work) {
1920
+ const database = await new Promise((resolve, reject) => {
1921
+ const request = indexedDB.open(databaseName, 1);
1922
+ request.addEventListener("upgradeneeded", () => request.result.createObjectStore(storeName));
1923
+ request.addEventListener("success", () => resolve(request.result));
1924
+ request.addEventListener("error", () => reject(request.error));
1925
+ });
1926
+ try {
1927
+ return await new Promise((resolve, reject) => {
1928
+ const transaction = database.transaction(storeName, mode);
1929
+ const request = work(transaction.objectStore(storeName));
1930
+ transaction.addEventListener("complete", () => resolve(request.result));
1931
+ transaction.addEventListener("abort", () => reject(transaction.error));
1932
+ });
1933
+ } finally {
1934
+ database.close();
1935
+ }
1936
+ }
1937
+ async function readRunInputs(scope) {
1938
+ const prefix = `${scopeKey(scope)}\u0000`;
1939
+ const records = await transact("readonly", (store) => store.getAll(IDBKeyRange.bound(prefix, `${scopeKey(scope)}\u0001`, false, true)));
1940
+ return new Map(records.flatMap((raw) => {
1941
+ const record = objectValue(raw);
1942
+ const values = objectValue(record?.values);
1943
+ return record != null && typeof record.key === "string" && typeof record.signature === "string" && typeof record.valid === "boolean" && values != null && isJsonValue(values) ? [[record.key, {
1944
+ signature: record.signature,
1945
+ valid: record.valid,
1946
+ values
1947
+ }]] : [];
1948
+ }));
1949
+ }
1950
+ async function writeRunInputs(scope, key, inputs) {
1951
+ await transact("readwrite", (store) => store.put({
1952
+ key,
1953
+ ...inputs
1954
+ }, `${scopeKey(scope)}\u0000${key}`));
1955
+ }
1956
+ //#endregion
1792
1957
  //#region src/workbench/browser/runtime/runs/runRequestStore.ts
1793
1958
  var initialState$5 = { starting: false };
1794
1959
  function inputSpec(draft, triggerId) {
@@ -1833,10 +1998,15 @@ var RunRequestStore = class {
1833
1998
  #requests = new Latest();
1834
1999
  #runs;
1835
2000
  #savedInputs = /* @__PURE__ */ new Map();
2001
+ #inputsLoaded = val(false);
2002
+ #restoreInputs;
2003
+ #inputSaves = Promise.resolve();
2004
+ #disposed = false;
1836
2005
  #setNotice;
1837
2006
  #state = val(initialState$5);
1838
2007
  #attempt;
1839
- constructor(client, runs, setNotice, prepareDraft, i18n = createI18n(), identity = randomId) {
2008
+ constructor(client, runs, setNotice, prepareDraft, i18n = createI18n(), identity = randomId, inputStorage) {
2009
+ this.inputStorage = inputStorage;
1840
2010
  this.#prepareDraft = prepareDraft;
1841
2011
  this.#client = client;
1842
2012
  this.#runs = runs;
@@ -1844,12 +2014,15 @@ var RunRequestStore = class {
1844
2014
  this.#i18n = i18n;
1845
2015
  this.#identity = identity;
1846
2016
  this.$ = {
2017
+ inputsLoaded: this.#inputsLoaded,
1847
2018
  inputRequest: derive(this.#state, (state) => state.inputRequest),
1848
2019
  starting: derive(this.#state, (state) => state.starting),
1849
2020
  submitting: derive(this.#state, (state) => state.submitting)
1850
2021
  };
2022
+ this.#restoreInputs = this.#restoreSavedInputs();
1851
2023
  }
1852
2024
  dispose() {
2025
+ this.#disposed = true;
1853
2026
  this.#lifetime.invalidate();
1854
2027
  this.#requests.invalidate();
1855
2028
  this.#disposeInputRequest();
@@ -1975,6 +2148,8 @@ var RunRequestStore = class {
1975
2148
  submitting: void 0
1976
2149
  });
1977
2150
  this.#disposeInputRequest(previous);
2151
+ await this.#restoreInputs;
2152
+ if (!current()) return "unavailable";
1978
2153
  const graph = revisionView(revision).graph();
1979
2154
  const triggers = Object.entries(graph?.nodes ?? {}).flatMap(([nodeId, node]) => !isTriggerNode(node) ? [] : [{
1980
2155
  nodeId,
@@ -2046,6 +2221,7 @@ var RunRequestStore = class {
2046
2221
  });
2047
2222
  try {
2048
2223
  const prepared = source == "draft" ? await this.#prepareDraft(flow.flowId) : revision;
2224
+ await this.#inputSaves;
2049
2225
  if (prepared == null || !alive() || !current() || !requestCurrent()) return false;
2050
2226
  const node = revisionView(prepared).trigger(trigger.nodeId);
2051
2227
  if (node == null || node.kind !== revisionView(revision).trigger(trigger.nodeId)?.kind || inputSignature(inputSpec(prepared, trigger.nodeId)) !== inputSignature(inputSpec(revision, trigger.nodeId))) {
@@ -2116,13 +2292,28 @@ var RunRequestStore = class {
2116
2292
  #inputKey(flowId, triggerId) {
2117
2293
  return `${flowId}\u0000${triggerId}`;
2118
2294
  }
2295
+ async #restoreSavedInputs() {
2296
+ try {
2297
+ if (this.inputStorage != null) {
2298
+ const inputs = await readRunInputs(this.inputStorage);
2299
+ if (this.#disposed) return;
2300
+ for (const [key, value] of inputs) this.#savedInputs.set(key, value);
2301
+ }
2302
+ } catch {} finally {
2303
+ if (!this.#disposed) this.#inputsLoaded.set(true);
2304
+ }
2305
+ }
2119
2306
  #rememberInputs(request) {
2120
2307
  if (request?.triggerId == null || request.editor == null) return;
2121
- this.#savedInputs.set(this.#inputKey(request.flow.flowId, request.triggerId), {
2308
+ const key = this.#inputKey(request.flow.flowId, request.triggerId);
2309
+ const inputs = {
2122
2310
  signature: inputSignature(inputSpec(request.revision, request.triggerId)),
2123
2311
  valid: request.valid.value,
2124
2312
  values: request.editor.values()
2125
- });
2313
+ };
2314
+ this.#savedInputs.set(key, inputs);
2315
+ const scope = this.inputStorage;
2316
+ if (scope != null) this.#inputSaves = this.#inputSaves.then(() => writeRunInputs(scope, key, inputs)).catch(() => {});
2126
2317
  }
2127
2318
  #set(patch) {
2128
2319
  this.#state.set({
@@ -4040,183 +4231,574 @@ var ConnectorStore = class {
4040
4231
  }
4041
4232
  };
4042
4233
  //#endregion
4043
- //#region src/workbench/browser/runtime/stores/triggerCatalog.ts
4044
- var TriggerCatalogStore = class {
4045
- #language;
4046
- #entries;
4047
- constructor(client, language, environment = "production") {
4048
- this.client = client;
4049
- this.environment = environment;
4050
- this.#entries = /* @__PURE__ */ new Map();
4051
- this.retry = () => {
4052
- this.get(true);
4053
- };
4054
- this.#language = val(language);
4055
- this.state = compute((get) => get(this.#entry(get(this.#language)).state));
4056
- }
4057
- #entry(locale) {
4058
- let entry = this.#entries.get(locale);
4059
- if (entry == null) {
4060
- const decode = (value) => {
4061
- const data = decodeTriggerCatalog(value);
4062
- if (data.locale != locale) throw new Error("Unexpected Trigger catalog language.");
4063
- return data;
4064
- };
4065
- const cache = createPersistentCache({
4066
- namespace: JSON.stringify(["open-flow:trigger-catalog", this.environment]),
4067
- schemaVersion: 1,
4068
- maxAge: 3e5,
4069
- key: () => locale,
4070
- decode,
4071
- load: async (_query, validation) => this.client.readCatalog({
4072
- path: `/v1/trigger-keys/catalog?locale=${encodeURIComponent(locale)}`,
4073
- decode
4074
- }, validation.etag, validation.signal)
4075
- });
4076
- entry = new CacheResource(async () => cache, void 0, true, 3e5);
4077
- this.#entries.set(locale, entry);
4078
- }
4079
- return entry;
4080
- }
4081
- setLanguage(language) {
4082
- this.#language.set(language);
4083
- }
4084
- get(force = false) {
4085
- this.#entry(this.#language.value).get(force);
4086
- return this.state;
4087
- }
4088
- refresh() {
4089
- return this.#entry(this.#language.value).refresh();
4090
- }
4091
- dispose() {
4092
- this.state.dispose();
4093
- this.#language.dispose();
4094
- for (const entry of this.#entries.values()) entry.dispose();
4095
- this.#entries.clear();
4096
- }
4234
+ //#region src/workbench/browser/runtime/stores/flowCatalog.ts
4235
+ var pageLimit = 50;
4236
+ var initialState$2 = {
4237
+ failed: false,
4238
+ loaded: false,
4239
+ loading: true,
4240
+ loadingMore: false,
4241
+ loadMoreFailed: false,
4242
+ refreshing: false,
4243
+ flows: []
4097
4244
  };
4098
- //#endregion
4099
- //#region src/workbench/browser/runtime/stores/triggerStore.ts
4100
- var initialState$2 = {};
4101
- var optionPrefix = "trigger:";
4102
- function target(selection) {
4103
- if (selection?.kind != "trigger") return;
4104
- const trigger = selection.trigger;
4105
- if (trigger.kind != "poll" && trigger.kind != "integration") return;
4106
- return {
4107
- ...trigger.connectionId == null ? {} : { connectionId: trigger.connectionId },
4108
- provider: trigger.definition.provider,
4109
- triggerId: selection.id
4110
- };
4111
- }
4112
- function option(definition, i18n, display) {
4113
- return {
4114
- description: display?.description ?? definition.description,
4115
- group: i18n.t("addNode.integrationTriggers"),
4116
- icon: providerIcon({
4117
- serviceId: definition.provider,
4118
- serviceName: definition.provider
4119
- }),
4120
- id: `${optionPrefix}${definition.key}`,
4121
- inputs: [],
4122
- kind: "trigger",
4123
- label: display?.displayName ?? definition.displayName,
4124
- outputs: definition.outputs.map((output) => {
4125
- const description = display?.outputs[output.handle];
4126
- return description == null ? output : {
4127
- ...output,
4128
- description
4129
- };
4130
- }),
4131
- trigger: {
4132
- definition,
4133
- kind: "catalog"
4134
- }
4135
- };
4136
- }
4137
- var TriggerStore = class {
4245
+ var FlowCatalog = class {
4138
4246
  #client;
4139
- #host;
4140
4247
  #i18n;
4141
- #refresh;
4142
- #stale;
4143
- #selected;
4248
+ #session = new Latest();
4144
4249
  #setNotice;
4145
- #state;
4146
- #workspace;
4147
- #disposed;
4148
- constructor(client, workspace, setNotice, host, i18n = createI18n()) {
4149
- this.#refresh = new Latest();
4150
- this.#stale = /* @__PURE__ */ new Set();
4151
- this.#state = val(initialState$2);
4152
- this.#disposed = false;
4153
- this.browseAddNodeOptions = (signal) => this.#options("", signal);
4154
- this.provideAddNodeOptions = (searchTerm, signal) => this.#options(searchTerm, signal);
4155
- this.catalog = new TriggerCatalogStore(client, resolveUiLanguage([i18n.lang]), host.cacheEnvironment);
4250
+ #state = val(initialState$2);
4251
+ #lifetime = new AbortController();
4252
+ #pending = /* @__PURE__ */ new Set();
4253
+ #disposed = false;
4254
+ #query = normalizeFlowQuery();
4255
+ #request;
4256
+ #stopWatch;
4257
+ constructor(client, setNotice, i18n) {
4156
4258
  this.#client = client;
4157
- this.#host = host;
4158
4259
  this.#i18n = i18n;
4159
4260
  this.#setNotice = setNotice;
4160
- this.#workspace = workspace;
4161
- this.#selected = compute((get) => {
4162
- const selection = get(workspace.$.selection);
4163
- const current = target(selection);
4164
- const state = get(this.#state);
4165
- if (selection?.kind != "trigger") return { authorizationPending: false };
4166
- const trigger = selection.trigger;
4167
- if (trigger.kind != "poll" && trigger.kind != "integration") return { authorizationPending: false };
4168
- const connections = current == null ? void 0 : get(workspace.catalogs.connections.get(current.provider, get(workspace.$.flowId))).data;
4169
- const catalog = connections == null ? void 0 : connectionCatalog(connections);
4170
- const connectionError = current == null ? void 0 : state.connectionError?.provider == current.provider ? new Error(state.connectionError.message) : get(workspace.catalogs.connections.get(current.provider, get(workspace.$.flowId))).error;
4171
- return {
4172
- activeConnections: catalog?.active,
4173
- authorizationPending: state.authorizationProvider == current?.provider,
4174
- connection: current?.connectionId == null ? void 0 : catalog?.byId.get(current.connectionId),
4175
- connectionError: connectionError == null ? void 0 : errorNotice(connectionError, this.#i18n.t).message,
4176
- definition: trigger.definition
4177
- };
4178
- });
4179
4261
  this.$ = {
4180
- connectionLoading: derive(this.#state, (state) => state.connectionLoading),
4181
- selectedActiveConnections: derive(this.#selected, (selection) => selection.activeConnections),
4182
- selectedAuthorizationPending: derive(this.#selected, (selection) => selection.authorizationPending),
4183
- selectedConnection: derive(this.#selected, (selection) => selection.connection),
4184
- selectedConnectionError: derive(this.#selected, (selection) => selection.connectionError),
4185
- selectedDefinition: derive(this.#selected, (selection) => selection.definition)
4262
+ failed: derive(this.#state, (state) => state.failed),
4263
+ loading: derive(this.#state, (state) => state.loading),
4264
+ loadingMore: derive(this.#state, (state) => state.loadingMore),
4265
+ loadMoreFailed: derive(this.#state, (state) => state.loadMoreFailed),
4266
+ refreshing: derive(this.#state, (state) => state.refreshing),
4267
+ nextCursor: derive(this.#state, (state) => state.nextCursor),
4268
+ flows: derive(this.#state, (state) => state.flows),
4269
+ total: derive(this.#state, (state) => state.total)
4186
4270
  };
4187
4271
  }
4272
+ get loaded() {
4273
+ return this.#state.value.loaded;
4274
+ }
4188
4275
  dispose() {
4276
+ if (this.#disposed) return;
4189
4277
  this.#disposed = true;
4190
- this.catalog.dispose();
4191
- this.#refresh.invalidate();
4278
+ this.#request?.abort();
4279
+ this.#stopWatch?.();
4280
+ this.#lifetime.abort();
4281
+ this.#session.invalidate();
4192
4282
  for (const value of Object.values(this.$)) value.dispose();
4193
- this.#selected.dispose();
4194
4283
  this.#state.dispose();
4195
4284
  }
4196
- reset() {
4197
- if (this.#disposed) return;
4198
- this.#stale.clear();
4199
- this.#refresh.invalidate();
4200
- this.#state.set(initialState$2);
4285
+ flow(flowId) {
4286
+ return this.#state.value.flows.find((flow) => flow.flowId == flowId);
4201
4287
  }
4202
- #options(searchTerm, signal) {
4203
- const source = this.catalog.get();
4204
- const query = searchTerm.trim().toLowerCase();
4205
- return scopedValue(signal, (get) => {
4206
- const state = get(source);
4207
- get(this.#i18n.t$);
4208
- const enabled = get(this.#workspace.$.flowId) != null && get(this.#workspace.$.draft) != null;
4209
- const catalog = state.data;
4210
- const definitions = catalog?.definitions.filter((item) => !query || [
4211
- catalog.display[item.key]?.displayName,
4212
- catalog.display[item.key]?.description,
4213
- item.description,
4214
- item.displayName,
4215
- item.key,
4216
- item.name,
4217
- item.provider,
4218
- item.type
4219
- ].some((value) => value?.toLowerCase().includes(query)));
4288
+ watch() {
4289
+ if (this.#stopWatch != null) return;
4290
+ const subscription = this.#client.watchFlowCatalog(() => void this.reload());
4291
+ this.#stopWatch = subscription.stop;
4292
+ subscription.ready.then(() => {
4293
+ if (!this.#disposed) this.reload();
4294
+ });
4295
+ }
4296
+ async setQuery(query) {
4297
+ const next = normalizeFlowQuery(query);
4298
+ if (JSON.stringify(next) == JSON.stringify(this.#query) && (this.loaded || this.#request != null)) return;
4299
+ this.#query = next;
4300
+ this.#state.set({ ...initialState$2 });
4301
+ await this.reload();
4302
+ }
4303
+ async reload() {
4304
+ if (this.#disposed) return;
4305
+ this.#request?.abort();
4306
+ const controller = this.#request = new AbortController();
4307
+ const current = this.#session.begin();
4308
+ const loaded = this.#state.value.loaded;
4309
+ if (loaded) this.#set({
4310
+ failed: false,
4311
+ loadingMore: false,
4312
+ loadMoreFailed: false,
4313
+ refreshing: true
4314
+ });
4315
+ else this.#set({
4316
+ failed: false,
4317
+ loading: true,
4318
+ loadingMore: false,
4319
+ loadMoreFailed: false,
4320
+ refreshing: false,
4321
+ nextCursor: void 0
4322
+ });
4323
+ try {
4324
+ const page = await this.#client.listFlows({
4325
+ ...this.#query,
4326
+ includeTotal: true,
4327
+ limit: pageLimit,
4328
+ signal: controller.signal
4329
+ });
4330
+ if (!current()) return;
4331
+ this.#set({
4332
+ loaded: true,
4333
+ loading: false,
4334
+ refreshing: false,
4335
+ nextCursor: page.nextCursor,
4336
+ flows: page.flows,
4337
+ total: page.total
4338
+ });
4339
+ } catch (error) {
4340
+ if (!current()) return;
4341
+ this.#set({
4342
+ failed: !loaded,
4343
+ loading: false,
4344
+ refreshing: false
4345
+ });
4346
+ this.#setNotice(errorNotice(error, this.#i18n.t));
4347
+ }
4348
+ }
4349
+ async loadMore() {
4350
+ const { loadingMore, nextCursor, refreshing } = this.#state.value;
4351
+ if (loadingMore || refreshing || nextCursor == null) return;
4352
+ const current = this.#session.capture();
4353
+ this.#set({
4354
+ loadingMore: true,
4355
+ loadMoreFailed: false
4356
+ });
4357
+ try {
4358
+ const page = await this.#client.listFlows({
4359
+ ...this.#query,
4360
+ cursor: nextCursor,
4361
+ limit: pageLimit,
4362
+ signal: this.#request?.signal
4363
+ });
4364
+ if (!current()) return;
4365
+ const flows = new Map(this.#state.value.flows.map((flow) => [flow.flowId, flow]));
4366
+ for (const flow of page.flows) flows.set(flow.flowId, flow);
4367
+ this.#set({
4368
+ loadingMore: false,
4369
+ nextCursor: page.nextCursor,
4370
+ flows: [...flows.values()]
4371
+ });
4372
+ } catch (error) {
4373
+ if (!current()) return;
4374
+ this.#set({
4375
+ loadingMore: false,
4376
+ loadMoreFailed: true
4377
+ });
4378
+ this.#setNotice(errorNotice(error, this.#i18n.t));
4379
+ }
4380
+ }
4381
+ async setEnabled(flow, enabled) {
4382
+ if (flow.live == null || this.#pending.has(flow.flowId)) return;
4383
+ this.#pending.add(flow.flowId);
4384
+ try {
4385
+ const changed = await this.#client.setFlowEnabled(flow.flowId, flow.live.publicationId, enabled);
4386
+ if (!this.#disposed) this.include(changed);
4387
+ } catch (error) {
4388
+ if (!this.#disposed) {
4389
+ this.#setNotice(errorNotice(error, this.#i18n.t));
4390
+ await this.reload();
4391
+ }
4392
+ } finally {
4393
+ this.#pending.delete(flow.flowId);
4394
+ }
4395
+ }
4396
+ async publish(flow) {
4397
+ if (this.#pending.has(flow.flowId)) return;
4398
+ this.#pending.add(flow.flowId);
4399
+ try {
4400
+ const initial = await this.#client.publishFlow(flow.flowId, flow.draftRevisionId, flow.live?.publicationId ?? null);
4401
+ if (this.#disposed) return;
4402
+ const operation = await Effect.runPromise(Effect.gen({ self: this }, function* () {
4403
+ let current = initial;
4404
+ while (current.status == "pending") {
4405
+ yield* Effect.sleep(1e3);
4406
+ current = yield* Effect.tryPromise((signal) => this.#client.getPublishOperation(flow.flowId, current.operationId, signal));
4407
+ }
4408
+ return current;
4409
+ }), { signal: this.#lifetime.signal });
4410
+ if (this.#disposed) return;
4411
+ if (operation.status == "failed") this.#setNotice({
4412
+ kind: "error",
4413
+ message: operation.issue.message
4414
+ });
4415
+ else this.#setNotice({
4416
+ kind: "success",
4417
+ message: this.#i18n.t("notice.published", { name: flow.name })
4418
+ });
4419
+ this.include(await this.#client.getFlow(flow.flowId));
4420
+ } catch (error) {
4421
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
4422
+ } finally {
4423
+ this.#pending.delete(flow.flowId);
4424
+ }
4425
+ }
4426
+ create(name, create, tagIds) {
4427
+ return this.#create(async () => create == null ? tagIds == null ? this.#client.createFlow(name) : this.#client.createFlow(name, void 0, void 0, tagIds) : this.#client.getFlow(await (tagIds == null ? create(name) : create(name, { tagIds }))));
4428
+ }
4429
+ import(file, name, key, importer, tagIds) {
4430
+ return this.#create(async () => importer == null ? this.#client.importFlow(file, {
4431
+ name,
4432
+ idempotencyKey: key,
4433
+ ...tagIds == null ? {} : { tagIds }
4434
+ }) : this.#client.getFlow(await (tagIds == null ? importer(file, name, key) : importer(file, name, key, { tagIds }))));
4435
+ }
4436
+ async #create(create) {
4437
+ try {
4438
+ const created = await create();
4439
+ if (this.#disposed) return;
4440
+ this.#insert(created);
4441
+ return created;
4442
+ } catch (error) {
4443
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
4444
+ }
4445
+ }
4446
+ async delete(flow) {
4447
+ if (flow.status == "retiring") return false;
4448
+ try {
4449
+ await this.#client.deleteFlow(flow.flowId);
4450
+ if (this.#disposed) return false;
4451
+ this.#remove(flow.flowId);
4452
+ this.#setNotice({
4453
+ kind: "success",
4454
+ message: this.#i18n.t("notice.flowDeleteAccepted", { name: flow.name })
4455
+ });
4456
+ return true;
4457
+ } catch (error) {
4458
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
4459
+ return false;
4460
+ }
4461
+ }
4462
+ async updateSettings(flowId, changes) {
4463
+ try {
4464
+ const changed = await this.#client.updateFlow(flowId, changes);
4465
+ if (this.#disposed) return false;
4466
+ this.include(changed);
4467
+ return true;
4468
+ } catch (error) {
4469
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
4470
+ return false;
4471
+ }
4472
+ }
4473
+ async rename(flow, name) {
4474
+ const nextName = name.trim();
4475
+ if (nextName.length == 0) return false;
4476
+ if (flow.name == nextName) return true;
4477
+ try {
4478
+ const changed = await this.#client.renameFlow(flow.flowId, nextName);
4479
+ if (this.#disposed) return false;
4480
+ this.include(changed);
4481
+ this.#setNotice({
4482
+ kind: "success",
4483
+ message: this.#i18n.t("notice.flowRenamed", { name: nextName })
4484
+ });
4485
+ return true;
4486
+ } catch (error) {
4487
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
4488
+ return false;
4489
+ }
4490
+ }
4491
+ include(flow) {
4492
+ const index = this.#state.value.flows.findIndex((candidate) => candidate.flowId == flow.flowId);
4493
+ if (index < 0) {
4494
+ this.#set({ flows: [...this.#state.value.flows, flow] });
4495
+ return;
4496
+ }
4497
+ const referenceCount = this.#state.value.flows[index].referenceCount;
4498
+ this.#set({ flows: this.#state.value.flows.with(index, {
4499
+ ...flow,
4500
+ ...referenceCount == null ? {} : { referenceCount }
4501
+ }) });
4502
+ }
4503
+ #insert(flow) {
4504
+ if (this.#state.value.flows.some((candidate) => candidate.flowId == flow.flowId)) return;
4505
+ this.#set({
4506
+ flows: [...this.#state.value.flows, flow],
4507
+ total: this.#state.value.total == null ? void 0 : this.#state.value.total + 1
4508
+ });
4509
+ }
4510
+ #remove(flowId) {
4511
+ this.#set({
4512
+ flows: this.#state.value.flows.filter((flow) => flow.flowId != flowId),
4513
+ total: this.#state.value.total == null ? void 0 : this.#state.value.total - 1
4514
+ });
4515
+ }
4516
+ advanceHead(flowId, revisionId, content) {
4517
+ this.#set({ flows: this.#state.value.flows.map((flow) => flow.flowId == flowId ? {
4518
+ ...flow,
4519
+ draftRevisionId: revisionId,
4520
+ resourceReferences: {
4521
+ ...flow.resourceReferences,
4522
+ draft: flowResourceReferences(content)
4523
+ }
4524
+ } : flow) });
4525
+ }
4526
+ #set(patch) {
4527
+ if (!this.#disposed) this.#state.set({
4528
+ ...this.#state.value,
4529
+ ...patch
4530
+ });
4531
+ }
4532
+ };
4533
+ //#endregion
4534
+ //#region src/workbench/browser/runtime/stores/tagCatalog.ts
4535
+ var TagCatalog = class {
4536
+ #controller;
4537
+ #stop;
4538
+ #disposed;
4539
+ constructor(client, notice, i18n, changed = async () => {}) {
4540
+ this.client = client;
4541
+ this.notice = notice;
4542
+ this.i18n = i18n;
4543
+ this.changed = changed;
4544
+ this.state = val({
4545
+ tags: [],
4546
+ loading: true,
4547
+ failed: false
4548
+ });
4549
+ this.#disposed = false;
4550
+ }
4551
+ start() {
4552
+ if (this.#stop != null || this.#disposed) return;
4553
+ const subscription = this.client.watchFlowCatalog(() => void this.reload());
4554
+ this.#stop = subscription.stop;
4555
+ this.reload();
4556
+ subscription.ready.then(() => {
4557
+ if (!this.#disposed) this.reload();
4558
+ });
4559
+ }
4560
+ async reload() {
4561
+ if (this.#disposed) return;
4562
+ this.#controller?.abort();
4563
+ const controller = this.#controller = new AbortController();
4564
+ this.state.set({
4565
+ ...this.state.value,
4566
+ failed: false
4567
+ });
4568
+ try {
4569
+ const result = await this.client.listTags(controller.signal);
4570
+ if (!controller.signal.aborted) this.state.set({
4571
+ tags: result.tags,
4572
+ loading: false,
4573
+ failed: false
4574
+ });
4575
+ } catch (error) {
4576
+ if (!controller.signal.aborted) {
4577
+ this.state.set({
4578
+ ...this.state.value,
4579
+ loading: false,
4580
+ failed: true
4581
+ });
4582
+ this.notice(errorNotice(error, this.i18n.t));
4583
+ }
4584
+ }
4585
+ }
4586
+ hasName(name, exceptTagId) {
4587
+ const normalized = name.trim().toLowerCase();
4588
+ return this.state.value.tags.some((tag) => tag.tagId != exceptTagId && tag.name.toLowerCase() == normalized);
4589
+ }
4590
+ async save(name, tagId, onConflict) {
4591
+ return this.mutate(async () => {
4592
+ const tag = tagId == null ? await this.client.createTag(name) : await this.client.renameTag(tagId, name);
4593
+ await this.changed();
4594
+ await this.reload();
4595
+ return tag;
4596
+ }, onConflict);
4597
+ }
4598
+ async remove(tagId) {
4599
+ return await this.mutate(async () => {
4600
+ await this.client.deleteTag(tagId);
4601
+ await this.changed();
4602
+ await this.reload();
4603
+ return true;
4604
+ }) ?? false;
4605
+ }
4606
+ async mutate(operation, onConflict) {
4607
+ try {
4608
+ const result = await operation();
4609
+ return this.#disposed ? void 0 : result;
4610
+ } catch (error) {
4611
+ if (this.#disposed) return;
4612
+ if (onConflict != null && error instanceof ApiError && error.code == controlErrorCode.tagConflict) onConflict();
4613
+ else this.notice(errorNotice(error, this.i18n.t));
4614
+ }
4615
+ }
4616
+ dispose() {
4617
+ if (this.#disposed) return;
4618
+ this.#disposed = true;
4619
+ this.#stop?.();
4620
+ this.#controller?.abort();
4621
+ this.state.dispose();
4622
+ }
4623
+ };
4624
+ //#endregion
4625
+ //#region src/workbench/browser/runtime/stores/triggerCatalog.ts
4626
+ var TriggerCatalogStore = class {
4627
+ #language;
4628
+ #entries;
4629
+ constructor(client, language, environment = "production") {
4630
+ this.client = client;
4631
+ this.environment = environment;
4632
+ this.#entries = /* @__PURE__ */ new Map();
4633
+ this.retry = () => {
4634
+ this.get(true);
4635
+ };
4636
+ this.#language = val(language);
4637
+ this.state = compute((get) => get(this.#entry(get(this.#language)).state));
4638
+ }
4639
+ #entry(locale) {
4640
+ let entry = this.#entries.get(locale);
4641
+ if (entry == null) {
4642
+ const decode = (value) => {
4643
+ const data = decodeTriggerCatalog(value);
4644
+ if (data.locale != locale) throw new Error("Unexpected Trigger catalog language.");
4645
+ return data;
4646
+ };
4647
+ const cache = createPersistentCache({
4648
+ namespace: JSON.stringify(["open-flow:trigger-catalog", this.environment]),
4649
+ schemaVersion: 1,
4650
+ maxAge: 3e5,
4651
+ key: () => locale,
4652
+ decode,
4653
+ load: async (_query, validation) => this.client.readCatalog({
4654
+ path: `/v1/trigger-keys/catalog?locale=${encodeURIComponent(locale)}`,
4655
+ decode
4656
+ }, validation.etag, validation.signal)
4657
+ });
4658
+ entry = new CacheResource(async () => cache, void 0, true, 3e5);
4659
+ this.#entries.set(locale, entry);
4660
+ }
4661
+ return entry;
4662
+ }
4663
+ setLanguage(language) {
4664
+ this.#language.set(language);
4665
+ }
4666
+ get(force = false) {
4667
+ this.#entry(this.#language.value).get(force);
4668
+ return this.state;
4669
+ }
4670
+ refresh() {
4671
+ return this.#entry(this.#language.value).refresh();
4672
+ }
4673
+ dispose() {
4674
+ this.state.dispose();
4675
+ this.#language.dispose();
4676
+ for (const entry of this.#entries.values()) entry.dispose();
4677
+ this.#entries.clear();
4678
+ }
4679
+ };
4680
+ //#endregion
4681
+ //#region src/workbench/browser/runtime/stores/triggerStore.ts
4682
+ var initialState$1 = {};
4683
+ var optionPrefix = "trigger:";
4684
+ function target(selection) {
4685
+ if (selection?.kind != "trigger") return;
4686
+ const trigger = selection.trigger;
4687
+ if (trigger.kind != "poll" && trigger.kind != "integration") return;
4688
+ return {
4689
+ ...trigger.connectionId == null ? {} : { connectionId: trigger.connectionId },
4690
+ provider: trigger.definition.provider,
4691
+ triggerId: selection.id
4692
+ };
4693
+ }
4694
+ function option(definition, i18n, display) {
4695
+ return {
4696
+ description: display?.description ?? definition.description,
4697
+ group: i18n.t("addNode.integrationTriggers"),
4698
+ icon: providerIcon({
4699
+ serviceId: definition.provider,
4700
+ serviceName: definition.provider
4701
+ }),
4702
+ id: `${optionPrefix}${definition.key}`,
4703
+ inputs: [],
4704
+ kind: "trigger",
4705
+ label: display?.displayName ?? definition.displayName,
4706
+ outputs: definition.outputs.map((output) => {
4707
+ const description = display?.outputs[output.handle];
4708
+ return description == null ? output : {
4709
+ ...output,
4710
+ description
4711
+ };
4712
+ }),
4713
+ trigger: {
4714
+ definition,
4715
+ kind: "catalog"
4716
+ }
4717
+ };
4718
+ }
4719
+ var TriggerStore = class {
4720
+ #client;
4721
+ #host;
4722
+ #i18n;
4723
+ #refresh;
4724
+ #stale;
4725
+ #selected;
4726
+ #setNotice;
4727
+ #state;
4728
+ #workspace;
4729
+ #disposed;
4730
+ constructor(client, workspace, setNotice, host, i18n = createI18n()) {
4731
+ this.#refresh = new Latest();
4732
+ this.#stale = /* @__PURE__ */ new Set();
4733
+ this.#state = val(initialState$1);
4734
+ this.#disposed = false;
4735
+ this.browseAddNodeOptions = (signal) => this.#options("", signal);
4736
+ this.provideAddNodeOptions = (searchTerm, signal) => this.#options(searchTerm, signal);
4737
+ this.catalog = new TriggerCatalogStore(client, resolveUiLanguage([i18n.lang]), host.cacheEnvironment);
4738
+ this.#client = client;
4739
+ this.#host = host;
4740
+ this.#i18n = i18n;
4741
+ this.#setNotice = setNotice;
4742
+ this.#workspace = workspace;
4743
+ this.#selected = compute((get) => {
4744
+ const selection = get(workspace.$.selection);
4745
+ const current = target(selection);
4746
+ const state = get(this.#state);
4747
+ if (selection?.kind != "trigger") return { authorizationPending: false };
4748
+ const trigger = selection.trigger;
4749
+ if (trigger.kind != "poll" && trigger.kind != "integration") return { authorizationPending: false };
4750
+ const connections = current == null ? void 0 : get(workspace.catalogs.connections.get(current.provider, get(workspace.$.flowId))).data;
4751
+ const catalog = connections == null ? void 0 : connectionCatalog(connections);
4752
+ const connectionError = current == null ? void 0 : state.connectionError?.provider == current.provider ? new Error(state.connectionError.message) : get(workspace.catalogs.connections.get(current.provider, get(workspace.$.flowId))).error;
4753
+ return {
4754
+ activeConnections: catalog?.active,
4755
+ authorizationPending: state.authorizationProvider == current?.provider,
4756
+ connection: current?.connectionId == null ? void 0 : catalog?.byId.get(current.connectionId),
4757
+ connectionError: connectionError == null ? void 0 : errorNotice(connectionError, this.#i18n.t).message,
4758
+ definition: trigger.definition
4759
+ };
4760
+ });
4761
+ this.$ = {
4762
+ connectionLoading: derive(this.#state, (state) => state.connectionLoading),
4763
+ selectedActiveConnections: derive(this.#selected, (selection) => selection.activeConnections),
4764
+ selectedAuthorizationPending: derive(this.#selected, (selection) => selection.authorizationPending),
4765
+ selectedConnection: derive(this.#selected, (selection) => selection.connection),
4766
+ selectedConnectionError: derive(this.#selected, (selection) => selection.connectionError),
4767
+ selectedDefinition: derive(this.#selected, (selection) => selection.definition)
4768
+ };
4769
+ }
4770
+ dispose() {
4771
+ this.#disposed = true;
4772
+ this.catalog.dispose();
4773
+ this.#refresh.invalidate();
4774
+ for (const value of Object.values(this.$)) value.dispose();
4775
+ this.#selected.dispose();
4776
+ this.#state.dispose();
4777
+ }
4778
+ reset() {
4779
+ if (this.#disposed) return;
4780
+ this.#stale.clear();
4781
+ this.#refresh.invalidate();
4782
+ this.#state.set(initialState$1);
4783
+ }
4784
+ #options(searchTerm, signal) {
4785
+ const source = this.catalog.get();
4786
+ const query = searchTerm.trim().toLowerCase();
4787
+ return scopedValue(signal, (get) => {
4788
+ const state = get(source);
4789
+ get(this.#i18n.t$);
4790
+ const enabled = get(this.#workspace.$.flowId) != null && get(this.#workspace.$.draft) != null;
4791
+ const catalog = state.data;
4792
+ const definitions = catalog?.definitions.filter((item) => !query || [
4793
+ catalog.display[item.key]?.displayName,
4794
+ catalog.display[item.key]?.description,
4795
+ item.description,
4796
+ item.displayName,
4797
+ item.key,
4798
+ item.name,
4799
+ item.provider,
4800
+ item.type
4801
+ ].some((value) => value?.toLowerCase().includes(query)));
4220
4802
  return {
4221
4803
  ...state,
4222
4804
  data: !enabled ? [] : definitions?.map((definition) => option(definition, this.#i18n, catalog.display[definition.key]))
@@ -5341,7 +5923,7 @@ var PresentationChanges = class {
5341
5923
  };
5342
5924
  //#endregion
5343
5925
  //#region src/workbench/browser/runtime/stores/workspaceModel.ts
5344
- var initialState$1 = {
5926
+ var initialState = {
5345
5927
  checkLoading: false,
5346
5928
  selectedNodeIds: [],
5347
5929
  workspaceLoadFailed: false,
@@ -5371,7 +5953,7 @@ function selectedModuleEditor(revision, nodeIds) {
5371
5953
  };
5372
5954
  }
5373
5955
  var WorkspaceModel = class {
5374
- #state = val(initialState$1);
5956
+ #state = val(initialState);
5375
5957
  #catalogValues;
5376
5958
  constructor(i18n, flows) {
5377
5959
  const busy = derive(this.#state, (state) => state.busy);
@@ -5898,270 +6480,6 @@ var EditSession = class {
5898
6480
  }
5899
6481
  };
5900
6482
  //#endregion
5901
- //#region src/workbench/browser/runtime/stores/flowCatalog.ts
5902
- var pageLimit = 50;
5903
- var initialState = {
5904
- failed: false,
5905
- loaded: false,
5906
- loading: true,
5907
- loadingMore: false,
5908
- loadMoreFailed: false,
5909
- refreshing: false,
5910
- flows: []
5911
- };
5912
- var FlowCatalog = class {
5913
- #client;
5914
- #i18n;
5915
- #session = new Latest();
5916
- #setNotice;
5917
- #state = val(initialState);
5918
- #lifetime = new AbortController();
5919
- #pending = /* @__PURE__ */ new Set();
5920
- #disposed = false;
5921
- constructor(client, setNotice, i18n) {
5922
- this.#client = client;
5923
- this.#i18n = i18n;
5924
- this.#setNotice = setNotice;
5925
- this.$ = {
5926
- failed: derive(this.#state, (state) => state.failed),
5927
- loading: derive(this.#state, (state) => state.loading),
5928
- loadingMore: derive(this.#state, (state) => state.loadingMore),
5929
- loadMoreFailed: derive(this.#state, (state) => state.loadMoreFailed),
5930
- refreshing: derive(this.#state, (state) => state.refreshing),
5931
- nextCursor: derive(this.#state, (state) => state.nextCursor),
5932
- flows: derive(this.#state, (state) => state.flows),
5933
- total: derive(this.#state, (state) => state.total)
5934
- };
5935
- }
5936
- get loaded() {
5937
- return this.#state.value.loaded;
5938
- }
5939
- dispose() {
5940
- this.#disposed = true;
5941
- this.#lifetime.abort();
5942
- this.#session.invalidate();
5943
- for (const value of Object.values(this.$)) value.dispose();
5944
- this.#state.dispose();
5945
- }
5946
- flow(flowId) {
5947
- return this.#state.value.flows.find((flow) => flow.flowId == flowId);
5948
- }
5949
- async reload() {
5950
- const current = this.#session.begin();
5951
- const loaded = this.#state.value.loaded;
5952
- if (loaded) this.#set({
5953
- failed: false,
5954
- loadingMore: false,
5955
- loadMoreFailed: false,
5956
- refreshing: true
5957
- });
5958
- else this.#set({
5959
- failed: false,
5960
- loading: true,
5961
- loadingMore: false,
5962
- loadMoreFailed: false,
5963
- refreshing: false,
5964
- nextCursor: void 0,
5965
- flows: [],
5966
- total: void 0
5967
- });
5968
- try {
5969
- const page = await this.#client.listFlows({
5970
- includeTotal: true,
5971
- limit: pageLimit
5972
- });
5973
- if (!current()) return;
5974
- this.#set({
5975
- loaded: true,
5976
- loading: false,
5977
- refreshing: false,
5978
- nextCursor: page.nextCursor,
5979
- flows: page.flows,
5980
- total: page.total
5981
- });
5982
- } catch (error) {
5983
- if (!current()) return;
5984
- this.#set({
5985
- failed: !loaded,
5986
- loading: false,
5987
- refreshing: false
5988
- });
5989
- this.#setNotice(errorNotice(error, this.#i18n.t));
5990
- }
5991
- }
5992
- async loadMore() {
5993
- const { loadingMore, nextCursor, refreshing } = this.#state.value;
5994
- if (loadingMore || refreshing || nextCursor == null) return;
5995
- const current = this.#session.capture();
5996
- this.#set({
5997
- loadingMore: true,
5998
- loadMoreFailed: false
5999
- });
6000
- try {
6001
- const page = await this.#client.listFlows({
6002
- cursor: nextCursor,
6003
- limit: pageLimit
6004
- });
6005
- if (!current()) return;
6006
- const flows = new Map(this.#state.value.flows.map((flow) => [flow.flowId, flow]));
6007
- for (const flow of page.flows) flows.set(flow.flowId, flow);
6008
- this.#set({
6009
- loadingMore: false,
6010
- nextCursor: page.nextCursor,
6011
- flows: [...flows.values()]
6012
- });
6013
- } catch (error) {
6014
- if (!current()) return;
6015
- this.#set({
6016
- loadingMore: false,
6017
- loadMoreFailed: true
6018
- });
6019
- this.#setNotice(errorNotice(error, this.#i18n.t));
6020
- }
6021
- }
6022
- async setEnabled(flow, enabled) {
6023
- if (flow.live == null || this.#pending.has(flow.flowId)) return;
6024
- this.#pending.add(flow.flowId);
6025
- try {
6026
- const changed = await this.#client.setFlowEnabled(flow.flowId, flow.live.publicationId, enabled);
6027
- if (!this.#disposed) this.include(changed);
6028
- } catch (error) {
6029
- if (!this.#disposed) {
6030
- this.#setNotice(errorNotice(error, this.#i18n.t));
6031
- await this.reload();
6032
- }
6033
- } finally {
6034
- this.#pending.delete(flow.flowId);
6035
- }
6036
- }
6037
- async publish(flow) {
6038
- if (this.#pending.has(flow.flowId)) return;
6039
- this.#pending.add(flow.flowId);
6040
- try {
6041
- const initial = await this.#client.publishFlow(flow.flowId, flow.draftRevisionId, flow.live?.publicationId ?? null);
6042
- if (this.#disposed) return;
6043
- const operation = await Effect.runPromise(Effect.gen({ self: this }, function* () {
6044
- let current = initial;
6045
- while (current.status == "pending") {
6046
- yield* Effect.sleep(1e3);
6047
- current = yield* Effect.tryPromise((signal) => this.#client.getPublishOperation(flow.flowId, current.operationId, signal));
6048
- }
6049
- return current;
6050
- }), { signal: this.#lifetime.signal });
6051
- if (this.#disposed) return;
6052
- if (operation.status == "failed") this.#setNotice({
6053
- kind: "error",
6054
- message: operation.issue.message
6055
- });
6056
- else this.#setNotice({
6057
- kind: "success",
6058
- message: this.#i18n.t("notice.published", { name: flow.name })
6059
- });
6060
- this.include(await this.#client.getFlow(flow.flowId));
6061
- } catch (error) {
6062
- if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
6063
- } finally {
6064
- this.#pending.delete(flow.flowId);
6065
- }
6066
- }
6067
- create(name, create) {
6068
- return this.#create(async () => create == null ? this.#client.createFlow(name) : this.#client.getFlow(await create(name)));
6069
- }
6070
- import(file, name, key, importer) {
6071
- return this.#create(async () => importer == null ? this.#client.importFlow(file, {
6072
- name,
6073
- idempotencyKey: key
6074
- }) : this.#client.getFlow(await importer(file, name, key)));
6075
- }
6076
- async #create(create) {
6077
- try {
6078
- const created = await create();
6079
- if (this.#disposed) return;
6080
- this.#insert(created);
6081
- return created;
6082
- } catch (error) {
6083
- if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
6084
- }
6085
- }
6086
- async delete(flowId) {
6087
- const flow = this.flow(flowId);
6088
- if (flow == null || flow.status == "retiring") return false;
6089
- try {
6090
- await this.#client.deleteFlow(flowId);
6091
- if (this.#disposed) return false;
6092
- this.#remove(flowId);
6093
- this.#setNotice({
6094
- kind: "success",
6095
- message: this.#i18n.t("notice.flowDeleteAccepted", { name: flow.name })
6096
- });
6097
- return true;
6098
- } catch (error) {
6099
- if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
6100
- return false;
6101
- }
6102
- }
6103
- async rename(flowId, name) {
6104
- const flow = this.flow(flowId);
6105
- const nextName = name.trim();
6106
- if (flow == null || nextName.length == 0) return false;
6107
- if (flow.name == nextName) return true;
6108
- try {
6109
- const changed = await this.#client.renameFlow(flowId, nextName);
6110
- if (this.#disposed) return false;
6111
- this.include(changed);
6112
- this.#setNotice({
6113
- kind: "success",
6114
- message: this.#i18n.t("notice.flowRenamed", { name: nextName })
6115
- });
6116
- return true;
6117
- } catch (error) {
6118
- if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
6119
- return false;
6120
- }
6121
- }
6122
- include(flow) {
6123
- const index = this.#state.value.flows.findIndex((candidate) => candidate.flowId == flow.flowId);
6124
- if (index < 0) {
6125
- this.#set({ flows: [...this.#state.value.flows, flow] });
6126
- return;
6127
- }
6128
- const referenceCount = this.#state.value.flows[index].referenceCount;
6129
- this.#set({ flows: this.#state.value.flows.with(index, {
6130
- ...flow,
6131
- ...referenceCount == null ? {} : { referenceCount }
6132
- }) });
6133
- }
6134
- #insert(flow) {
6135
- if (this.#state.value.flows.some((candidate) => candidate.flowId == flow.flowId)) return;
6136
- this.#set({
6137
- flows: [...this.#state.value.flows, flow],
6138
- total: this.#state.value.total == null ? void 0 : this.#state.value.total + 1
6139
- });
6140
- }
6141
- #remove(flowId) {
6142
- this.#set({
6143
- flows: this.#state.value.flows.filter((flow) => flow.flowId != flowId),
6144
- total: this.#state.value.total == null ? void 0 : this.#state.value.total - 1
6145
- });
6146
- }
6147
- advanceHead(flowId, revisionId, content) {
6148
- this.#set({ flows: this.#state.value.flows.map((flow) => flow.flowId == flowId ? {
6149
- ...flow,
6150
- draftRevisionId: revisionId,
6151
- resourceReferences: {
6152
- ...flow.resourceReferences,
6153
- draft: flowResourceReferences(content)
6154
- }
6155
- } : flow) });
6156
- }
6157
- #set(patch) {
6158
- if (!this.#disposed) this.#state.set({
6159
- ...this.#state.value,
6160
- ...patch
6161
- });
6162
- }
6163
- };
6164
- //#endregion
6165
6483
  //#region src/workbench/browser/runtime/stores/nodeEdits.ts
6166
6484
  var NodeEdits = class {
6167
6485
  constructor(revision, save, identity) {
@@ -6384,6 +6702,7 @@ var WorkspaceStore = class {
6384
6702
  };
6385
6703
  };
6386
6704
  this.#client = client;
6705
+ this.state = client;
6387
6706
  this.#setNotice = setNotice;
6388
6707
  this.#identity = identity;
6389
6708
  this.#clipboard = new CanvasClipboard(identity, i18n.t);
@@ -6622,11 +6941,11 @@ var WorkspaceStore = class {
6622
6941
  this.#edits.set({ busy: void 0 });
6623
6942
  }
6624
6943
  }
6625
- createFlow(name, create) {
6626
- return this.#createFlow(() => this.#flows.create(name, create));
6944
+ createFlow(name, create, tagIds) {
6945
+ return this.#createFlow(() => this.#flows.create(name, create, tagIds));
6627
6946
  }
6628
- importFlow(file, name, key, importer) {
6629
- return this.#createFlow(() => this.#flows.import(file, name, key, importer));
6947
+ importFlow(file, name, key, importer, tagIds) {
6948
+ return this.#createFlow(() => this.#flows.import(file, name, key, importer, tagIds));
6630
6949
  }
6631
6950
  async #createFlow(create) {
6632
6951
  if (!await this.saveModuleEditor()) return;
@@ -6647,19 +6966,22 @@ var WorkspaceStore = class {
6647
6966
  getErrorListeners(flowId, signal) {
6648
6967
  return this.#client.getErrorListeners(flowId, signal);
6649
6968
  }
6650
- async deleteFlow(flowId) {
6969
+ async deleteFlow(flow) {
6651
6970
  if (!await this.saveModuleEditor()) return false;
6652
6971
  this.#edits.set({ busy: "flow" });
6653
6972
  try {
6654
- return await this.#flows.delete(flowId);
6973
+ return await this.#flows.delete(flow);
6655
6974
  } finally {
6656
6975
  this.#edits.set({ busy: void 0 });
6657
6976
  }
6658
6977
  }
6659
- async renameFlow(flowId, name) {
6978
+ updateFlowSettings(flowId, changes) {
6979
+ return this.#flows.updateSettings(flowId, changes);
6980
+ }
6981
+ async renameFlow(flow, name) {
6660
6982
  this.#edits.set({ busy: "flow" });
6661
6983
  try {
6662
- return await this.#flows.rename(flowId, name);
6984
+ return await this.#flows.rename(flow, name);
6663
6985
  } finally {
6664
6986
  this.#edits.set({ busy: void 0 });
6665
6987
  }
@@ -6914,6 +7236,16 @@ function indexRunEventNodes(draft, run, events, nodes) {
6914
7236
  }
6915
7237
  var blockedExternalPages = { openExternalPage: async () => false };
6916
7238
  var WorkbenchStore = class {
7239
+ createFlowCatalog() {
7240
+ return new FlowCatalog(this.#client, (notice) => {
7241
+ if (!this.#disposed) this.#notice.set(notice);
7242
+ }, this.#i18n);
7243
+ }
7244
+ async updateFlowSettings(flowId, changes) {
7245
+ const updated = await this.workspace.updateFlowSettings(flowId, changes);
7246
+ if (updated) await this.tags.reload();
7247
+ return updated;
7248
+ }
6917
7249
  #client;
6918
7250
  #externalRuns;
6919
7251
  #i18n;
@@ -6955,12 +7287,14 @@ var WorkbenchStore = class {
6955
7287
  this.interactiveMode$ = val(readCanvasInteractiveMode(preferences));
6956
7288
  this.interactiveMode$.reaction((mode) => writeCanvasInteractiveMode(preferences, mode), true);
6957
7289
  this.#client = client;
7290
+ this.state = client;
6958
7291
  this.results = client;
6959
7292
  this.#i18n = i18n;
6960
7293
  this.#variables = variables;
6961
7294
  const setNotice = (notice) => {
6962
7295
  if (!this.#disposed) this.#notice.set(notice);
6963
7296
  };
7297
+ this.tags = new TagCatalog(client, setNotice, i18n, () => this.workspace.reloadFlows());
6964
7298
  this.connectorAccess = new ConnectorAccessStore(client, setNotice, i18n, (flowId) => this.workspace.catalogs.refreshFlow(flowId));
6965
7299
  this.runs = new RunStore(client, setNotice, i18n);
6966
7300
  this.workspace = new WorkspaceStore(client, setNotice, createAuthoringId, i18n, (event) => {
@@ -6986,7 +7320,11 @@ var WorkbenchStore = class {
6986
7320
  this.runRequests = new RunRequestStore(client, this.runs, setNotice, async (flowId) => {
6987
7321
  if (flowId != this.workspace.$.flowId.value || !await this.workspace.saveDraft() || this.#disposed || flowId != this.workspace.$.flowId.value) return;
6988
7322
  return this.workspace.$.draft.value;
6989
- }, i18n, identity);
7323
+ }, i18n, identity, {
7324
+ sessionId: cacheSessionId,
7325
+ environment: host.cacheEnvironment,
7326
+ connectorOwnerId: host.connectorOwnerId
7327
+ });
6990
7328
  const diagnostics = compute((get) => {
6991
7329
  const check = get(this.workspace.$.diagnostics);
6992
7330
  if (check == null) return;
@@ -7073,6 +7411,7 @@ var WorkbenchStore = class {
7073
7411
  };
7074
7412
  }
7075
7413
  dispose() {
7414
+ this.tags.dispose();
7076
7415
  this.#disposed = true;
7077
7416
  this.#stopAccessReaction();
7078
7417
  this.interactiveMode$.dispose();
@@ -7110,7 +7449,7 @@ var WorkbenchStore = class {
7110
7449
  const model = this.$.designer.value;
7111
7450
  const title = this.workspace.$.flow.value?.name ?? this.workspace.$.flowId.value ?? "Workflow";
7112
7451
  try {
7113
- const { downloadFlowSvg } = await import("./exportSvg-Dcz4HTo_.js");
7452
+ const { downloadFlowSvg } = await import("./exportSvg-CG8hEUNk.js");
7114
7453
  await downloadFlowSvg(model, title, language, options);
7115
7454
  if (!this.#disposed) this.#notice.set({
7116
7455
  kind: "success",
@@ -7173,11 +7512,11 @@ var WorkbenchStore = class {
7173
7512
  this.#openingCreatedFlow = false;
7174
7513
  }
7175
7514
  }
7176
- async importFlow(file, name, key, importer) {
7177
- return await this.#openNewFlow(await this.workspace.importFlow(file, name, key, importer), "notice.flowImported");
7515
+ async importFlow(file, name, key, importer, tagIds) {
7516
+ return await this.#openNewFlow(await this.workspace.importFlow(file, name, key, importer, tagIds), "notice.flowImported");
7178
7517
  }
7179
- async createFlow(name, create) {
7180
- return await this.#openNewFlow(await this.workspace.createFlow(name, create));
7518
+ async createFlow(name, create, tagIds) {
7519
+ return await this.#openNewFlow(await this.workspace.createFlow(name, create, tagIds));
7181
7520
  }
7182
7521
  async #openNewFlow(created, notice) {
7183
7522
  if (this.#disposed || created == null) return false;
@@ -7364,7 +7703,7 @@ var WorkbenchStore = class {
7364
7703
  };
7365
7704
  //#endregion
7366
7705
  //#region src/workbench/browser/runtime/openFlowWorkbench.tsx
7367
- var FlowWorkspace = lazy(() => import("./flowWorkspace-Bl0SDehy.js"));
7706
+ var FlowWorkspace = lazy(() => import("./flowWorkspace-BFHGev7s.js"));
7368
7707
  function OpenFlowSessionGate({ action, description, error, onSubmit, onTokenChange, pending = false, title, token, tokenLabel }) {
7369
7708
  const hasToken = token != null && tokenLabel != null && onTokenChange != null;
7370
7709
  const tokenValue = token ?? "";
@@ -7485,8 +7824,8 @@ function Workbench({ importRef, catalogWidth, createFlow, importFlow, createFlow
7485
7824
  }),
7486
7825
  initializing,
7487
7826
  language,
7488
- onCreateFlow: (name) => navigation.createFlow(name, createFlow),
7489
- onImportFlow: (file, name, key) => navigation.importFlow(file, name, key, importFlow),
7827
+ onCreateFlow: (name, tagIds) => navigation.createFlow(name, createFlow, tagIds),
7828
+ onImportFlow: (file, name, key, tagIds) => navigation.importFlow(file, name, key, importFlow, tagIds),
7490
7829
  onLanguageChange,
7491
7830
  onSelectFlow: (flow, view) => void navigation.selectFlow(flow, view),
7492
7831
  store