@revenexx/studio-integrations 1.2.0 → 1.3.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 +155 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -2
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/NodeConfigField.vue +65 -1
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/NodeInspector.vue +103 -25
- 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/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/WorkflowEditor.vue +39 -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/composables/useIntegrationsTemplates.js +5 -0
- package/dist/runtime/composables/useIntegrationsWorkflows.js +6 -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 +53 -4
- package/dist/runtime/i18n/messages/en.json +53 -4
- package/dist/runtime/pages/integrations/workflows/[id].vue +30 -20
- package/dist/runtime/pages/integrations/workflows/index.vue +23 -4
- package/dist/runtime/types/integrations.d.ts +44 -1
- package/dist/runtime/utils/buildSettle.d.ts +68 -0
- package/dist/runtime/utils/buildSettle.js +29 -0
- package/dist/runtime/utils/settingCondition.d.ts +48 -0
- package/dist/runtime/utils/settingCondition.js +84 -0
- package/dist/runtime/utils/stateNamespaceName.d.ts +23 -0
- package/dist/runtime/utils/stateNamespaceName.js +8 -0
- package/package.json +1 -1
|
@@ -158,11 +158,28 @@ export interface WorkflowIcon {
|
|
|
158
158
|
/** Image URL, e.g. a node-package logo (kind=image). */
|
|
159
159
|
src?: string;
|
|
160
160
|
}
|
|
161
|
+
/** The four things a workflow can remember between runs (PO-374). */
|
|
162
|
+
export type WorkflowStateRole = 'mapping' | 'cursor' | 'dedupe' | 'digest';
|
|
163
|
+
/** `private` belongs to this workflow; `shared` is tenant-wide and outlives it. */
|
|
164
|
+
export type WorkflowStateVisibility = 'private' | 'shared';
|
|
165
|
+
/**
|
|
166
|
+
* One state namespace a workflow declares it may reach. The engine mints the
|
|
167
|
+
* run's grant from exactly this list, so an undeclared namespace is not
|
|
168
|
+
* undocumented — it is refused at run time.
|
|
169
|
+
*/
|
|
170
|
+
export interface WorkflowStateNamespace {
|
|
171
|
+
namespace: string;
|
|
172
|
+
role: WorkflowStateRole;
|
|
173
|
+
/** Absent means `private`, which is what the engine's schema defaults to. */
|
|
174
|
+
visibility?: WorkflowStateVisibility;
|
|
175
|
+
}
|
|
161
176
|
export interface WorkflowBlob {
|
|
162
177
|
nodeManifestVersion: string;
|
|
163
178
|
name: LocalizedString;
|
|
164
179
|
description?: LocalizedString;
|
|
165
180
|
variables?: Record<string, WorkflowVariable>;
|
|
181
|
+
/** State namespaces this workflow may reach at run time (BlobDef `state`). */
|
|
182
|
+
state?: WorkflowStateNamespace[];
|
|
166
183
|
triggers: WorkflowTrigger[];
|
|
167
184
|
nodes: WorkflowNode[];
|
|
168
185
|
edges: WorkflowEdge[];
|
|
@@ -247,11 +264,25 @@ export interface NodeManifestOutput {
|
|
|
247
264
|
* publishes them. Author-time schema only, never live run data. */
|
|
248
265
|
fields?: string[] | Record<string, unknown>;
|
|
249
266
|
}
|
|
250
|
-
export type ConfigFieldType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref'
|
|
267
|
+
export type ConfigFieldType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref'
|
|
268
|
+
/** Names one of the workflow's declared state namespaces (PO-374). */
|
|
269
|
+
| 'state-ref' | 'dynamic-schema';
|
|
251
270
|
export interface ConfigFieldOption {
|
|
252
271
|
value: string | number | boolean;
|
|
253
272
|
label: LocalizedString;
|
|
254
273
|
}
|
|
274
|
+
/** The comparison words a settings condition may use. One vocabulary with the
|
|
275
|
+
* condition and filter nodes, so an author who has met these words in one
|
|
276
|
+
* place does not learn a second set for the other. */
|
|
277
|
+
export type SettingConditionOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'greaterThan' | 'greaterThanOrEqual' | 'lessThan' | 'lessThanOrEqual' | 'exists' | 'notExists' | 'isEmpty' | 'isNotEmpty';
|
|
278
|
+
/** A condition on another setting's value, deciding whether this one applies. */
|
|
279
|
+
export interface SettingCondition {
|
|
280
|
+
/** The `key` of another setting at the same level. */
|
|
281
|
+
key: string;
|
|
282
|
+
op: SettingConditionOperator;
|
|
283
|
+
/** Left out by the four operators that read presence rather than content. */
|
|
284
|
+
value?: string | number | boolean;
|
|
285
|
+
}
|
|
255
286
|
export interface ConfigField {
|
|
256
287
|
key: string;
|
|
257
288
|
label: LocalizedString;
|
|
@@ -278,8 +309,20 @@ export interface ConfigField {
|
|
|
278
309
|
* (e.g. 'revenexx:business-central'). An array lets a node accept more than
|
|
279
310
|
* one credential type; the picker merges the tenant's credentials across all. */
|
|
280
311
|
credentialType?: string | string[];
|
|
312
|
+
/**
|
|
313
|
+
* Only for `type: 'state-ref'` (PO-374): which kind of state namespace the
|
|
314
|
+
* field accepts. The picker lists the workflow's declarations of that role;
|
|
315
|
+
* the stored value is the namespace NAME.
|
|
316
|
+
*/
|
|
317
|
+
stateRole?: WorkflowStateRole;
|
|
281
318
|
/** keys of other config fields this field's options/schema depend on. */
|
|
282
319
|
dependsOn?: string[];
|
|
320
|
+
/** When this setting applies at all (PO-410): another setting's key, an
|
|
321
|
+
* operator, and where the operator takes one, the value to compare against.
|
|
322
|
+
* Drawn only while the condition holds — see utils/settingCondition. Not
|
|
323
|
+
* `dependsOn`, which decides what a setting resolves to rather than whether
|
|
324
|
+
* it is drawn. */
|
|
325
|
+
showIf?: SettingCondition;
|
|
283
326
|
validation?: {
|
|
284
327
|
pattern?: string;
|
|
285
328
|
min?: number;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watching a queued bundle build out, on any surface that reports one.
|
|
3
|
+
*
|
|
4
|
+
* A save queues a build, and until the platform has finished it the workflow cannot
|
|
5
|
+
* run. Both surfaces that say so read the state off the workflow row — the editor's
|
|
6
|
+
* chip beside Save, the build column in the workflows list — so neither of them
|
|
7
|
+
* learns that a build finished unless the row is read again.
|
|
8
|
+
*
|
|
9
|
+
* Two things can make that happen. A realtime build report, which is the fast path
|
|
10
|
+
* and is not always there: the topics have no producer in every environment, the
|
|
11
|
+
* connection can be off for a tenant, and a socket can drop in the second after the
|
|
12
|
+
* save. And re-reading the row, which is this — the floor under the fast path, and
|
|
13
|
+
* the only one of the two that works everywhere today.
|
|
14
|
+
*
|
|
15
|
+
* The editor has had it since PO-398, where a chip that never cleared was reported
|
|
16
|
+
* as "saving does not work"; the list kept the same defect for as long as it had no
|
|
17
|
+
* loop of its own (PO-416). So the loop lives here rather than in either surface:
|
|
18
|
+
* one interval, one ceiling, and one rule about which call wins, for both.
|
|
19
|
+
*
|
|
20
|
+
* WHAT A CALLER SUPPLIES is what differs between the two surfaces, and it is only
|
|
21
|
+
* ever these two things: whether anything is still unsettled — one workflow in the
|
|
22
|
+
* editor, any row in the list — and how to read it again. `settle()` decides
|
|
23
|
+
* nothing about the answer; the caller's own state is what every surface renders
|
|
24
|
+
* from.
|
|
25
|
+
*
|
|
26
|
+
* A GENERATION RATHER THAN A BUSY FLAG. Two saves in quick succession must not
|
|
27
|
+
* leave the second one unwatched, and the earlier loop cannot be relied on to cover
|
|
28
|
+
* it: a read already in flight can land after the newer save and put the pre-save
|
|
29
|
+
* row back into the cache, after which the older loop reads `ready` and stops. So
|
|
30
|
+
* the newest call wins and every older loop retires at its next turn.
|
|
31
|
+
*
|
|
32
|
+
* THE CEILING is not a timeout in the usual sense — nothing is reported when it is
|
|
33
|
+
* reached. A build is seconds; a build still queued after five minutes is a state
|
|
34
|
+
* worth leaving on screen rather than asking about forever, and the operator's own
|
|
35
|
+
* reload starts a fresh watch (which is why arriving on an unsettled row starts one).
|
|
36
|
+
*/
|
|
37
|
+
/** Between reads. Same cadence the editor's queued-run wait uses. */
|
|
38
|
+
export declare const BUILD_SETTLE_POLL_MS = 2500;
|
|
39
|
+
/** After this, the state stands as last read and nothing asks again. */
|
|
40
|
+
export declare const BUILD_SETTLE_TIMEOUT_MS: number;
|
|
41
|
+
/**
|
|
42
|
+
* Whether a build state is one that changes on its own.
|
|
43
|
+
*
|
|
44
|
+
* `ready` and `failed` are where a build lands; anything else — including a row
|
|
45
|
+
* whose state the platform did not send — is nothing to wait for.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isBuildUnsettled(status?: string | null): boolean;
|
|
48
|
+
export interface BuildSettleWatch {
|
|
49
|
+
/** Whether anything is still worth waiting for. Read fresh on every turn. */
|
|
50
|
+
unsettled: () => boolean;
|
|
51
|
+
/** Reads the state again. Its own failures are its business — see below. */
|
|
52
|
+
read: () => Promise<unknown>;
|
|
53
|
+
}
|
|
54
|
+
export interface BuildSettleOptions {
|
|
55
|
+
pollMs?: number;
|
|
56
|
+
timeoutMs?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface BuildSettle {
|
|
59
|
+
/**
|
|
60
|
+
* Reads until nothing is unsettled, or until the ceiling. Resolves either way and
|
|
61
|
+
* throws nothing: a read that fails leaves the state as it stands and the next
|
|
62
|
+
* turn tries again, which is the same answer a surface gives its reader anyway.
|
|
63
|
+
*/
|
|
64
|
+
settle: (watch: BuildSettleWatch) => Promise<void>;
|
|
65
|
+
/** Retires whatever is watching — for a surface being left. */
|
|
66
|
+
stop: () => void;
|
|
67
|
+
}
|
|
68
|
+
export declare function createBuildSettle(options?: BuildSettleOptions): BuildSettle;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const BUILD_SETTLE_POLL_MS = 2500;
|
|
2
|
+
export const BUILD_SETTLE_TIMEOUT_MS = 5 * 6e4;
|
|
3
|
+
export function isBuildUnsettled(status) {
|
|
4
|
+
return status === "pending" || status === "building";
|
|
5
|
+
}
|
|
6
|
+
export function createBuildSettle(options = {}) {
|
|
7
|
+
const pollMs = options.pollMs ?? BUILD_SETTLE_POLL_MS;
|
|
8
|
+
const timeoutMs = options.timeoutMs ?? BUILD_SETTLE_TIMEOUT_MS;
|
|
9
|
+
let generation = 0;
|
|
10
|
+
return {
|
|
11
|
+
async settle({ unsettled, read }) {
|
|
12
|
+
const mine = ++generation;
|
|
13
|
+
const started = Date.now();
|
|
14
|
+
while (Date.now() - started < timeoutMs) {
|
|
15
|
+
if (mine !== generation) return;
|
|
16
|
+
if (!unsettled()) return;
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
18
|
+
if (mine !== generation || !unsettled()) return;
|
|
19
|
+
try {
|
|
20
|
+
await read();
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
stop() {
|
|
26
|
+
generation += 1;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ConfigField, SettingConditionOperator } from '../types/integrations.js';
|
|
2
|
+
/**
|
|
3
|
+
* What an operator means. Mirrors the SDK's `evaluate` row for row, including
|
|
4
|
+
* the leniency of `equals` across the string/number line: a select carries `1`
|
|
5
|
+
* where a text box carries `'1'`, and an author means one thing by both.
|
|
6
|
+
*/
|
|
7
|
+
export declare function evaluateOperator(left: unknown, op: SettingConditionOperator, right: unknown): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Whether one setting applies, given the values at its own level — inside a
|
|
10
|
+
* group, the siblings in that group, which is where the node wrote the
|
|
11
|
+
* condition and where the platform resolves it.
|
|
12
|
+
*
|
|
13
|
+
* A setting with no condition always applies, and so does one whose condition
|
|
14
|
+
* is malformed: whatever the node meant, an author is better off seeing a field
|
|
15
|
+
* than having it silently withheld.
|
|
16
|
+
*/
|
|
17
|
+
export declare function settingApplies(field: Pick<ConfigField, 'showIf'>, values: Record<string, unknown>): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether a change to `changedKey` leaves this setting's value stale.
|
|
20
|
+
*
|
|
21
|
+
* Two reasons a setting can depend on another, and they do not go stale at the
|
|
22
|
+
* same moment. `dependsOn` names what a dynamic field resolves its options
|
|
23
|
+
* from, and any change to it invalidates what was chosen out of the old
|
|
24
|
+
* options. A condition names the setting that decides whether the field is
|
|
25
|
+
* drawn at all, and what was typed only becomes unreachable when the condition
|
|
26
|
+
* stops holding: while it still holds the field is on screen, and clearing it
|
|
27
|
+
* on every keystroke in the setting it reads would empty a field the author is
|
|
28
|
+
* looking at — `showIf: { key: 'url', op: 'isNotEmpty' }` stays true from the
|
|
29
|
+
* first character on.
|
|
30
|
+
*/
|
|
31
|
+
export declare function valueGoesStale(field: Pick<ConfigField, 'dependsOn' | 'showIf'>, changedKey: string, values: Record<string, unknown>): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The settings of one level whose values are stale once `changedKey` has been
|
|
34
|
+
* set — the cascade, walked to a fixpoint over that level's own fields.
|
|
35
|
+
*
|
|
36
|
+
* `after` is the config as it stands *after* the write, because a condition is
|
|
37
|
+
* read against it: whether a setting still applies is the whole question for a
|
|
38
|
+
* `showIf` dependant. Each cleared key is taken out of that config as the walk
|
|
39
|
+
* goes, so a setting withdrawn by a clearing is found in the same pass.
|
|
40
|
+
*
|
|
41
|
+
* The setting being written is never cleared, which is also what guards a
|
|
42
|
+
* cyclic `dependsOn`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function staleKeys(fields: Pick<ConfigField, 'key' | 'dependsOn' | 'showIf'>[], changedKey: string, after: Record<string, unknown>): Set<string>;
|
|
45
|
+
/**
|
|
46
|
+
* The settings of a level that apply, in the order the node declared them.
|
|
47
|
+
*/
|
|
48
|
+
export declare function applyingFields(fields: ConfigField[], values: Record<string, unknown>): ConfigField[];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const READS_PRESENCE = ["exists", "notExists", "isEmpty", "isNotEmpty"];
|
|
2
|
+
const OPERATORS = [
|
|
3
|
+
"equals",
|
|
4
|
+
"notEquals",
|
|
5
|
+
"contains",
|
|
6
|
+
"notContains",
|
|
7
|
+
"startsWith",
|
|
8
|
+
"endsWith",
|
|
9
|
+
"greaterThan",
|
|
10
|
+
"greaterThanOrEqual",
|
|
11
|
+
"lessThan",
|
|
12
|
+
"lessThanOrEqual",
|
|
13
|
+
...READS_PRESENCE
|
|
14
|
+
];
|
|
15
|
+
export function evaluateOperator(left, op, right) {
|
|
16
|
+
switch (op) {
|
|
17
|
+
case "exists":
|
|
18
|
+
return left !== void 0 && left !== null;
|
|
19
|
+
case "notExists":
|
|
20
|
+
return left === void 0 || left === null;
|
|
21
|
+
case "isEmpty":
|
|
22
|
+
return left === "" || left === null || left === void 0 || Array.isArray(left) && left.length === 0;
|
|
23
|
+
case "isNotEmpty":
|
|
24
|
+
return !evaluateOperator(left, "isEmpty", right);
|
|
25
|
+
case "equals":
|
|
26
|
+
return String(left) === String(right);
|
|
27
|
+
case "notEquals":
|
|
28
|
+
return String(left) !== String(right);
|
|
29
|
+
case "contains":
|
|
30
|
+
return typeof left === "string" && left.includes(String(right));
|
|
31
|
+
case "notContains":
|
|
32
|
+
return typeof left === "string" && !left.includes(String(right));
|
|
33
|
+
case "startsWith":
|
|
34
|
+
return typeof left === "string" && left.startsWith(String(right));
|
|
35
|
+
case "endsWith":
|
|
36
|
+
return typeof left === "string" && left.endsWith(String(right));
|
|
37
|
+
case "greaterThan":
|
|
38
|
+
return Number(left) > Number(right);
|
|
39
|
+
case "greaterThanOrEqual":
|
|
40
|
+
return Number(left) >= Number(right);
|
|
41
|
+
case "lessThan":
|
|
42
|
+
return Number(left) < Number(right);
|
|
43
|
+
case "lessThanOrEqual":
|
|
44
|
+
return Number(left) <= Number(right);
|
|
45
|
+
default: {
|
|
46
|
+
const unhandled = op;
|
|
47
|
+
void unhandled;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function settingApplies(field, values) {
|
|
53
|
+
const condition = field.showIf;
|
|
54
|
+
if (!condition || typeof condition !== "object")
|
|
55
|
+
return true;
|
|
56
|
+
const { key, op } = condition;
|
|
57
|
+
if (typeof key !== "string" || !OPERATORS.includes(op))
|
|
58
|
+
return true;
|
|
59
|
+
return evaluateOperator(values[key], op, condition.value);
|
|
60
|
+
}
|
|
61
|
+
export function valueGoesStale(field, changedKey, values) {
|
|
62
|
+
if ((field.dependsOn ?? []).includes(changedKey))
|
|
63
|
+
return true;
|
|
64
|
+
return field.showIf?.key === changedKey && !settingApplies(field, values);
|
|
65
|
+
}
|
|
66
|
+
export function staleKeys(fields, changedKey, after) {
|
|
67
|
+
const remaining = { ...after };
|
|
68
|
+
const out = /* @__PURE__ */ new Set();
|
|
69
|
+
const queue = [changedKey];
|
|
70
|
+
while (queue.length > 0) {
|
|
71
|
+
const dep = queue.shift();
|
|
72
|
+
for (const f of fields) {
|
|
73
|
+
if (f.key === changedKey || out.has(f.key)) continue;
|
|
74
|
+
if (!valueGoesStale(f, dep, remaining)) continue;
|
|
75
|
+
out.add(f.key);
|
|
76
|
+
delete remaining[f.key];
|
|
77
|
+
queue.push(f.key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
export function applyingFields(fields, values) {
|
|
83
|
+
return fields.filter((field) => settingApplies(field, values));
|
|
84
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine's grammar for a state namespace (PO-374), mirrored in the browser
|
|
3
|
+
* so a name it would refuse is rejected while the author types rather than as a
|
|
4
|
+
* 422 about `blob.state.0` on save.
|
|
5
|
+
*
|
|
6
|
+
* One copy on purpose. Two surfaces declare namespaces — the workflow's State
|
|
7
|
+
* dialog and the picker inside a node's state setting — and a grammar spelled
|
|
8
|
+
* once per surface is one that widens or narrows in a single place, leaving the
|
|
9
|
+
* other accepting names the engine has stopped taking.
|
|
10
|
+
*/
|
|
11
|
+
export declare const STATE_NAMESPACE_PATTERN: RegExp;
|
|
12
|
+
/**
|
|
13
|
+
* Which message a typed namespace name earns, as a translation key — or `null`
|
|
14
|
+
* when there is nothing to say. Both callers show the same sentence for the same
|
|
15
|
+
* mistake, which is the other half of holding the grammar in one place.
|
|
16
|
+
*
|
|
17
|
+
* An empty field returns `null`: nobody has answered yet, and a rejection shown
|
|
18
|
+
* before the first keystroke reads as a rejection of the dialog.
|
|
19
|
+
*
|
|
20
|
+
* @param name what the author has typed, trimmed here rather than by the caller
|
|
21
|
+
* @param taken the names the workflow already declares
|
|
22
|
+
*/
|
|
23
|
+
export declare function stateNamespaceErrorKey(name: string, taken: string[]): string | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const STATE_NAMESPACE_PATTERN = /^[a-z][a-z0-9]*([._-][a-z0-9]+)*$/;
|
|
2
|
+
export function stateNamespaceErrorKey(name, taken) {
|
|
3
|
+
const value = name.trim();
|
|
4
|
+
if (!value) return null;
|
|
5
|
+
if (!STATE_NAMESPACE_PATTERN.test(value)) return "integration.workflows.state.error.invalidName";
|
|
6
|
+
if (taken.includes(value)) return "integration.workflows.state.error.nameExists";
|
|
7
|
+
return null;
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revenexx/studio-integrations",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Integration Studio (core: workflows, node editor, runs, schedules, credentials, secrets, audit) as a standalone, publishable Nuxt module.",
|
|
6
6
|
"repository": {
|