@revenexx/studio-integrations 0.1.4 → 0.2.0
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.
- package/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/CredentialCreateDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +279 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.d.vue.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +342 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue.d.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialLogo.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue +37 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +364 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +71 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.d.vue.ts +5 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +151 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue.d.ts +5 -0
- package/dist/runtime/components/Integrations/EditorTip.d.vue.ts +17 -0
- package/dist/runtime/components/Integrations/EditorTip.vue +46 -0
- package/dist/runtime/components/Integrations/EditorTip.vue.d.ts +17 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +64 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +19 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +236 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +19 -0
- package/dist/runtime/components/Integrations/GroupFrameView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +222 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +184 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +102 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +23 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +587 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +23 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue +80 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +22 -3
- package/dist/runtime/components/Integrations/NodeInspector.vue +1074 -294
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +22 -3
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +38 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +22 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +411 -90
- package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +22 -1
- package/dist/runtime/components/Integrations/QueryBuilder.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue +239 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.d.vue.ts → RunActionsMenu.d.vue.ts} +3 -4
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +86 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.vue.d.ts → RunActionsMenu.vue.d.ts} +3 -4
- package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +777 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +175 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +27 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue +84 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +27 -0
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/RunsTable.vue +114 -0
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.d.vue.ts +11 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +91 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue.d.ts +11 -0
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue +208 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +29 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +201 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +29 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +138 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +322 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +68 -6
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +1424 -144
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +417 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +89 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue +35 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +114 -84
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +654 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +136 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +35 -15
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +265 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +4 -1
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +122 -39
- package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +4 -1
- package/dist/runtime/components/Integrations/workflowLayout.d.ts +5 -0
- package/dist/runtime/components/Integrations/workflowLayout.js +5 -3
- package/dist/runtime/composables/integrationsApi.d.ts +6 -2
- package/dist/runtime/composables/integrationsApi.js +5 -4
- package/dist/runtime/composables/useCanvasShortcuts.d.ts +37 -0
- package/dist/runtime/composables/useCanvasShortcuts.js +62 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +22 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +50 -0
- package/dist/runtime/composables/useIntegrationsNodes.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +6 -3
- package/dist/runtime/composables/useIntegrationsRevisions.d.ts +40 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +51 -0
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +21 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +92 -4
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsTemplates.js +32 -4
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsWorkflows.js +57 -1
- package/dist/runtime/composables/useMiniMapLayout.d.ts +34 -0
- package/dist/runtime/composables/useMiniMapLayout.js +119 -0
- package/dist/runtime/composables/useNodeConfigResolve.d.ts +43 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +123 -0
- package/dist/runtime/composables/useNodeDrag.d.ts +22 -0
- package/dist/runtime/composables/useNodeDrag.js +10 -0
- package/dist/runtime/composables/usePaletteLayout.d.ts +41 -0
- package/dist/runtime/composables/usePaletteLayout.js +113 -0
- package/dist/runtime/data/expressionEditor.d.ts +22 -0
- package/dist/runtime/data/expressionEditor.js +40 -0
- package/dist/runtime/data/integrationNodeCatalog.js +8 -8
- package/dist/runtime/data/nodeInputData.d.ts +37 -0
- package/dist/runtime/data/nodeInputData.js +62 -0
- package/dist/runtime/data/nodeRunData.d.ts +56 -0
- package/dist/runtime/data/nodeRunData.js +91 -0
- package/dist/runtime/data/payloadForm.d.ts +44 -0
- package/dist/runtime/data/payloadForm.js +126 -0
- package/dist/runtime/data/workflowIconCatalog.d.ts +34 -0
- package/dist/runtime/data/workflowIconCatalog.js +116 -0
- package/dist/runtime/pages/integrations/audit.vue +25 -35
- package/dist/runtime/pages/integrations/credentials.vue +117 -122
- package/dist/runtime/pages/integrations/index.vue +133 -36
- package/dist/runtime/pages/integrations/runs.vue +125 -75
- package/dist/runtime/pages/integrations/schedules.vue +5 -16
- package/dist/runtime/pages/integrations/secrets.vue +1 -3
- package/dist/runtime/pages/integrations/workflows/[id].vue +599 -194
- package/dist/runtime/pages/integrations/workflows/index.vue +10 -8
- package/dist/runtime/types/integrations.d.ts +190 -4
- package/dist/runtime/utils/credentialState.d.ts +28 -0
- package/dist/runtime/utils/credentialState.js +29 -0
- package/dist/runtime/utils/localize.d.ts +3 -1
- package/dist/runtime/utils/localize.js +1 -2
- package/dist/runtime/utils/statusStyle.d.ts +6 -0
- package/dist/runtime/utils/statusStyle.js +15 -0
- package/package.json +11 -1
- package/dist/runtime/components/Integrations/RunDetailDialog.vue +0 -169
- package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +0 -2
- package/dist/runtime/pages/integrations/workflows/new.vue +0 -153
- package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +0 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useCanvasShortcuts — document-level keyboard shortcuts for the workflow
|
|
3
|
+
* canvas, with editing-context scoping.
|
|
4
|
+
*
|
|
5
|
+
* Modeled on n8n's useKeybindings (editor-ui/src/app/composables/useKeybindings.ts)
|
|
6
|
+
* but dependency-free (no @vueuse) and with the platform-neutral `mod` token.
|
|
7
|
+
*
|
|
8
|
+
* Map keys:
|
|
9
|
+
* - tokens joined by '+': modifiers `mod` (⌘ on mac, Ctrl elsewhere), `shift`,
|
|
10
|
+
* `alt`, plus one key: a single character ('a', '1', '+'), or a named key
|
|
11
|
+
* ('enter', 'escape', 'tab', 'space', 'f2', 'delete', 'backspace',
|
|
12
|
+
* 'arrowup', ...). Case-insensitive.
|
|
13
|
+
* - alternatives via '|': 'f2|space' fires on either.
|
|
14
|
+
*
|
|
15
|
+
* Handlers are a function or `{ disabled: () => boolean, run: fn }` — a disabled
|
|
16
|
+
* handler lets the event fall through untouched (no preventDefault), so e.g.
|
|
17
|
+
* Enter still types a newline when nothing is selected.
|
|
18
|
+
*
|
|
19
|
+
* Scoping — a keydown is IGNORED entirely when:
|
|
20
|
+
* 1. `options.disabled` is truthy (the editor passes "config lightbox open").
|
|
21
|
+
* 2. The event target (or activeElement fallback) is INPUT / TEXTAREA / SELECT,
|
|
22
|
+
* is contenteditable, or sits inside a [contenteditable] ancestor.
|
|
23
|
+
* 3. The target or an ancestor carries `data-canvas-shortcuts-ignore`.
|
|
24
|
+
* 4. `event.defaultPrevented` — someone upstream already claimed the key.
|
|
25
|
+
*
|
|
26
|
+
* On a match: preventDefault() + stopPropagation(), then run. Non-matching
|
|
27
|
+
* combos are never touched, so browser shortcuts keep working.
|
|
28
|
+
*/
|
|
29
|
+
import type { MaybeRefOrGetter } from 'vue';
|
|
30
|
+
export type ShortcutHandler = ((event: KeyboardEvent) => void) | {
|
|
31
|
+
disabled: () => boolean;
|
|
32
|
+
run: (event: KeyboardEvent) => void;
|
|
33
|
+
};
|
|
34
|
+
export type ShortcutMap = Record<string, ShortcutHandler>;
|
|
35
|
+
export declare function useCanvasShortcuts(keymap: MaybeRefOrGetter<ShortcutMap>, options?: {
|
|
36
|
+
disabled?: MaybeRefOrGetter<boolean>;
|
|
37
|
+
}): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { computed, onBeforeUnmount, onMounted, toValue } from "vue";
|
|
2
|
+
const IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "");
|
|
3
|
+
function canonical(parts) {
|
|
4
|
+
return parts.map((p) => p.toLowerCase()).sort((a, b) => a.localeCompare(b)).join("+");
|
|
5
|
+
}
|
|
6
|
+
function normalizeShortcut(shortcut) {
|
|
7
|
+
if (shortcut.length === 1) return shortcut.toLowerCase();
|
|
8
|
+
return canonical(shortcut.split("+"));
|
|
9
|
+
}
|
|
10
|
+
function codeToKey(code) {
|
|
11
|
+
if (code.startsWith("Key")) return code.slice(3).toLowerCase();
|
|
12
|
+
if (code.startsWith("Digit")) return code.slice(5);
|
|
13
|
+
return code.toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
function eventShortcuts(event) {
|
|
16
|
+
const mods = [];
|
|
17
|
+
if (event.shiftKey) mods.push("shift");
|
|
18
|
+
if (event.altKey) mods.push("alt");
|
|
19
|
+
if (IS_MAC ? event.metaKey : event.ctrlKey) mods.push("mod");
|
|
20
|
+
const key = event.key === " " ? "space" : event.key.toLowerCase();
|
|
21
|
+
const code = codeToKey(event.code ?? "");
|
|
22
|
+
return {
|
|
23
|
+
byKey: canonical([...mods, key]),
|
|
24
|
+
byCode: canonical([...mods, code])
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function isEditingContext(event) {
|
|
28
|
+
const el = event.target instanceof Element ? event.target : document.activeElement;
|
|
29
|
+
if (!el) return false;
|
|
30
|
+
const tag = el.tagName;
|
|
31
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
32
|
+
if (el.isContentEditable) return true;
|
|
33
|
+
if (el.closest('[contenteditable=""],[contenteditable="true"]')) return true;
|
|
34
|
+
if (el.closest("[data-canvas-shortcuts-ignore]")) return true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
export function useCanvasShortcuts(keymap, options) {
|
|
38
|
+
const normalized = computed(() => {
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const [combo, handler] of Object.entries(toValue(keymap))) {
|
|
41
|
+
for (const alt of combo.split("|")) {
|
|
42
|
+
out[normalizeShortcut(alt.trim())] = handler;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
});
|
|
47
|
+
function onKeyDown(event) {
|
|
48
|
+
if (toValue(options?.disabled)) return;
|
|
49
|
+
if (event.defaultPrevented) return;
|
|
50
|
+
if (isEditingContext(event)) return;
|
|
51
|
+
const { byKey, byCode } = eventShortcuts(event);
|
|
52
|
+
const handler = normalized.value[byKey] ?? normalized.value[byCode];
|
|
53
|
+
if (!handler) return;
|
|
54
|
+
const run = typeof handler === "function" ? handler : handler.disabled() ? void 0 : handler.run;
|
|
55
|
+
if (!run) return;
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
run(event);
|
|
59
|
+
}
|
|
60
|
+
onMounted(() => document.addEventListener("keydown", onKeyDown));
|
|
61
|
+
onBeforeUnmount(() => document.removeEventListener("keydown", onKeyDown));
|
|
62
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DeadLetterRow } from '../types/integrations.js';
|
|
2
|
+
/** A dead letter enriched with its workflow's name (global "Missed runs" view). */
|
|
3
|
+
export interface GlobalDeadLetterRow extends DeadLetterRow {
|
|
4
|
+
workflowName: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function useIntegrationsDeadLetters(): {
|
|
7
|
+
list: (workflowId: number | string, opts?: {
|
|
8
|
+
page?: number;
|
|
9
|
+
perPage?: number;
|
|
10
|
+
}) => Promise<{
|
|
11
|
+
items: DeadLetterRow[];
|
|
12
|
+
total: number;
|
|
13
|
+
page: number;
|
|
14
|
+
lastPage: number;
|
|
15
|
+
}>;
|
|
16
|
+
replay: (id: number | string) => Promise<unknown>;
|
|
17
|
+
discard: (id: number | string) => Promise<unknown>;
|
|
18
|
+
loadGlobal: (force?: boolean) => Promise<GlobalDeadLetterRow[]>;
|
|
19
|
+
globalItems: any;
|
|
20
|
+
globalLoading: any;
|
|
21
|
+
pendingCount: import("vue").ComputedRef<any>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
|
+
export function useIntegrationsDeadLetters() {
|
|
4
|
+
const api = useIntegrationsApi();
|
|
5
|
+
async function list(workflowId, opts = {}) {
|
|
6
|
+
const page = opts.page ?? 1;
|
|
7
|
+
const perPage = opts.perPage ?? 25;
|
|
8
|
+
const res = await api.get(
|
|
9
|
+
`/workflows/${workflowId}/dead-letters?page=${page}&per_page=${perPage}`
|
|
10
|
+
);
|
|
11
|
+
return {
|
|
12
|
+
items: res?.data ?? [],
|
|
13
|
+
total: res?.meta?.total ?? (res?.data?.length ?? 0),
|
|
14
|
+
page: res?.meta?.current_page ?? page,
|
|
15
|
+
lastPage: res?.meta?.last_page ?? 1
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const replay = (id) => api.post(`/dead-letters/${id}/replay`, {});
|
|
19
|
+
const discard = (id) => api.post(`/dead-letters/${id}/discard`, {});
|
|
20
|
+
const { workflows, load: loadWorkflows } = useIntegrationsWorkflows();
|
|
21
|
+
const globalItems = useState("integrations.deadletters.global", () => []);
|
|
22
|
+
const globalLoading = useState("integrations.deadletters.global.loading", () => false);
|
|
23
|
+
const globalLoaded = useState("integrations.deadletters.global.loaded", () => false);
|
|
24
|
+
const pendingCount = computed(() => globalItems.value.filter((i) => i.status === "pending").length);
|
|
25
|
+
async function loadGlobal(force = false) {
|
|
26
|
+
if (globalLoading.value) return globalItems.value;
|
|
27
|
+
if (globalLoaded.value && !force) return globalItems.value;
|
|
28
|
+
globalLoading.value = true;
|
|
29
|
+
try {
|
|
30
|
+
await loadWorkflows();
|
|
31
|
+
const MAX_PAGES = 20;
|
|
32
|
+
const per = await Promise.allSettled(workflows.value.map(async (wf) => {
|
|
33
|
+
const out = [];
|
|
34
|
+
let page = 1;
|
|
35
|
+
while (page <= MAX_PAGES) {
|
|
36
|
+
const res = await list(wf.id, { page, perPage: 100 });
|
|
37
|
+
out.push(...res.items.map((i) => ({ ...i, workflowName: wf.name })));
|
|
38
|
+
if (page >= res.lastPage) break;
|
|
39
|
+
page++;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}));
|
|
43
|
+
globalItems.value = per.flatMap((p) => p.status === "fulfilled" ? p.value : []).sort((a, b) => new Date(b.created_at ?? 0).getTime() - new Date(a.created_at ?? 0).getTime());
|
|
44
|
+
globalLoaded.value = true;
|
|
45
|
+
} finally {
|
|
46
|
+
globalLoading.value = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { list, replay, discard, loadGlobal, globalItems, globalLoading, pendingCount };
|
|
50
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { findManifest } from '../data/integrationNodeCatalog.js';
|
|
2
|
+
import { resolveLocalized } from '../utils/localize.js';
|
|
1
3
|
export declare function useIntegrationsNodes(): {
|
|
2
4
|
loading: any;
|
|
3
5
|
error: any;
|
|
4
6
|
loaded: any;
|
|
5
7
|
load: (force?: boolean) => Promise<void>;
|
|
8
|
+
findManifest: typeof findManifest;
|
|
9
|
+
resolveLocalized: typeof resolveLocalized;
|
|
6
10
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { applyNodeCatalog } from "../data/integrationNodeCatalog.js";
|
|
1
|
+
import { applyNodeCatalog, findManifest } from "../data/integrationNodeCatalog.js";
|
|
2
|
+
import { resolveLocalized } from "../utils/localize.js";
|
|
2
3
|
import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
|
|
3
4
|
export function useIntegrationsNodes() {
|
|
4
5
|
const loading = useState("integrations.nodes.loading", () => false);
|
|
@@ -17,7 +18,9 @@ export function useIntegrationsNodes() {
|
|
|
17
18
|
...n.manifest,
|
|
18
19
|
slug: n.manifest.slug ?? n.slug,
|
|
19
20
|
version: n.manifest.version ?? n.version,
|
|
20
|
-
name: n.manifest.name ?? n.name
|
|
21
|
+
name: n.manifest.name ?? n.name,
|
|
22
|
+
// Keep the source package — the palette groups app nodes by it.
|
|
23
|
+
__package: n.package ? { name: n.package.name, label: n.package.label ?? null } : void 0
|
|
21
24
|
}));
|
|
22
25
|
applyNodeCatalog(manifests);
|
|
23
26
|
loaded.value = true;
|
|
@@ -28,5 +31,5 @@ export function useIntegrationsNodes() {
|
|
|
28
31
|
loading.value = false;
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
return { loading, error, loaded, load };
|
|
34
|
+
return { loading, error, loaded, load, findManifest, resolveLocalized };
|
|
32
35
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { WorkflowBlob } from '../types/integrations.js';
|
|
2
|
+
/**
|
|
3
|
+
* Workflow revision history (PO — `workflow_histories`): every blob-changing
|
|
4
|
+
* save snapshots the previous revision. These endpoints expose that history
|
|
5
|
+
* and restore an archived revision as a brand-new one.
|
|
6
|
+
*
|
|
7
|
+
* GET /workflows/{id}/revisions → metadata list (paginated, newest first)
|
|
8
|
+
* GET /workflows/{id}/revisions/{rev} → full snapshot incl. blob + triggers
|
|
9
|
+
* POST /workflows/{id}/revisions/{rev}/restore
|
|
10
|
+
*/
|
|
11
|
+
export interface WorkflowRevisionMeta {
|
|
12
|
+
id: number;
|
|
13
|
+
revision: number;
|
|
14
|
+
name: string;
|
|
15
|
+
description: string | null;
|
|
16
|
+
active: boolean;
|
|
17
|
+
blob_definition_version: string;
|
|
18
|
+
created_at: string | null;
|
|
19
|
+
updated_at: string | null;
|
|
20
|
+
archived_at: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface WorkflowRevisionDetail extends WorkflowRevisionMeta {
|
|
23
|
+
/** Cockpit-shaped blob: triggers merged in, like useIntegrationsWorkflows.toCockpit. */
|
|
24
|
+
blob: WorkflowBlob;
|
|
25
|
+
}
|
|
26
|
+
export declare function useIntegrationsRevisions(): {
|
|
27
|
+
loading: import("vue").Ref<boolean, boolean>;
|
|
28
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
29
|
+
list: (workflowId: number, page?: number, perPage?: number) => Promise<{
|
|
30
|
+
revisions: WorkflowRevisionMeta[];
|
|
31
|
+
meta: {
|
|
32
|
+
current_page: number;
|
|
33
|
+
per_page: number;
|
|
34
|
+
total: number;
|
|
35
|
+
last_page: number;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
38
|
+
get: (workflowId: number, revision: number) => Promise<WorkflowRevisionDetail>;
|
|
39
|
+
restore: (workflowId: number, revision: number) => Promise<void>;
|
|
40
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
|
|
3
|
+
export function useIntegrationsRevisions() {
|
|
4
|
+
const api = useIntegrationsApi();
|
|
5
|
+
const loading = ref(false);
|
|
6
|
+
const error = ref(null);
|
|
7
|
+
function captureError(e, fallback) {
|
|
8
|
+
if (e instanceof IntegrationsApiError) return e.message;
|
|
9
|
+
return e?.message ?? fallback;
|
|
10
|
+
}
|
|
11
|
+
async function list(workflowId, page = 1, perPage = 25) {
|
|
12
|
+
loading.value = true;
|
|
13
|
+
error.value = null;
|
|
14
|
+
try {
|
|
15
|
+
const res = await api.get(`/workflows/${workflowId}/revisions?page=${page}&per_page=${perPage}`);
|
|
16
|
+
return { revisions: res.data ?? [], meta: res.meta };
|
|
17
|
+
} catch (e) {
|
|
18
|
+
error.value = captureError(e, "Could not load the revision history.");
|
|
19
|
+
throw e;
|
|
20
|
+
} finally {
|
|
21
|
+
loading.value = false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function get(workflowId, revision) {
|
|
25
|
+
const res = await api.get(`/workflows/${workflowId}/revisions/${revision}`);
|
|
26
|
+
const apiBlob = res.blob ?? {};
|
|
27
|
+
const triggers = (res.triggers ?? []).map((t) => ({
|
|
28
|
+
id: t.handle ?? String(t.id ?? ""),
|
|
29
|
+
type: t.type,
|
|
30
|
+
name: typeof t.name === "string" ? t.name : t.name ?? void 0,
|
|
31
|
+
active: t.active,
|
|
32
|
+
config: t.config ?? {}
|
|
33
|
+
}));
|
|
34
|
+
return {
|
|
35
|
+
...res,
|
|
36
|
+
blob: {
|
|
37
|
+
nodeManifestVersion: apiBlob.nodeManifestVersion ?? "v0-draft",
|
|
38
|
+
name: apiBlob.name ?? res.name,
|
|
39
|
+
nodes: apiBlob.nodes ?? [],
|
|
40
|
+
edges: apiBlob.edges ?? [],
|
|
41
|
+
triggerPositions: apiBlob.triggerPositions ?? {},
|
|
42
|
+
stickies: apiBlob.stickies ?? [],
|
|
43
|
+
triggers
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function restore(workflowId, revision) {
|
|
48
|
+
await api.post(`/workflows/${workflowId}/revisions/${revision}/restore`, {});
|
|
49
|
+
}
|
|
50
|
+
return { loading, error, list, get, restore };
|
|
51
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunStatus, WorkflowRun } from '../types/integrations.js';
|
|
1
|
+
import type { RunStatus, RunStepRow, WorkflowRun } from '../types/integrations.js';
|
|
2
2
|
export declare function useIntegrationsRuns(): {
|
|
3
3
|
runs: any;
|
|
4
4
|
loading: any;
|
|
@@ -8,17 +8,37 @@ export declare function useIntegrationsRuns(): {
|
|
|
8
8
|
failedCount: import("vue").ComputedRef<any>;
|
|
9
9
|
runningCount: import("vue").ComputedRef<any>;
|
|
10
10
|
load: (force?: boolean) => Promise<WorkflowRun[]>;
|
|
11
|
+
loadForWorkflow: (workflowId: number, workflowName: string, opts?: {
|
|
12
|
+
reconcile?: boolean;
|
|
13
|
+
page?: number;
|
|
14
|
+
perPage?: number;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
runs: WorkflowRun[];
|
|
17
|
+
total: number;
|
|
18
|
+
page: number;
|
|
19
|
+
lastPage: number;
|
|
20
|
+
}>;
|
|
11
21
|
list: (filter?: {
|
|
12
22
|
workflowId?: number;
|
|
13
23
|
status?: RunStatus;
|
|
14
24
|
}) => WorkflowRun[];
|
|
15
25
|
get: (id: string) => WorkflowRun | null;
|
|
26
|
+
fetchRun: (workflowId: number | string, runId: number | string, workflowName: string) => Promise<WorkflowRun>;
|
|
16
27
|
trigger: (workflowId: number | string, payload?: Record<string, unknown>, mode?: "sync" | "async") => Promise<unknown>;
|
|
28
|
+
cancelRun: (workflowId: number | string, runId: number | string, reason?: string) => Promise<unknown>;
|
|
29
|
+
terminateRun: (workflowId: number | string, runId: number | string, reason?: string) => Promise<unknown>;
|
|
30
|
+
retryRun: (workflowId: number | string, runId: number | string) => Promise<unknown>;
|
|
31
|
+
resumeRun: (workflowId: number | string, runId: number | string, reason?: string) => Promise<unknown>;
|
|
17
32
|
getRun: (workflowId: number | string, runId: number | string) => Promise<unknown>;
|
|
18
33
|
getDetails: (workflowId: number | string, runId: number | string) => Promise<{
|
|
19
34
|
source: string;
|
|
20
35
|
data: Record<string, unknown>;
|
|
21
36
|
}>;
|
|
37
|
+
getSteps: (workflowId: number | string, runId: number | string, includePayloads?: boolean) => Promise<{
|
|
38
|
+
source: string;
|
|
39
|
+
run: Record<string, unknown>;
|
|
40
|
+
steps: RunStepRow[];
|
|
41
|
+
}>;
|
|
22
42
|
getHistory: (workflowId: number | string, runId: number | string, params?: {
|
|
23
43
|
page_token?: string;
|
|
24
44
|
page_size?: number;
|
|
@@ -8,14 +8,18 @@ function mapStatus(status) {
|
|
|
8
8
|
return "queued";
|
|
9
9
|
case "terminated":
|
|
10
10
|
return "failed";
|
|
11
|
+
case "timed_out":
|
|
12
|
+
return "failed";
|
|
11
13
|
case "running":
|
|
12
14
|
return "running";
|
|
13
15
|
case "failed":
|
|
14
16
|
return "failed";
|
|
15
17
|
case "cancelled":
|
|
16
18
|
return "cancelled";
|
|
19
|
+
// An unknown/unexpected status must never surface as a stuck `running` —
|
|
20
|
+
// fail closed so a run the backend can't classify is visibly terminal.
|
|
17
21
|
default:
|
|
18
|
-
return "
|
|
22
|
+
return "failed";
|
|
19
23
|
}
|
|
20
24
|
}
|
|
21
25
|
export function useIntegrationsRuns() {
|
|
@@ -23,19 +27,25 @@ export function useIntegrationsRuns() {
|
|
|
23
27
|
const loading = useState("integrations.runs.loading", () => false);
|
|
24
28
|
const error = useState("integrations.runs.error", () => null);
|
|
25
29
|
const loaded = useState("integrations.runs.loaded", () => false);
|
|
30
|
+
const reconciling = useState("integrations.runs.reconciling", () => false);
|
|
31
|
+
const SYNC_LIMIT = 5;
|
|
26
32
|
const api = useIntegrationsApi();
|
|
27
33
|
const { workflows, load: loadWorkflows } = useIntegrationsWorkflows();
|
|
28
34
|
function mapRun(r, workflowName) {
|
|
35
|
+
const startedMs = r.started_at ? new Date(r.started_at).getTime() : null;
|
|
36
|
+
const finishedMs = r.completed_at ? new Date(r.completed_at).getTime() : null;
|
|
29
37
|
return {
|
|
30
38
|
id: String(r.id),
|
|
31
39
|
workflowId: r.workflow_id,
|
|
32
40
|
workflowName,
|
|
33
41
|
status: mapStatus(r.status),
|
|
42
|
+
temporalRunId: r.temporal_run_id ?? null,
|
|
34
43
|
triggerId: "",
|
|
35
44
|
triggerType: "manual",
|
|
36
45
|
startedAt: r.started_at ?? "",
|
|
37
46
|
finishedAt: r.completed_at ?? null,
|
|
38
|
-
|
|
47
|
+
// Derived from the timestamps — the list payload carries no duration.
|
|
48
|
+
durationMs: startedMs != null && finishedMs != null && finishedMs >= startedMs ? finishedMs - startedMs : null,
|
|
39
49
|
steps: []
|
|
40
50
|
};
|
|
41
51
|
}
|
|
@@ -46,13 +56,41 @@ export function useIntegrationsRuns() {
|
|
|
46
56
|
error.value = null;
|
|
47
57
|
try {
|
|
48
58
|
await loadWorkflows();
|
|
59
|
+
const MAX_PAGES = 100;
|
|
49
60
|
const perWorkflow = await Promise.allSettled(
|
|
50
61
|
workflows.value.map(async (wf) => {
|
|
51
|
-
const
|
|
52
|
-
|
|
62
|
+
const out = [];
|
|
63
|
+
let page = 1;
|
|
64
|
+
while (page <= MAX_PAGES) {
|
|
65
|
+
const res = await api.get(
|
|
66
|
+
`/workflows/${wf.id}/runs?page=${page}&per_page=100`
|
|
67
|
+
);
|
|
68
|
+
out.push(...(res?.data ?? []).map((r) => mapRun(r, wf.name)));
|
|
69
|
+
const last = res?.meta?.last_page ?? 1;
|
|
70
|
+
if (page >= last) break;
|
|
71
|
+
page++;
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
53
74
|
})
|
|
54
75
|
);
|
|
55
76
|
runs.value = perWorkflow.flatMap((p) => p.status === "fulfilled" ? p.value : []).sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
|
|
77
|
+
if (!reconciling.value) {
|
|
78
|
+
reconciling.value = true;
|
|
79
|
+
try {
|
|
80
|
+
const active = runs.value.filter((r) => r.status === "running" || r.status === "queued").sort((a, b) => Number(b.id) - Number(a.id)).slice(0, SYNC_LIMIT);
|
|
81
|
+
await Promise.allSettled(active.map(async (r) => {
|
|
82
|
+
try {
|
|
83
|
+
const raw = await api.get(`/workflows/${r.workflowId}/runs/${r.id}`);
|
|
84
|
+
const apiRun = raw?.data ?? raw;
|
|
85
|
+
const fresh = mapRun(apiRun, r.workflowName);
|
|
86
|
+
runs.value = runs.value.map((x) => x.id === fresh.id ? fresh : x);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
} finally {
|
|
91
|
+
reconciling.value = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
56
94
|
loaded.value = true;
|
|
57
95
|
} catch (e) {
|
|
58
96
|
error.value = e instanceof IntegrationsApiError ? e.message : e?.message ?? "Could not load runs.";
|
|
@@ -61,6 +99,32 @@ export function useIntegrationsRuns() {
|
|
|
61
99
|
}
|
|
62
100
|
return runs.value;
|
|
63
101
|
}
|
|
102
|
+
async function loadForWorkflow(workflowId, workflowName, opts = {}) {
|
|
103
|
+
const page = opts.page ?? 1;
|
|
104
|
+
const perPage = opts.perPage ?? 25;
|
|
105
|
+
const res = await api.get(
|
|
106
|
+
`/workflows/${workflowId}/runs?page=${page}&per_page=${perPage}`
|
|
107
|
+
);
|
|
108
|
+
let list2 = (res?.data ?? []).map((r) => mapRun(r, workflowName)).sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
|
|
109
|
+
if (opts.reconcile) {
|
|
110
|
+
const active = list2.filter((r) => r.status === "running" || r.status === "queued").sort((a, b) => Number(b.id) - Number(a.id)).slice(0, SYNC_LIMIT);
|
|
111
|
+
await Promise.allSettled(active.map(async (r) => {
|
|
112
|
+
try {
|
|
113
|
+
const raw = await api.get(`/workflows/${workflowId}/runs/${r.id}`);
|
|
114
|
+
const apiRun = raw?.data ?? raw;
|
|
115
|
+
const fresh = mapRun(apiRun, workflowName);
|
|
116
|
+
list2 = list2.map((x) => x.id === fresh.id ? fresh : x);
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
runs: list2,
|
|
123
|
+
total: res?.meta?.total ?? list2.length,
|
|
124
|
+
page: res?.meta?.current_page ?? page,
|
|
125
|
+
lastPage: res?.meta?.last_page ?? 1
|
|
126
|
+
};
|
|
127
|
+
}
|
|
64
128
|
function list(filter) {
|
|
65
129
|
let result = [...runs.value];
|
|
66
130
|
if (filter?.workflowId !== void 0) result = result.filter((r) => r.workflowId === filter.workflowId);
|
|
@@ -76,8 +140,25 @@ export function useIntegrationsRuns() {
|
|
|
76
140
|
const res = await api.post(`/workflows/${workflowId}/runs`, body);
|
|
77
141
|
return res?.data ?? res;
|
|
78
142
|
}
|
|
143
|
+
async function runAction(workflowId, runId, action, reason) {
|
|
144
|
+
const body = reason ? { reason } : {};
|
|
145
|
+
const res = await api.post(`/workflows/${workflowId}/runs/${runId}/${action}`, body);
|
|
146
|
+
return res?.data ?? res;
|
|
147
|
+
}
|
|
148
|
+
const cancelRun = (workflowId, runId, reason) => runAction(workflowId, runId, "cancel", reason);
|
|
149
|
+
const terminateRun = (workflowId, runId, reason) => runAction(workflowId, runId, "terminate", reason);
|
|
150
|
+
const retryRun = (workflowId, runId) => runAction(workflowId, runId, "retry");
|
|
151
|
+
const resumeRun = (workflowId, runId, reason) => runAction(workflowId, runId, "resume", reason);
|
|
79
152
|
const getRun = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}`);
|
|
153
|
+
async function fetchRun(workflowId, runId, workflowName) {
|
|
154
|
+
const raw = await getRun(workflowId, runId);
|
|
155
|
+
const apiRun = raw?.data ?? raw;
|
|
156
|
+
return mapRun(apiRun, workflowName);
|
|
157
|
+
}
|
|
80
158
|
const getDetails = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}/details`);
|
|
159
|
+
const getSteps = (workflowId, runId, includePayloads = false) => api.get(
|
|
160
|
+
`/workflows/${workflowId}/runs/${runId}/steps${includePayloads ? "?include_payloads=1" : ""}`
|
|
161
|
+
);
|
|
81
162
|
const getHistory = (workflowId, runId, params = {}) => {
|
|
82
163
|
const qs = new URLSearchParams();
|
|
83
164
|
if (params.page_token) qs.set("page_token", params.page_token);
|
|
@@ -98,11 +179,18 @@ export function useIntegrationsRuns() {
|
|
|
98
179
|
failedCount,
|
|
99
180
|
runningCount,
|
|
100
181
|
load,
|
|
182
|
+
loadForWorkflow,
|
|
101
183
|
list,
|
|
102
184
|
get,
|
|
185
|
+
fetchRun,
|
|
103
186
|
trigger,
|
|
187
|
+
cancelRun,
|
|
188
|
+
terminateRun,
|
|
189
|
+
retryRun,
|
|
190
|
+
resumeRun,
|
|
104
191
|
getRun,
|
|
105
192
|
getDetails,
|
|
193
|
+
getSteps,
|
|
106
194
|
getHistory,
|
|
107
195
|
getStackTrace
|
|
108
196
|
};
|
|
@@ -10,6 +10,7 @@ export declare function useIntegrationsTemplates(): {
|
|
|
10
10
|
list: () => WorkflowTemplate[];
|
|
11
11
|
get: (slug: string) => WorkflowTemplate | null;
|
|
12
12
|
fetchDetail: (slug: string) => Promise<WorkflowTemplate | null>;
|
|
13
|
+
ensureDetails: (slugs: string[], concurrency?: number) => Promise<void>;
|
|
13
14
|
requirements: (slug: string) => Promise<TemplateRequirements | null>;
|
|
14
15
|
instantiate: (slug: string, opts?: {
|
|
15
16
|
name?: string;
|
|
@@ -3,6 +3,14 @@ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
|
|
|
3
3
|
import { resolveLocalized } from "../utils/localize.js";
|
|
4
4
|
const BLOB_VERSION = "v0-draft";
|
|
5
5
|
const INDUSTRY_VALUES = ["mechanical-engineering", "chemicals", "wholesale", "manufacturing", "retail", "any"];
|
|
6
|
+
function plainExcerpt(md) {
|
|
7
|
+
for (const raw of md.split("\n")) {
|
|
8
|
+
const line = raw.trim();
|
|
9
|
+
if (!line || line.startsWith("#")) continue;
|
|
10
|
+
return line.replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
6
14
|
function publisherFor(slug, vendors) {
|
|
7
15
|
const ns = slug.includes(":") ? slug.split(":")[0] : vendors[0] ?? "revenexx";
|
|
8
16
|
const kind = ns === "revenexx" ? "revenexx" : "partner";
|
|
@@ -15,10 +23,16 @@ function emptyBlob(name) {
|
|
|
15
23
|
return { nodeManifestVersion: BLOB_VERSION, name, nodes: [], edges: [], triggerPositions: {}, triggers: [] };
|
|
16
24
|
}
|
|
17
25
|
function mapListItem(api) {
|
|
26
|
+
const short = resolveLocalized(api.shortDescription ?? "");
|
|
27
|
+
const md = resolveLocalized(api.description ?? "");
|
|
18
28
|
return {
|
|
19
29
|
slug: api.slug,
|
|
20
30
|
name: resolveLocalized(api.name),
|
|
21
|
-
description:
|
|
31
|
+
description: short || plainExcerpt(md),
|
|
32
|
+
descriptionMd: md,
|
|
33
|
+
triggerTypes: api.triggerTypes ?? [],
|
|
34
|
+
images: api.images ?? [],
|
|
35
|
+
package: api.package ? { name: api.package.name, label: api.package.label ?? null } : null,
|
|
22
36
|
category: api.category ?? "sync",
|
|
23
37
|
industries: mapIndustries(api.industries),
|
|
24
38
|
vendors: api.vendors ?? [],
|
|
@@ -48,13 +62,17 @@ function applyDetail(base, api) {
|
|
|
48
62
|
name: def.name ?? base.name,
|
|
49
63
|
nodes,
|
|
50
64
|
edges: def.edges ?? [],
|
|
51
|
-
triggerPositions: {},
|
|
65
|
+
triggerPositions: def.triggerPositions ?? {},
|
|
66
|
+
variables: def.variables,
|
|
52
67
|
triggers
|
|
53
68
|
};
|
|
69
|
+
const md = resolveLocalized(api.description ?? "") || base.descriptionMd;
|
|
54
70
|
return {
|
|
55
71
|
...base,
|
|
56
|
-
description:
|
|
72
|
+
description: base.description || plainExcerpt(md),
|
|
73
|
+
descriptionMd: md,
|
|
57
74
|
blob,
|
|
75
|
+
detailLoaded: true,
|
|
58
76
|
nodeCount: nodes.length,
|
|
59
77
|
outline: nodes.map((n) => n.nodeSlug ?? n.id).filter(Boolean)
|
|
60
78
|
};
|
|
@@ -107,6 +125,16 @@ export function useIntegrationsTemplates() {
|
|
|
107
125
|
return base;
|
|
108
126
|
}
|
|
109
127
|
}
|
|
128
|
+
async function ensureDetails(slugs, concurrency = 4) {
|
|
129
|
+
const queue = slugs.filter((slug) => get(slug)?.fromApi && !get(slug)?.detailLoaded);
|
|
130
|
+
const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
131
|
+
while (queue.length) {
|
|
132
|
+
const slug = queue.shift();
|
|
133
|
+
if (slug) await fetchDetail(slug);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
await Promise.all(workers);
|
|
137
|
+
}
|
|
110
138
|
async function requirements(slug) {
|
|
111
139
|
try {
|
|
112
140
|
const raw = unwrap(await api.get(`/templates/${slug}/requirements`));
|
|
@@ -137,5 +165,5 @@ export function useIntegrationsTemplates() {
|
|
|
137
165
|
}
|
|
138
166
|
const total = computed(() => templates.value.length);
|
|
139
167
|
const popular = computed(() => [...templates.value].sort((a, b) => b.installs - a.installs).slice(0, 4));
|
|
140
|
-
return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, requirements, instantiate, install };
|
|
168
|
+
return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, ensureDetails, requirements, instantiate, install };
|
|
141
169
|
}
|
|
@@ -23,6 +23,7 @@ export declare function useIntegrationsWorkflows(): {
|
|
|
23
23
|
active: boolean;
|
|
24
24
|
executionMode?: WorkflowExecutionMode;
|
|
25
25
|
}) => Promise<Workflow>;
|
|
26
|
+
duplicate: (id: number, name?: string) => Promise<Workflow>;
|
|
26
27
|
remove: (id: number) => Promise<void>;
|
|
27
28
|
toggleActive: (id: number) => Promise<Workflow | null>;
|
|
28
29
|
};
|