@uipath/maestro-builder-sdk 6.25.1 → 6.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-index.md +22 -21
- package/dist/api-members.md +87 -84
- package/dist/check.js +83 -18
- package/dist/core/carried-definitions.d.ts +33 -0
- package/dist/core/carried-definitions.js +63 -0
- package/dist/core/nodes/versioned.d.ts +35 -0
- package/dist/core/nodes/versioned.js +35 -0
- package/dist/decompile.js +152 -57
- package/dist/flow-expr-check.js +6 -1
- package/dist/flow-node-type.d.ts +8 -3
- package/dist/flow-sdk.d.ts +91 -12
- package/dist/flow-sdk.js +74 -6
- package/dist/merge.js +2 -1
- package/dist/serialize.js +76 -28
- package/package.json +2 -2
package/dist/check.js
CHANGED
|
@@ -29,7 +29,7 @@ import { ownEntry } from './core/own-entry.js';
|
|
|
29
29
|
import { FLOW_NODE_TYPES } from './flow-node-registry.js';
|
|
30
30
|
import { stepExits } from './flow-step-exits.js';
|
|
31
31
|
import { plainHttpOnErrorMessage } from './nodes/http/flow/family.js';
|
|
32
|
-
import { resolveVersion } from './core/nodes/versioned.js';
|
|
32
|
+
import { resolveVersion, resolveVersionCarried } from './core/nodes/versioned.js';
|
|
33
33
|
import { declaredErrorPortRefusal } from './core/error-port.js';
|
|
34
34
|
export function check(built, opts = {}) {
|
|
35
35
|
let diags = [];
|
|
@@ -275,7 +275,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
275
275
|
declared.add(s.name); // a step's output is referenceable by name
|
|
276
276
|
}
|
|
277
277
|
if ((s.kind === 'stepToList' || s.kind === 'stepToRef') && s.port === 'error')
|
|
278
|
-
checkOnError(s, prevPortSource, diags, prevPortSource && checkedSteps.get(prevPortSource.name));
|
|
278
|
+
checkOnError(s, prevPortSource, diags, prevPortSource && checkedSteps.get(prevPortSource.name), carriedLookup([built, ...ancestors]));
|
|
279
279
|
if ((s.kind === 'stepToList' || s.kind === 'stepToRef') && s.port.startsWith('branch-')) {
|
|
280
280
|
const want = s.port.slice('branch-'.length);
|
|
281
281
|
const prevSpec = prevAction?.spec;
|
|
@@ -381,7 +381,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
381
381
|
prevPortSource = s;
|
|
382
382
|
const registered = registeredKind(s.spec);
|
|
383
383
|
if (registered) {
|
|
384
|
-
const { placed, checked } = checkRegisteredStep(registered, s, exprs, errorSource, opts, diags, typeOf, triggerId);
|
|
384
|
+
const { placed, checked } = checkRegisteredStep(registered, s, exprs, errorSource, opts, diags, typeOf, triggerId, carriedLookup([built, ...ancestors]));
|
|
385
385
|
checkedSteps.set(s.name, checked);
|
|
386
386
|
if (placed)
|
|
387
387
|
registeredSteps.set(s.name, checked);
|
|
@@ -518,13 +518,51 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
518
518
|
walk(s.otherwise, inLoop);
|
|
519
519
|
}
|
|
520
520
|
else if (s.kind === 'switch') {
|
|
521
|
-
|
|
521
|
+
if (s.on)
|
|
522
|
+
addExpr(s.on.js, s.name);
|
|
523
|
+
for (const c of s.cases)
|
|
524
|
+
if (c.when)
|
|
525
|
+
addExpr(c.when.js, s.name);
|
|
522
526
|
switchNames.add(s.name);
|
|
527
|
+
s.cases.forEach((c, i) => {
|
|
528
|
+
const hasValue = c.value !== undefined;
|
|
529
|
+
const hasWhen = c.when !== undefined;
|
|
530
|
+
if (hasValue === hasWhen || (hasValue && !s.on)) {
|
|
531
|
+
diags.push({
|
|
532
|
+
level: 'error',
|
|
533
|
+
code: 'SWITCH_CASE_FORM',
|
|
534
|
+
message: hasValue && hasWhen
|
|
535
|
+
? `Switch "${s.name}" case ${i + 1} has both \`value\` and \`when\`. Give it one: \`value\` is compared with \`on\`, \`when\` is the case's own condition.`
|
|
536
|
+
: hasValue
|
|
537
|
+
? `Switch "${s.name}" case ${i + 1} has a \`value\` but the switch has no \`on\` to compare it with. Pass \`on\`, or give the case a \`when\` condition.`
|
|
538
|
+
: `Switch "${s.name}" case ${i + 1} has neither \`value\` nor \`when\`.`,
|
|
539
|
+
step: s.name,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
if (hasWhen && c.when.literal) {
|
|
543
|
+
diags.push({
|
|
544
|
+
level: 'error',
|
|
545
|
+
code: 'SWITCH_CONST_WHEN',
|
|
546
|
+
message: `Switch "${s.name}" case ${i + 1}'s \`when\` is the constant ${JSON.stringify(c.when.value)}, so it is always or never taken. Pass an expression.`,
|
|
547
|
+
step: s.name,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
});
|
|
523
551
|
// A constant discriminant can never match a case — it is what
|
|
524
552
|
// `.switch('q', 'quarter', …)` (a name where an expression belongs)
|
|
525
553
|
// degrades into once types are stripped, and it would emit
|
|
526
554
|
// `"quarter" === 2`: valid JS, always false, default arm every time.
|
|
527
|
-
|
|
555
|
+
// `on` is compared only with `value` cases; with none, it is unused.
|
|
556
|
+
const hasValueCase = s.cases.some((c) => c.value !== undefined);
|
|
557
|
+
if (s.on && !hasValueCase && s.cases.length > 0)
|
|
558
|
+
diags.push({
|
|
559
|
+
level: 'warning',
|
|
560
|
+
code: 'SWITCH_ON_UNUSED',
|
|
561
|
+
message: `Switch "${s.name}" has an \`on\`, but every case has its own \`when\` condition, so \`on\` is never compared. `
|
|
562
|
+
+ 'Pass `undefined` for `on`.',
|
|
563
|
+
step: s.name,
|
|
564
|
+
});
|
|
565
|
+
if (s.on?.literal && hasValueCase)
|
|
528
566
|
diags.push({
|
|
529
567
|
level: 'error',
|
|
530
568
|
code: 'SWITCH_CONST_ON',
|
|
@@ -563,12 +601,12 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
563
601
|
seenIds.add(String(caseId));
|
|
564
602
|
});
|
|
565
603
|
for (const c of s.cases) {
|
|
566
|
-
const key = `${typeof c.value}:${String(c.value)}`;
|
|
604
|
+
const key = c.when !== undefined ? `when:${c.when.js}` : `${typeof c.value}:${String(c.value)}`;
|
|
567
605
|
if (seenValues.has(key))
|
|
568
606
|
diags.push({
|
|
569
607
|
level: 'error',
|
|
570
608
|
code: 'DUP_CASE',
|
|
571
|
-
message: `Switch "${s.name}" has two cases for ${JSON.stringify(c.value)}; the second is unreachable `
|
|
609
|
+
message: `Switch "${s.name}" has two cases for ${c.when !== undefined ? `when ${c.when.js}` : JSON.stringify(c.value)}; the second is unreachable `
|
|
572
610
|
+ '(the first matching case wins).',
|
|
573
611
|
step: s.name,
|
|
574
612
|
});
|
|
@@ -577,7 +615,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
577
615
|
diags.push({
|
|
578
616
|
level: 'warning',
|
|
579
617
|
code: 'EMPTY_ARM',
|
|
580
|
-
message: `Switch "${s.name}" has an empty arm for case ${JSON.stringify(c.value)}.`,
|
|
618
|
+
message: `Switch "${s.name}" has an empty arm for case ${c.when !== undefined ? `when ${c.when.js}` : JSON.stringify(c.value)}.`,
|
|
581
619
|
step: s.name,
|
|
582
620
|
});
|
|
583
621
|
walk(c.body, inLoop);
|
|
@@ -3935,6 +3973,8 @@ function reportUnreachableHitlOutcomes(choices, exprs, diags) {
|
|
|
3935
3973
|
* and so are object/array/any/file discriminants — nothing useful to compare.
|
|
3936
3974
|
*/
|
|
3937
3975
|
function checkCaseValueTypes(s, typeOf, diags, triggerId) {
|
|
3976
|
+
if (!s.on)
|
|
3977
|
+
return;
|
|
3938
3978
|
const refName = declaredRefName(s.on.js, triggerId);
|
|
3939
3979
|
if (!refName)
|
|
3940
3980
|
return;
|
|
@@ -3946,6 +3986,8 @@ function checkCaseValueTypes(s, typeOf, diags, triggerId) {
|
|
|
3946
3986
|
if (!expected)
|
|
3947
3987
|
return;
|
|
3948
3988
|
for (const c of s.cases) {
|
|
3989
|
+
if (c.value === undefined)
|
|
3990
|
+
continue;
|
|
3949
3991
|
const actual = typeof c.value;
|
|
3950
3992
|
if (actual === expected)
|
|
3951
3993
|
continue;
|
|
@@ -3977,6 +4019,11 @@ const LOOP_FIELDS = new Set(['currentItem', 'currentIteration', 'collection', 'o
|
|
|
3977
4019
|
* before the 2.4 default (#868); its own fields are not audited here.
|
|
3978
4020
|
*/
|
|
3979
4021
|
const LOOP_FIELDS_1_0_0 = new Set(['currentItem', 'currentIndex', 'collection', 'output', 'error']);
|
|
4022
|
+
/** The {@link CarriedLookup} over `scopes` — the flow being checked first, then its ancestors. */
|
|
4023
|
+
function carriedLookup(scopes) {
|
|
4024
|
+
const all = scopes.flatMap((b) => b.definitions ?? []);
|
|
4025
|
+
return Object.assign((typeName, version) => all.find((d) => d.nodeType === typeName && d.version === version), { versions: (typeName) => [...new Set(all.filter((d) => d.nodeType === typeName).map((d) => String(d.version)))] });
|
|
4026
|
+
}
|
|
3980
4027
|
/** The registered node type for a spec's kind, or undefined (a custom FlowAction has no kind). */
|
|
3981
4028
|
function registeredKind(spec) {
|
|
3982
4029
|
const kind = spec?.kind;
|
|
@@ -3994,14 +4041,15 @@ function registeredKind(spec) {
|
|
|
3994
4041
|
* which serialize enforces (#942) and the type's tests pin; an author cannot
|
|
3995
4042
|
* cause the gap, so there is no author diagnostic to report.
|
|
3996
4043
|
*/
|
|
3997
|
-
function checkRegisteredStep(entry, s, exprs, errorSource, opts, diags, typeOf, triggerId) {
|
|
4044
|
+
function checkRegisteredStep(entry, s, exprs, errorSource, opts, diags, typeOf, triggerId, carried) {
|
|
3998
4045
|
const spec = s.spec;
|
|
3999
4046
|
/** The step's node-local rules and expressions, as `impl` defines them. */
|
|
4000
|
-
const runRules = (impl) => {
|
|
4047
|
+
const runRules = (impl, version = impl.version, carriedDef) => {
|
|
4001
4048
|
const ctx = {
|
|
4002
4049
|
step: s.name,
|
|
4003
|
-
version
|
|
4004
|
-
|
|
4050
|
+
// At a carried version, the version and definition serialize emits.
|
|
4051
|
+
version,
|
|
4052
|
+
definition: () => carriedDef ?? bundledDefinition(impl.typeName, impl.version),
|
|
4005
4053
|
report: (code, message, suggestion) => diags.push(catalogued(entry, s.name, code, message, suggestion)),
|
|
4006
4054
|
declaredRef: (js) => {
|
|
4007
4055
|
const name = declaredRefName(js, triggerId);
|
|
@@ -4024,8 +4072,14 @@ function checkRegisteredStep(entry, s, exprs, errorSource, opts, diags, typeOf,
|
|
|
4024
4072
|
return ctx;
|
|
4025
4073
|
};
|
|
4026
4074
|
let impl;
|
|
4075
|
+
let version;
|
|
4076
|
+
let carriedDef;
|
|
4027
4077
|
try {
|
|
4028
|
-
|
|
4078
|
+
// A pin the registry lacks but the flow carries resolves (#1128), as in serialize.
|
|
4079
|
+
const resolved = resolveVersionCarried(entry.type, spec, s.options?.version, `step "${s.name}"`, carried);
|
|
4080
|
+
impl = resolved.impl;
|
|
4081
|
+
version = resolved.version;
|
|
4082
|
+
carriedDef = resolved.carried;
|
|
4029
4083
|
}
|
|
4030
4084
|
catch (e) {
|
|
4031
4085
|
// A select() that picks an unregistered version is the node type's bug, not the author's pin.
|
|
@@ -4043,8 +4097,14 @@ function checkRegisteredStep(entry, s, exprs, errorSource, opts, diags, typeOf,
|
|
|
4043
4097
|
checked: { entry, impl: fallback, spec, placement: { step: s.name, kind: fallback.kind, spec, version: fallback.version }, ctx: fallbackCtx },
|
|
4044
4098
|
};
|
|
4045
4099
|
}
|
|
4046
|
-
const ctx = runRules(impl);
|
|
4047
|
-
return {
|
|
4100
|
+
const ctx = runRules(impl, version, carriedDef);
|
|
4101
|
+
return {
|
|
4102
|
+
placed: true,
|
|
4103
|
+
checked: {
|
|
4104
|
+
entry, impl, spec, placement: { step: s.name, kind: impl.kind, spec, version }, ctx,
|
|
4105
|
+
...(carriedDef !== undefined ? { carried: carriedDef } : {}),
|
|
4106
|
+
},
|
|
4107
|
+
};
|
|
4048
4108
|
}
|
|
4049
4109
|
/** Every non-literal Expr reachable in `value`, as expressions to check. */
|
|
4050
4110
|
function pushDeepExprs(value, step, errorSource, out) {
|
|
@@ -4171,7 +4231,7 @@ const ERROR_ENVELOPE_FIELDS = ['code', 'message', 'detail', 'category', 'status'
|
|
|
4171
4231
|
*/
|
|
4172
4232
|
function checkOnError(_handler, step, diags,
|
|
4173
4233
|
/** The step's registry record, when its kind is registered and its version resolved. */
|
|
4174
|
-
placed) {
|
|
4234
|
+
placed, carried) {
|
|
4175
4235
|
// The builder refuses a handler with no eligible step before it, so a missing
|
|
4176
4236
|
// source here means a hand-built `BuiltFlow`; there is nothing to say about it.
|
|
4177
4237
|
if (!step)
|
|
@@ -4243,7 +4303,7 @@ placed) {
|
|
|
4243
4303
|
}
|
|
4244
4304
|
}
|
|
4245
4305
|
else {
|
|
4246
|
-
const def = bundledDefinition(impl.typeName, impl.version);
|
|
4306
|
+
const def = placed.carried ?? bundledDefinition(impl.typeName, impl.version);
|
|
4247
4307
|
if (def !== undefined && !definitionHasErrorPort(def)) {
|
|
4248
4308
|
diags.push({ level: 'error', code: 'ONERROR_NO_ERROR_PORT', message: typedNoErrorPortMessage(step.name, impl.kind, def), step: step.name });
|
|
4249
4309
|
return;
|
|
@@ -4262,7 +4322,12 @@ placed) {
|
|
|
4262
4322
|
// A version the SDK does not bundle (an inline agent's registry version) has
|
|
4263
4323
|
// no definition here to ask; version resolution reports that, not this check.
|
|
4264
4324
|
const bundledType = bundledActionNodeType(spec);
|
|
4265
|
-
|
|
4325
|
+
// A version the flow CARRIES (`.definitions()`, #1128) answers the same way
|
|
4326
|
+
// a bundled one does: it is the definition serialize will emit.
|
|
4327
|
+
const pinned = step.options?.version;
|
|
4328
|
+
const bundledDef = bundledType
|
|
4329
|
+
? bundledDefinition(bundledType, actionVersion(spec, pinned)) ?? (pinned !== undefined ? carried?.(bundledType, pinned) : undefined)
|
|
4330
|
+
: undefined;
|
|
4266
4331
|
// A per-tenant family: ask the definition serialize synthesizes for it (#1040).
|
|
4267
4332
|
let perCallDef;
|
|
4268
4333
|
if (!bundledType) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node definitions a flow CARRIES — the third source a pinned `{ version }`
|
|
3
|
+
* resolves from, after the SDK's bundle (#1128).
|
|
4
|
+
*
|
|
5
|
+
* Every `.flow` file holds the exact definition of every node it uses, in
|
|
6
|
+
* `definitions[]`. The SDK bundles only the versions it was released with, so
|
|
7
|
+
* a version the registry served later (`node add` writes
|
|
8
|
+
* `uipath.human-in-the-loop.quick-form@1.6`; the SDK ships 1.0 and 1.1) used
|
|
9
|
+
* to be refused, and the source decompile wrote for an untouched product flow
|
|
10
|
+
* did not compile. A carried definition closes that: decompile copies the
|
|
11
|
+
* file's own entry into `.definitions([...])`, and compile resolves the pin
|
|
12
|
+
* against it.
|
|
13
|
+
*
|
|
14
|
+
* A carried definition is an exact copy of a real one, never a way to invent a
|
|
15
|
+
* version. So it is validated the way a `rawNode` snapshot manifest is: it
|
|
16
|
+
* names its own `nodeType` and `version`, and it has the `display.label` every
|
|
17
|
+
* node manifest has.
|
|
18
|
+
*/
|
|
19
|
+
/** A carried definition, by its `<nodeType>@<version>` key. */
|
|
20
|
+
export type CarriedDefinitions = ReadonlyMap<string, Record<string, unknown>>;
|
|
21
|
+
/**
|
|
22
|
+
* Why `manifest` cannot be carried, or `undefined` when it can.
|
|
23
|
+
*
|
|
24
|
+
* For instance `{ nodeType: 'x', version: '1.0' }` is refused for lacking
|
|
25
|
+
* `display.label`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function carriedDefinitionRefusal(manifest: unknown, where: string): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Index `manifests` by `<nodeType>@<version>`. Throws on an invalid manifest,
|
|
30
|
+
* or on two different manifests for one pair (which one compiles would be a
|
|
31
|
+
* guess).
|
|
32
|
+
*/
|
|
33
|
+
export declare function indexCarriedDefinitions(manifests: readonly unknown[], where: string, into?: Map<string, Record<string, unknown>>): Map<string, Record<string, unknown>>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node definitions a flow CARRIES — the third source a pinned `{ version }`
|
|
3
|
+
* resolves from, after the SDK's bundle (#1128).
|
|
4
|
+
*
|
|
5
|
+
* Every `.flow` file holds the exact definition of every node it uses, in
|
|
6
|
+
* `definitions[]`. The SDK bundles only the versions it was released with, so
|
|
7
|
+
* a version the registry served later (`node add` writes
|
|
8
|
+
* `uipath.human-in-the-loop.quick-form@1.6`; the SDK ships 1.0 and 1.1) used
|
|
9
|
+
* to be refused, and the source decompile wrote for an untouched product flow
|
|
10
|
+
* did not compile. A carried definition closes that: decompile copies the
|
|
11
|
+
* file's own entry into `.definitions([...])`, and compile resolves the pin
|
|
12
|
+
* against it.
|
|
13
|
+
*
|
|
14
|
+
* A carried definition is an exact copy of a real one, never a way to invent a
|
|
15
|
+
* version. So it is validated the way a `rawNode` snapshot manifest is: it
|
|
16
|
+
* names its own `nodeType` and `version`, and it has the `display.label` every
|
|
17
|
+
* node manifest has.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Why `manifest` cannot be carried, or `undefined` when it can.
|
|
21
|
+
*
|
|
22
|
+
* For instance `{ nodeType: 'x', version: '1.0' }` is refused for lacking
|
|
23
|
+
* `display.label`.
|
|
24
|
+
*/
|
|
25
|
+
export function carriedDefinitionRefusal(manifest, where) {
|
|
26
|
+
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
27
|
+
return `${where}: a carried definition must be a node manifest object.`;
|
|
28
|
+
}
|
|
29
|
+
const m = manifest;
|
|
30
|
+
if (typeof m.nodeType !== 'string' || !m.nodeType || typeof m.version !== 'string' || !m.version) {
|
|
31
|
+
return `${where}: a carried definition must name its exact nodeType and version.`;
|
|
32
|
+
}
|
|
33
|
+
if (typeof m.display?.label !== 'string' || !m.display.label) {
|
|
34
|
+
return `${where}: the carried definition ${m.nodeType}@${m.version} lacks display.label — not a valid node manifest.`;
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Index `manifests` by `<nodeType>@<version>`. Throws on an invalid manifest,
|
|
40
|
+
* or on two different manifests for one pair (which one compiles would be a
|
|
41
|
+
* guess).
|
|
42
|
+
*/
|
|
43
|
+
export function indexCarriedDefinitions(manifests, where, into = new Map()) {
|
|
44
|
+
for (const m of manifests) {
|
|
45
|
+
const refusal = carriedDefinitionRefusal(m, where);
|
|
46
|
+
if (refusal)
|
|
47
|
+
throw new Error(refusal);
|
|
48
|
+
const def = m;
|
|
49
|
+
const key = `${def.nodeType}@${def.version}`;
|
|
50
|
+
const prior = into.get(key);
|
|
51
|
+
if (prior !== undefined && stableJson(prior) !== stableJson(def)) {
|
|
52
|
+
throw new Error(`${where}: two different carried definitions for ${key}. Keep the one the file holds.`);
|
|
53
|
+
}
|
|
54
|
+
into.set(key, def);
|
|
55
|
+
}
|
|
56
|
+
return into;
|
|
57
|
+
}
|
|
58
|
+
/** `JSON.stringify` with object keys sorted, so two copies of one manifest compare equal whatever their key order. */
|
|
59
|
+
function stableJson(value) {
|
|
60
|
+
return JSON.stringify(value, (_key, v) => (v && typeof v === 'object' && !Array.isArray(v)
|
|
61
|
+
? Object.fromEntries(Object.keys(v).sort().map((k) => [k, v[k]]))
|
|
62
|
+
: v));
|
|
63
|
+
}
|
|
@@ -48,3 +48,38 @@ export declare function asVersioned<Ctx extends ArtifactContexts>(t: AnyNodeType
|
|
|
48
48
|
* node type's bug, not the author's, and throws as an internal SDK error.
|
|
49
49
|
*/
|
|
50
50
|
export declare function resolveVersion<Spec, Ctx extends ArtifactContexts>(t: VersionedNodeType<Spec, unknown, Ctx>, spec: Spec, requested: string | undefined, where: string): NodeType<Spec, unknown, Ctx>;
|
|
51
|
+
/**
|
|
52
|
+
* Looks up a definition the flow carries (`.definitions()`) for `typeName@version`.
|
|
53
|
+
* `versions`, when present, lists every version of `typeName` the flow carries,
|
|
54
|
+
* so a refusal can name them.
|
|
55
|
+
*/
|
|
56
|
+
export type CarriedDefinitionLookup = ((typeName: string, version: string) => Record<string, unknown> | undefined) & {
|
|
57
|
+
readonly versions?: (typeName: string) => readonly string[];
|
|
58
|
+
};
|
|
59
|
+
/** What {@link resolveVersionCarried} resolved: the implementation, the version to emit, and a carried definition when one answered. */
|
|
60
|
+
export interface ResolvedNodeVersion<Impl> {
|
|
61
|
+
readonly impl: Impl;
|
|
62
|
+
/** The version the node is emitted at — `impl.version`, or the carried version. */
|
|
63
|
+
readonly version: string;
|
|
64
|
+
/** The definition the FLOW carries for `typeName@version`, when the registry has no implementation at that version. */
|
|
65
|
+
readonly carried?: Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* {@link resolveVersion} with the third definition source (#1128): a definition
|
|
69
|
+
* the flow CARRIES (`.definitions()`, decompile's copy of the file's own
|
|
70
|
+
* `definitions[]` entry).
|
|
71
|
+
*
|
|
72
|
+
* Order: a registered version resolves as before. An explicit version the type
|
|
73
|
+
* does not register, but the flow carries, is emitted AT that version with the
|
|
74
|
+
* carried definition, through the implementation the type would use unpinned
|
|
75
|
+
* (`select(spec)`, else the default) — the registry has no implementation of
|
|
76
|
+
* its own for it, and the node keeps the version the file pins instead of being
|
|
77
|
+
* refused. Anything else is refused as {@link resolveVersion} refuses it, except
|
|
78
|
+
* that an unregistered pin also names the versions the flow carries and the
|
|
79
|
+
* `.definitions()` remedy.
|
|
80
|
+
*
|
|
81
|
+
* For instance a file pinning `core.action.script@1.0` (registered: 1.0.0, 1.1)
|
|
82
|
+
* and carrying that definition emits the node at 1.0 through the default
|
|
83
|
+
* implementation.
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveVersionCarried<Spec, Ctx extends ArtifactContexts>(t: VersionedNodeType<Spec, unknown, Ctx>, spec: Spec, requested: string | undefined, where: string, carried: CarriedDefinitionLookup | undefined): ResolvedNodeVersion<NodeType<Spec, unknown, Ctx>>;
|
|
@@ -44,3 +44,38 @@ export function resolveVersion(t, spec, requested, where) {
|
|
|
44
44
|
}
|
|
45
45
|
return impl;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* {@link resolveVersion} with the third definition source (#1128): a definition
|
|
49
|
+
* the flow CARRIES (`.definitions()`, decompile's copy of the file's own
|
|
50
|
+
* `definitions[]` entry).
|
|
51
|
+
*
|
|
52
|
+
* Order: a registered version resolves as before. An explicit version the type
|
|
53
|
+
* does not register, but the flow carries, is emitted AT that version with the
|
|
54
|
+
* carried definition, through the implementation the type would use unpinned
|
|
55
|
+
* (`select(spec)`, else the default) — the registry has no implementation of
|
|
56
|
+
* its own for it, and the node keeps the version the file pins instead of being
|
|
57
|
+
* refused. Anything else is refused as {@link resolveVersion} refuses it, except
|
|
58
|
+
* that an unregistered pin also names the versions the flow carries and the
|
|
59
|
+
* `.definitions()` remedy.
|
|
60
|
+
*
|
|
61
|
+
* For instance a file pinning `core.action.script@1.0` (registered: 1.0.0, 1.1)
|
|
62
|
+
* and carrying that definition emits the node at 1.0 through the default
|
|
63
|
+
* implementation.
|
|
64
|
+
*/
|
|
65
|
+
export function resolveVersionCarried(t, spec, requested, where, carried) {
|
|
66
|
+
if (requested !== undefined && !ownEntry(t.versions, requested)) {
|
|
67
|
+
const def = carried?.(t.typeName, requested);
|
|
68
|
+
if (def !== undefined) {
|
|
69
|
+
return { impl: resolveVersion(t, spec, undefined, where), version: requested, carried: def };
|
|
70
|
+
}
|
|
71
|
+
// Refused: name both sources, and the way to add the second one.
|
|
72
|
+
const registered = Object.keys(t.versions).sort().join(', ');
|
|
73
|
+
const carriedVersions = [...(carried?.versions?.(t.typeName) ?? [])].sort();
|
|
74
|
+
throw new Error(`${where} requests ${t.typeName}@${requested}, but this SDK registers only ${registered}`
|
|
75
|
+
+ (carriedVersions.length > 0 ? ` and the flow carries only ${carriedVersions.join(', ')}` : '')
|
|
76
|
+
+ '. Versions are exact — omit { version } to use the default, request one listed here, or carry '
|
|
77
|
+
+ "that version's definition (the .flow file's definitions[] entry) with .definitions([...]).");
|
|
78
|
+
}
|
|
79
|
+
const impl = resolveVersion(t, spec, requested, where);
|
|
80
|
+
return { impl, version: impl.version };
|
|
81
|
+
}
|