@revenexx/studio-integrations 0.5.1 → 0.7.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/CHANGELOG.md +428 -0
- package/README.md +48 -4
- package/dist/module.d.mts +17 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +10 -2
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +43 -41
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +123 -50
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +108 -71
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +12 -15
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +1 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +7 -4
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +1 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +4 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +67 -37
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +4 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +30 -20
- package/dist/runtime/components/Integrations/Layout.vue +48 -4
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +23 -23
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +11 -13
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +2 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +187 -93
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +2 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +6 -0
- package/dist/runtime/components/Integrations/NodeInspector.vue +1157 -376
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +2 -30
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/NodeMark.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/NodeMark.vue +79 -0
- package/dist/runtime/components/Integrations/NodeMark.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +43 -0
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/NodePalette.vue +122 -55
- package/dist/runtime/components/Integrations/QueryBuilder.vue +46 -38
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +38 -12
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +27 -13
- package/dist/runtime/components/Integrations/RunResponseModal.vue +11 -10
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +0 -1
- package/dist/runtime/components/Integrations/RunsTable.vue +38 -16
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +0 -1
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +8 -7
- package/dist/runtime/components/Integrations/Sidebar.vue +14 -10
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +14 -4
- package/dist/runtime/components/Integrations/StickyNoteView.vue +38 -23
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
- package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue +119 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +16 -18
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +52 -67
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +18 -4
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +29 -11
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +18 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +20 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +240 -116
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +20 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +28 -43
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +19 -1
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +17 -7
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +19 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +4 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +198 -56
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +4 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +56 -59
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +28 -21
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +1 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +15 -9
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +1 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -10
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +50 -36
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +126 -48
- package/dist/runtime/composables/integrationsApi.js +4 -2
- package/dist/runtime/composables/useCredentialTypes.js +8 -6
- package/dist/runtime/composables/useIntegrationsAudit.js +4 -3
- package/dist/runtime/composables/useIntegrationsCredentials.js +10 -8
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +7 -6
- package/dist/runtime/composables/useIntegrationsFocus.d.ts +12 -0
- package/dist/runtime/composables/useIntegrationsLocale.d.ts +26 -0
- package/dist/runtime/composables/useIntegrationsLocale.js +8 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +7 -5
- package/dist/runtime/composables/useIntegrationsRealtime.d.ts +149 -0
- package/dist/runtime/composables/useIntegrationsRealtime.js +80 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +9 -7
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +1 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +14 -7
- package/dist/runtime/composables/useIntegrationsSchedules.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsSchedules.js +10 -3
- package/dist/runtime/composables/useIntegrationsSecrets.js +14 -9
- package/dist/runtime/composables/useIntegrationsT.d.ts +35 -0
- package/dist/runtime/composables/useIntegrationsT.js +9 -0
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +3 -3
- package/dist/runtime/composables/useIntegrationsTemplates.js +28 -16
- package/dist/runtime/composables/useIntegrationsTriggers.js +10 -8
- package/dist/runtime/composables/useIntegrationsWorkflows.js +13 -7
- package/dist/runtime/composables/useLoadGate.d.ts +17 -0
- package/dist/runtime/composables/useLoadGate.js +8 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
- package/dist/runtime/data/branchReorder.d.ts +37 -0
- package/dist/runtime/data/branchReorder.js +71 -0
- package/dist/runtime/data/handleId.d.ts +23 -0
- package/dist/runtime/data/handleId.js +6 -0
- package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
- package/dist/runtime/data/integrationNodeCatalog.js +14 -0
- package/dist/runtime/data/integrationTemplates.d.ts +48 -0
- package/dist/runtime/data/integrationTemplates.js +21 -0
- package/dist/runtime/data/integrationWorkflowSamples.js +1 -1
- package/dist/runtime/data/nodeIcons.d.ts +2 -0
- package/dist/runtime/data/nodeIcons.js +73 -0
- package/dist/runtime/data/nodeInputData.d.ts +16 -4
- package/dist/runtime/data/nodeInputData.js +4 -0
- package/dist/runtime/data/payloadForm.d.ts +9 -2
- package/dist/runtime/data/payloadForm.js +29 -4
- package/dist/runtime/data/portTone.d.ts +17 -0
- package/dist/runtime/data/portTone.js +7 -0
- package/dist/runtime/data/webhookAuth.d.ts +18 -0
- package/dist/runtime/data/webhookAuth.js +19 -0
- package/dist/runtime/data/workflowIconCatalog.js +4 -6
- package/dist/runtime/i18n/messages/de.json +1207 -0
- package/dist/runtime/i18n/messages/en.json +1207 -0
- package/dist/runtime/i18n/translate.d.ts +83 -0
- package/dist/runtime/i18n/translate.js +41 -0
- package/dist/runtime/pages/integrations/audit.vue +140 -37
- package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +9 -6
- package/dist/runtime/pages/integrations/credentials.vue +180 -64
- package/dist/runtime/pages/integrations/index.vue +91 -92
- package/dist/runtime/pages/integrations/runs.vue +33 -21
- package/dist/runtime/pages/integrations/schedules.vue +88 -23
- package/dist/runtime/pages/integrations/secrets.vue +57 -35
- package/dist/runtime/pages/integrations/templates/[slug].vue +119 -88
- package/dist/runtime/pages/integrations/templates/index.vue +82 -103
- package/dist/runtime/pages/integrations/workflows/[id].vue +344 -91
- package/dist/runtime/pages/integrations/workflows/index.vue +23 -5
- package/dist/runtime/plugins/locale.d.ts +10 -0
- package/dist/runtime/plugins/locale.js +6 -0
- package/dist/runtime/plugins/realtime.client.d.ts +32 -0
- package/dist/runtime/plugins/realtime.client.js +4 -0
- package/dist/runtime/types/integrations.d.ts +65 -6
- package/dist/runtime/utils/apiError.d.ts +47 -0
- package/dist/runtime/utils/apiError.js +32 -0
- package/dist/runtime/utils/cockpitSources.d.ts +153 -0
- package/dist/runtime/utils/cockpitSources.js +14 -0
- package/dist/runtime/utils/credentialState.d.ts +24 -1
- package/dist/runtime/utils/credentialState.js +26 -11
- package/dist/runtime/utils/loadGate.d.ts +61 -0
- package/dist/runtime/utils/loadGate.js +22 -0
- package/dist/runtime/utils/localize.d.ts +46 -4
- package/dist/runtime/utils/localize.js +34 -7
- package/dist/runtime/utils/notify.d.ts +59 -0
- package/dist/runtime/utils/notify.js +21 -0
- package/dist/runtime/utils/pollBudget.d.ts +72 -0
- package/dist/runtime/utils/pollBudget.js +17 -0
- package/dist/runtime/utils/revenexxCredentialSource.d.ts +26 -0
- package/dist/runtime/utils/revenexxCredentialSource.js +8 -0
- package/dist/runtime/utils/schedule.d.ts +116 -0
- package/dist/runtime/utils/schedule.js +219 -0
- package/dist/runtime/utils/statusStyle.d.ts +26 -2
- package/dist/runtime/utils/statusStyle.js +9 -1
- package/dist/runtime/utils/validationFindings.d.ts +101 -0
- package/dist/runtime/utils/validationFindings.js +54 -0
- package/dist/runtime/utils/whenIdle.d.ts +42 -0
- package/dist/runtime/utils/whenIdle.js +12 -0
- package/dist/types.d.mts +2 -6
- package/package.json +10 -3
|
@@ -17,6 +17,7 @@ defineProps({
|
|
|
17
17
|
runId: { type: [String, null], required: false }
|
|
18
18
|
});
|
|
19
19
|
const emit = defineEmits(["update:open", "view-run"]);
|
|
20
|
+
const t = useIntegrationsT();
|
|
20
21
|
</script>
|
|
21
22
|
|
|
22
23
|
<template>
|
|
@@ -25,16 +26,16 @@ const emit = defineEmits(["update:open", "view-run"]);
|
|
|
25
26
|
JSON <pre> scrolling INSIDE instead of blowing the dialog open. -->
|
|
26
27
|
<DialogContent class="max-w-[94vw] overflow-hidden sm:max-w-4xl">
|
|
27
28
|
<DialogHeader>
|
|
28
|
-
<DialogTitle>{{ mode === "async" ? "
|
|
29
|
+
<DialogTitle>{{ mode === "async" ? t("integration.runs.response.title.async") : t("integration.runs.response.title.sync") }}</DialogTitle>
|
|
29
30
|
<DialogDescription class="font-mono text-sm">
|
|
30
|
-
{{ mode === "async" ? "
|
|
31
|
+
{{ mode === "async" ? t("integration.runs.response.description.async") : t("integration.runs.response.description.sync") }}
|
|
31
32
|
</DialogDescription>
|
|
32
33
|
</DialogHeader>
|
|
33
34
|
|
|
34
35
|
<div class="min-w-0 max-w-full text-sm">
|
|
35
36
|
<div v-if="loading" class="flex items-center gap-2 py-6 text-muted-foreground">
|
|
36
37
|
<Loader2 :size="16" class="animate-spin" />
|
|
37
|
-
{{ mode === "async" ? "
|
|
38
|
+
{{ mode === "async" ? t("integration.runs.response.loading.async") : t("integration.runs.response.loading.sync") }}
|
|
38
39
|
</div>
|
|
39
40
|
|
|
40
41
|
<template v-else-if="error">
|
|
@@ -43,7 +44,7 @@ const emit = defineEmits(["update:open", "view-run"]);
|
|
|
43
44
|
</div>
|
|
44
45
|
<div v-if="runId" class="mt-3 flex justify-end">
|
|
45
46
|
<Button class="h-8 text-sm" @click="emit('view-run', runId)">
|
|
46
|
-
|
|
47
|
+
{{ t("integration.runs.response.action.viewRunWithId", { id: runId }) }}
|
|
47
48
|
</Button>
|
|
48
49
|
</div>
|
|
49
50
|
</template>
|
|
@@ -51,15 +52,15 @@ const emit = defineEmits(["update:open", "view-run"]);
|
|
|
51
52
|
<template v-else-if="mode === 'async'">
|
|
52
53
|
<div class="flex items-center gap-2 rounded-md border border-emerald-500/20 bg-emerald-500/5 px-3 py-2 text-emerald-700">
|
|
53
54
|
<CheckCircle2 :size="15" class="shrink-0" />
|
|
54
|
-
<span v-if="runId">
|
|
55
|
-
<span v-else>
|
|
55
|
+
<span v-if="runId">{{ t("integration.runs.response.async.startedWithId", { id: runId }) }}</span>
|
|
56
|
+
<span v-else>{{ t("integration.runs.response.async.started") }}</span>
|
|
56
57
|
</div>
|
|
57
58
|
<div class="mt-3 flex justify-end gap-2">
|
|
58
59
|
<Button variant="outline" class="h-8 text-sm" @click="emit('update:open', false)">
|
|
59
|
-
|
|
60
|
+
{{ t("integration.common.action.close") }}
|
|
60
61
|
</Button>
|
|
61
62
|
<Button v-if="runId" class="h-8 text-sm" @click="emit('view-run', runId)">
|
|
62
|
-
|
|
63
|
+
{{ t("integration.runs.response.action.viewRun") }}
|
|
63
64
|
</Button>
|
|
64
65
|
</div>
|
|
65
66
|
</template>
|
|
@@ -70,13 +71,13 @@ const emit = defineEmits(["update:open", "view-run"]);
|
|
|
70
71
|
>{{ JSON.stringify(response, null, 2) }}</pre>
|
|
71
72
|
<div v-if="runId" class="mt-3 flex justify-end">
|
|
72
73
|
<Button variant="outline" class="h-8 text-sm" @click="emit('view-run', runId)">
|
|
73
|
-
|
|
74
|
+
{{ t("integration.runs.response.action.viewRun") }}
|
|
74
75
|
</Button>
|
|
75
76
|
</div>
|
|
76
77
|
</template>
|
|
77
78
|
|
|
78
79
|
<p v-else class="py-6 text-muted-foreground">
|
|
79
|
-
|
|
80
|
+
{{ t("integration.runs.response.empty") }}
|
|
80
81
|
</p>
|
|
81
82
|
</div>
|
|
82
83
|
</DialogContent>
|
|
@@ -14,7 +14,6 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {
|
|
|
14
14
|
onChanged?: (() => any) | undefined;
|
|
15
15
|
}>, {
|
|
16
16
|
loading: boolean;
|
|
17
|
-
emptyText: string;
|
|
18
17
|
pagination: boolean;
|
|
19
18
|
showWorkflow: boolean;
|
|
20
19
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, onMounted, onUnmounted, ref } from "vue";
|
|
3
3
|
import { Badge } from "@revenexx/studio";
|
|
4
|
-
import { statusClass } from "../../utils/statusStyle";
|
|
4
|
+
import { RUN_STATUS_WORDS, normalisedRunStatus, runStatusLabel, statusClass } from "../../utils/statusStyle";
|
|
5
|
+
import { TRIGGER_TYPE_WORDS, triggerTypeLabel } from "../../data/integrationNodeCatalog";
|
|
5
6
|
const props = defineProps({
|
|
6
7
|
runs: { type: Array, required: true },
|
|
7
8
|
loading: { type: Boolean, required: false, default: false },
|
|
8
|
-
emptyText: { type: String, required: false
|
|
9
|
+
emptyText: { type: String, required: false },
|
|
9
10
|
pagination: { type: Boolean, required: false, default: true },
|
|
10
11
|
showWorkflow: { type: Boolean, required: false, default: true }
|
|
11
12
|
});
|
|
12
13
|
const emit = defineEmits(["changed"]);
|
|
14
|
+
const t = useIntegrationsT();
|
|
13
15
|
const router = useRouter();
|
|
16
|
+
const emptyText = computed(() => props.emptyText ?? t("integration.runs.table.empty"));
|
|
14
17
|
const now = ref(Date.now());
|
|
15
18
|
let tick = null;
|
|
16
19
|
onMounted(() => {
|
|
@@ -31,10 +34,10 @@ function formatRelative(iso) {
|
|
|
31
34
|
if (!iso) return "\u2014";
|
|
32
35
|
const ms = Date.now() - new Date(iso).getTime();
|
|
33
36
|
if (Number.isNaN(ms)) return "\u2014";
|
|
34
|
-
if (ms < 6e4) return
|
|
35
|
-
if (ms < 36e5) return
|
|
36
|
-
if (ms < 864e5) return
|
|
37
|
-
return
|
|
37
|
+
if (ms < 6e4) return t("integration.common.time.secondsAgo", Math.max(1, Math.floor(ms / 1e3)));
|
|
38
|
+
if (ms < 36e5) return t("integration.common.time.minutesAgo", Math.floor(ms / 6e4));
|
|
39
|
+
if (ms < 864e5) return t("integration.common.time.hoursAgo", Math.floor(ms / 36e5));
|
|
40
|
+
return t("integration.common.time.daysAgo", Math.floor(ms / 864e5));
|
|
38
41
|
}
|
|
39
42
|
function formatDuration(ms) {
|
|
40
43
|
if (ms == null) return "\u2014";
|
|
@@ -49,16 +52,35 @@ function liveDuration(run) {
|
|
|
49
52
|
const end = run.finishedAt ? Date.parse(run.finishedAt) : now.value;
|
|
50
53
|
return end - start;
|
|
51
54
|
}
|
|
55
|
+
const filterTabs = computed(() => props.pagination ? [
|
|
56
|
+
{
|
|
57
|
+
key: "status",
|
|
58
|
+
label: t("integration.runs.table.column.status"),
|
|
59
|
+
getter: (r) => normalisedRunStatus(r.status),
|
|
60
|
+
options: RUN_STATUS_WORDS.map((v) => ({ value: v, label: runStatusLabel(v) }))
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: "trigger",
|
|
64
|
+
label: t("integration.runs.table.column.trigger"),
|
|
65
|
+
getter: (r) => r.triggerType ?? "",
|
|
66
|
+
options: TRIGGER_TYPE_WORDS.map((v) => ({ value: v, label: triggerTypeLabel(v) }))
|
|
67
|
+
}
|
|
68
|
+
] : void 0);
|
|
52
69
|
const columns = computed(() => [
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
// Sorted and searched by the word on screen, not by the raw field: a column
|
|
71
|
+
// with no `searchGetter` is searched through its `sortGetter`, so a German
|
|
72
|
+
// reader typing the status they can see has to find the row that shows it.
|
|
73
|
+
{ key: "status", label: t("integration.runs.table.column.status"), width: "100px", sortable: true, sortGetter: (r) => runStatusLabel(r.status) },
|
|
74
|
+
...props.showWorkflow ? [{ key: "workflow", label: t("integration.runs.table.column.workflow"), sortable: true, sortGetter: (r) => r.workflowName.toLowerCase(), searchGetter: (r) => r.workflowName }] : [],
|
|
55
75
|
// Fixed floor wide enough for a full UUID, flexible above it — header and
|
|
56
76
|
// body rows are separate grids, so a content-sized track (max-content)
|
|
57
77
|
// would misalign them. The flex share also lets the table fill the width.
|
|
58
|
-
{ key: "id", label: "
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
{ key: "
|
|
78
|
+
{ key: "id", label: t("integration.runs.table.column.id"), width: "minmax(280px,1fr)", searchGetter: (r) => `${r.temporalRunId ?? ""} ${r.id}` },
|
|
79
|
+
// Same rule as the status column above: the word on screen is what sorts and
|
|
80
|
+
// what a search has to match.
|
|
81
|
+
{ key: "trigger", label: t("integration.runs.table.column.trigger"), width: "120px", sortable: true, sortGetter: (r) => triggerTypeLabel(r.triggerType) },
|
|
82
|
+
{ key: "started", label: t("integration.runs.table.column.started"), width: "120px", sortable: true, sortGetter: (r) => r.startedAt },
|
|
83
|
+
{ key: "duration", label: t("integration.runs.table.column.duration"), width: "100px", sortable: true, sortGetter: (r) => r.durationMs ?? -1 },
|
|
62
84
|
{ key: "actions", label: "", width: "52px" }
|
|
63
85
|
]);
|
|
64
86
|
</script>
|
|
@@ -69,8 +91,8 @@ const columns = computed(() => [
|
|
|
69
91
|
:columns="columns"
|
|
70
92
|
:row-key="(r) => r.id"
|
|
71
93
|
:default-sort="{ column: 'started', direction: 'desc' }"
|
|
72
|
-
:search="{ placeholder: '
|
|
73
|
-
:filter="
|
|
94
|
+
:search="{ placeholder: t('integration.runs.table.search.placeholder') }"
|
|
95
|
+
:filter-tabs="filterTabs"
|
|
74
96
|
:loading="loading"
|
|
75
97
|
:pagination="pagination"
|
|
76
98
|
:empty-text="emptyText"
|
|
@@ -78,7 +100,7 @@ const columns = computed(() => [
|
|
|
78
100
|
>
|
|
79
101
|
<template #cell-status="{ row: run }">
|
|
80
102
|
<Badge variant="outline" :class="['border text-xs font-medium uppercase', statusClass(run.status)]">
|
|
81
|
-
{{ run.status }}
|
|
103
|
+
{{ runStatusLabel(run.status) }}
|
|
82
104
|
</Badge>
|
|
83
105
|
</template>
|
|
84
106
|
|
|
@@ -93,7 +115,7 @@ const columns = computed(() => [
|
|
|
93
115
|
|
|
94
116
|
<template #cell-trigger="{ row: run }">
|
|
95
117
|
<span class="inline-flex items-center whitespace-nowrap rounded-md border border-black/[0.06] bg-muted/50 px-1.5 py-0.5 text-xs font-medium uppercase text-muted-foreground">
|
|
96
|
-
{{ run.triggerType }}
|
|
118
|
+
{{ triggerTypeLabel(run.triggerType) }}
|
|
97
119
|
</span>
|
|
98
120
|
</template>
|
|
99
121
|
|
|
@@ -14,7 +14,6 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {
|
|
|
14
14
|
onChanged?: (() => any) | undefined;
|
|
15
15
|
}>, {
|
|
16
16
|
loading: boolean;
|
|
17
|
-
emptyText: string;
|
|
18
17
|
pagination: boolean;
|
|
19
18
|
showWorkflow: boolean;
|
|
20
19
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -5,6 +5,7 @@ import { Badge, Button, Dialog, DialogContent, DialogDescription, DialogHeader,
|
|
|
5
5
|
import { useIntegrationsCredentials } from "../../composables/useIntegrationsCredentials";
|
|
6
6
|
const open = defineModel("open", { type: Boolean, ...{ default: false } });
|
|
7
7
|
const emit = defineEmits(["created"]);
|
|
8
|
+
const t = useIntegrationsT();
|
|
8
9
|
const sftp = typeof useConsoleSftp === "function" ? useConsoleSftp() : {
|
|
9
10
|
accounts: ref([]),
|
|
10
11
|
loading: ref(false),
|
|
@@ -30,7 +31,7 @@ async function importAccount(account) {
|
|
|
30
31
|
try {
|
|
31
32
|
const creds = await sftp.revealPassword(tenant, account.id);
|
|
32
33
|
if (!creds?.password) {
|
|
33
|
-
error.value = "
|
|
34
|
+
error.value = t("integration.credentials.sftpImport.error.readPassword");
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
const created = await create({
|
|
@@ -46,7 +47,7 @@ async function importAccount(account) {
|
|
|
46
47
|
emit("created", created.id);
|
|
47
48
|
open.value = false;
|
|
48
49
|
} catch (err) {
|
|
49
|
-
error.value = err instanceof Error ? err.message : "
|
|
50
|
+
error.value = err instanceof Error ? err.message : t("integration.credentials.sftpImport.error.importFailed");
|
|
50
51
|
} finally {
|
|
51
52
|
busyId.value = null;
|
|
52
53
|
}
|
|
@@ -57,9 +58,9 @@ async function importAccount(account) {
|
|
|
57
58
|
<Dialog v-model:open="open">
|
|
58
59
|
<DialogContent class="sm:max-w-md">
|
|
59
60
|
<DialogHeader>
|
|
60
|
-
<DialogTitle>
|
|
61
|
+
<DialogTitle>{{ t("integration.credentials.sftpImport.title") }}</DialogTitle>
|
|
61
62
|
<DialogDescription class="text-sm">
|
|
62
|
-
|
|
63
|
+
{{ t("integration.credentials.sftpImport.description") }}
|
|
63
64
|
</DialogDescription>
|
|
64
65
|
</DialogHeader>
|
|
65
66
|
|
|
@@ -67,7 +68,7 @@ async function importAccount(account) {
|
|
|
67
68
|
<div v-for="n in 3" :key="`sk-${n}`" class="h-9 rounded-md bg-black/[0.07] animate-pulse" />
|
|
68
69
|
</div>
|
|
69
70
|
<p v-else-if="!activeAccounts.length" class="py-4 text-center text-sm text-muted-foreground">
|
|
70
|
-
|
|
71
|
+
{{ t("integration.credentials.sftpImport.empty") }}
|
|
71
72
|
</p>
|
|
72
73
|
<div v-else class="max-h-72 space-y-1 overflow-y-auto">
|
|
73
74
|
<button
|
|
@@ -82,7 +83,7 @@ async function importAccount(account) {
|
|
|
82
83
|
<span class="block truncate font-mono text-sm">{{ account.login_username }}</span>
|
|
83
84
|
<span class="block truncate text-xs text-muted-foreground">{{ account.home_directory ?? account.server ?? "" }}</span>
|
|
84
85
|
</span>
|
|
85
|
-
<Badge variant="outline" class="text-xs font-medium uppercase shrink-0">
|
|
86
|
+
<Badge variant="outline" class="text-xs font-medium uppercase shrink-0">{{ t("integration.credentials.sftpImport.badge.active") }}</Badge>
|
|
86
87
|
<Loader2 v-if="busyId === account.id" :size="14" class="shrink-0 animate-spin text-muted-foreground" />
|
|
87
88
|
</button>
|
|
88
89
|
</div>
|
|
@@ -90,7 +91,7 @@ async function importAccount(account) {
|
|
|
90
91
|
<p v-if="error" class="text-sm text-rose-600">{{ error }}</p>
|
|
91
92
|
|
|
92
93
|
<div class="flex justify-end">
|
|
93
|
-
<Button variant="outline" size="sm" class="h-8 text-sm" @click="open = false">
|
|
94
|
+
<Button variant="outline" size="sm" class="h-8 text-sm" @click="open = false">{{ t("integration.common.action.close") }}</Button>
|
|
94
95
|
</div>
|
|
95
96
|
</DialogContent>
|
|
96
97
|
</Dialog>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { Pulse as Activity, ClockCircle as Clock, Home, Key as KeyRound, Layers, ShieldCheck, ShieldKeyhole as Credentials, Programming as Workflow } from "#solar-icons";
|
|
3
3
|
import SidebarNav from "@revenexx/studio-shared/components/Shared/SidebarNav.vue";
|
|
4
|
+
const t = useIntegrationsT();
|
|
4
5
|
const { workflows } = useIntegrationsWorkflows();
|
|
5
6
|
const { runningCount } = useIntegrationsRuns();
|
|
6
7
|
const { activeCount: schedulesActive } = useIntegrationsSchedules();
|
|
@@ -10,25 +11,28 @@ const sections = computed(() => [
|
|
|
10
11
|
{
|
|
11
12
|
key: "pinned",
|
|
12
13
|
items: [
|
|
13
|
-
{ label: "
|
|
14
|
+
{ label: t("integration.nav.item.dashboard"), to: "/integrations", icon: Home, exact: true }
|
|
14
15
|
]
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
key: "integrations",
|
|
18
|
-
|
|
19
|
+
// A rail's section heading is a proper noun and stays English in every UI
|
|
20
|
+
// language. It is still a key, so the decision is written down where a
|
|
21
|
+
// translator will look for it rather than only in a review comment.
|
|
22
|
+
label: t("integration.nav.section.integrations"),
|
|
19
23
|
items: [
|
|
20
|
-
{ label: "
|
|
21
|
-
{ label: "
|
|
22
|
-
{ label: "
|
|
23
|
-
{ label: "
|
|
24
|
-
{ label: "
|
|
25
|
-
{ label: "
|
|
26
|
-
{ label: "
|
|
24
|
+
{ label: t("integration.nav.item.workflows"), to: "/integrations/workflows", icon: Workflow, badge: workflows.value.length || null },
|
|
25
|
+
{ label: t("integration.nav.item.templates"), to: "/integrations/templates", icon: Layers },
|
|
26
|
+
{ label: t("integration.nav.item.runs"), to: "/integrations/runs", icon: Activity, badge: runningCount.value || null },
|
|
27
|
+
{ label: t("integration.nav.item.schedules"), to: "/integrations/schedules", icon: Clock, badge: schedulesActive.value || null },
|
|
28
|
+
{ label: t("integration.nav.item.credentials"), to: "/integrations/credentials", icon: Credentials, badge: credentialsTotal.value || null },
|
|
29
|
+
{ label: t("integration.nav.item.secrets"), to: "/integrations/secrets", icon: KeyRound, badge: secretsTotal.value || null },
|
|
30
|
+
{ label: t("integration.nav.item.audit"), to: "/integrations/audit", icon: ShieldCheck }
|
|
27
31
|
]
|
|
28
32
|
}
|
|
29
33
|
]);
|
|
30
34
|
</script>
|
|
31
35
|
|
|
32
36
|
<template>
|
|
33
|
-
<SidebarNav title="
|
|
37
|
+
<SidebarNav :title="t('integration.nav.title')" :icon="Workflow" :sections="sections" minimal />
|
|
34
38
|
</template>
|
|
@@ -11,13 +11,23 @@ import type { WorkflowSticky } from '../../types/integrations.js';
|
|
|
11
11
|
* - NodeToolbar on hover/selection: 7-color pastel palette + delete.
|
|
12
12
|
* - Hand-rolled bottom-right resize handle (no @vue-flow/node-resizer dep);
|
|
13
13
|
* size persists once on pointer-up.
|
|
14
|
+
*
|
|
15
|
+
* **Absence of the callbacks is what makes a note read-only** — the run replay and
|
|
16
|
+
* the history preview hand it none, and then it draws no toolbar, no resize handle
|
|
17
|
+
* and cannot be opened for typing (behaviour specs/sticky-notes.md AC-9). They used
|
|
18
|
+
* to be handed no-ops instead, which left every one of those affordances on screen
|
|
19
|
+
* doing nothing: a reader could not tell "not allowed here" from "broken" (PO-284).
|
|
20
|
+
* `GroupFrameView` gates its own toolbar on the same absence.
|
|
14
21
|
*/
|
|
15
22
|
export interface StickyNodeData {
|
|
16
23
|
sticky: WorkflowSticky;
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Persist a partial change into the blob (editor-side, syncs + pushes).
|
|
26
|
+
* Absent on a read-only canvas — see the note above.
|
|
27
|
+
*/
|
|
28
|
+
onUpdate?: (patch: Partial<Pick<WorkflowSticky, 'content' | 'size' | 'color'>>) => void;
|
|
29
|
+
/** Remove this sticky from the blob (editor-side). Absent when read-only. */
|
|
30
|
+
onRemove?: () => void;
|
|
21
31
|
}
|
|
22
32
|
type __VLS_Props = {
|
|
23
33
|
id: string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { computed, nextTick, ref, watch } from "vue";
|
|
3
3
|
import { Position, useVueFlow } from "@vue-flow/core";
|
|
4
4
|
import { NodeToolbar } from "@vue-flow/node-toolbar";
|
|
5
|
-
import {
|
|
5
|
+
import { TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
6
6
|
const props = defineProps({
|
|
7
7
|
id: { type: String, required: true },
|
|
8
8
|
data: { type: Object, required: true },
|
|
@@ -10,20 +10,22 @@ const props = defineProps({
|
|
|
10
10
|
});
|
|
11
11
|
defineOptions({ inheritAttrs: false });
|
|
12
12
|
defineEmits(["updateNodeInternals"]);
|
|
13
|
+
const t = useIntegrationsT();
|
|
13
14
|
const flow = useVueFlow();
|
|
14
15
|
const STICKY_COLORS = [
|
|
15
|
-
{ bg: "#fef3c7", border: "#fde68a",
|
|
16
|
-
{ bg: "#ffedd5", border: "#fed7aa",
|
|
17
|
-
{ bg: "#ffe4e6", border: "#fecdd3",
|
|
18
|
-
{ bg: "#dcfce7", border: "#bbf7d0",
|
|
19
|
-
{ bg: "#dbeafe", border: "#bfdbfe",
|
|
20
|
-
{ bg: "#ede9fe", border: "#ddd6fe",
|
|
21
|
-
{ bg: "#f5f5f4", border: "#e7e5e4",
|
|
16
|
+
{ bg: "#fef3c7", border: "#fde68a", nameKey: "integration.canvas.color.amber" },
|
|
17
|
+
{ bg: "#ffedd5", border: "#fed7aa", nameKey: "integration.canvas.color.orange" },
|
|
18
|
+
{ bg: "#ffe4e6", border: "#fecdd3", nameKey: "integration.canvas.color.rose" },
|
|
19
|
+
{ bg: "#dcfce7", border: "#bbf7d0", nameKey: "integration.canvas.color.green" },
|
|
20
|
+
{ bg: "#dbeafe", border: "#bfdbfe", nameKey: "integration.canvas.color.blue" },
|
|
21
|
+
{ bg: "#ede9fe", border: "#ddd6fe", nameKey: "integration.canvas.color.violet" },
|
|
22
|
+
{ bg: "#f5f5f4", border: "#e7e5e4", nameKey: "integration.canvas.color.gray" }
|
|
22
23
|
];
|
|
23
24
|
const color = computed(() => STICKY_COLORS[props.data.sticky.color ?? 0] ?? STICKY_COLORS[0]);
|
|
25
|
+
const editable = computed(() => !!props.data.onUpdate && !!props.data.onRemove);
|
|
24
26
|
function pickColor(i) {
|
|
25
27
|
if ((props.data.sticky.color ?? 0) === i) return;
|
|
26
|
-
props.data.onUpdate({ color: i });
|
|
28
|
+
props.data.onUpdate?.({ color: i });
|
|
27
29
|
}
|
|
28
30
|
const DEFAULT_WIDTH = 240;
|
|
29
31
|
const DEFAULT_HEIGHT = 160;
|
|
@@ -54,13 +56,18 @@ function onResizeMove(e) {
|
|
|
54
56
|
function onResizeEnd() {
|
|
55
57
|
if (!resizing.value) return;
|
|
56
58
|
resizing.value = false;
|
|
57
|
-
props.data.onUpdate({ size: { width: width.value, height: height.value } });
|
|
59
|
+
props.data.onUpdate?.({ size: { width: width.value, height: height.value } });
|
|
58
60
|
}
|
|
59
61
|
const editing = ref(false);
|
|
60
62
|
const draft = ref("");
|
|
61
63
|
const textareaEl = ref(null);
|
|
64
|
+
function onDoubleClick(event) {
|
|
65
|
+
if (!editable.value) return;
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
startEdit();
|
|
68
|
+
}
|
|
62
69
|
function startEdit() {
|
|
63
|
-
if (editing.value) return;
|
|
70
|
+
if (!editable.value || editing.value) return;
|
|
64
71
|
draft.value = props.data.sticky.content;
|
|
65
72
|
editing.value = true;
|
|
66
73
|
nextTick(() => textareaEl.value?.focus());
|
|
@@ -69,7 +76,7 @@ function commitEdit() {
|
|
|
69
76
|
if (!editing.value) return;
|
|
70
77
|
editing.value = false;
|
|
71
78
|
if (draft.value !== props.data.sticky.content) {
|
|
72
|
-
props.data.onUpdate({ content: draft.value });
|
|
79
|
+
props.data.onUpdate?.({ content: draft.value });
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
function onTextareaKeydown(e) {
|
|
@@ -91,7 +98,7 @@ function onHoverLeave() {
|
|
|
91
98
|
hovered.value = false;
|
|
92
99
|
}, 250);
|
|
93
100
|
}
|
|
94
|
-
const toolbarVisible = computed(() => !editing.value && (hovered.value || !!props.selected));
|
|
101
|
+
const toolbarVisible = computed(() => editable.value && !editing.value && (hovered.value || !!props.selected));
|
|
95
102
|
function escapeHtml(s) {
|
|
96
103
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
97
104
|
}
|
|
@@ -133,17 +140,17 @@ function onViewMouseDown(e) {
|
|
|
133
140
|
class="size-5 rounded-full border transition hover:scale-110"
|
|
134
141
|
:class="(data.sticky.color ?? 0) === i ? 'ring-2 ring-black/25 ring-offset-1' : ''"
|
|
135
142
|
:style="{ backgroundColor: c.bg, borderColor: c.border }"
|
|
136
|
-
:title="c.
|
|
143
|
+
:title="t(c.nameKey)"
|
|
137
144
|
@click="pickColor(i)"
|
|
138
145
|
/>
|
|
139
146
|
<div class="mx-0.5 h-4 w-px bg-black/10" />
|
|
140
147
|
<button
|
|
141
148
|
type="button"
|
|
142
149
|
class="flex size-6 items-center justify-center rounded-md text-muted-foreground transition hover:bg-rose-50 hover:text-rose-600"
|
|
143
|
-
title="
|
|
144
|
-
@click="data.onRemove()"
|
|
150
|
+
:title="t('integration.canvas.sticky.action.delete')"
|
|
151
|
+
@click="data.onRemove?.()"
|
|
145
152
|
>
|
|
146
|
-
<
|
|
153
|
+
<TrashBin :size="13" />
|
|
147
154
|
</button>
|
|
148
155
|
</div>
|
|
149
156
|
</NodeToolbar>
|
|
@@ -163,7 +170,7 @@ function onViewMouseDown(e) {
|
|
|
163
170
|
}"
|
|
164
171
|
@mouseenter="onHoverEnter"
|
|
165
172
|
@mouseleave="onHoverLeave"
|
|
166
|
-
@dblclick
|
|
173
|
+
@dblclick="onDoubleClick"
|
|
167
174
|
>
|
|
168
175
|
<!-- View mode -->
|
|
169
176
|
<div
|
|
@@ -172,7 +179,11 @@ function onViewMouseDown(e) {
|
|
|
172
179
|
@mousedown="onViewMouseDown"
|
|
173
180
|
>
|
|
174
181
|
<div v-if="data.sticky.content" v-html="rendered" />
|
|
175
|
-
|
|
182
|
+
<!-- The hint is an instruction, not content, so it goes with the rest of the
|
|
183
|
+
affordances: on a read-only canvas it would name the one gesture
|
|
184
|
+
`startEdit` refuses, which is the false affordance AC-9 removes rather
|
|
185
|
+
than a smaller version of it. An empty note there simply reads empty. -->
|
|
186
|
+
<span v-else-if="editable" class="select-none text-stone-400">{{ t("integration.canvas.sticky.state.emptyHint") }}</span>
|
|
176
187
|
</div>
|
|
177
188
|
|
|
178
189
|
<!-- Edit mode — nodrag/nowheel so typing and scrolling never drag or zoom
|
|
@@ -181,8 +192,9 @@ function onViewMouseDown(e) {
|
|
|
181
192
|
v-else
|
|
182
193
|
ref="textareaEl"
|
|
183
194
|
v-model="draft"
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
:aria-label="t('integration.canvas.sticky.textarea.aria')"
|
|
196
|
+
class="nodrag nowheel h-full w-full resize-none bg-transparent px-3 py-2 text-sm leading-snug text-stone-700 outline-none placeholder:text-stone-400 focus:ring-2 focus:ring-inset focus:ring-stone-500/40"
|
|
197
|
+
:placeholder="t('integration.canvas.sticky.textarea.placeholder')"
|
|
186
198
|
spellcheck="false"
|
|
187
199
|
@blur="commitEdit"
|
|
188
200
|
@keydown="onTextareaKeydown"
|
|
@@ -190,10 +202,13 @@ function onViewMouseDown(e) {
|
|
|
190
202
|
@dblclick.stop
|
|
191
203
|
/>
|
|
192
204
|
|
|
193
|
-
<!-- Bottom-right resize handle (hand-rolled; min 150x80)
|
|
205
|
+
<!-- Bottom-right resize handle (hand-rolled; min 150x80). Gone on a
|
|
206
|
+
read-only canvas: a handle that cannot resize is the same lie as a bin
|
|
207
|
+
that cannot delete. -->
|
|
194
208
|
<div
|
|
209
|
+
v-if="editable"
|
|
195
210
|
class="nodrag absolute bottom-0 right-0 flex size-4 cursor-se-resize items-end justify-end p-0.5 text-stone-400/80"
|
|
196
|
-
title="
|
|
211
|
+
:title="t('integration.canvas.sticky.action.resize')"
|
|
197
212
|
@pointerdown="onResizeStart"
|
|
198
213
|
@pointermove="onResizeMove"
|
|
199
214
|
@pointerup="onResizeEnd"
|
|
@@ -11,13 +11,23 @@ import type { WorkflowSticky } from '../../types/integrations.js';
|
|
|
11
11
|
* - NodeToolbar on hover/selection: 7-color pastel palette + delete.
|
|
12
12
|
* - Hand-rolled bottom-right resize handle (no @vue-flow/node-resizer dep);
|
|
13
13
|
* size persists once on pointer-up.
|
|
14
|
+
*
|
|
15
|
+
* **Absence of the callbacks is what makes a note read-only** — the run replay and
|
|
16
|
+
* the history preview hand it none, and then it draws no toolbar, no resize handle
|
|
17
|
+
* and cannot be opened for typing (behaviour specs/sticky-notes.md AC-9). They used
|
|
18
|
+
* to be handed no-ops instead, which left every one of those affordances on screen
|
|
19
|
+
* doing nothing: a reader could not tell "not allowed here" from "broken" (PO-284).
|
|
20
|
+
* `GroupFrameView` gates its own toolbar on the same absence.
|
|
14
21
|
*/
|
|
15
22
|
export interface StickyNodeData {
|
|
16
23
|
sticky: WorkflowSticky;
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Persist a partial change into the blob (editor-side, syncs + pushes).
|
|
26
|
+
* Absent on a read-only canvas — see the note above.
|
|
27
|
+
*/
|
|
28
|
+
onUpdate?: (patch: Partial<Pick<WorkflowSticky, 'content' | 'size' | 'color'>>) => void;
|
|
29
|
+
/** Remove this sticky from the blob (editor-side). Absent when read-only. */
|
|
30
|
+
onRemove?: () => void;
|
|
21
31
|
}
|
|
22
32
|
type __VLS_Props = {
|
|
23
33
|
id: string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WorkflowTemplate } from '../../types/integrations.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
template: WorkflowTemplate;
|
|
4
|
+
/** How much room the surface has. See the comment above; no default on purpose. */
|
|
5
|
+
density: 'full' | 'compact';
|
|
6
|
+
/** Given → the card is a link there. Omitted → a button that emits `select`. */
|
|
7
|
+
to?: string;
|
|
8
|
+
/** This card is the one being acted on: it says so and cannot be pressed again. */
|
|
9
|
+
busy?: boolean;
|
|
10
|
+
/** Another card on the surface is being acted on, so this one is out of reach. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
select: () => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
onSelect?: (() => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
export default _default;
|