@revenexx/studio-integrations 1.2.0 → 1.4.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 +581 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/CanvasShortcutsDialog.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/CanvasShortcutsDialog.vue +53 -0
- package/dist/runtime/components/Integrations/CanvasShortcutsDialog.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/EditorTip.vue +2 -15
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -2
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +15 -1
- package/dist/runtime/components/Integrations/NodeConfigField.vue +158 -49
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +15 -1
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/NodeInspector.vue +136 -81
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/NodeVersionMoveDialog.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/NodeVersionMoveDialog.vue +133 -0
- package/dist/runtime/components/Integrations/NodeVersionMoveDialog.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +80 -33
- package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.vue +111 -0
- package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +0 -4
- package/dist/runtime/components/Integrations/TemplateCard.vue +3 -6
- package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +0 -4
- package/dist/runtime/components/Integrations/TemplateInstallStep.d.vue.ts +25 -0
- package/dist/runtime/components/Integrations/TemplateInstallStep.vue +198 -0
- package/dist/runtime/components/Integrations/TemplateInstallStep.vue.d.ts +25 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +22 -40
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +94 -65
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +36 -2
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +200 -54
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +36 -2
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +6 -2
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +4 -106
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +6 -2
- package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.vue +216 -0
- package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +22 -11
- package/dist/runtime/composables/useCanvasShortcuts.d.ts +34 -0
- package/dist/runtime/composables/useCanvasShortcuts.js +11 -8
- package/dist/runtime/composables/useCredentialTypes.js +3 -9
- package/dist/runtime/composables/useIntegrationsCredentials.js +6 -14
- package/dist/runtime/composables/useIntegrationsNodes.js +2 -2
- package/dist/runtime/composables/useIntegrationsRuns.js +8 -13
- package/dist/runtime/composables/useIntegrationsSecrets.js +5 -5
- package/dist/runtime/composables/useIntegrationsTemplates.js +10 -8
- package/dist/runtime/composables/useIntegrationsTriggers.js +1 -4
- package/dist/runtime/composables/useIntegrationsWorkflows.js +11 -7
- package/dist/runtime/data/canvasShortcuts.d.ts +208 -0
- package/dist/runtime/data/canvasShortcuts.js +68 -0
- package/dist/runtime/data/integrationNodeCatalog.d.ts +25 -0
- package/dist/runtime/data/integrationNodeCatalog.js +8 -0
- package/dist/runtime/data/nodeVersionMove.d.ts +72 -0
- package/dist/runtime/data/nodeVersionMove.js +65 -0
- package/dist/runtime/i18n/messages/de.json +83 -20
- package/dist/runtime/i18n/messages/en.json +83 -20
- package/dist/runtime/pages/integrations/templates/[slug].vue +22 -132
- package/dist/runtime/pages/integrations/workflows/[id].vue +158 -53
- package/dist/runtime/pages/integrations/workflows/index.vue +23 -4
- package/dist/runtime/types/integrations.d.ts +44 -1
- package/dist/runtime/utils/apiEnvelope.d.ts +27 -0
- package/dist/runtime/utils/apiEnvelope.js +20 -0
- package/dist/runtime/utils/buildSettle.d.ts +68 -0
- package/dist/runtime/utils/buildSettle.js +29 -0
- package/dist/runtime/utils/configDefaults.d.ts +18 -0
- package/dist/runtime/utils/configDefaults.js +7 -0
- package/dist/runtime/utils/errorPathAdvice.d.ts +60 -0
- package/dist/runtime/utils/errorPathAdvice.js +19 -0
- package/dist/runtime/utils/notify.d.ts +2 -2
- package/dist/runtime/utils/runEnvelope.d.ts +12 -0
- package/dist/runtime/utils/runEnvelope.js +2 -1
- package/dist/runtime/utils/selectOptions.d.ts +48 -0
- package/dist/runtime/utils/selectOptions.js +19 -0
- package/dist/runtime/utils/settingCondition.d.ts +48 -0
- package/dist/runtime/utils/settingCondition.js +84 -0
- package/dist/runtime/utils/shortcutLabel.d.ts +40 -0
- package/dist/runtime/utils/shortcutLabel.js +48 -0
- package/dist/runtime/utils/stateNamespaceName.d.ts +23 -0
- package/dist/runtime/utils/stateNamespaceName.js +8 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { resolveLocalized } from "../utils/localize.js";
|
|
|
3
3
|
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
4
4
|
import { useIntegrationsT } from "./useIntegrationsT.js";
|
|
5
5
|
import { errorReason } from "../utils/apiError.js";
|
|
6
|
+
import { unwrapList } from "../utils/apiEnvelope.js";
|
|
6
7
|
import { useLoadGate } from "./useLoadGate.js";
|
|
7
8
|
const RETRY_COOLDOWN_MS = 3e4;
|
|
8
9
|
export function useIntegrationsNodes() {
|
|
@@ -18,8 +19,7 @@ export function useIntegrationsNodes() {
|
|
|
18
19
|
if (!await gate.claim(force)) return;
|
|
19
20
|
error.value = null;
|
|
20
21
|
try {
|
|
21
|
-
const
|
|
22
|
-
const list = Array.isArray(res) ? res : res?.data ?? [];
|
|
22
|
+
const list = unwrapList(await api.get("/nodes"));
|
|
23
23
|
const manifests = list.filter((n) => n.manifest).map((n) => ({
|
|
24
24
|
...n.manifest,
|
|
25
25
|
slug: n.manifest.slug ?? n.slug,
|
|
@@ -2,6 +2,8 @@ import { computed } from "vue";
|
|
|
2
2
|
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
3
|
import { useIntegrationsT } from "./useIntegrationsT.js";
|
|
4
4
|
import { errorReason } from "../utils/apiError.js";
|
|
5
|
+
import { unwrapRecord } from "../utils/apiEnvelope.js";
|
|
6
|
+
import { RUN_OWN_FIELDS } from "../utils/runEnvelope.js";
|
|
5
7
|
import { useLoadGate } from "./useLoadGate.js";
|
|
6
8
|
import {
|
|
7
9
|
DEFAULT_RUN_DIRECTION,
|
|
@@ -93,9 +95,7 @@ export function useIntegrationsRuns() {
|
|
|
93
95
|
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);
|
|
94
96
|
await Promise.allSettled(active.map(async (r) => {
|
|
95
97
|
try {
|
|
96
|
-
const
|
|
97
|
-
const apiRun = raw?.data ?? raw;
|
|
98
|
-
const fresh = mapRun(apiRun, r.workflowName);
|
|
98
|
+
const fresh = await fetchRun(r.workflowId, r.id, r.workflowName);
|
|
99
99
|
runs.value = runs.value.map((x) => x.id === fresh.id ? fresh : x);
|
|
100
100
|
} catch {
|
|
101
101
|
}
|
|
@@ -124,9 +124,7 @@ export function useIntegrationsRuns() {
|
|
|
124
124
|
const active = list2.filter((r) => r.status === "running" || r.status === "queued").sort((a, b) => Number(b.id) - Number(a.id)).slice(0, SYNC_LIMIT);
|
|
125
125
|
await Promise.allSettled(active.map(async (r) => {
|
|
126
126
|
try {
|
|
127
|
-
const
|
|
128
|
-
const apiRun = raw?.data ?? raw;
|
|
129
|
-
const fresh = mapRun(apiRun, workflowName);
|
|
127
|
+
const fresh = await fetchRun(workflowId, r.id, workflowName);
|
|
130
128
|
list2 = list2.map((x) => x.id === fresh.id ? fresh : x);
|
|
131
129
|
} catch {
|
|
132
130
|
}
|
|
@@ -185,13 +183,11 @@ export function useIntegrationsRuns() {
|
|
|
185
183
|
async function trigger(workflowId, payload = {}, mode) {
|
|
186
184
|
const body = { trigger_payload: payload };
|
|
187
185
|
if (mode) body.mode = mode;
|
|
188
|
-
|
|
189
|
-
return res?.data ?? res;
|
|
186
|
+
return unwrapRecord(await api.post(`/workflows/${workflowId}/runs`, body));
|
|
190
187
|
}
|
|
191
188
|
async function runAction(workflowId, runId, action, reason) {
|
|
192
189
|
const body = reason ? { reason } : {};
|
|
193
|
-
|
|
194
|
-
return res?.data ?? res;
|
|
190
|
+
return unwrapRecord(await api.post(`/workflows/${workflowId}/runs/${runId}/${action}`, body));
|
|
195
191
|
}
|
|
196
192
|
const cancelRun = (workflowId, runId, reason) => runAction(workflowId, runId, "cancel", reason);
|
|
197
193
|
const terminateRun = (workflowId, runId, reason) => runAction(workflowId, runId, "terminate", reason);
|
|
@@ -199,13 +195,12 @@ export function useIntegrationsRuns() {
|
|
|
199
195
|
const resumeRun = (workflowId, runId, reason) => runAction(workflowId, runId, "resume", reason);
|
|
200
196
|
const getRun = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}`);
|
|
201
197
|
async function fetchRun(workflowId, runId, workflowName) {
|
|
202
|
-
const
|
|
203
|
-
const apiRun = raw?.data ?? raw;
|
|
198
|
+
const apiRun = unwrapRecord(await getRun(workflowId, runId), RUN_OWN_FIELDS);
|
|
204
199
|
return mapRun(apiRun, workflowName);
|
|
205
200
|
}
|
|
206
201
|
const getDetails = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}/details`);
|
|
207
202
|
const getSteps = (workflowId, runId, includePayloads = false) => api.get(
|
|
208
|
-
`/workflows/${workflowId}/runs/${runId}/steps
|
|
203
|
+
`/workflows/${workflowId}/runs/${runId}/steps?include_payloads=${includePayloads ? "1" : "0"}`
|
|
209
204
|
);
|
|
210
205
|
const getHistory = (workflowId, runId, params = {}) => {
|
|
211
206
|
const qs = new URLSearchParams();
|
|
@@ -2,6 +2,7 @@ import { computed } from "vue";
|
|
|
2
2
|
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
3
|
import { useIntegrationsT } from "./useIntegrationsT.js";
|
|
4
4
|
import { errorReason } from "../utils/apiError.js";
|
|
5
|
+
import { unwrapList } from "../utils/apiEnvelope.js";
|
|
5
6
|
import { useLoadGate } from "./useLoadGate.js";
|
|
6
7
|
const KEY_PATTERN = /^[A-Z0-9_]+$/;
|
|
7
8
|
export function useIntegrationsSecrets() {
|
|
@@ -17,11 +18,10 @@ export function useIntegrationsSecrets() {
|
|
|
17
18
|
}
|
|
18
19
|
function normalize(res) {
|
|
19
20
|
const obj = res;
|
|
20
|
-
if (Array.isArray(obj?.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return [];
|
|
21
|
+
if (obj?.data === void 0 && Array.isArray(obj?.keys)) {
|
|
22
|
+
return obj.keys.map((key) => ({ key, created_at: "", updated_at: "" }));
|
|
23
|
+
}
|
|
24
|
+
return unwrapList(res).map(toRecord);
|
|
25
25
|
}
|
|
26
26
|
function toRecord(r) {
|
|
27
27
|
return { key: r.key ?? "", created_at: r.created_at ?? "", updated_at: r.updated_at ?? "" };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computed } from "vue";
|
|
2
2
|
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
3
|
import { errorReason } from "../utils/apiError.js";
|
|
4
|
+
import { unwrapList, unwrapRecord } from "../utils/apiEnvelope.js";
|
|
4
5
|
import { resolveLocalized } from "../utils/localize.js";
|
|
5
6
|
import { useLoadGate } from "./useLoadGate.js";
|
|
6
7
|
const BLOB_VERSION = "v0-draft";
|
|
@@ -70,6 +71,11 @@ function applyDetail(base, api) {
|
|
|
70
71
|
edges: def.edges ?? [],
|
|
71
72
|
triggerPositions: def.triggerPositions ?? {},
|
|
72
73
|
variables: def.variables,
|
|
74
|
+
// Enumerated key by key like every block above it, so one missing here is
|
|
75
|
+
// one lost in this direction. Harmless today — a template becomes a workflow
|
|
76
|
+
// server-side, so this blob only feeds the preview — but the preview is
|
|
77
|
+
// where an author would look to see what a template remembers.
|
|
78
|
+
state: def.state,
|
|
73
79
|
triggers
|
|
74
80
|
};
|
|
75
81
|
const md = resolveLocalized(api.description ?? "") || base.descriptionMd;
|
|
@@ -97,14 +103,11 @@ export function useIntegrationsTemplates() {
|
|
|
97
103
|
const error = useState("integrations.templates.error", () => null);
|
|
98
104
|
const gate = useLoadGate("integrations.templates", { loading, loaded });
|
|
99
105
|
const api = useIntegrationsApi();
|
|
100
|
-
function unwrap(res) {
|
|
101
|
-
return res?.data ?? res;
|
|
102
|
-
}
|
|
103
106
|
async function load(force = false) {
|
|
104
107
|
if (!await gate.claim(force)) return templates.value;
|
|
105
108
|
error.value = null;
|
|
106
109
|
try {
|
|
107
|
-
const list2 =
|
|
110
|
+
const list2 = unwrapList(await api.get("/templates"));
|
|
108
111
|
rawList.value = list2;
|
|
109
112
|
rawDetails.value = {};
|
|
110
113
|
loaded.value = true;
|
|
@@ -128,7 +131,7 @@ export function useIntegrationsTemplates() {
|
|
|
128
131
|
const base = get(slug);
|
|
129
132
|
if (!base?.fromApi) return base;
|
|
130
133
|
try {
|
|
131
|
-
const detail =
|
|
134
|
+
const detail = unwrapRecord(await api.get(`/templates/${slug}`));
|
|
132
135
|
rawDetails.value = { ...rawDetails.value, [slug]: detail };
|
|
133
136
|
return get(slug);
|
|
134
137
|
} catch {
|
|
@@ -147,8 +150,7 @@ export function useIntegrationsTemplates() {
|
|
|
147
150
|
}
|
|
148
151
|
async function requirements(slug) {
|
|
149
152
|
try {
|
|
150
|
-
const raw =
|
|
151
|
-
if (!raw) return null;
|
|
153
|
+
const raw = unwrapRecord(await api.get(`/templates/${slug}/requirements`));
|
|
152
154
|
return {
|
|
153
155
|
...raw,
|
|
154
156
|
credentialTypes: (raw.credentialTypes ?? []).map((c) => ({
|
|
@@ -167,7 +169,7 @@ export function useIntegrationsTemplates() {
|
|
|
167
169
|
Object.entries(opts.credentialBindings ?? {}).filter(([, v]) => v)
|
|
168
170
|
);
|
|
169
171
|
if (Object.keys(bindings).length) body.credential_bindings = bindings;
|
|
170
|
-
const created =
|
|
172
|
+
const created = unwrapRecord(await api.post(`/templates/${slug}/instantiate`, body));
|
|
171
173
|
return Number(created.id);
|
|
172
174
|
}
|
|
173
175
|
async function install(slug) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
2
2
|
import { useIntegrationsT } from "./useIntegrationsT.js";
|
|
3
3
|
import { errorReason } from "../utils/apiError.js";
|
|
4
|
+
import { unwrapList } from "../utils/apiEnvelope.js";
|
|
4
5
|
import { useLoadGate } from "./useLoadGate.js";
|
|
5
6
|
export function tenantTriggers(workflows, byWorkflow) {
|
|
6
7
|
return workflows.flatMap((wf) => (byWorkflow[wf.id] ?? []).flatMap((t) => {
|
|
@@ -24,10 +25,6 @@ export function useIntegrationsTriggers() {
|
|
|
24
25
|
const t = useIntegrationsT();
|
|
25
26
|
const { workflows, apiTriggers, load: loadWorkflows, loaded: workflowsLoaded, error: workflowsError } = useIntegrationsWorkflows();
|
|
26
27
|
const gate = useLoadGate("integrations.triggers", { loading, loaded });
|
|
27
|
-
function unwrapList(res) {
|
|
28
|
-
if (Array.isArray(res)) return res;
|
|
29
|
-
return res?.data ?? [];
|
|
30
|
-
}
|
|
31
28
|
async function list(workflowId) {
|
|
32
29
|
return unwrapList(await api.get(`/workflows/${workflowId}/triggers`));
|
|
33
30
|
}
|
|
@@ -3,6 +3,7 @@ import { useIntegrationsApi } from "./integrationsApi.js";
|
|
|
3
3
|
import { useIntegrationsT } from "./useIntegrationsT.js";
|
|
4
4
|
import { deepClone } from "../utils/deepClone.js";
|
|
5
5
|
import { errorReason } from "../utils/apiError.js";
|
|
6
|
+
import { unwrapList, unwrapRecord } from "../utils/apiEnvelope.js";
|
|
6
7
|
import { useLoadGate } from "./useLoadGate.js";
|
|
7
8
|
const BLOB_VERSION = "v0-draft";
|
|
8
9
|
function toCockpit(api) {
|
|
@@ -20,6 +21,7 @@ function toCockpit(api) {
|
|
|
20
21
|
nodes: apiBlob.nodes ?? [],
|
|
21
22
|
edges: apiBlob.edges ?? [],
|
|
22
23
|
variables: apiBlob.variables ?? void 0,
|
|
24
|
+
state: apiBlob.state ?? void 0,
|
|
23
25
|
triggerPositions: apiBlob.triggerPositions ?? {},
|
|
24
26
|
stickies: apiBlob.stickies ?? [],
|
|
25
27
|
groups: apiBlob.groups ?? [],
|
|
@@ -64,6 +66,11 @@ function toPayload(meta) {
|
|
|
64
66
|
...b.groups?.length ? { groups: b.groups } : {},
|
|
65
67
|
// variables were silently dropped before — the schema always allowed them.
|
|
66
68
|
...b.variables && Object.keys(b.variables).length ? { variables: b.variables } : {},
|
|
69
|
+
// Same shape, and the same trap: both mappers enumerate blob keys, so a
|
|
70
|
+
// block missing from either one is lost in that direction. `state` is the
|
|
71
|
+
// declaration that makes the run grant reachable at all (PO-374) — losing
|
|
72
|
+
// it on save would take the workflow's memory away without a message.
|
|
73
|
+
...b.state?.length ? { state: b.state } : {},
|
|
67
74
|
...b.tags?.length ? { tags: b.tags } : {},
|
|
68
75
|
...b.icon ? { icon: b.icon } : {}
|
|
69
76
|
},
|
|
@@ -87,9 +94,6 @@ export function useIntegrationsWorkflows() {
|
|
|
87
94
|
const gate = useLoadGate("integrations.workflows", { loading, loaded });
|
|
88
95
|
const api = useIntegrationsApi();
|
|
89
96
|
const t = useIntegrationsT();
|
|
90
|
-
function unwrap(res) {
|
|
91
|
-
return res?.data ?? res;
|
|
92
|
-
}
|
|
93
97
|
function remember(api2, into) {
|
|
94
98
|
if (api2.triggers !== void 0) into[Number(api2.id)] = api2.triggers;
|
|
95
99
|
}
|
|
@@ -116,7 +120,7 @@ export function useIntegrationsWorkflows() {
|
|
|
116
120
|
if (!await gate.claim(force)) return workflows.value;
|
|
117
121
|
error.value = null;
|
|
118
122
|
try {
|
|
119
|
-
const list2 =
|
|
123
|
+
const list2 = unwrapList(await api.get("/workflows"));
|
|
120
124
|
workflows.value = ingestList(list2);
|
|
121
125
|
loaded.value = true;
|
|
122
126
|
} catch (e) {
|
|
@@ -133,7 +137,7 @@ export function useIntegrationsWorkflows() {
|
|
|
133
137
|
}
|
|
134
138
|
async function get(id) {
|
|
135
139
|
try {
|
|
136
|
-
const wf = ingest(
|
|
140
|
+
const wf = ingest(unwrapRecord(await api.get(`/workflows/${id}`)));
|
|
137
141
|
upsertCache(wf);
|
|
138
142
|
return wf;
|
|
139
143
|
} catch {
|
|
@@ -141,7 +145,7 @@ export function useIntegrationsWorkflows() {
|
|
|
141
145
|
}
|
|
142
146
|
}
|
|
143
147
|
async function create(input) {
|
|
144
|
-
const wf = ingest(
|
|
148
|
+
const wf = ingest(unwrapRecord(await api.post("/workflows", toPayload({
|
|
145
149
|
name: input.name,
|
|
146
150
|
description: input.description,
|
|
147
151
|
blob: input.blob,
|
|
@@ -192,7 +196,7 @@ export function useIntegrationsWorkflows() {
|
|
|
192
196
|
throw lastError instanceof Error ? lastError : new Error(t("integration.workflows.error.duplicateFailed"));
|
|
193
197
|
}
|
|
194
198
|
async function update(id, patch) {
|
|
195
|
-
const wf = ingest(
|
|
199
|
+
const wf = ingest(unwrapRecord(await api.put(`/workflows/${id}`, toPayload({
|
|
196
200
|
name: patch.name,
|
|
197
201
|
description: patch.description,
|
|
198
202
|
blob: patch.blob,
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every key the workflow canvas answers, in one list.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS FILE EXISTS. The bindings used to live only in the editor's keymap,
|
|
5
|
+
* which is `key → function` and carries no words at all — so each surface that
|
|
6
|
+
* shows a key wrote the token out again by hand: fifteen in the canvas context
|
|
7
|
+
* menu, twelve more in the control pane's tooltips, for twenty-two bindings.
|
|
8
|
+
* They had already drifted. The menu offered renaming as `F2` while the keymap
|
|
9
|
+
* answered `F2` *and* `Space`, and it showed `⌫` for a key the keymap does not
|
|
10
|
+
* hold at all — deleting is the canvas library's own, configured somewhere else
|
|
11
|
+
* entirely. Two of the twenty-two were shown nowhere.
|
|
12
|
+
*
|
|
13
|
+
* So the tokens live here, keyed by an id, and the three surfaces are readers:
|
|
14
|
+
* the keymap binds handlers by id, the menu and the tooltips ask for one id's
|
|
15
|
+
* keys, and the overview shows the list. Adding a key is one entry, and it
|
|
16
|
+
* reaches all three.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IS NOT HERE. The handlers, because they belong to the surface that owns
|
|
19
|
+
* the canvas and close over its selection. And the menu's own labels: an entry
|
|
20
|
+
* there says "Copy {n} nodes" against this list's "Copy", because a menu speaks
|
|
21
|
+
* about what is under the pointer while an overview speaks in general. Only the
|
|
22
|
+
* TOKENS are shared, which is what actually drifted.
|
|
23
|
+
*/
|
|
24
|
+
/** The heading a key is listed under. Ordered as the overview shows them. */
|
|
25
|
+
export declare const SHORTCUT_GROUPS: readonly ["nodes", "selection", "canvas", "view", "workflow"];
|
|
26
|
+
export type ShortcutGroup = typeof SHORTCUT_GROUPS[number];
|
|
27
|
+
export interface CanvasShortcut {
|
|
28
|
+
/** Stable id: what a handler, a menu item and a tooltip refer to. */
|
|
29
|
+
id: string;
|
|
30
|
+
/**
|
|
31
|
+
* The binding in the matcher's token form; `|` separates two gestures worth
|
|
32
|
+
* knowing about — renaming answers `F2` and `Space`, and neither can be
|
|
33
|
+
* guessed from the other.
|
|
34
|
+
*
|
|
35
|
+
* Everything bound is listed, and nothing is bound that is not: a key that
|
|
36
|
+
* answers without appearing here would be exactly the secret this file exists
|
|
37
|
+
* to abolish. Zooming in was briefly `+` with `=` bound quietly beside it, for
|
|
38
|
+
* the US keyboards that reach the same character that way — and zooming out has
|
|
39
|
+
* no such twin, so there was no symmetry to keep and one key each is the answer.
|
|
40
|
+
*
|
|
41
|
+
* Punctuation is written as the character, never as `shift+<key>`: a layout
|
|
42
|
+
* that needs Shift to type it says so in the event and the matcher drops that
|
|
43
|
+
* Shift, so `shift+/` would bind a US keyboard only.
|
|
44
|
+
*/
|
|
45
|
+
keys: string;
|
|
46
|
+
/** The catalogue key for the words the overview shows. */
|
|
47
|
+
labelKey: string;
|
|
48
|
+
group: ShortcutGroup;
|
|
49
|
+
/**
|
|
50
|
+
* Answered by the canvas library rather than by the keymap, so there is no
|
|
51
|
+
* handler to bind — it is listed because an overview that omits the
|
|
52
|
+
* most-used key of the surface is worse than none.
|
|
53
|
+
*/
|
|
54
|
+
library?: true;
|
|
55
|
+
}
|
|
56
|
+
export declare const CANVAS_SHORTCUTS: readonly [{
|
|
57
|
+
readonly id: "open";
|
|
58
|
+
readonly keys: "enter";
|
|
59
|
+
readonly labelKey: "integration.canvas.contextMenu.item.open";
|
|
60
|
+
readonly group: "nodes";
|
|
61
|
+
}, {
|
|
62
|
+
readonly id: "rename";
|
|
63
|
+
readonly keys: "f2|space";
|
|
64
|
+
readonly labelKey: "integration.canvas.contextMenu.item.rename";
|
|
65
|
+
readonly group: "nodes";
|
|
66
|
+
}, {
|
|
67
|
+
readonly id: "addNode";
|
|
68
|
+
readonly keys: "tab";
|
|
69
|
+
readonly labelKey: "integration.canvas.contextMenu.item.addNode";
|
|
70
|
+
readonly group: "nodes";
|
|
71
|
+
}, {
|
|
72
|
+
readonly id: "copy";
|
|
73
|
+
readonly keys: "mod+c";
|
|
74
|
+
readonly labelKey: "integration.canvas.contextMenu.item.copy";
|
|
75
|
+
readonly group: "nodes";
|
|
76
|
+
}, {
|
|
77
|
+
readonly id: "paste";
|
|
78
|
+
readonly keys: "mod+v";
|
|
79
|
+
readonly labelKey: "integration.canvas.contextMenu.item.paste";
|
|
80
|
+
readonly group: "nodes";
|
|
81
|
+
}, {
|
|
82
|
+
readonly id: "duplicate";
|
|
83
|
+
readonly keys: "mod+d";
|
|
84
|
+
readonly labelKey: "integration.canvas.contextMenu.item.duplicate";
|
|
85
|
+
readonly group: "nodes";
|
|
86
|
+
}, {
|
|
87
|
+
readonly id: "delete";
|
|
88
|
+
readonly keys: "backspace|delete";
|
|
89
|
+
readonly labelKey: "integration.canvas.shortcuts.item.delete";
|
|
90
|
+
readonly group: "nodes";
|
|
91
|
+
readonly library: true;
|
|
92
|
+
}, {
|
|
93
|
+
readonly id: "selectAll";
|
|
94
|
+
readonly keys: "mod+a";
|
|
95
|
+
readonly labelKey: "integration.canvas.contextMenu.item.selectAll";
|
|
96
|
+
readonly group: "selection";
|
|
97
|
+
}, {
|
|
98
|
+
readonly id: "clearSelection";
|
|
99
|
+
readonly keys: "escape";
|
|
100
|
+
readonly labelKey: "integration.canvas.contextMenu.item.clearSelection";
|
|
101
|
+
readonly group: "selection";
|
|
102
|
+
}, {
|
|
103
|
+
readonly id: "group";
|
|
104
|
+
readonly keys: "mod+g";
|
|
105
|
+
readonly labelKey: "integration.canvas.shortcuts.item.group";
|
|
106
|
+
readonly group: "selection";
|
|
107
|
+
}, {
|
|
108
|
+
readonly id: "pointerSelect";
|
|
109
|
+
readonly keys: "v";
|
|
110
|
+
readonly labelKey: "integration.editor.tool.select";
|
|
111
|
+
readonly group: "canvas";
|
|
112
|
+
}, {
|
|
113
|
+
readonly id: "pointerHand";
|
|
114
|
+
readonly keys: "h";
|
|
115
|
+
readonly labelKey: "integration.editor.tool.hand";
|
|
116
|
+
readonly group: "canvas";
|
|
117
|
+
}, {
|
|
118
|
+
readonly id: "addSticky";
|
|
119
|
+
readonly keys: "shift+s";
|
|
120
|
+
readonly labelKey: "integration.editor.tool.addSticky.tooltip";
|
|
121
|
+
readonly group: "canvas";
|
|
122
|
+
}, {
|
|
123
|
+
readonly id: "tidyUp";
|
|
124
|
+
readonly keys: "shift+alt+t";
|
|
125
|
+
readonly labelKey: "integration.editor.tool.tidyUp";
|
|
126
|
+
readonly group: "canvas";
|
|
127
|
+
}, {
|
|
128
|
+
readonly id: "overview";
|
|
129
|
+
readonly keys: "?";
|
|
130
|
+
readonly labelKey: "integration.canvas.shortcuts.item.overview";
|
|
131
|
+
readonly group: "canvas";
|
|
132
|
+
}, {
|
|
133
|
+
readonly id: "zoomIn";
|
|
134
|
+
readonly keys: "+";
|
|
135
|
+
readonly labelKey: "integration.editor.tool.zoomIn";
|
|
136
|
+
readonly group: "view";
|
|
137
|
+
}, {
|
|
138
|
+
readonly id: "zoomOut";
|
|
139
|
+
readonly keys: "-";
|
|
140
|
+
readonly labelKey: "integration.editor.tool.zoomOut";
|
|
141
|
+
readonly group: "view";
|
|
142
|
+
}, {
|
|
143
|
+
readonly id: "zoomReset";
|
|
144
|
+
readonly keys: "0";
|
|
145
|
+
readonly labelKey: "integration.canvas.shortcuts.item.zoomReset";
|
|
146
|
+
readonly group: "view";
|
|
147
|
+
}, {
|
|
148
|
+
readonly id: "fitView";
|
|
149
|
+
readonly keys: "1";
|
|
150
|
+
readonly labelKey: "integration.canvas.contextMenu.item.fitView";
|
|
151
|
+
readonly group: "view";
|
|
152
|
+
}, {
|
|
153
|
+
readonly id: "run";
|
|
154
|
+
readonly keys: "mod+enter";
|
|
155
|
+
readonly labelKey: "integration.canvas.contextMenu.item.runWorkflow";
|
|
156
|
+
readonly group: "workflow";
|
|
157
|
+
}, {
|
|
158
|
+
readonly id: "toggleActive";
|
|
159
|
+
readonly keys: "shift+a";
|
|
160
|
+
readonly labelKey: "integration.canvas.shortcuts.item.toggleActive";
|
|
161
|
+
readonly group: "workflow";
|
|
162
|
+
}, {
|
|
163
|
+
readonly id: "variables";
|
|
164
|
+
readonly keys: "shift+v";
|
|
165
|
+
readonly labelKey: "integration.editor.tool.variables.tooltip";
|
|
166
|
+
readonly group: "workflow";
|
|
167
|
+
}, {
|
|
168
|
+
readonly id: "state";
|
|
169
|
+
readonly keys: "shift+t";
|
|
170
|
+
readonly labelKey: "integration.canvas.shortcuts.item.state";
|
|
171
|
+
readonly group: "workflow";
|
|
172
|
+
}, {
|
|
173
|
+
readonly id: "settings";
|
|
174
|
+
readonly keys: "shift+alt+s";
|
|
175
|
+
readonly labelKey: "integration.editor.tool.settings.tooltip";
|
|
176
|
+
readonly group: "workflow";
|
|
177
|
+
}];
|
|
178
|
+
/**
|
|
179
|
+
* The ids, as a type.
|
|
180
|
+
*
|
|
181
|
+
* `as const` above is what makes this a union of the literal ids rather than
|
|
182
|
+
* `string` — so a mistyped id in a template (`primaryShortcut('zoomln')`) fails
|
|
183
|
+
* the type check instead of throwing while the editor renders, which is where
|
|
184
|
+
* the lookup below would otherwise report it.
|
|
185
|
+
*/
|
|
186
|
+
export type ShortcutId = typeof CANVAS_SHORTCUTS[number]['id'];
|
|
187
|
+
/**
|
|
188
|
+
* The catalogue key per group heading, written out rather than composed.
|
|
189
|
+
*
|
|
190
|
+
* A key built from the group name (`…group.${g}`) would work and would be
|
|
191
|
+
* invisible to every search for it — including the catalogue's own record of
|
|
192
|
+
* where a string is used, which is maintained by hand.
|
|
193
|
+
*/
|
|
194
|
+
export declare const SHORTCUT_GROUP_LABELS: Record<ShortcutGroup, string>;
|
|
195
|
+
/** One id's binding, every way of pressing it included. */
|
|
196
|
+
export declare function shortcutKeys(id: ShortcutId): string;
|
|
197
|
+
/** Its gestures one by one, which is a cap each in the overview. */
|
|
198
|
+
export declare function shortcutCaps(id: ShortcutId): string[];
|
|
199
|
+
/**
|
|
200
|
+
* The one way of pressing it that a hint shows.
|
|
201
|
+
*
|
|
202
|
+
* A menu row and a tooltip have room for one gesture, and showing `F2|Space`
|
|
203
|
+
* there would read as a key nobody has. The overview is where both appear —
|
|
204
|
+
* which is also how `Space` stopped being a key the product answers and names
|
|
205
|
+
* nowhere.
|
|
206
|
+
*/
|
|
207
|
+
export declare function primaryShortcut(id: ShortcutId): string;
|
|
208
|
+
export declare function libraryKeyCodes(id: ShortcutId): string[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const SHORTCUT_GROUPS = ["nodes", "selection", "canvas", "view", "workflow"];
|
|
2
|
+
export const CANVAS_SHORTCUTS = [
|
|
3
|
+
// ── A node, and what is done to it ────────────────────────────────────────
|
|
4
|
+
{ id: "open", keys: "enter", labelKey: "integration.canvas.contextMenu.item.open", group: "nodes" },
|
|
5
|
+
{ id: "rename", keys: "f2|space", labelKey: "integration.canvas.contextMenu.item.rename", group: "nodes" },
|
|
6
|
+
{ id: "addNode", keys: "tab", labelKey: "integration.canvas.contextMenu.item.addNode", group: "nodes" },
|
|
7
|
+
{ id: "copy", keys: "mod+c", labelKey: "integration.canvas.contextMenu.item.copy", group: "nodes" },
|
|
8
|
+
{ id: "paste", keys: "mod+v", labelKey: "integration.canvas.contextMenu.item.paste", group: "nodes" },
|
|
9
|
+
{ id: "duplicate", keys: "mod+d", labelKey: "integration.canvas.contextMenu.item.duplicate", group: "nodes" },
|
|
10
|
+
{ id: "delete", keys: "backspace|delete", labelKey: "integration.canvas.shortcuts.item.delete", group: "nodes", library: true },
|
|
11
|
+
// ── The selection ─────────────────────────────────────────────────────────
|
|
12
|
+
{ id: "selectAll", keys: "mod+a", labelKey: "integration.canvas.contextMenu.item.selectAll", group: "selection" },
|
|
13
|
+
{ id: "clearSelection", keys: "escape", labelKey: "integration.canvas.contextMenu.item.clearSelection", group: "selection" },
|
|
14
|
+
{ id: "group", keys: "mod+g", labelKey: "integration.canvas.shortcuts.item.group", group: "selection" },
|
|
15
|
+
// ── Working on the canvas itself ──────────────────────────────────────────
|
|
16
|
+
{ id: "pointerSelect", keys: "v", labelKey: "integration.editor.tool.select", group: "canvas" },
|
|
17
|
+
{ id: "pointerHand", keys: "h", labelKey: "integration.editor.tool.hand", group: "canvas" },
|
|
18
|
+
{ id: "addSticky", keys: "shift+s", labelKey: "integration.editor.tool.addSticky.tooltip", group: "canvas" },
|
|
19
|
+
{ id: "tidyUp", keys: "shift+alt+t", labelKey: "integration.editor.tool.tidyUp", group: "canvas" },
|
|
20
|
+
// Last of the canvas's own keys, and not one of the workflow's: the overview is
|
|
21
|
+
// about the surface an operator is standing on, not about the thing on it. It
|
|
22
|
+
// lists itself so the key is learnt for next time, the way it is the only key a
|
|
23
|
+
// reader of this list has already found.
|
|
24
|
+
{ id: "overview", keys: "?", labelKey: "integration.canvas.shortcuts.item.overview", group: "canvas" },
|
|
25
|
+
// ── What is on screen ─────────────────────────────────────────────────────
|
|
26
|
+
{ id: "zoomIn", keys: "+", labelKey: "integration.editor.tool.zoomIn", group: "view" },
|
|
27
|
+
{ id: "zoomOut", keys: "-", labelKey: "integration.editor.tool.zoomOut", group: "view" },
|
|
28
|
+
{ id: "zoomReset", keys: "0", labelKey: "integration.canvas.shortcuts.item.zoomReset", group: "view" },
|
|
29
|
+
{ id: "fitView", keys: "1", labelKey: "integration.canvas.contextMenu.item.fitView", group: "view" },
|
|
30
|
+
// ── The workflow the canvas is showing ────────────────────────────────────
|
|
31
|
+
{ id: "run", keys: "mod+enter", labelKey: "integration.canvas.contextMenu.item.runWorkflow", group: "workflow" },
|
|
32
|
+
{ id: "toggleActive", keys: "shift+a", labelKey: "integration.canvas.shortcuts.item.toggleActive", group: "workflow" },
|
|
33
|
+
{ id: "variables", keys: "shift+v", labelKey: "integration.editor.tool.variables.tooltip", group: "workflow" },
|
|
34
|
+
{ id: "state", keys: "shift+t", labelKey: "integration.canvas.shortcuts.item.state", group: "workflow" },
|
|
35
|
+
{ id: "settings", keys: "shift+alt+s", labelKey: "integration.editor.tool.settings.tooltip", group: "workflow" }
|
|
36
|
+
];
|
|
37
|
+
export const SHORTCUT_GROUP_LABELS = {
|
|
38
|
+
nodes: "integration.canvas.shortcuts.group.nodes",
|
|
39
|
+
selection: "integration.canvas.shortcuts.group.selection",
|
|
40
|
+
canvas: "integration.canvas.shortcuts.group.canvas",
|
|
41
|
+
view: "integration.canvas.shortcuts.group.view",
|
|
42
|
+
workflow: "integration.canvas.shortcuts.group.workflow"
|
|
43
|
+
};
|
|
44
|
+
const BY_ID = new Map(CANVAS_SHORTCUTS.map((s) => [s.id, s]));
|
|
45
|
+
export function shortcutKeys(id) {
|
|
46
|
+
const found = BY_ID.get(id);
|
|
47
|
+
if (!found) throw new Error(`Unknown canvas shortcut: ${id}`);
|
|
48
|
+
return found.keys;
|
|
49
|
+
}
|
|
50
|
+
export function shortcutCaps(id) {
|
|
51
|
+
return shortcutKeys(id).split("|");
|
|
52
|
+
}
|
|
53
|
+
export function primaryShortcut(id) {
|
|
54
|
+
return shortcutCaps(id)[0];
|
|
55
|
+
}
|
|
56
|
+
const LIBRARY_KEY_CODES = {
|
|
57
|
+
backspace: "Backspace",
|
|
58
|
+
delete: "Delete"
|
|
59
|
+
};
|
|
60
|
+
export function libraryKeyCodes(id) {
|
|
61
|
+
const entry = BY_ID.get(id);
|
|
62
|
+
if (!entry?.library) throw new Error(`Canvas shortcut ${id} is not the canvas library's own key`);
|
|
63
|
+
return shortcutKeys(id).split("|").map((token) => {
|
|
64
|
+
const code = LIBRARY_KEY_CODES[token];
|
|
65
|
+
if (!code) throw new Error(`No canvas-library key code known for ${token}`);
|
|
66
|
+
return code;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -81,6 +81,31 @@ export declare function isVersionExecutable(slug: string, version: string): bool
|
|
|
81
81
|
* only what is wrong.
|
|
82
82
|
*/
|
|
83
83
|
export declare function newestExecutableVersion(slug: string): string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Every version of this node the platform can execute, newest first — the
|
|
86
|
+
* choice offered when a step is moved onto another version (PO-339).
|
|
87
|
+
*
|
|
88
|
+
* Order is the catalogue's own: the registry returns each slug's versions
|
|
89
|
+
* semver-descending, which is the assumption `newestExecutableVersion` above and
|
|
90
|
+
* the palette's own list already rest on. So "newer than the pinned one" is
|
|
91
|
+
* "earlier in this array", and nothing here has to parse a semver.
|
|
92
|
+
*
|
|
93
|
+
* A version whose code was never built is left out, for the reason the palette
|
|
94
|
+
* leaves it out: it is not a choice, it is a dead end with a version number
|
|
95
|
+
* (PO-338). The version a step is pinned to is therefore not guaranteed to be in
|
|
96
|
+
* here — which is exactly the state the move exists to get out of.
|
|
97
|
+
*/
|
|
98
|
+
export declare function executableVersionsOf(slug: string): NodeManifest[];
|
|
99
|
+
/**
|
|
100
|
+
* The versions of this node that are newer than the one pinned, and runnable.
|
|
101
|
+
*
|
|
102
|
+
* Empty when the step is already on the newest — which is the common case, and
|
|
103
|
+
* the reason the inspector can stay silent rather than telling most authors that
|
|
104
|
+
* their node is fine. A pinned version the catalogue does not carry counts as
|
|
105
|
+
* "nothing newer": there is no position to be newer *than*, and inventing one
|
|
106
|
+
* would put an upgrade notice on a node whose real problem is different.
|
|
107
|
+
*/
|
|
108
|
+
export declare function newerExecutableVersions(slug: string, version: string): NodeManifest[];
|
|
84
109
|
export declare function findManifest(slug: string, version?: string): NodeManifest | null;
|
|
85
110
|
export declare const TRIGGER_OUTPUT_PORT = "default";
|
|
86
111
|
export interface TriggerTemplate {
|
|
@@ -34,6 +34,14 @@ export function newestExecutableVersion(slug) {
|
|
|
34
34
|
const hit = INTEGRATION_NODE_CATALOG.find((m) => m.slug === slug && m.__hasBundle !== false);
|
|
35
35
|
return hit?.version ?? null;
|
|
36
36
|
}
|
|
37
|
+
export function executableVersionsOf(slug) {
|
|
38
|
+
return INTEGRATION_NODE_CATALOG.filter((m) => m.slug === slug && m.__hasBundle !== false);
|
|
39
|
+
}
|
|
40
|
+
export function newerExecutableVersions(slug, version) {
|
|
41
|
+
const versions = executableVersionsOf(slug);
|
|
42
|
+
const at = versions.findIndex((m) => m.version === version);
|
|
43
|
+
return at < 0 ? [] : versions.slice(0, at);
|
|
44
|
+
}
|
|
37
45
|
export function findManifest(slug, version) {
|
|
38
46
|
const matches = INTEGRATION_NODE_CATALOG.filter((m) => m.slug === slug);
|
|
39
47
|
if (!matches.length) return null;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ConfigField, NodeManifest } from '../types/integrations.js';
|
|
2
|
+
/**
|
|
3
|
+
* What moving a step onto another version of its node costs (PO-339).
|
|
4
|
+
*
|
|
5
|
+
* A version is chosen when a node is inserted and, until this, never again — so
|
|
6
|
+
* the only route onto another one was to delete the step and build it back,
|
|
7
|
+
* losing its settings, its name, its note and its edges. Moving instead is
|
|
8
|
+
* cheap in the blob (one field changes) and expensive in trust: the two
|
|
9
|
+
* versions may not declare the same settings, and an author who is not told
|
|
10
|
+
* beforehand finds out by a value having quietly gone.
|
|
11
|
+
*
|
|
12
|
+
* This module answers "what would the move cost" and nothing else. It builds no
|
|
13
|
+
* words: labels are localised strings on the manifest, so the caller resolves
|
|
14
|
+
* them and this stays testable without an i18n context.
|
|
15
|
+
*
|
|
16
|
+
* **A value that is not there is not a loss.** Only settings the node actually
|
|
17
|
+
* holds are weighed. A field the author never filled has nothing to carry, drop
|
|
18
|
+
* or check, and listing it would bury the two or three that matter.
|
|
19
|
+
*
|
|
20
|
+
* **The comparison is over the config's own top-level keys**, which is what
|
|
21
|
+
* `nextConfig` can act on: the blob stores settings as one flat object, and
|
|
22
|
+
* removing `attachments` is removing one key from it. A version that changed
|
|
23
|
+
* something *inside* an object or an array element is caught as far as it can
|
|
24
|
+
* be — such a field lands in `review` rather than being silently carried — but
|
|
25
|
+
* the stale value inside it is not pruned. `specs/node-configuration.md`
|
|
26
|
+
* records that limit rather than this pretending to close it.
|
|
27
|
+
*/
|
|
28
|
+
/** A setting the move has something to say about, with the field it was read from. */
|
|
29
|
+
export interface MoveEntry {
|
|
30
|
+
key: string;
|
|
31
|
+
/**
|
|
32
|
+
* The declaration to name it by. For a carried or reviewed setting that is the
|
|
33
|
+
* TARGET's field, because that is what the author will be looking at after the
|
|
34
|
+
* move; for a dropped one it is the SOURCE's, which is the only version that
|
|
35
|
+
* still has a word for it. `null` where neither declares it — a key left behind
|
|
36
|
+
* by an earlier move, which has no label anywhere and shows as its own name.
|
|
37
|
+
*/
|
|
38
|
+
field: ConfigField | null;
|
|
39
|
+
/** Why it needs looking at. Only set on `review`. */
|
|
40
|
+
reason?: 'changed' | 'nowRequired';
|
|
41
|
+
}
|
|
42
|
+
export interface VersionMovePlan {
|
|
43
|
+
/** Settings that travel untouched: the target declares the same key, the same way. */
|
|
44
|
+
carried: MoveEntry[];
|
|
45
|
+
/** Settings the target does not declare. Their values are gone from `nextConfig`. */
|
|
46
|
+
dropped: MoveEntry[];
|
|
47
|
+
/** Settings that arrive but need the author's eye — see `reason`. Values are kept. */
|
|
48
|
+
review: MoveEntry[];
|
|
49
|
+
/** The config to save with the new version: the current one minus what was dropped. */
|
|
50
|
+
nextConfig: Record<string, unknown>;
|
|
51
|
+
/** Nothing is dropped and nothing needs checking — the move is a version change and no more. */
|
|
52
|
+
lossless: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the version being left is still in the catalogue, so the two
|
|
55
|
+
* declarations could actually be held against each other. `false` for a
|
|
56
|
+
* withdrawn version: the move still works and nothing here is wrong, but
|
|
57
|
+
* `carried` then means "kept" rather than "checked and unchanged", and the
|
|
58
|
+
* dialog owes the author that sentence.
|
|
59
|
+
*/
|
|
60
|
+
comparable: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* What it would cost to move a step from `from` to `to`, given the settings it
|
|
64
|
+
* holds today.
|
|
65
|
+
*
|
|
66
|
+
* Either manifest may be `null` — a version the catalogue does not carry, which
|
|
67
|
+
* happens to a workflow pinned to something later withdrawn. A missing SOURCE
|
|
68
|
+
* only costs labels; a missing TARGET would drop every setting, so the caller
|
|
69
|
+
* must not offer a move onto a version it cannot find, and `planVersionMove`
|
|
70
|
+
* does not defend against being asked anyway.
|
|
71
|
+
*/
|
|
72
|
+
export declare function planVersionMove(from: NodeManifest | null, to: NodeManifest | null, config: Record<string, unknown> | null | undefined): VersionMovePlan;
|