@uipath/maestro-builder-sdk 6.56.0 → 6.58.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 +6 -2
- package/dist/api-members.md +1 -1
- package/dist/case/subscription.d.ts +4 -2
- package/dist/case/subscription.js +5 -2
- package/dist/cli/commands/flow.js +2 -0
- package/dist/core/actions.js +2 -2
- package/dist/core/binding-messages.d.ts +2 -1
- package/dist/core/binding-messages.js +3 -4
- package/dist/core/byoa.d.ts +3 -2
- package/dist/core/byoa.js +3 -3
- package/dist/core/carried-definitions.d.ts +0 -18
- package/dist/core/carried-definitions.js +3 -2
- package/dist/core/ceql.d.ts +2 -1
- package/dist/core/ceql.js +3 -2
- package/dist/core/compile-refusal.d.ts +21 -0
- package/dist/core/compile-refusal.js +26 -0
- package/dist/core/connector-inputs.js +2 -1
- package/dist/core/connector-resolve-error.d.ts +2 -1
- package/dist/core/connector-resolve-error.js +3 -4
- package/dist/core/connectors.js +2 -1
- package/dist/core/event-filters.js +2 -1
- package/dist/core/hitl-routing.d.ts +7 -82
- package/dist/core/hitl-routing.js +95 -6
- package/dist/core/library.js +4 -3
- package/dist/core/lookups.js +6 -5
- package/dist/core/nodes/registry.js +3 -2
- package/dist/core/nodes/versioned.js +3 -2
- package/dist/core/process-resource-sort-order.js +1 -1
- package/dist/core/read-text.d.ts +0 -28
- package/dist/core/read-text.js +3 -2
- package/dist/core/refusals.d.ts +465 -0
- package/dist/core/refusals.js +504 -0
- package/dist/core/script-function.d.ts +126 -0
- package/dist/core/script-function.js +511 -0
- package/dist/core/typescript-module.js +2 -1
- package/dist/decompile-cli.js +11 -3
- package/dist/decompile.d.ts +9 -0
- package/dist/decompile.js +76 -3
- package/dist/flow-expr-check.d.ts +2 -1
- package/dist/flow-expr-check.js +4 -2
- package/dist/flow-node-type.d.ts +21 -0
- package/dist/flow-sdk.js +17 -16
- package/dist/flow-step-tree.js +1 -1
- package/dist/flow-variables.js +3 -2
- package/dist/load.js +19 -5
- package/dist/nodes/agent/flow/inline-agent.js +7 -6
- package/dist/nodes/connector/flow/node-type.js +2 -1
- package/dist/nodes/data-fabric/flow/factories.js +2 -1
- package/dist/nodes/data-fabric/flow/verbs.js +2 -1
- package/dist/nodes/delay/flow/factories.js +2 -1
- package/dist/nodes/document/flow/ixp.js +5 -1
- package/dist/nodes/hitl/flow/check.js +4 -2
- package/dist/nodes/hitl/flow/node-type.js +6 -5
- package/dist/nodes/http/flow/factories.js +2 -1
- package/dist/nodes/http/flow/node-type.js +6 -5
- package/dist/nodes/process/flow/factories.js +2 -1
- package/dist/nodes/queue/flow/node-type.js +3 -2
- package/dist/nodes/script/flow/factories.d.ts +42 -1
- package/dist/nodes/script/flow/factories.js +8 -9
- package/dist/nodes/script/flow/node-type.js +71 -0
- package/dist/nodes/script/flow/runtime.d.ts +20 -0
- package/dist/nodes/script/flow/runtime.js +29 -0
- package/dist/nodes/script/flow/spec.d.ts +32 -1
- package/dist/nodes/transform/flow/node-type.js +2 -1
- package/dist/raw-node.js +3 -2
- package/dist/ref-resolve.d.ts +0 -36
- package/dist/ref-resolve.js +9 -8
- package/dist/schema-version.d.ts +0 -38
- package/dist/schema-version.js +3 -2
- package/dist/script-function-scope.d.ts +57 -0
- package/dist/script-function-scope.js +203 -0
- package/dist/sdk-resolve.js +2 -1
- package/dist/serialize.d.ts +5 -2
- package/dist/serialize.js +115 -83
- package/package.json +2 -2
package/dist/decompile.js
CHANGED
|
@@ -112,6 +112,38 @@ let runAbsorbed = new Map();
|
|
|
112
112
|
let runFlowId = 'flow';
|
|
113
113
|
/** `DecompileOptions.readSidecar` for the current run. */
|
|
114
114
|
let runReadSidecar;
|
|
115
|
+
/**
|
|
116
|
+
* A host's own say on decompile's default script form, beside the TypeScript
|
|
117
|
+
* hand-over in core/typescript-module.ts:
|
|
118
|
+
* `globalThis[Symbol.for('uipath.maestro-builder-sdk.scripts')] = 'string'`.
|
|
119
|
+
*/
|
|
120
|
+
const HOST_SCRIPTS = Symbol.for('uipath.maestro-builder-sdk.scripts');
|
|
121
|
+
/**
|
|
122
|
+
* Function scripts by default only under Node. The exact text a function form
|
|
123
|
+
* stores is what the loader's `Function.prototype.toString()` returns, which is
|
|
124
|
+
* the author's own text under Node's type stripping (what `compile` and `uip`
|
|
125
|
+
* run) and a reprint under other loaders. A host that runs the SDK in a page has
|
|
126
|
+
* not been measured, so there a decompiled script stays a string unless asked.
|
|
127
|
+
*/
|
|
128
|
+
function defaultScripts() {
|
|
129
|
+
const host = globalThis[HOST_SCRIPTS];
|
|
130
|
+
if (host === 'function' || host === 'string')
|
|
131
|
+
return host;
|
|
132
|
+
// Real Node, tested as core/typescript-module.ts does: a page bundle may shim
|
|
133
|
+
// `process.versions.node` (cli-browser's banner sets it) but does not define
|
|
134
|
+
// `process.getBuiltinModule`.
|
|
135
|
+
return typeof globalThis.process?.getBuiltinModule === 'function' ? 'function' : 'string';
|
|
136
|
+
}
|
|
137
|
+
/** How this run prints scripts (`DecompileOptions.scripts`). */
|
|
138
|
+
let runScripts = 'function';
|
|
139
|
+
/** The module-level functions print hooks declared this run, in order. */
|
|
140
|
+
let runModuleFunctions = [];
|
|
141
|
+
/**
|
|
142
|
+
* Each step's reserved function name, by node: keyed by the node itself, not its
|
|
143
|
+
* id, because a root step and a subflow step may share an id, and two
|
|
144
|
+
* `function script_<id>` declarations would make the module unloadable.
|
|
145
|
+
*/
|
|
146
|
+
let runFunctionNames = new Map();
|
|
115
147
|
function warnOnce(message) {
|
|
116
148
|
if (runWarned.has(message))
|
|
117
149
|
return;
|
|
@@ -336,6 +368,8 @@ class Imports {
|
|
|
336
368
|
locals = new Set(['flow']);
|
|
337
369
|
/** Names a package may not take bare: the SDK's helpers and every built-in factory. */
|
|
338
370
|
reserved;
|
|
371
|
+
/** The SDK's type-only names (`FlowVars`): `import { type X }`, erased under type stripping. */
|
|
372
|
+
types = new Set();
|
|
339
373
|
constructor() {
|
|
340
374
|
this.reserved = new Set([
|
|
341
375
|
...SDK_HELPER_NAMES,
|
|
@@ -368,14 +402,25 @@ class Imports {
|
|
|
368
402
|
this.locals.add(local);
|
|
369
403
|
return local;
|
|
370
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* A type-only name from the SDK: `import { type X }`, erased under type
|
|
407
|
+
* stripping, so it never needs a runtime export. Taken as a local, so no
|
|
408
|
+
* package factory binds the same name.
|
|
409
|
+
*/
|
|
410
|
+
useType(name) {
|
|
411
|
+
this.types.add(name);
|
|
412
|
+
this.locals.add(name);
|
|
413
|
+
return name;
|
|
414
|
+
}
|
|
371
415
|
/** One import per module: the SDK first, then the packages by name. */
|
|
372
416
|
line(specifier) {
|
|
373
417
|
const lines = [];
|
|
374
418
|
for (const [module, names] of [...this.used].sort(([a], [b]) => (a === undefined ? -1 : b === undefined ? 1 : a.localeCompare(b)))) {
|
|
375
|
-
|
|
419
|
+
const types = module === undefined ? [...this.types].filter((t) => !names.has(t)).sort().map((t) => `type ${t}`) : [];
|
|
420
|
+
if (names.size === 0 && types.length === 0)
|
|
376
421
|
continue;
|
|
377
422
|
const bindings = [...names].sort(([a], [b]) => a.localeCompare(b)).map(([name, local]) => (local === name ? name : `${name} as ${local}`));
|
|
378
|
-
lines.push(`import { ${bindings.join(', ')} } from ${tsLiteral(module ?? specifier)};`);
|
|
423
|
+
lines.push(`import { ${[...bindings, ...types].join(', ')} } from ${tsLiteral(module ?? specifier)};`);
|
|
379
424
|
}
|
|
380
425
|
return lines.join('\n');
|
|
381
426
|
}
|
|
@@ -826,6 +871,11 @@ function emitRegisteredSpec(node, imp, inputNames, graph) {
|
|
|
826
871
|
json: (v) => tsLiteral(v),
|
|
827
872
|
key: (k) => key(k),
|
|
828
873
|
helper: (name) => imp.use(name),
|
|
874
|
+
typeHelper: (name) => imp.useType(name),
|
|
875
|
+
warn: (message) => warnOnce(`step ${tsLiteral(node.id)}: ${message}`),
|
|
876
|
+
functionName: () => moduleFunctionName(node),
|
|
877
|
+
declareFunction: (source) => { runModuleFunctions.push(source); },
|
|
878
|
+
scripts: runScripts,
|
|
829
879
|
};
|
|
830
880
|
const call = impl.print?.(spec, print) ?? {
|
|
831
881
|
factory: impl.factory,
|
|
@@ -2838,6 +2888,9 @@ function foldOutputBacking(flow) {
|
|
|
2838
2888
|
}
|
|
2839
2889
|
export function decompile(flow, options = {}) {
|
|
2840
2890
|
flow = foldOutputBacking(flow);
|
|
2891
|
+
runScripts = options.scripts ?? defaultScripts();
|
|
2892
|
+
runModuleFunctions = [];
|
|
2893
|
+
runFunctionNames = new Map();
|
|
2841
2894
|
const importSpecifier = options.importSpecifier ?? './flow-sdk.js';
|
|
2842
2895
|
runStrict = options.bestEffort === false;
|
|
2843
2896
|
runWarn = options.warn;
|
|
@@ -2991,7 +3044,27 @@ export function decompile(flow, options = {}) {
|
|
|
2991
3044
|
const hoisted = [...carriedConsts, ...runRawManifests]
|
|
2992
3045
|
.map(([name, json]) => `const ${name} = ${json};`)
|
|
2993
3046
|
.join('\n\n');
|
|
2994
|
-
|
|
3047
|
+
const functions = runModuleFunctions.join('\n\n');
|
|
3048
|
+
return [imp.line(importSpecifier), hoisted, chain, functions].filter((p) => p.length > 0).join('\n\n') + '\n';
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* A step's module-level function name: `script_<id>`, with what an identifier
|
|
3052
|
+
* cannot hold replaced by `_`, and a numeric suffix when two steps (in different
|
|
3053
|
+
* scopes, or with ids that sanitize alike) would get the same one.
|
|
3054
|
+
* The `script_` prefix keeps it clear of every SDK export (all camelCase) and of
|
|
3055
|
+
* the hoisted constants.
|
|
3056
|
+
*/
|
|
3057
|
+
function moduleFunctionName(node) {
|
|
3058
|
+
const have = runFunctionNames.get(node);
|
|
3059
|
+
if (have)
|
|
3060
|
+
return have;
|
|
3061
|
+
const base = `script_${node.id.replace(/[^A-Za-z0-9_$]/g, '_')}`;
|
|
3062
|
+
const taken = new Set(runFunctionNames.values());
|
|
3063
|
+
let name = base;
|
|
3064
|
+
for (let i = 2; taken.has(name); i++)
|
|
3065
|
+
name = `${base}_${i}`;
|
|
3066
|
+
runFunctionNames.set(node, name);
|
|
3067
|
+
return name;
|
|
2995
3068
|
}
|
|
2996
3069
|
/**
|
|
2997
3070
|
* Top-level nodes (no `parentId`) that no root trigger reaches over ANY edge —
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ExprDiagnostic } from './core/expr-check.js';
|
|
2
2
|
import type { BuiltFlow } from './flow-sdk.js';
|
|
3
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
3
4
|
/** A diagnostic plus the node / field it was found in. */
|
|
4
5
|
export interface LocatedDiagnostic extends ExprDiagnostic {
|
|
5
6
|
where: string;
|
|
@@ -28,7 +29,7 @@ export declare function checkCookedEscapes(built: BuiltFlow): (LocatedDiagnostic
|
|
|
28
29
|
* `message` lists every problem (so a runner that prints `err.message` shows them
|
|
29
30
|
* all at once); `diagnostics` carries them structured for a caller that wants them.
|
|
30
31
|
*/
|
|
31
|
-
export declare class FlowBuildError extends
|
|
32
|
+
export declare class FlowBuildError extends CompileRefusal {
|
|
32
33
|
readonly diagnostics: LocatedDiagnostic[];
|
|
33
34
|
constructor(built: BuiltFlow, diagnostics: LocatedDiagnostic[]);
|
|
34
35
|
}
|
package/dist/flow-expr-check.js
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
import { Expr, cookedEscapesOf } from './core/expr.js';
|
|
31
31
|
import { checkExpression, cookedEscapeDiagnostics } from './core/expr-check.js';
|
|
32
32
|
import { stepDeclaredVariables } from './flow-step-variables.js';
|
|
33
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
33
34
|
const NOUN = 'input, variable, or step';
|
|
34
35
|
/**
|
|
35
36
|
* Every `$vars` root a reference may legitimately resolve to, across the WHOLE
|
|
@@ -269,12 +270,13 @@ export function checkCookedEscapes(built) {
|
|
|
269
270
|
* `message` lists every problem (so a runner that prints `err.message` shows them
|
|
270
271
|
* all at once); `diagnostics` carries them structured for a caller that wants them.
|
|
271
272
|
*/
|
|
272
|
-
export class FlowBuildError extends
|
|
273
|
+
export class FlowBuildError extends CompileRefusal {
|
|
273
274
|
diagnostics;
|
|
274
275
|
constructor(built, diagnostics) {
|
|
275
276
|
const label = built.name || built.id || 'flow';
|
|
276
277
|
const lines = diagnostics.map((d) => ` ✗ [${d.code}] in ${d.where}: ${d.message}${d.suggestion ? ` (did you mean "${d.suggestion}"?)` : ''}`);
|
|
277
|
-
|
|
278
|
+
// The first problem's code; `diagnostics` carries every one.
|
|
279
|
+
super(diagnostics[0]?.code ?? 'EXPR_SYNTAX', `Cannot build flow "${label}" — ${diagnostics.length} expression problem(s):\n${lines.join('\n')}`);
|
|
278
280
|
this.name = 'FlowBuildError';
|
|
279
281
|
this.diagnostics = diagnostics;
|
|
280
282
|
}
|
package/dist/flow-node-type.d.ts
CHANGED
|
@@ -327,6 +327,27 @@ export interface FlowPrintContext {
|
|
|
327
327
|
key(name: string): string;
|
|
328
328
|
/** An authoring helper (`lit`, `input`, …) by name: imported into the printed source, and its identifier. */
|
|
329
329
|
helper(name: string): string;
|
|
330
|
+
/** An exported TYPE (`FlowVars`) by name: imported as `type <name>`, and its identifier. */
|
|
331
|
+
typeHelper(name: string): string;
|
|
332
|
+
/**
|
|
333
|
+
* Report a finding about this step, printed once with the step named.
|
|
334
|
+
* Informational: it never fails `--strict`, so it is for a choice of spelling
|
|
335
|
+
* that loses nothing, not for something recompiling would drop.
|
|
336
|
+
*/
|
|
337
|
+
warn(message: string): void;
|
|
338
|
+
/**
|
|
339
|
+
* The identifier of this step's module-level function: unique in the printed
|
|
340
|
+
* module, the same on every call for the step. Reserving it declares nothing;
|
|
341
|
+
* {@link declareFunction} does.
|
|
342
|
+
*/
|
|
343
|
+
functionName(): string;
|
|
344
|
+
/** Add a declaration to the end of the printed module, after the flow, verbatim. */
|
|
345
|
+
declareFunction(source: string): void;
|
|
346
|
+
/**
|
|
347
|
+
* How scripts print: `function` (the default) as a TypeScript function where
|
|
348
|
+
* the body allows it, `string` always as `script({ code })`.
|
|
349
|
+
*/
|
|
350
|
+
readonly scripts: 'function' | 'string';
|
|
330
351
|
}
|
|
331
352
|
/** What a node-local `check` may ask of the checker. */
|
|
332
353
|
export interface FlowCheckContext {
|
package/dist/flow-sdk.js
CHANGED
|
@@ -48,6 +48,7 @@ import { checkFlowExpressions, FlowBuildError } from './flow-expr-check.js';
|
|
|
48
48
|
import { FLOW_SCHEMA_VERSIONS } from './format-profile.js';
|
|
49
49
|
import { schemaVersionRefusal } from './schema-version.js';
|
|
50
50
|
import { indexCarriedDefinitions } from './core/carried-definitions.js';
|
|
51
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
51
52
|
export { FlowBuildError, checkFlowExpressions } from './flow-expr-check.js';
|
|
52
53
|
const TYPE_NAMES = Object.values(types);
|
|
53
54
|
/**
|
|
@@ -69,7 +70,7 @@ const TYPE_NAMES = Object.values(types);
|
|
|
69
70
|
*/
|
|
70
71
|
function assertShape(method, shape) {
|
|
71
72
|
if (typeof shape !== 'object' || shape === null || Array.isArray(shape)) {
|
|
72
|
-
throw new
|
|
73
|
+
throw new CompileRefusal('DECLARATION_SHAPE', `.${method}() takes ONE object mapping names to types — got ${Array.isArray(shape) ? 'an array' : typeof shape}`
|
|
73
74
|
+ `${typeof shape === 'string' ? ` (${JSON.stringify(shape)})` : ''}. `
|
|
74
75
|
+ `Write .${method}({ ${typeof shape === 'string' ? shape : 'name'}: types.string }), not `
|
|
75
76
|
+ `.${method}('name', 'string'): a string would be iterated character by character and `
|
|
@@ -89,12 +90,12 @@ function varDeclOf(method, name, value) {
|
|
|
89
90
|
assertType(method, name, spec.type);
|
|
90
91
|
if (spec.shared !== undefined) {
|
|
91
92
|
if (method !== 'input') {
|
|
92
|
-
throw new
|
|
93
|
+
throw new CompileRefusal('SHARED_INPUT_NOT_INPUT', `.${method}() declared ${JSON.stringify(name)} with { shared }, which only an input takes: it names `
|
|
93
94
|
+ `the flow variable a root's input is copied into. Declare the variable with .var() and feed it from `
|
|
94
95
|
+ `.input({ <name>: { type, shared: ${JSON.stringify(spec.shared)} } }).`);
|
|
95
96
|
}
|
|
96
97
|
if (typeof spec.shared !== 'string' || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.shared)) {
|
|
97
|
-
throw new
|
|
98
|
+
throw new CompileRefusal('SHARED_INPUT_NAME_INVALID', `Input ${JSON.stringify(name)}: { shared } must be a variable name (letters, digits, _), got `
|
|
98
99
|
+ `${JSON.stringify(spec.shared)}.`);
|
|
99
100
|
}
|
|
100
101
|
}
|
|
@@ -114,7 +115,7 @@ function varDeclOf(method, name, value) {
|
|
|
114
115
|
function assertType(method, name, type) {
|
|
115
116
|
if (typeof type === 'string' && TYPE_NAMES.includes(type))
|
|
116
117
|
return;
|
|
117
|
-
throw new
|
|
118
|
+
throw new CompileRefusal('DECLARATION_TYPE_UNKNOWN', `.${method}() declared ${JSON.stringify(name)} with type ${JSON.stringify(type)}, which is not `
|
|
118
119
|
+ `a type. Use one of ${TYPE_NAMES.map((t) => `types.${t}`).join(', ')}.`);
|
|
119
120
|
}
|
|
120
121
|
// ─── Triggers (what starts the flow) ────────────────────────────────────────
|
|
@@ -374,7 +375,7 @@ class StepList {
|
|
|
374
375
|
*/
|
|
375
376
|
err(field) {
|
|
376
377
|
if (!this.errorSource) {
|
|
377
|
-
throw new
|
|
378
|
+
throw new CompileRefusal('ERROR_READ_OUTSIDE_HANDLER', 'err(field) with no step name is only available inside an error handler — the sub-builder '
|
|
378
379
|
+ '.onError((h) => …) / .stepToList(\'error\', (h) => …) passes. Elsewhere, name the step: '
|
|
379
380
|
+ "err('<step>', 'message').");
|
|
380
381
|
}
|
|
@@ -438,7 +439,7 @@ class StepList {
|
|
|
438
439
|
*/
|
|
439
440
|
stepSwitch(name, spec, cases, options) {
|
|
440
441
|
if (!Array.isArray(cases) || cases.length === 0) {
|
|
441
|
-
throw new
|
|
442
|
+
throw new CompileRefusal('STEP_SWITCH_NO_ARMS', `.stepSwitch('${name}'): needs at least one arm. With nothing to route, use .step().`);
|
|
442
443
|
}
|
|
443
444
|
const arms = cases.map((c) => {
|
|
444
445
|
const arm = new ArmBuilder();
|
|
@@ -448,7 +449,7 @@ class StepList {
|
|
|
448
449
|
// is already named by the outcome or branch it leaves. Refusing beats
|
|
449
450
|
// accepting it and dropping it on the floor.
|
|
450
451
|
if (arm.armLabel !== undefined) {
|
|
451
|
-
throw new
|
|
452
|
+
throw new CompileRefusal('STEP_SWITCH_ARM_LABEL', `.stepSwitch('${name}') arm '${c.value}': .label() has nowhere to go — a port edge carries no `
|
|
452
453
|
+ `label, and the exit is named by "${c.value}" already. Use options.label to rename the NODE.`);
|
|
453
454
|
}
|
|
454
455
|
return { value: String(c.value), body: arm.steps };
|
|
@@ -551,14 +552,14 @@ class StepList {
|
|
|
551
552
|
// version the container emits (#1010).
|
|
552
553
|
const containerPort = port === 'error' && (source?.kind === 'loop' || source?.kind === 'doWhile');
|
|
553
554
|
if (!source || (source.kind !== 'action' && !containerPort)) {
|
|
554
|
-
throw new
|
|
555
|
+
throw new CompileRefusal('PORT_NO_SOURCE_STEP', `${called} leaves a port of the step BEFORE it, and ${source ? `the previous step is a ${source.kind}` : 'there is no previous step'}. `
|
|
555
556
|
+ `Write .step('fetch', http({…}))${called}`
|
|
556
557
|
+ ' — a branch, parallel, terminate or return has no port to leave from; a .loop() or .doWhile() has one '
|
|
557
558
|
+ "only for 'error'.");
|
|
558
559
|
}
|
|
559
560
|
const taken = attached.some((s) => (s.kind === 'stepToList' || s.kind === 'stepToRef') && s.port === port);
|
|
560
561
|
if (taken) {
|
|
561
|
-
throw new
|
|
562
|
+
throw new CompileRefusal('PORT_ALREADY_ROUTED', `${called} is already set on step "${source.name}"'s "${port}" port. A port takes exactly ONE outgoing edge `
|
|
562
563
|
+ `(the definition says so: maxConnections: 1), so put everything that path needs in one place.`);
|
|
563
564
|
}
|
|
564
565
|
return source;
|
|
@@ -611,7 +612,7 @@ class StepList {
|
|
|
611
612
|
h.errorSource = prev.name;
|
|
612
613
|
bodyFn?.(h);
|
|
613
614
|
if (h.steps.length === 0) {
|
|
614
|
-
throw new
|
|
615
|
+
throw new CompileRefusal('PORT_EMPTY_PATH', `${called} on step "${prev.name}" has an empty ${noun}. An edge to nothing is worse than none — `
|
|
615
616
|
+ `the path still leaves the main flow. Return a value, terminate the run, or do the compensating work.`);
|
|
616
617
|
}
|
|
617
618
|
this.steps.push({ kind: 'stepToList', port, body: h.steps });
|
|
@@ -637,7 +638,7 @@ class StepList {
|
|
|
637
638
|
*/
|
|
638
639
|
branch(name, cond, thenFn, elseFn, options) {
|
|
639
640
|
if (options?.updates) {
|
|
640
|
-
throw new
|
|
641
|
+
throw new CompileRefusal('UPDATES_NOT_SUPPORTED', '.branch(): { updates } is not supported here — the platform strips variable updates from '
|
|
641
642
|
+ 'decision/switch/merge nodes on save. Attach them to a step before or after instead.');
|
|
642
643
|
}
|
|
643
644
|
const t = new ArmBuilder();
|
|
@@ -707,7 +708,7 @@ class StepList {
|
|
|
707
708
|
*/
|
|
708
709
|
switch(name, on, cases, defaultFn, options) {
|
|
709
710
|
if (options?.updates) {
|
|
710
|
-
throw new
|
|
711
|
+
throw new CompileRefusal('UPDATES_NOT_SUPPORTED', '.switch(): { updates } is not supported here — the platform strips variable updates from '
|
|
711
712
|
+ 'decision/switch/merge nodes on save. Attach them to a step before or after instead.');
|
|
712
713
|
}
|
|
713
714
|
const arms = (cases ?? []).map((c) => {
|
|
@@ -789,7 +790,7 @@ class StepList {
|
|
|
789
790
|
*/
|
|
790
791
|
parallel(name, arms, options) {
|
|
791
792
|
if (options?.updates) {
|
|
792
|
-
throw new
|
|
793
|
+
throw new CompileRefusal('UPDATES_NOT_SUPPORTED', '.parallel(): { updates } is not supported here — the platform strips variable updates from '
|
|
793
794
|
+ 'decision/switch/merge nodes on save. Attach them to a step before or after instead.');
|
|
794
795
|
}
|
|
795
796
|
const built = (arms ?? []).map((armFn) => {
|
|
@@ -924,7 +925,7 @@ class StepList {
|
|
|
924
925
|
const port = maybeTarget === undefined ? 'output' : portOrTarget;
|
|
925
926
|
const target = maybeTarget === undefined ? portOrTarget : maybeTarget;
|
|
926
927
|
if (typeof target !== 'string' || target.length === 0) {
|
|
927
|
-
throw new
|
|
928
|
+
throw new CompileRefusal('REF_TARGET_NOT_NAME', `.stepToRef() takes the NAME of the step to continue at — .stepToRef('priceIt') — and got ${JSON.stringify(target)}. `
|
|
928
929
|
+ `It is the same name you passed to .step() (or a .return() End's \`name\`), so the edge can be wired after the whole flow is built.`);
|
|
929
930
|
}
|
|
930
931
|
// A non-default port leaves the PRECEDING step, so it is guarded the same way
|
|
@@ -1103,7 +1104,7 @@ class FlowBuilder extends StepList {
|
|
|
1103
1104
|
*/
|
|
1104
1105
|
trigger(spec, options) {
|
|
1105
1106
|
if (options?.updates) {
|
|
1106
|
-
throw new
|
|
1107
|
+
throw new CompileRefusal('UPDATES_NOT_SUPPORTED', '.trigger(): { updates } is not supported — a trigger has not run anything to assign from.');
|
|
1107
1108
|
}
|
|
1108
1109
|
this._trigger = spec;
|
|
1109
1110
|
if (options)
|
|
@@ -1263,7 +1264,7 @@ class FlowBuilder extends StepList {
|
|
|
1263
1264
|
schemaVersion(version) {
|
|
1264
1265
|
const refusal = schemaVersionRefusal(FLOW_SCHEMA_VERSIONS, version);
|
|
1265
1266
|
if (refusal)
|
|
1266
|
-
throw new
|
|
1267
|
+
throw new CompileRefusal('SCHEMA_VERSION_UNSUPPORTED', refusal);
|
|
1267
1268
|
this._schemaVersion = version;
|
|
1268
1269
|
return this;
|
|
1269
1270
|
}
|
package/dist/flow-step-tree.js
CHANGED
|
@@ -44,7 +44,7 @@ export function childStepLists(step) {
|
|
|
44
44
|
return [];
|
|
45
45
|
default: {
|
|
46
46
|
const unhandled = step;
|
|
47
|
-
throw new Error(`childStepLists: unhandled step kind ${JSON.stringify(unhandled.kind)}`);
|
|
47
|
+
throw new Error(`internal SDK error: childStepLists: unhandled step kind ${JSON.stringify(unhandled.kind)}`);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
package/dist/flow-variables.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { globalNameCollisionMessage, globalNameCollisions } from './core/global-names.js';
|
|
2
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
2
3
|
/** The keys of `variables` that hold declarations rather than name one (the map spelling). */
|
|
3
4
|
export const CONTAINER_KEYS = new Set(['globals', 'nodes', 'variableUpdates']);
|
|
4
5
|
const DIRECTIONS = new Set(['in', 'out', 'inout']);
|
|
@@ -8,7 +9,7 @@ export function readFlowGlobals(variables, strict = false) {
|
|
|
8
9
|
return [];
|
|
9
10
|
if (typeof variables !== 'object' || Array.isArray(variables)) {
|
|
10
11
|
if (strict)
|
|
11
|
-
throw new
|
|
12
|
+
throw new CompileRefusal('DECOMPILE_VARIABLES_SHAPE', 'decompile: variables must be an object.');
|
|
12
13
|
return [];
|
|
13
14
|
}
|
|
14
15
|
const source = variables;
|
|
@@ -37,7 +38,7 @@ export function readFlowGlobals(variables, strict = false) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
if (strict && unknown.length > 0) {
|
|
40
|
-
throw new
|
|
41
|
+
throw new CompileRefusal('DECOMPILE_VARIABLES_SHAPE', `decompile: unrecognized variables key${unknown.length === 1 ? '' : 's'}: `
|
|
41
42
|
+ `${unknown.map((key) => JSON.stringify(key)).join(', ')}.`);
|
|
42
43
|
}
|
|
43
44
|
return globals;
|
package/dist/load.js
CHANGED
|
@@ -3,6 +3,9 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
3
3
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
4
|
import { statSync } from 'node:fs';
|
|
5
5
|
import { useThisSdkForAuthoredSource } from './sdk-resolve.js';
|
|
6
|
+
import { scriptFunctionLowerings } from './core/script-function.js';
|
|
7
|
+
import { scriptFunctionScopeProblem } from './script-function-scope.js';
|
|
8
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
6
9
|
/** Resolve a user argument (a `.ts` path or a BaseName) to a flow source file. */
|
|
7
10
|
export function resolveFlowFile(arg) {
|
|
8
11
|
const candidates = [arg, `${arg}.flow.ts`, `${arg}.ts`];
|
|
@@ -17,10 +20,11 @@ export function resolveFlowFile(arg) {
|
|
|
17
20
|
}
|
|
18
21
|
return resolvedCandidate;
|
|
19
22
|
}
|
|
20
|
-
throw new
|
|
23
|
+
throw new CompileRefusal('FLOW_SOURCE_NOT_FOUND', `flow source not found: tried ${candidates.join(', ')}`);
|
|
21
24
|
}
|
|
22
25
|
export async function loadBuiltFlow(file) {
|
|
23
26
|
useThisSdkForAuthoredSource();
|
|
27
|
+
const firstLowering = scriptFunctionLowerings().length;
|
|
24
28
|
let mod;
|
|
25
29
|
try {
|
|
26
30
|
mod = await import(pathToFileURL(file).href);
|
|
@@ -30,10 +34,20 @@ export async function loadBuiltFlow(file) {
|
|
|
30
34
|
}
|
|
31
35
|
const exp = mod.default ?? mod.flow ?? mod.workflow;
|
|
32
36
|
if (!exp)
|
|
33
|
-
throw new
|
|
37
|
+
throw new CompileRefusal('FLOW_NO_DEFAULT_EXPORT', `${file} has no default export. Add: export default flow('id')...build();`);
|
|
34
38
|
const built = typeof exp.build === 'function' ? exp.build() : exp;
|
|
35
39
|
if (!built || !Array.isArray(built.steps))
|
|
36
|
-
throw new
|
|
40
|
+
throw new CompileRefusal('FLOW_EXPORT_NOT_BUILT', `${file} default export is not a built flow. Did you call .build()?`);
|
|
41
|
+
// A `script(fn)` function was lowered from its own text, which cannot show a
|
|
42
|
+
// name it takes from its module; this source can (script-function-scope.ts).
|
|
43
|
+
// Only a flow that lowered one pays for the program this builds.
|
|
44
|
+
// The functions this import lowered; see scriptFunctionLowerings.
|
|
45
|
+
const lowered = scriptFunctionLowerings().slice(firstLowering);
|
|
46
|
+
if (lowered.length > 0) {
|
|
47
|
+
const problem = scriptFunctionScopeProblem(file, lowered);
|
|
48
|
+
if (problem)
|
|
49
|
+
throw new CompileRefusal('SCRIPT_FUNCTION_SCOPE', problem);
|
|
50
|
+
}
|
|
37
51
|
return built;
|
|
38
52
|
}
|
|
39
53
|
/**
|
|
@@ -83,7 +97,7 @@ export function connectorImportDiagnostic(error) {
|
|
|
83
97
|
return undefined;
|
|
84
98
|
}
|
|
85
99
|
if (parent === 'connectors') {
|
|
86
|
-
return new
|
|
100
|
+
return new CompileRefusal('CONNECTOR_IMPORT_NOT_A_FILE', `connector import "${unresolved}" does not name a file. The generated ` +
|
|
87
101
|
`descriptors are TypeScript, so the specifier keeps its real extension; ` +
|
|
88
102
|
`import "${moduleFile}"`);
|
|
89
103
|
}
|
|
@@ -91,7 +105,7 @@ export function connectorImportDiagnostic(error) {
|
|
|
91
105
|
if (!statSync(descriptorData, { throwIfNoEntry: false })?.isDirectory()) {
|
|
92
106
|
return undefined;
|
|
93
107
|
}
|
|
94
|
-
return new
|
|
108
|
+
return new CompileRefusal('CONNECTOR_IMPORT_NOT_A_FILE', `connector import "${unresolved}" does not name a file; module candidate ` +
|
|
95
109
|
`"${moduleFile}" and descriptor data "${descriptorData}" are separate; ` +
|
|
96
110
|
`import "${moduleFile}"`);
|
|
97
111
|
}
|
|
@@ -10,6 +10,7 @@ import { processResourceBindings } from '../../../core/process-resource.js';
|
|
|
10
10
|
import { fnv1aX4Uuid, schemaId } from '../../../core/stable-id.js';
|
|
11
11
|
import { synthesizedDefinitionRefusal } from '../../../core/synthesized-definition.js';
|
|
12
12
|
import { BUILTIN_TOOL_DEFS, INLINE_AGENT, PROCESS_TOOL_META, a2aToolDef, builtinToolResourceJson, connectorToolDef, connectorToolDescription, connectorToolDetail, connectorToolResourceJson, contextIndexDef, contextResourceJson, declaresMemoryHandle, escalationAppFolder, escalationDef, escalationResourceJson, fieldSchema, httpRequestToolResourceJson, inlineAgentDef, ixpToolDef, mcpToolDef, memoryResourceDef, processToolDef, renderContextQuery, toolResourceJson, } from './resources.js';
|
|
13
|
+
import { CompileRefusal } from '../../../core/compile-refusal.js';
|
|
13
14
|
/** Every bundled version of the inline agent, oldest first; the newest is the default (#956). */
|
|
14
15
|
export const INLINE_AGENT_VERSIONS = ['1.2', '1.3', '1.4'];
|
|
15
16
|
export const INLINE_AGENT_DEFAULT_VERSION = INLINE_AGENT_VERSIONS[INLINE_AGENT_VERSIONS.length - 1];
|
|
@@ -39,7 +40,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
39
40
|
if (!def)
|
|
40
41
|
throw new Error(`internal SDK error: step "${step}": no definition resolves for ${INLINE_AGENT}@${version}.`);
|
|
41
42
|
if (s.memory !== undefined && !declaresMemoryHandle(def)) {
|
|
42
|
-
throw new
|
|
43
|
+
throw new CompileRefusal('AGENT_MEMORY_VERSION_NO_HANDLE', `"${step}" attaches \`memory\`, but ${INLINE_AGENT}@${version} declares no \`memory\` handle. `
|
|
43
44
|
+ (version === INLINE_AGENT_DEFAULT_VERSION
|
|
44
45
|
? `Pin a version that has it with inlineAgent({ ..., version: '<x.y>' }) `
|
|
45
46
|
+ `(bundled with memory: ${INLINE_AGENT_VERSIONS.filter((v) => declaresMemoryHandle(ctx.definition(INLINE_AGENT, v))).join(', ') || 'none'}), or drop \`memory\`.`
|
|
@@ -134,8 +135,8 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
134
135
|
// the type `…context.index.<slug>.`, which no tenant serves, and nothing
|
|
135
136
|
// after this point would say so.
|
|
136
137
|
if (typeof cx?.id !== 'string' || !GUID_RE.test(cx.id)) {
|
|
137
|
-
throw
|
|
138
|
-
+ `got ${JSON.stringify(cx?.id)}. \`uip solution resources list --kind Index --source remote --search "<name>"\` returns it as \`Key\`.`)
|
|
138
|
+
throw new CompileRefusal('AGENT_CONTEXT_NO_ID', `inline agent "${step}": context index "${cx?.name ?? ''}" needs its index id (a GUID) as \`id\`; `
|
|
139
|
+
+ `got ${JSON.stringify(cx?.id)}. \`uip solution resources list --kind Index --source remote --search "<name>"\` returns it as \`Key\`.`);
|
|
139
140
|
}
|
|
140
141
|
const cdef = contextIndexDef({ name: cx.name, id: cx.id });
|
|
141
142
|
// `<step>Context`, deduped for a second index on the same agent, unless the
|
|
@@ -207,7 +208,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
207
208
|
const bdef = ownEntry(BUILTIN_TOOL_DEFS, tr.tool);
|
|
208
209
|
tdef = bdef === undefined ? undefined : jsonClone(bdef);
|
|
209
210
|
if (!tdef)
|
|
210
|
-
throw new
|
|
211
|
+
throw new CompileRefusal('INLINE_AGENT_TOOL_UNKNOWN_BUILTIN', `inline agent "${step}": unknown builtin tool "${tr.tool}" (analyzefiles | summarize | batchtransform).`);
|
|
211
212
|
label = tdef.display.label;
|
|
212
213
|
// The designer-placed node carries the def's `inputDefaults` with the
|
|
213
214
|
// author's values over them — the context rule, kept per builtin (each def
|
|
@@ -242,7 +243,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
242
243
|
}
|
|
243
244
|
else if (tr.kind === 'connector') {
|
|
244
245
|
if (!ctx.library)
|
|
245
|
-
throw new
|
|
246
|
+
throw new CompileRefusal('LIBRARY_REQUIRED', `inline agent "${step}"'s connector tool needs a library (pass --library / set $FLOW_SDK_LIBRARY_JSON).`);
|
|
246
247
|
const rc = ctx.library.resolve(tr.connector, tr.operation, tr.version, tr.object);
|
|
247
248
|
// An entry `prepare` synthesized from Integration Service compiles in BPMN
|
|
248
249
|
// only — for a tool as for a step: `connectorToolDef` reads the same
|
|
@@ -337,7 +338,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
337
338
|
else {
|
|
338
339
|
const meta = ownEntry(PROCESS_TOOL_META, tr.kind);
|
|
339
340
|
if (!meta)
|
|
340
|
-
throw new
|
|
341
|
+
throw new CompileRefusal('INLINE_AGENT_TOOL_UNKNOWN_KIND', `inline agent "${step}": unknown tool kind "${tr.kind}".`);
|
|
341
342
|
tdef = processToolDef(tr.kind, tr);
|
|
342
343
|
label = tr.name;
|
|
343
344
|
// The definition's `model.context` names `<bindings.name>` /
|
|
@@ -9,6 +9,7 @@ import { jsonClone } from '../../../core/json-clone.js';
|
|
|
9
9
|
import { subNodeId } from '../../../core/node-id-placeholder.js';
|
|
10
10
|
import { synthesizedDefinitionRefusal } from '../../../core/synthesized-definition.js';
|
|
11
11
|
import { tsLiteral, tsObject } from '../../../core/ts-literal.js';
|
|
12
|
+
import { CompileRefusal } from '../../../core/compile-refusal.js';
|
|
12
13
|
const CONNECTOR_PREFIX = 'uipath.connector.';
|
|
13
14
|
/** `<key>.<action>` after the prefix, split on the last dot; undefined for an event, a trigger, or a name with no action. */
|
|
14
15
|
function splitOperation(typeName) {
|
|
@@ -65,7 +66,7 @@ function listOutputs(rc, nodeId) {
|
|
|
65
66
|
function serializeConnector(spec, ctx) {
|
|
66
67
|
const step = ctx.step;
|
|
67
68
|
if (!ctx.library)
|
|
68
|
-
throw new
|
|
69
|
+
throw new CompileRefusal('LIBRARY_REQUIRED', `connector "${step}" needs a library (pass --library / set $FLOW_SDK_LIBRARY_JSON).`);
|
|
69
70
|
let rc;
|
|
70
71
|
try {
|
|
71
72
|
rc = ctx.library.resolve(spec.key, spec.action, spec.version, spec.object);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* them, so authors import them exactly as before.
|
|
6
6
|
*/
|
|
7
7
|
import { builtinActionSpec } from '../../../core/actions.js';
|
|
8
|
+
import { CompileRefusal } from '../../../core/compile-refusal.js';
|
|
8
9
|
/**
|
|
9
10
|
* Query entity records from Data Fabric (`core.datafabric.read`). The result
|
|
10
11
|
* publishes to the step's `output`; downstream references re-fetch fresh data
|
|
@@ -129,6 +130,6 @@ function assertOneRecordRef(record, factory) {
|
|
|
129
130
|
const byId = record?.byId !== undefined;
|
|
130
131
|
const fromRead = typeof record?.fromRead === 'string';
|
|
131
132
|
if (byId === fromRead) {
|
|
132
|
-
throw new
|
|
133
|
+
throw new CompileRefusal('DATAFABRIC_RECORD_REF', `${factory}() targets exactly ONE of record: { byId } or record: { fromRead: '<step>' }.`);
|
|
133
134
|
}
|
|
134
135
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { applyScope, baseConfig, entityConfigOf, fieldRows, filterRows, liftFields, liftRecord, liftScope, liftValue, printFields, printRecord, scopeOptions, targetRecord, } from './shared.js';
|
|
2
2
|
import { checkCreate, checkRead, checkUpdate, checkWrite } from './check.js';
|
|
3
3
|
import { rawNodeAdvice } from './raw.js';
|
|
4
|
+
import { CompileRefusal } from '../../../core/compile-refusal.js';
|
|
4
5
|
/**
|
|
5
6
|
* Every Data Fabric node carries no node-level `model`, whichever path emits it
|
|
6
7
|
* (a verb, or a rawNode of its definition). Studio Web never writes one
|
|
@@ -36,7 +37,7 @@ function readAt(version) {
|
|
|
36
37
|
nodeModel: NODE_MODEL, rawNode: READ_ADVICE,
|
|
37
38
|
serialize: ({ inputs }, ctx) => {
|
|
38
39
|
if (version === '1.0' && inputs.resultMode !== undefined) {
|
|
39
|
-
throw new
|
|
40
|
+
throw new CompileRefusal('DATAFABRIC_RESULT_MODE_VERSION', "dataFabricRead(): { resultMode } needs the node's 1.4 definition, but { version: '1.0' } was "
|
|
40
41
|
+ 'requested. Drop the version, request 1.4, or drop resultMode.');
|
|
41
42
|
}
|
|
42
43
|
const cfg = baseConfig(inputs);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* docs/NODE_TYPE_ARCHITECTURE_DESIGN.md; the package entry re-exports it.
|
|
4
4
|
*/
|
|
5
5
|
import { builtinActionSpec } from '../../../core/actions.js';
|
|
6
|
+
import { CompileRefusal } from '../../../core/compile-refusal.js';
|
|
6
7
|
/**
|
|
7
8
|
* Declare a delay — a step that pauses the flow for a fixed length of time and
|
|
8
9
|
* then carries on.
|
|
@@ -31,7 +32,7 @@ export function delay(inputs) {
|
|
|
31
32
|
const hasDuration = 'duration' in inputs;
|
|
32
33
|
const hasUntil = 'until' in inputs;
|
|
33
34
|
if (hasDuration === hasUntil) {
|
|
34
|
-
throw new
|
|
35
|
+
throw new CompileRefusal('DELAY_DURATION_XOR_UNTIL', "delay() takes exactly ONE of { duration } (an ISO-8601 duration, e.g. 'PT15M') or "
|
|
35
36
|
+ "{ until } (an ISO-8601 date-time, e.g. '2026-09-01T09:00:00Z').");
|
|
36
37
|
}
|
|
37
38
|
return builtinActionSpec({ kind: 'delay', inputs });
|
|
@@ -14,6 +14,7 @@ import { definitionHasErrorPort } from '../../../core/error-port.js';
|
|
|
14
14
|
import { Expr } from '../../../core/expr.js';
|
|
15
15
|
import { jsonClone } from '../../../core/json-clone.js';
|
|
16
16
|
import { parseIxpProjectType } from './spec.js';
|
|
17
|
+
import { CompileRefusal } from '../../../core/compile-refusal.js';
|
|
17
18
|
const IXP_PREFIX = 'uipath.ixp.';
|
|
18
19
|
export const IXP_DIAGNOSTICS = {
|
|
19
20
|
IXP_FORBIDDEN_INPUT: { severity: 'error', summary: 'an IxP step setting an input the current node does not declare' },
|
|
@@ -65,7 +66,10 @@ const IXP_DEF = ixpDefinition;
|
|
|
65
66
|
export function ixpExtractDef(spec) {
|
|
66
67
|
const parts = parseIxpProjectType(spec.project);
|
|
67
68
|
if (!parts) {
|
|
68
|
-
|
|
69
|
+
// The three conditions check tells apart, under check's three codes.
|
|
70
|
+
const code = typeof spec.project !== 'string' || spec.project.trim() === '' ? 'IXP_NO_PROJECT'
|
|
71
|
+
: !spec.project.startsWith('uipath.ixp.') ? 'IXP_PROJECT_NOT_NODE_TYPE' : 'IXP_PROJECT_TAIL_UNPARSEABLE';
|
|
72
|
+
throw new CompileRefusal(code, `ixpExtract: "${spec.project}" is not a resolvable extractor node type. It must be `
|
|
69
73
|
+ `uipath.ixp.<slug>.<projectVersionId>-<folderKey> — copy it from `
|
|
70
74
|
+ `\`uip maestro flow registry pull --force && uip maestro flow registry search -f "type:startsWith=uipath.ixp"\`.`);
|
|
71
75
|
}
|
|
@@ -7,7 +7,8 @@ export const HITL_DIAGNOSTICS = {
|
|
|
7
7
|
HITL_VARIANT_UNKNOWN: { severity: 'error', summary: 'a variant that names no human-task node type' },
|
|
8
8
|
HITL_OUTCOME_PORTS_VARIANT: { severity: 'error', summary: 'exposeError or outcomePorts on a variant that cannot honour it' },
|
|
9
9
|
HITL_LEGACY_COMPLETED: { severity: 'warning', summary: "a task with outcomes that leaves on the legacy 'completed' handle" },
|
|
10
|
-
HITL_LEGACY_COMPLETED_CONFLICT: { severity: 'error', summary: 'legacyCompleted with outcomePorts or exposeError
|
|
10
|
+
HITL_LEGACY_COMPLETED_CONFLICT: { severity: 'error', summary: 'legacyCompleted with outcomePorts or exposeError' },
|
|
11
|
+
HITL_LEGACY_COMPLETED_NO_EFFECT: { severity: 'error', summary: "legacyCompleted where 'completed' is already the only exit" },
|
|
11
12
|
HITL_DOCUMENT_WITHOUT_VARIANT: { severity: 'error', summary: 'a document on a task that is not a validation station' },
|
|
12
13
|
HITL_DOCVAL_WITH_FIELDS: { severity: 'error', summary: 'form fields on a validation station' },
|
|
13
14
|
HITL_DOCVAL_NO_DOCUMENT: { severity: 'error', summary: 'a validation station with no document' },
|
|
@@ -103,7 +104,8 @@ export function checkHitl(inputs, ctx) {
|
|
|
103
104
|
return;
|
|
104
105
|
}
|
|
105
106
|
if (!hitlDerivesOutcomeHandles(variant) || outcomes.length === 0) {
|
|
106
|
-
|
|
107
|
+
// Its own code: this flow compiles, so the code is check's alone.
|
|
108
|
+
at('HITL_LEGACY_COMPLETED_NO_EFFECT', `Human task "${step}" sets legacyCompleted: true, but ${outcomes.length === 0 ? 'it has no outcomes'
|
|
107
109
|
: `its variant is "${variant}", which reads its outcomes from its app`}, so 'completed' is already its only exit.`, 'Drop `legacyCompleted`.');
|
|
108
110
|
return;
|
|
109
111
|
}
|