@oomol-lab/open-flow 0.1.0-beta.62 → 0.1.0-beta.63

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 (51) hide show
  1. package/dist/browser/{createResourceDialog-DZgJsr3e.js → createResourceDialog-BAJXbzNH.js} +20 -7
  2. package/dist/browser/flow-authoring.js +8 -7
  3. package/dist/browser/flow-change-schema.d.ts +1 -0
  4. package/dist/browser/flow-change.d.ts +1 -0
  5. package/dist/browser/flow-change.js +8 -7
  6. package/dist/browser/{flowChanges-CzYIVevI.js → flowChanges-DpZOQuJE.js} +784 -443
  7. package/dist/browser/{flowRunInputEditorStore-Dsxyfwae.js → flowRunInputEditorStore-B2Rw4VsT.js} +1 -1
  8. package/dist/browser/{flowWorkspace-BgZaFKZm.js → flowWorkspace-B7a5NNA_.js} +41 -29
  9. package/dist/browser/{icons-CajfDpy_.js → icons-Db2Jb2iX.js} +30 -10
  10. package/dist/browser/preview.css +5 -6
  11. package/dist/browser/preview.js +437 -402
  12. package/dist/browser/{typeScriptSession-fXPMFGjW.js → typeScriptSession-D0bjYDeg.js} +1 -1
  13. package/dist/browser/ui.css +4 -5
  14. package/dist/browser/ui.js +2 -2
  15. package/dist/browser/{value-DL_poNiC.js → value-BADHeZ9I.js} +12 -1
  16. package/dist/browser/workbench-contract.d.ts +10 -0
  17. package/dist/browser/workbench.css +5 -6
  18. package/dist/browser/workbench.d.ts +5 -3
  19. package/dist/browser/workbench.js +198 -171
  20. package/dist/browser/{workbenchSelect-H9wzEFkH.js → workbenchSelect-C-jf6NIM.js} +1 -1
  21. package/dist/command/index.js +40 -39
  22. package/dist/common/authoringSchema.d.ts +1 -0
  23. package/dist/common/connector-action.d.ts +1 -0
  24. package/dist/common/connector-action.js +15 -12
  25. package/dist/common/control-api-conformance.js +107 -8
  26. package/dist/common/control-api.d.ts +8 -0
  27. package/dist/common/control-api.js +47 -8
  28. package/dist/common/control-requests.d.ts +6 -0
  29. package/dist/common/control-requests.js +41 -18
  30. package/dist/common/decision.js +8 -7
  31. package/dist/common/flow-connectionUsage.d.ts +1 -0
  32. package/dist/common/flow-encoding.d.ts +1 -0
  33. package/dist/common/flow-encoding.js +202 -10
  34. package/dist/common/flow-semantics.js +64 -46
  35. package/dist/common/flow-workflowFile.d.ts +23 -0
  36. package/dist/common/integration-trigger.js +8 -7
  37. package/dist/common/localization.d.ts +1 -0
  38. package/dist/common/localization.js +12 -1
  39. package/dist/common/mcp.d.ts +61 -0
  40. package/dist/common/mcp.js +92 -9
  41. package/dist/common/openapi.js +8 -7
  42. package/dist/common/poll-trigger.js +8 -7
  43. package/dist/common/runtime-contract.js +1 -1
  44. package/dist/common/scheduler.d.ts +22 -2
  45. package/dist/common/scheduler.js +354 -83
  46. package/dist/common/trigger-config.d.ts +2 -0
  47. package/dist/common/webhook-trigger.js +8 -7
  48. package/dist/index.d.ts +2 -2
  49. package/package.json +1 -1
  50. package/skills/open-flow/SKILL.md +1 -1
  51. package/skills/open-flow/references/flow-authoring.md +20 -0
@@ -1,6 +1,7 @@
1
+ import type { WorkflowFile } from '../common/flow-workflowFile.js';
1
2
  export { EventSourcesPage } from './event-sources.js';
2
3
  import type { FormEvent, ReactElement } from 'react';
3
- import type { ConnectionHref, WorkbenchHost, WorkbenchLanguage, WorkbenchLocation, WorkbenchNavigationOptions, WorkbenchPreferences, WorkbenchTheme } from './workbench-contract.js';
4
+ import type { ConnectionHref, OpenFlowWorkbenchHandle, WorkbenchHost, WorkbenchLanguage, WorkbenchLocation, WorkbenchNavigationOptions, WorkbenchPreferences, WorkbenchTheme } from './workbench-contract.js';
4
5
  export declare function OpenFlowSessionGate({ action, description, error, onSubmit, onTokenChange, pending, title, token, tokenLabel, }: {
5
6
  readonly action?: string | undefined;
6
7
  readonly description: string;
@@ -13,11 +14,12 @@ export declare function OpenFlowSessionGate({ action, description, error, onSubm
13
14
  readonly tokenLabel?: string | undefined;
14
15
  }): ReactElement;
15
16
  export type { WorkbenchActor } from './workbench-contract.js';
16
- export type { FlowCatalogEvent, FlowChangeEvent, WorkbenchHost, WorkbenchLanguage, WorkbenchLocation, WorkbenchNavigationOptions, WorkbenchNotification, WorkbenchPreferences, WorkbenchTheme, WorkbenchView, } from './workbench-contract.js';
17
+ export type { OpenFlowWorkbenchHandle, WorkbenchImportInput, FlowCatalogEvent, FlowChangeEvent, WorkbenchHost, WorkbenchLanguage, WorkbenchLocation, WorkbenchNavigationOptions, WorkbenchNotification, WorkbenchPreferences, WorkbenchTheme, WorkbenchView, } from './workbench-contract.js';
17
18
  export { isUiLanguage as isWorkbenchLanguage, resolveUiLanguage as resolveWorkbenchLanguage, uiLanguageNames as workbenchLanguageNames, uiLanguages as workbenchLanguages, } from '../common/localization.js';
18
19
  export interface OpenFlowWorkbenchProps {
19
20
  /** Embedded catalogs fill a host-owned page without adding width limits or page padding. */
20
21
  readonly catalogWidth?: 'default' | 'full' | 'embedded' | undefined;
22
+ readonly importFlow?: ((file: WorkflowFile, name: string, idempotencyKey: string) => Promise<string>) | undefined;
21
23
  readonly createFlow?: ((name: string) => Promise<string>) | undefined;
22
24
  readonly createFlowDisabled?: boolean | undefined;
23
25
  readonly createFlowField?: {
@@ -62,4 +64,4 @@ export interface OpenFlowWorkbenchProps {
62
64
  readonly theme: WorkbenchTheme;
63
65
  readonly variables?: boolean;
64
66
  }
65
- export declare function OpenFlowWorkbench({ sessionKey, ...props }: OpenFlowWorkbenchProps): ReactElement;
67
+ export declare const OpenFlowWorkbench: import("react").ForwardRefExoticComponent<OpenFlowWorkbenchProps & import("react").RefAttributes<OpenFlowWorkbenchHandle>>;
@@ -1,9 +1,9 @@
1
- import { $ as RunStore, $n as connection, $t as DropdownMenuContent, At as followWorkbenchLink, B as CacheResource, Bn as errorNotice, Cn as Badge, Ct as setCodeActions, Dn as createI18n, Dt as updateTriggerConfig, Et as setTriggerConnection, F as feishuResourceKind, Gn as decodeTriggerCatalog, Gt as Empty, H as resourceData, Hn as randomId, Ht as InputGroup, I as supportsFeishuChatFilter, It as PopoverTitle, J as providerIcon, Jn as sampleErrorOutputs, Jt as EmptyHeader, Kt as EmptyContent, L as combineSources, M as addNodeIntent, Mt as PopoverContent, N as deriveAddNodeOptions, Ot as updateTriggerSchedule, St as resetInputValues, Tt as setInputSources, U as resourceValue, Un as allConnectorConnectionsQuery, Ut as InputGroupAddon, Vn as WorkbenchClient, W as designerGraph, Wt as InputGroupInput, Xn as isJsonObject, Xt as EmptyTitle, Yn as triggerOutputDefinitions, Yt as EmptyMedia, Zt as DropdownMenu, _ as updateValue, _t as setComment, a as nameCreatedNodes, an as DropdownMenuSeparator, at as deriveInspectorDiagnostics, bt as setNodePositions, c as updateCondition, d as updateNodeName, dr as invalidResponse, dt as connectionCatalog, er as connectorActionMetadata, f as updateNodeSettings, fr as jsonValue, ft as canvasPresentationChange, g as updateTaskPorts, gr as controlErrorCode, gt as restoreCanvasPresentation, h as updateTaskAdditionalInputs, hr as ApiError, ht as removeComments, i as deleteSelection, it as Latest, jt as Popover, kt as IdTooltip, l as updateNodeDescription, ln as DropdownMenuTrigger, m as updateTask, mr as string, mt as flowPresentation, nt as revisionView, o as setInputValue, ot as diagnosticItems, p as updateResolution, pr as record, pt as commentIds, qt as EmptyDescription, r as applyFlowChanges, rt as flowResourceReferences, s as setInputVariable, t as addNode, tn as DropdownMenuItem, tr as providerIconAppearance, u as updateNodeIcon, ur as inputValue, ut as actionWithConnections, v as updateWebhook, vt as setFlowViewport, wn as EventSourcesPage, wt as setConnectorConnection, xt as repairNodeNames, yr as IconThemeContext, yt as setNodeContentHidden, z as scopedValue } from "./flowChanges-CzYIVevI.js";
1
+ import { $n as connection, $t as DropdownMenu, At as WorkflowExportMenuItem, B as CacheResource, Ct as resetInputValues, Dt as setTriggerConnection, En as EventSourcesPage, Et as setInputSources, F as feishuResourceKind, G as designerGraph, Gn as allConnectorConnectionsQuery, Gt as InputGroupAddon, H as resourceData, Hn as errorNotice, I as supportsFeishuChatFilter, Jt as EmptyContent, Kt as InputGroupInput, L as combineSources, M as addNodeIntent, Mt as followWorkbenchLink, N as deriveAddNodeOptions, Nt as Popover, Ot as updateTriggerConfig, Pt as PopoverContent, Qn as isJsonObject, Qt as EmptyTitle, Rt as PopoverTitle, Sr as controlErrorCode, St as repairNodeNames, Tn as Badge, Tr as IconThemeContext, Tt as setConnectorConnection, U as resourceValue, Un as WorkbenchClient, W as connectorActionPorts, Wn as randomId, Wt as InputGroup, Xn as sampleErrorOutputs, Xt as EmptyHeader, Y as providerIcon, Yt as EmptyDescription, Zn as triggerOutputDefinitions, Zt as EmptyMedia, _ as updateValue, _r as invalidResponse, _t as restoreCanvasPresentation, a as nameCreatedNodes, at as Latest, br as string, bt as setNodeContentHidden, c as updateCondition, d as updateNodeName, dn as DropdownMenuTrigger, dt as actionWithConnections, er as connectorActionMetadata, et as RunStore, f as updateNodeSettings, ft as connectionCatalog, g as updateTaskPorts, gr as inputValue, gt as removeComments, h as updateTaskAdditionalInputs, ht as flowPresentation, i as deleteSelection, ir as workflowNameFromFile, it as flowResourceReferences, jt as IdTooltip, kn as createI18n, kt as updateTriggerSchedule, l as updateNodeDescription, m as updateTask, mt as commentIds, nr as parseWorkflowFile, o as setInputValue, ot as deriveInspectorDiagnostics, p as updateResolution, pt as canvasPresentationChange, qn as decodeTriggerCatalog, qt as Empty, r as applyFlowChanges, rn as DropdownMenuItem, rr as workflowFileName, rt as revisionView, s as setInputVariable, sn as DropdownMenuSeparator, sr as providerIconAppearance, st as diagnosticItems, t as addNode, tn as DropdownMenuContent, tr as encodeWorkflowFile, u as updateNodeIcon, v as updateWebhook, vr as jsonValue, vt as setComment, wt as setCodeActions, xr as ApiError, xt as setNodePositions, yr as record, yt as setFlowViewport, z as scopedValue } from "./flowChanges-DpZOQuJE.js";
2
2
  import { a as derive, i as compute, l as val } from "./dist-bO8nvHFH.js";
3
- import { a as toPlainObject, c as useLang, l as useTranslate, o as I18nProvider, u as useVal } from "./dist-BFmpkX4P.js";
4
- import { B as applyFlowChanges$1, E as FieldLabel, K as resourceNameIssue, S as Field, T as FieldGroup, X as nodeInputMappings, Y as mapConditionSources, a as DialogDescription, c as DialogTitle, d as TooltipContent, i as DialogContent, n as Dialog, o as DialogFooter, p as TooltipTrigger, r as DialogClose, rt as dequal$1, s as DialogHeader, sr as Input, t as Icon, u as Tooltip, w as FieldError, x as Spinner, xi as cn, yi as Button, z as FlowChangeError } from "./icons-CajfDpy_.js";
5
- import { c as isUiLanguage, d as uiLanguages, l as resolveUiLanguage, u as uiLanguageNames } from "./value-DL_poNiC.js";
6
- import { Suspense, lazy, useEffect, useRef, useState } from "react";
3
+ import { c as useLang, l as useTranslate, o as I18nProvider, u as useVal } from "./dist-BFmpkX4P.js";
4
+ import { B as FlowChangeError, Ci as cn, E as FieldLabel, Q as nodeInputMappings, S as Field, T as FieldGroup, V as applyFlowChanges$1, Z as mapConditionSources, a as DialogDescription, at as dequal$1, c as DialogTitle, d as TooltipContent, i as DialogContent, lr as Input, n as Dialog, o as DialogFooter, p as TooltipTrigger, q as resourceNameIssue, r as DialogClose, s as DialogHeader, t as Icon, u as Tooltip, w as FieldError, x as Spinner, xi as Button } from "./icons-Db2Jb2iX.js";
5
+ import { c as importedWorkflowName, d as uiLanguageNames, f as uiLanguages, l as isUiLanguage, u as resolveUiLanguage } from "./value-BADHeZ9I.js";
6
+ import { Suspense, forwardRef, lazy, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  import * as Cause from "effect/Cause";
9
9
  import * as Effect from "effect/Effect";
@@ -95,11 +95,17 @@ var NavigationStore = class {
95
95
  this.#syncing = false;
96
96
  this.#write(view, false, { runSource });
97
97
  }
98
+ async importFlow(file, name, key, importer) {
99
+ return await this.#openNewFlow(() => this.#store.importFlow(file, name, key, importer));
100
+ }
98
101
  async createFlow(name, create) {
102
+ return await this.#openNewFlow(() => this.#store.createFlow(name, create));
103
+ }
104
+ async #openNewFlow(create) {
99
105
  const change = ++this.#change;
100
106
  this.#syncing = true;
101
107
  try {
102
- const created = await this.#store.createFlow(name, create);
108
+ const created = await create();
103
109
  if (created && change == this.#change) this.#write("design", false);
104
110
  return created;
105
111
  } finally {
@@ -184,7 +190,7 @@ function Skeleton({ className, ...props }) {
184
190
  }
185
191
  //#endregion
186
192
  //#region src/workbench/browser/runtime/shell/resourceBrowser.tsx
187
- var CreateResourceDialog = lazy(() => import("./createResourceDialog-DZgJsr3e.js"));
193
+ var CreateResourceDialog = lazy(() => import("./createResourceDialog-BAJXbzNH.js"));
188
194
  var flowIdTooltipAlignOffset = 44;
189
195
  var renamePopoverAlignOffset = -12;
190
196
  var renamePopoverVerticalShift = 8;
@@ -531,6 +537,10 @@ function FlowItem({ showTeam, badge, busy, flow, href, onSelect, store }) {
531
537
  }), t(flow.live.enabled ? "resource.stop" : "resource.start")]
532
538
  }),
533
539
  /* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "mx-2 bg-border/50" }),
540
+ /* @__PURE__ */ jsx(WorkflowExportMenuItem, {
541
+ disabled: flow.status != "active" || busy != null,
542
+ onClick: () => void store.workspace.exportFlow(flow.flowId)
543
+ }),
534
544
  /* @__PURE__ */ jsxs(DropdownMenuItem, {
535
545
  disabled: flow.status != "active",
536
546
  onClick: () => onSelect(flow, "publications"),
@@ -622,7 +632,7 @@ function FlowSkeleton({ showTeam }) {
622
632
  })]
623
633
  });
624
634
  }
625
- function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBadges, hrefForFlow, initializing = false, onCreateFlow, onSelectFlow, store }) {
635
+ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBadges, hrefForFlow, initializing = false, onCreateFlow, onImportFlow, onSelectFlow, store, language }, ref) {
626
636
  const t = useTranslate();
627
637
  const busy = useVal(store.workspace.$.busy);
628
638
  const loadFailed = useVal(store.workspace.$.flowLoadFailed);
@@ -634,17 +644,54 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
634
644
  const refreshing = useVal(store.workspace.$.flowRefreshing);
635
645
  const flows = useVal(store.workspace.$.flows);
636
646
  const total = useVal(store.workspace.$.flowTotal);
637
- const [creating, setCreating] = useState(false);
647
+ const [creation$] = useState(() => val(void 0));
648
+ const creation = useVal(creation$);
649
+ const [importError, setImportError] = useState(false);
650
+ const fileInput = useRef(null);
638
651
  const [filter, setFilter] = useState("");
639
652
  const [name, setName] = useState("");
640
653
  const showTeam = flows.some((flow) => (flowBadges?.[flow.flowId]?.trim().length ?? 0) > 0);
641
654
  const normalized = filter.trim().toLocaleLowerCase();
642
655
  const visible = flows.filter((flow) => flow.name.toLocaleLowerCase().includes(normalized));
656
+ const command = useRef();
657
+ useImperativeHandle(ref, () => ({ openImport: (input) => command.current?.(input) ?? "unavailable" }), []);
658
+ useLayoutEffect(() => {
659
+ command.current = openImport;
660
+ return () => {
661
+ command.current = void 0;
662
+ };
663
+ });
664
+ function openImport(input) {
665
+ if (initializing || store.workspace.$.busy.value != null || creation$.value != null || onImportFlow == null) return "unavailable";
666
+ setImportError(false);
667
+ let file;
668
+ try {
669
+ file = parseWorkflowFile(input.text);
670
+ } catch {
671
+ setImportError(true);
672
+ return "invalid-file";
673
+ }
674
+ setName(file.name ?? (input.fileName == null ? importedWorkflowName(language) : workflowNameFromFile(input.fileName)));
675
+ creation$.set({
676
+ kind: "import",
677
+ file,
678
+ key: randomId(),
679
+ ...input.sourceUrl == null ? {} : { sourceUrl: input.sourceUrl }
680
+ });
681
+ return "opened";
682
+ }
683
+ function openCreate() {
684
+ if (creation$.value != null) return;
685
+ setName("");
686
+ creation$.set({ kind: "create" });
687
+ }
643
688
  async function create(event) {
644
689
  event.preventDefault();
690
+ const request = creation$.value;
645
691
  const nextName = name.trim();
646
- if (resourceNameIssue(nextName) != null || !await onCreateFlow(nextName)) return;
647
- setCreating(false);
692
+ if (request == null || resourceNameIssue(nextName) != null) return;
693
+ if (!(request.kind == "create" ? await onCreateFlow(nextName) : await onImportFlow?.(request.file, nextName, request.key)) || command.current == null || creation$.value !== request) return;
694
+ creation$.set(void 0);
648
695
  setName("");
649
696
  }
650
697
  return /* @__PURE__ */ jsx("main", {
@@ -675,30 +722,61 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
675
722
  }), !loading && /* @__PURE__ */ jsx("span", { children: t("resource.flowCount", { count: total ?? flows.length }) })]
676
723
  }), /* @__PURE__ */ jsxs("div", {
677
724
  className: "resource-list-actions",
678
- children: [/* @__PURE__ */ jsxs(InputGroup, {
679
- className: "w-full sm:w-56",
680
- children: [/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, {
681
- name: "search",
682
- size: 17
683
- }) }), /* @__PURE__ */ jsx(InputGroupInput, {
684
- autoComplete: "off",
685
- "aria-label": t("resource.searchByName"),
686
- name: "flow-search",
687
- onChange: (event) => setFilter(event.target.value),
688
- placeholder: t("resource.searchByName"),
689
- value: filter
690
- })]
691
- }), /* @__PURE__ */ jsxs(Button, {
692
- className: "pr-3",
693
- disabled: initializing || busy != null,
694
- onClick: () => setCreating(true),
695
- children: [/* @__PURE__ */ jsx(Icon, {
696
- "data-icon": "inline-start",
697
- name: "plus"
698
- }), t("resource.newFlow")]
699
- })]
725
+ children: [
726
+ /* @__PURE__ */ jsxs(InputGroup, {
727
+ className: "w-full sm:w-56",
728
+ children: [/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, {
729
+ name: "search",
730
+ size: 17
731
+ }) }), /* @__PURE__ */ jsx(InputGroupInput, {
732
+ autoComplete: "off",
733
+ "aria-label": t("resource.searchByName"),
734
+ name: "flow-search",
735
+ onChange: (event) => setFilter(event.target.value),
736
+ placeholder: t("resource.searchByName"),
737
+ value: filter
738
+ })]
739
+ }),
740
+ onImportFlow != null && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Input, {
741
+ ref: fileInput,
742
+ className: "hidden",
743
+ type: "file",
744
+ accept: ".openflow.json,.json,application/json",
745
+ "aria-label": t("resource.importFlow"),
746
+ onChange: (event) => {
747
+ const file = event.target.files?.[0];
748
+ event.target.value = "";
749
+ if (file == null) return;
750
+ setImportError(false);
751
+ file.text().then((text) => {
752
+ command.current?.({
753
+ text,
754
+ fileName: file.name
755
+ });
756
+ }).catch(() => setImportError(true));
757
+ }
758
+ }), /* @__PURE__ */ jsx(Button, {
759
+ variant: "outline",
760
+ disabled: initializing || busy != null || createFlowDisabled,
761
+ onClick: () => fileInput.current?.click(),
762
+ children: t("resource.importFlow")
763
+ })] }),
764
+ /* @__PURE__ */ jsxs(Button, {
765
+ className: "pr-3",
766
+ disabled: initializing || busy != null,
767
+ onClick: openCreate,
768
+ children: [/* @__PURE__ */ jsx(Icon, {
769
+ "data-icon": "inline-start",
770
+ name: "plus"
771
+ }), t("resource.newFlow")]
772
+ })
773
+ ]
700
774
  })]
701
775
  }),
776
+ importError && /* @__PURE__ */ jsx("p", {
777
+ role: "alert",
778
+ children: t("resource.importInvalid")
779
+ }),
702
780
  /* @__PURE__ */ jsx("div", {
703
781
  className: "resource-table-scroll",
704
782
  children: /* @__PURE__ */ jsxs("div", {
@@ -762,7 +840,7 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
762
840
  /* @__PURE__ */ jsx(EmptyTitle, { children: t(normalized.length == 0 ? "resource.noFlows" : "resource.noMatchingFlows") }),
763
841
  /* @__PURE__ */ jsx(EmptyDescription, { children: t(normalized.length == 0 ? "resource.noFlowsDescription" : "resource.noMatchingDescription") })
764
842
  ] }), normalized.length == 0 && /* @__PURE__ */ jsx(EmptyContent, { children: /* @__PURE__ */ jsxs(Button, {
765
- onClick: () => setCreating(true),
843
+ onClick: openCreate,
766
844
  variant: "outline",
767
845
  children: [/* @__PURE__ */ jsx(Icon, {
768
846
  "data-icon": "inline-start",
@@ -792,7 +870,7 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
792
870
  })
793
871
  ]
794
872
  }),
795
- creating && /* @__PURE__ */ jsx(Suspense, {
873
+ creation != null && /* @__PURE__ */ jsx(Suspense, {
796
874
  fallback: null,
797
875
  children: /* @__PURE__ */ jsx(CreateResourceDialog, {
798
876
  disabled: createFlowDisabled,
@@ -802,16 +880,23 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
802
880
  label: t("resource.flowName"),
803
881
  name,
804
882
  onNameChange: setName,
805
- onOpenChange: setCreating,
883
+ onOpenChange: (open) => {
884
+ if (!open) {
885
+ creation$.set(void 0);
886
+ setName("");
887
+ }
888
+ },
806
889
  onSubmit: (event) => void create(event),
807
890
  pending: busy == "flow",
808
- title: t("resource.newFlow")
891
+ sourceUrl: creation.kind == "import" ? creation.sourceUrl : void 0,
892
+ submitLabel: creation.kind == "import" ? t("resource.importFlow") : void 0,
893
+ title: t(creation.kind == "create" ? "resource.newFlow" : "resource.importFlow")
809
894
  })
810
895
  })
811
896
  ]
812
897
  })
813
898
  });
814
- }
899
+ });
815
900
  //#endregion
816
901
  //#region ../../node_modules/.bun/dequal@2.0.3/node_modules/dequal/dist/index.mjs
817
902
  var has = Object.prototype.hasOwnProperty;
@@ -1542,7 +1627,7 @@ function inputSignature(spec) {
1542
1627
  }
1543
1628
  async function inputEditor(spec, language, values) {
1544
1629
  if (spec == null) return void 0;
1545
- const { FlowRunInputEditorStore } = await import("./flowRunInputEditorStore-Dsxyfwae.js").then((n) => n.t);
1630
+ const { FlowRunInputEditorStore } = await import("./flowRunInputEditorStore-B2Rw4VsT.js").then((n) => n.t);
1546
1631
  const editor = new FlowRunInputEditorStore(spec.definitions, language);
1547
1632
  const initial = values ?? spec.samples;
1548
1633
  if (initial != null) editor.replaceValues(initial);
@@ -2506,116 +2591,6 @@ function getConnectionsCache(options) {
2506
2591
  return getSingleton(singletonKey(oomolNamespaces.connections, options, [query.ownerId], options.sessionId), () => createConnectionsCacheInternal(options, validated));
2507
2592
  }
2508
2593
  //#endregion
2509
- //#region src/connector/common/actionSchema.ts
2510
- function connectorActionPorts(inputSchema, outputSchema) {
2511
- const inputs = inputPorts(inputSchema);
2512
- return {
2513
- inputs,
2514
- initialInputs: inputs.flatMap((input) => input.value === void 0 ? [] : [{
2515
- handle: input.handle,
2516
- value: input.value
2517
- }]),
2518
- outputs: outputPorts(outputSchema)
2519
- };
2520
- }
2521
- function inputPorts(schema) {
2522
- const root = toPlainObject(schema);
2523
- if (root == null || root.type != "object" && root.properties == null) throw new Error("Connector action inputSchema must be an object schema.");
2524
- const properties = toPlainObject(root.properties);
2525
- if (properties == null) return [];
2526
- const required = stringSet(root.required);
2527
- return Object.entries(properties).map(([handle, propertySchema]) => {
2528
- const nullable = !required.has(handle) || schemaAllowsNull(propertySchema, root);
2529
- return {
2530
- description: schemaDescription(root, propertySchema),
2531
- handle,
2532
- json_schema: propertySchemaWithDefinitions(root, propertySchema),
2533
- nullable,
2534
- value: schemaDefault(root, propertySchema, nullable)
2535
- };
2536
- });
2537
- }
2538
- function outputPorts(schema) {
2539
- const root = toPlainObject(schema);
2540
- const properties = toPlainObject(root?.properties);
2541
- if (root != null && (root.type == "object" || properties != null)) {
2542
- if (properties == null) return [];
2543
- const required = stringSet(root.required);
2544
- return Object.entries(properties).map(([handle, propertySchema]) => ({
2545
- description: schemaDescription(root, propertySchema),
2546
- handle,
2547
- json_schema: propertySchemaWithDefinitions(root, propertySchema),
2548
- nullable: !required.has(handle) || schemaAllowsNull(propertySchema, root)
2549
- }));
2550
- } else return [{
2551
- description: schemaDescription(root, schema),
2552
- handle: "output",
2553
- json_schema: schema,
2554
- nullable: schemaAllowsNull(schema)
2555
- }];
2556
- }
2557
- function propertySchemaWithDefinitions(root, schema) {
2558
- const property = toPlainObject(schema);
2559
- if (property == null) return schema;
2560
- const result = Object.assign({}, property);
2561
- if (result.$defs == null && root.$defs != null) result.$defs = root.$defs;
2562
- if (result.definitions == null && root.definitions != null) result.definitions = root.definitions;
2563
- return result;
2564
- }
2565
- function schemaDescription(root, schema) {
2566
- for (const object of referencedSchemas(root, schema)) if (typeof object.description == "string") return object.description;
2567
- }
2568
- function schemaDefault(root, schema, nullable) {
2569
- for (const object of referencedSchemas(root, schema)) if (Object.hasOwn(object, "default")) return object.default;
2570
- return nullable ? null : void 0;
2571
- }
2572
- function stringSet(value) {
2573
- if (!Array.isArray(value)) return /* @__PURE__ */ new Set();
2574
- return new Set(value.filter((item) => typeof item == "string"));
2575
- }
2576
- function schemaAllowsNull(schema, root = schema, references = /* @__PURE__ */ new Set()) {
2577
- const object = toPlainObject(schema);
2578
- if (object == null) return false;
2579
- if (Object.hasOwn(object, "type")) return object.type == "null" || Array.isArray(object.type) && object.type.includes("null");
2580
- if (Object.hasOwn(object, "enum")) return Array.isArray(object.enum) && object.enum.includes(null);
2581
- for (const key of ["anyOf", "oneOf"]) if (Object.hasOwn(object, key)) return Array.isArray(object[key]) && object[key].some((item) => schemaAllowsNull(item, root, references));
2582
- const reference = typeof object.$ref == "string" ? object.$ref : void 0;
2583
- if (reference == null || references.has(reference)) return false;
2584
- const target = resolveLocalReference(root, reference);
2585
- if (target === void 0) return false;
2586
- const nextReferences = new Set(references);
2587
- nextReferences.add(reference);
2588
- return schemaAllowsNull(target, root, nextReferences);
2589
- }
2590
- function referencedSchemas(root, schema) {
2591
- const result = [];
2592
- const references = /* @__PURE__ */ new Set();
2593
- let current = schema;
2594
- while (true) {
2595
- const object = toPlainObject(current);
2596
- if (object == null) return result;
2597
- result.push(object);
2598
- const reference = typeof object.$ref == "string" ? object.$ref : void 0;
2599
- if (reference == null || references.has(reference)) return result;
2600
- const target = resolveLocalReference(root, reference);
2601
- if (target === void 0) return result;
2602
- references.add(reference);
2603
- current = target;
2604
- }
2605
- }
2606
- function resolveLocalReference(root, reference) {
2607
- if (reference == "#") return root;
2608
- if (!reference.startsWith("#/")) return void 0;
2609
- let current = root;
2610
- for (const encodedToken of reference.slice(2).split("/")) {
2611
- const object = toPlainObject(current);
2612
- const token = encodedToken.replaceAll("~1", "/").replaceAll("~0", "~");
2613
- if (object == null || !Object.hasOwn(object, token)) return void 0;
2614
- current = object[token];
2615
- }
2616
- return current;
2617
- }
2618
- //#endregion
2619
2594
  //#region src/workbench/browser/runtime/stores/proxyCatalog.ts
2620
2595
  /** Validate the fields we consume without discarding upstream representation fields. */
2621
2596
  function proxyResponse(value, validate) {
@@ -4903,12 +4878,6 @@ var FlowCatalog = class {
4903
4878
  this.#pending.delete(flow.flowId);
4904
4879
  }
4905
4880
  }
4906
- async create(name) {
4907
- const flow = await this.#client.createFlow(name);
4908
- if (this.#disposed) return;
4909
- this.insert(flow);
4910
- return flow;
4911
- }
4912
4881
  include(flow) {
4913
4882
  const index = this.#state.value.flows.findIndex((candidate) => candidate.flowId == flow.flowId);
4914
4883
  if (index < 0) {
@@ -5092,6 +5061,7 @@ var PresentationChanges = class {
5092
5061
  #changes = Promise.resolve();
5093
5062
  #committed;
5094
5063
  #disposed = false;
5064
+ #failed = false;
5095
5065
  #pending = [];
5096
5066
  constructor(client, setNotice, setPresentation, i18n) {
5097
5067
  this.#client = client;
@@ -5100,6 +5070,7 @@ var PresentationChanges = class {
5100
5070
  this.#i18n = i18n;
5101
5071
  }
5102
5072
  reset(presentation) {
5073
+ this.#failed = false;
5103
5074
  this.#committed = presentation;
5104
5075
  this.#pending = [];
5105
5076
  }
@@ -5107,8 +5078,16 @@ var PresentationChanges = class {
5107
5078
  this.#disposed = true;
5108
5079
  this.#pending = [];
5109
5080
  }
5081
+ async flush() {
5082
+ await this.settled();
5083
+ return !this.#failed && !this.#disposed;
5084
+ }
5110
5085
  async settled() {
5111
- await this.#changes;
5086
+ let pending;
5087
+ do {
5088
+ pending = this.#changes;
5089
+ await pending;
5090
+ } while (pending !== this.#changes);
5112
5091
  }
5113
5092
  async change(flowId, presentation, current, update) {
5114
5093
  if (this.#disposed || this.#committed == null) return false;
@@ -5141,6 +5120,7 @@ var PresentationChanges = class {
5141
5120
  success = true;
5142
5121
  } catch (error) {
5143
5122
  if (this.#disposed || !current()) return;
5123
+ this.#failed = true;
5144
5124
  if (error instanceof ApiError && error.code == "flow.presentation-conflict") try {
5145
5125
  const latest = await this.#client.getPresentation(flowId);
5146
5126
  if (this.#disposed || !current()) return;
@@ -5555,14 +5535,49 @@ var WorkspaceStore = class {
5555
5535
  });
5556
5536
  return true;
5557
5537
  }
5558
- async createFlow(name, create) {
5538
+ async exportFlow(flowId) {
5539
+ const current = this.#draftSession.capture();
5540
+ const selected = this.#model.value.flowId == flowId;
5541
+ if (selected && (!await this.saveDraft() || !await this.#presentationChanges.flush() || !current())) {
5542
+ this.#setNotice({
5543
+ kind: "error",
5544
+ message: this.#i18n.t("notice.exportSaveFailed")
5545
+ });
5546
+ return;
5547
+ }
5548
+ this.#set({ busy: "flow" });
5549
+ try {
5550
+ const file = await this.#client.exportFlow(flowId);
5551
+ if (this.#disposed || selected && !current()) return;
5552
+ const url = URL.createObjectURL(new Blob([encodeWorkflowFile(file)], { type: "application/json;charset=utf-8" }));
5553
+ const link = document.createElement("a");
5554
+ link.href = url;
5555
+ link.download = workflowFileName(file.name ?? "workflow");
5556
+ link.click();
5557
+ setTimeout(() => URL.revokeObjectURL(url), 0);
5558
+ } catch (error) {
5559
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
5560
+ } finally {
5561
+ this.#set({ busy: void 0 });
5562
+ }
5563
+ }
5564
+ createFlow(name, create) {
5565
+ return this.#createFlow(async () => create == null ? this.#client.createFlow(name) : this.#client.getFlow(await create(name)));
5566
+ }
5567
+ importFlow(file, name, key, importer) {
5568
+ return this.#createFlow(async () => importer == null ? this.#client.importFlow(file, {
5569
+ name,
5570
+ idempotencyKey: key
5571
+ }) : this.#client.getFlow(await importer(file, name, key)));
5572
+ }
5573
+ async #createFlow(create) {
5559
5574
  if (!await this.saveModuleEditor()) return;
5560
5575
  this.#set({ busy: "flow" });
5561
5576
  try {
5562
- if (create == null) return await this.#flows.create(name);
5563
- const flow = await this.#client.getFlow(await create(name));
5564
- this.#flows.insert(flow);
5565
- return flow;
5577
+ const created = await create();
5578
+ if (this.#disposed) return;
5579
+ this.#flows.insert(created);
5580
+ return created;
5566
5581
  } catch (error) {
5567
5582
  if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
5568
5583
  } finally {
@@ -6607,13 +6622,18 @@ var WorkbenchStore = class {
6607
6622
  this.#openingCreatedFlow = false;
6608
6623
  }
6609
6624
  }
6625
+ async importFlow(file, name, key, importer) {
6626
+ return await this.#openNewFlow(await this.workspace.importFlow(file, name, key, importer), "notice.flowImported");
6627
+ }
6610
6628
  async createFlow(name, create) {
6611
- const flow = await this.workspace.createFlow(name, create);
6612
- if (this.#disposed || flow == null) return false;
6613
- await this.selectFlow(flow.flowId);
6629
+ return await this.#openNewFlow(await this.workspace.createFlow(name, create), "notice.created");
6630
+ }
6631
+ async #openNewFlow(created, notice) {
6632
+ if (this.#disposed || created == null) return false;
6633
+ await this.selectFlow(created.flowId);
6614
6634
  if (!this.#disposed) this.#notice.set({
6615
6635
  kind: "success",
6616
- message: this.#i18n.t("notice.created", { name: flow.name })
6636
+ message: this.#i18n.t(notice, { name: created.name })
6617
6637
  });
6618
6638
  return true;
6619
6639
  }
@@ -6793,7 +6813,7 @@ var WorkbenchStore = class {
6793
6813
  };
6794
6814
  //#endregion
6795
6815
  //#region src/workbench/browser/runtime/openFlowWorkbench.tsx
6796
- var FlowWorkspace = lazy(() => import("./flowWorkspace-BgZaFKZm.js"));
6816
+ var FlowWorkspace = lazy(() => import("./flowWorkspace-B7a5NNA_.js"));
6797
6817
  function OpenFlowSessionGate({ action, description, error, onSubmit, onTokenChange, pending = false, title, token, tokenLabel }) {
6798
6818
  const hasToken = token != null && tokenLabel != null && onTokenChange != null;
6799
6819
  const tokenValue = token ?? "";
@@ -6899,11 +6919,12 @@ function FlowNameBridge({ store, onChange }) {
6899
6919
  useEffect(() => () => onChange(void 0), [onChange]);
6900
6920
  return null;
6901
6921
  }
6902
- function Workbench({ catalogWidth, createFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, hrefFor, initializing, language, navigation, onConfigureConnector, onManageConnectorAccess, onLanguageChange, store, theme }) {
6922
+ function Workbench({ importRef, catalogWidth, createFlow, importFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, hrefFor, initializing, language, navigation, onConfigureConnector, onManageConnectorAccess, onLanguageChange, store, theme }) {
6903
6923
  const flowId = useVal(store.workspace.$.flowId);
6904
6924
  return /* @__PURE__ */ jsx("div", {
6905
6925
  className: "app-shell",
6906
6926
  children: flowId == null ? /* @__PURE__ */ jsx(FlowBrowser, {
6927
+ ref: importRef,
6907
6928
  catalogWidth,
6908
6929
  createFlowDisabled,
6909
6930
  createFlowField,
@@ -6915,6 +6936,7 @@ function Workbench({ catalogWidth, createFlow, createFlowDisabled, createFlowFie
6915
6936
  initializing,
6916
6937
  language,
6917
6938
  onCreateFlow: (name) => navigation.createFlow(name, createFlow),
6939
+ onImportFlow: (file, name, key) => navigation.importFlow(file, name, key, importFlow),
6918
6940
  onLanguageChange,
6919
6941
  onSelectFlow: (flow, view) => void navigation.selectFlow(flow, view),
6920
6942
  store
@@ -6935,7 +6957,9 @@ function Workbench({ catalogWidth, createFlow, createFlowDisabled, createFlowFie
6935
6957
  })
6936
6958
  });
6937
6959
  }
6938
- function Session({ catalogWidth, createFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, host, hrefFor, language, location, onConfigureConnector, onManageConnectorAccess, onLanguageChange, onNavigate, onFlowNameChange, preferences, sessionKey, theme, variables = true }) {
6960
+ var Session = forwardRef(function Session({ catalogWidth, createFlow, importFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, host, hrefFor, language, location, onConfigureConnector, onManageConnectorAccess, onLanguageChange, onNavigate, onFlowNameChange, preferences, sessionKey, theme, variables = true }, ref) {
6961
+ const importRef = useRef(null);
6962
+ useImperativeHandle(ref, () => ({ openImport: (input) => importRef.current?.openImport(input) ?? "unavailable" }), []);
6939
6963
  const navigate = useRef(onNavigate);
6940
6964
  navigate.current = onNavigate;
6941
6965
  const [{ i18n, navigation, store }] = useState(() => {
@@ -6999,8 +7023,10 @@ function Session({ catalogWidth, createFlow, createFlowDisabled, createFlowField
6999
7023
  onChange: onFlowNameChange
7000
7024
  }),
7001
7025
  started || location.flowId == null ? /* @__PURE__ */ jsx(Workbench, {
7026
+ importRef: location.flowId == null ? importRef : void 0,
7002
7027
  catalogWidth,
7003
7028
  createFlow,
7029
+ importFlow,
7004
7030
  createFlowDisabled,
7005
7031
  createFlowField,
7006
7032
  flowBadges,
@@ -7022,12 +7048,13 @@ function Session({ catalogWidth, createFlow, createFlowDisabled, createFlowField
7022
7048
  })
7023
7049
  })
7024
7050
  });
7025
- }
7026
- function OpenFlowWorkbench({ sessionKey, ...props }) {
7051
+ });
7052
+ var OpenFlowWorkbench = forwardRef(function OpenFlowWorkbench({ sessionKey, ...props }, ref) {
7027
7053
  return /* @__PURE__ */ jsx(Session, {
7054
+ ref,
7028
7055
  sessionKey,
7029
7056
  ...props
7030
7057
  }, `${sessionKey}:${props.host.connectorOwnerId ?? ""}`);
7031
- }
7058
+ });
7032
7059
  //#endregion
7033
7060
  export { EventSourcesPage, OpenFlowSessionGate, OpenFlowWorkbench, isUiLanguage as isWorkbenchLanguage, resolveUiLanguage as resolveWorkbenchLanguage, uiLanguageNames as workbenchLanguageNames, uiLanguages as workbenchLanguages };
@@ -1,4 +1,4 @@
1
- import { Ct as SelectContent, Dt as SelectTrigger, Ot as SelectValue, Tt as SelectItem, wt as SelectGroup, xi as cn, xt as Select } from "./icons-CajfDpy_.js";
1
+ import { At as SelectValue, Ci as cn, Ct as Select, Dt as SelectItem, Et as SelectGroup, Tt as SelectContent, kt as SelectTrigger } from "./icons-Db2Jb2iX.js";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  //#region src/workbench/browser/runtime/shell/workbenchSelect.tsx
4
4
  function WorkbenchSelect({ ariaLabel, className, disabled = false, id, onValueChange, options, portalRoot, size, value, variant }) {