@uipath/maestro-builder-sdk 6.62.0 → 6.64.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 +82 -81
- package/dist/check-cli.js +79 -8
- package/dist/check.d.ts +80 -0
- package/dist/check.js +123 -5
- package/dist/cli/commands/flow.js +12 -3
- package/dist/cli-result.d.ts +21 -16
- package/dist/cli-result.js +21 -6
- package/dist/compile-cli.js +55 -45
- package/dist/core/refusals.d.ts +1 -0
- package/dist/core/refusals.js +3 -0
- package/dist/decompile-cli.js +2 -2
- package/dist/decompile.js +36 -11
- package/dist/flow-node-type.d.ts +9 -0
- package/dist/flow-sdk.d.ts +24 -3
- package/dist/flow-sdk.js +1 -1
- package/dist/merge-compile.d.ts +9 -0
- package/dist/merge-compile.js +14 -2
- package/dist/nodes/agent/flow/check.js +4 -4
- package/dist/nodes/connector/flow/node-type.js +36 -1
- package/dist/serialize.d.ts +24 -0
- package/dist/serialize.js +114 -4
- package/package.json +2 -2
package/dist/check.d.ts
CHANGED
|
@@ -3,12 +3,34 @@ import type { BuiltFlow } from './flow-sdk.js';
|
|
|
3
3
|
import { type EventCheckOpts } from './core/event-checks.js';
|
|
4
4
|
export type { EventParamFields } from './core/event-checks.js';
|
|
5
5
|
import { type ConnectorCheckOpts } from './core/connector-checks.js';
|
|
6
|
+
import { type RefusalClass } from './core/refusals.js';
|
|
6
7
|
export interface Diagnostic {
|
|
7
8
|
level: 'error' | 'warning';
|
|
8
9
|
code: string;
|
|
9
10
|
message: string;
|
|
10
11
|
step?: string;
|
|
11
12
|
suggestion?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The id of the node the finding is on, in the flow `compile` would write:
|
|
15
|
+
* the step's node, or for a step inside a subflow the node inside it.
|
|
16
|
+
* Absent for a finding that names no node (a flow-level rule, a refusal
|
|
17
|
+
* about the whole scope such as two steps with one name).
|
|
18
|
+
*/
|
|
19
|
+
nodeId?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The code's class (`core/refusals.ts`): `lint`, the flow is probably wrong
|
|
22
|
+
* but `compile` would still write it; `lowering`, `compile` cannot write a
|
|
23
|
+
* correct `.flow`; `draft`, an unfinished canvas node. Set on every finding
|
|
24
|
+
* whose code the table lists.
|
|
25
|
+
*/
|
|
26
|
+
class?: RefusalClass;
|
|
27
|
+
/**
|
|
28
|
+
* Only when `check` is given a baseline (`flow check --baseline`, {@link scopeToEdit}):
|
|
29
|
+
* `pre-existing` when the finding's node is unchanged from the baseline and
|
|
30
|
+
* the baseline reports the same code on it, so the edit did not cause it
|
|
31
|
+
* (such a finding is a warning); `edited` for every other finding.
|
|
32
|
+
*/
|
|
33
|
+
origin?: 'edited' | 'pre-existing';
|
|
12
34
|
}
|
|
13
35
|
export interface ListEnvelopeFields {
|
|
14
36
|
envelope: string | null;
|
|
@@ -47,6 +69,64 @@ export interface CheckOpts extends ConnectorCheckOpts, EventCheckOpts {
|
|
|
47
69
|
serializer?: false;
|
|
48
70
|
}
|
|
49
71
|
export declare function check(built: BuiltFlow, opts?: CheckOpts): Diagnostic[];
|
|
72
|
+
/**
|
|
73
|
+
* What {@link checkCompiled} found, with the flow its serializer pass compiled,
|
|
74
|
+
* which {@link scopeToEdit} compares against a baseline.
|
|
75
|
+
*/
|
|
76
|
+
export interface CheckedFlow {
|
|
77
|
+
diagnostics: Diagnostic[];
|
|
78
|
+
/**
|
|
79
|
+
* The collect-mode compile, stand-ins and all (`SerializeOpts.collect`);
|
|
80
|
+
* nothing writes it. `undefined` when collection stopped or the serializer
|
|
81
|
+
* pass was skipped.
|
|
82
|
+
*/
|
|
83
|
+
flow: any;
|
|
84
|
+
/**
|
|
85
|
+
* The refusal that stopped collection (a scope-level one, such as two Ends
|
|
86
|
+
* with one name), or the internal error that did: no serializer finding past
|
|
87
|
+
* it is reported. Absent when collection ran to the end or was skipped.
|
|
88
|
+
*/
|
|
89
|
+
stoppedAt?: {
|
|
90
|
+
code?: string;
|
|
91
|
+
message: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* `check`, keeping the flow its serializer pass compiled. Each finding is
|
|
96
|
+
* placed on its node (`nodeId`: a refusal on the node collect mode claimed for
|
|
97
|
+
* its step, any other finding on the node its step emitted) and given its
|
|
98
|
+
* code's `class`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function checkCompiled(built: BuiltFlow, opts?: CheckOpts): CheckedFlow;
|
|
101
|
+
/**
|
|
102
|
+
* Scope `edited`'s findings to the edit, against `baseline`: the original
|
|
103
|
+
* `.flow` decompiled and checked the same way (`flow check --baseline`,
|
|
104
|
+
* docs/CHECK_COMPILE_CONTRACT_DESIGN.md §3.3). Every finding gets an `origin`:
|
|
105
|
+
*
|
|
106
|
+
* - `pre-existing` when its node is unchanged from the baseline (by id and
|
|
107
|
+
* content, as merge judges it: `classifyDrafts`; a node inside a subflow
|
|
108
|
+
* by its subflow) AND the baseline reports the same code on that node,
|
|
109
|
+
* once per occurrence. A finding that names no node is `pre-existing` when
|
|
110
|
+
* the baseline reports the same code and message (`splitUnplaced`).
|
|
111
|
+
* - `edited` otherwise, including a finding on an untouched node that the
|
|
112
|
+
* baseline does not have: an edit elsewhere caused it (a deleted variable
|
|
113
|
+
* the node updates, a deleted step it reads).
|
|
114
|
+
*
|
|
115
|
+
* A pre-existing error becomes a warning, so it does not fail the check. A
|
|
116
|
+
* pre-existing `lowering` one also says it is probably an SDK defect, since the
|
|
117
|
+
* designer's node is one the SDK cannot write back: `compile` still refuses it,
|
|
118
|
+
* and `merge --baseline-from` keeps it as saved.
|
|
119
|
+
*
|
|
120
|
+
* When the edited flow's collection stopped at a scope-level refusal, the rest
|
|
121
|
+
* of the flow was never judged, so one more error, CHECK_INCOMPLETE (class
|
|
122
|
+
* `lowering`: `compile` stops at the refusal it names), says so:
|
|
123
|
+
* passing never means "not checked".
|
|
124
|
+
*
|
|
125
|
+
* For instance, an `EMPTY_ARM` on a branch the edit did not touch, which the
|
|
126
|
+
* baseline reports too, comes back as a `pre-existing` warning; the same
|
|
127
|
+
* finding after the edit changed that branch's condition is an `edited` error.
|
|
128
|
+
*/
|
|
129
|
+
export declare function scopeToEdit(edited: CheckedFlow, baseline: CheckedFlow): Diagnostic[];
|
|
50
130
|
/**
|
|
51
131
|
* The ISO-8601 repeating-interval pattern, copied VERBATIM from the
|
|
52
132
|
* `core.trigger.scheduled@1.1` definition (`inputDefinition.then.properties.
|
package/dist/check.js
CHANGED
|
@@ -30,7 +30,18 @@ import { stepDeclaredVariables } from './flow-step-variables.js';
|
|
|
30
30
|
import { plainHttpOnErrorMessage } from './nodes/http/flow/family.js';
|
|
31
31
|
import { resolveVersion, resolveVersionCarried } from './core/nodes/versioned.js';
|
|
32
32
|
import { loopParallelWarning, resolveLoopDefinition, loopVersion } from './loop-version.js';
|
|
33
|
+
import { refusalClass } from './core/refusals.js';
|
|
34
|
+
import { classifyDrafts, findingCounts, placeDiagnostics, splitUnplaced } from './edit-scope.js';
|
|
33
35
|
export function check(built, opts = {}) {
|
|
36
|
+
return checkCompiled(built, opts).diagnostics;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `check`, keeping the flow its serializer pass compiled. Each finding is
|
|
40
|
+
* placed on its node (`nodeId`: a refusal on the node collect mode claimed for
|
|
41
|
+
* its step, any other finding on the node its step emitted) and given its
|
|
42
|
+
* code's `class`.
|
|
43
|
+
*/
|
|
44
|
+
export function checkCompiled(built, opts = {}) {
|
|
34
45
|
// A connection two connectors use, and an agent resource's `nodeId` another
|
|
35
46
|
// node of the file has, are the serializer's CONNECTION_CONNECTOR_CONFLICT /
|
|
36
47
|
// INLINE_AGENT_RESOURCE_NODE_ID: it claims ids and resolves connections file-wide.
|
|
@@ -44,10 +55,109 @@ export function check(built, opts = {}) {
|
|
|
44
55
|
...(d.suggestion ? { suggestion: d.suggestion } : {}),
|
|
45
56
|
});
|
|
46
57
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
const serialized = opts.serializer !== false
|
|
59
|
+
? serializerRefusals(built, { library: opts.library, bindings: opts.bindings }, diags)
|
|
60
|
+
: { diagnostics: [], flow: undefined };
|
|
61
|
+
diags.push(...serialized.diagnostics);
|
|
62
|
+
const placed = serialized.flow !== undefined ? placeDiagnostics(diags, serialized.flow) : undefined;
|
|
63
|
+
const diagnostics = diags.map((d, i) => {
|
|
64
|
+
const nodeId = d.nodeId ?? (placed?.[i].nodeId || undefined);
|
|
65
|
+
const cls = refusalClass(d.code);
|
|
66
|
+
return { ...d, ...(nodeId !== undefined ? { nodeId } : {}), ...(cls !== undefined ? { class: cls } : {}) };
|
|
67
|
+
});
|
|
68
|
+
return { diagnostics, flow: serialized.flow, ...(serialized.stoppedAt ? { stoppedAt: serialized.stoppedAt } : {}) };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Scope `edited`'s findings to the edit, against `baseline`: the original
|
|
72
|
+
* `.flow` decompiled and checked the same way (`flow check --baseline`,
|
|
73
|
+
* docs/CHECK_COMPILE_CONTRACT_DESIGN.md §3.3). Every finding gets an `origin`:
|
|
74
|
+
*
|
|
75
|
+
* - `pre-existing` when its node is unchanged from the baseline (by id and
|
|
76
|
+
* content, as merge judges it: `classifyDrafts`; a node inside a subflow
|
|
77
|
+
* by its subflow) AND the baseline reports the same code on that node,
|
|
78
|
+
* once per occurrence. A finding that names no node is `pre-existing` when
|
|
79
|
+
* the baseline reports the same code and message (`splitUnplaced`).
|
|
80
|
+
* - `edited` otherwise, including a finding on an untouched node that the
|
|
81
|
+
* baseline does not have: an edit elsewhere caused it (a deleted variable
|
|
82
|
+
* the node updates, a deleted step it reads).
|
|
83
|
+
*
|
|
84
|
+
* A pre-existing error becomes a warning, so it does not fail the check. A
|
|
85
|
+
* pre-existing `lowering` one also says it is probably an SDK defect, since the
|
|
86
|
+
* designer's node is one the SDK cannot write back: `compile` still refuses it,
|
|
87
|
+
* and `merge --baseline-from` keeps it as saved.
|
|
88
|
+
*
|
|
89
|
+
* When the edited flow's collection stopped at a scope-level refusal, the rest
|
|
90
|
+
* of the flow was never judged, so one more error, CHECK_INCOMPLETE (class
|
|
91
|
+
* `lowering`: `compile` stops at the refusal it names), says so:
|
|
92
|
+
* passing never means "not checked".
|
|
93
|
+
*
|
|
94
|
+
* For instance, an `EMPTY_ARM` on a branch the edit did not touch, which the
|
|
95
|
+
* baseline reports too, comes back as a `pre-existing` warning; the same
|
|
96
|
+
* finding after the edit changed that branch's condition is an `edited` error.
|
|
97
|
+
*/
|
|
98
|
+
export function scopeToEdit(edited, baseline) {
|
|
99
|
+
const locate = (c) => placeDiagnostics(c.diagnostics, c.flow)
|
|
100
|
+
.map((d, i) => ({ ...d, nodeId: c.diagnostics[i].nodeId ?? d.nodeId, index: i }));
|
|
101
|
+
const sorted = classifyDrafts(baseline.flow ?? {}, edited.flow ?? {}, baseline.flow, locate(edited));
|
|
102
|
+
// The baseline's findings on each node, by code; each one vouches for one edited finding.
|
|
103
|
+
const onNode = (f) => `${f.nodeId}\n${f.code}`;
|
|
104
|
+
const pool = new Map();
|
|
105
|
+
for (const b of locate(baseline)) {
|
|
106
|
+
if (b.nodeId === '')
|
|
107
|
+
continue;
|
|
108
|
+
pool.set(onNode(b), [...(pool.get(onNode(b)) ?? []), b.message]);
|
|
109
|
+
}
|
|
110
|
+
const preExisting = new Set();
|
|
111
|
+
// Exact messages are matched first: of two findings of one code on a node,
|
|
112
|
+
// the one the baseline has word for word is the pre-existing one. The second
|
|
113
|
+
// pass forgives wording the edit can shift without touching the finding (a
|
|
114
|
+
// "Known steps: ..." list), but only between messages that name the same
|
|
115
|
+
// quoted subjects: `UNKNOWN_REF "lookup" in "audit"` is a different finding
|
|
116
|
+
// from the baseline's `UNKNOWN_REF "ghost" in "audit"`, so it stays edited.
|
|
117
|
+
for (const exact of [true, false]) {
|
|
118
|
+
for (const f of sorted.kept) {
|
|
119
|
+
if (preExisting.has(f.index))
|
|
120
|
+
continue;
|
|
121
|
+
const messages = pool.get(onNode(f)) ?? [];
|
|
122
|
+
const j = exact ? messages.indexOf(f.message) : messages.findIndex((m) => quotedSubjects(m) === quotedSubjects(f.message));
|
|
123
|
+
if (j < 0)
|
|
124
|
+
continue;
|
|
125
|
+
messages.splice(j, 1);
|
|
126
|
+
preExisting.add(f.index);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const f of splitUnplaced(sorted.unplaced, findingCounts(baseline.diagnostics)).known)
|
|
130
|
+
preExisting.add(f.index);
|
|
131
|
+
const out = edited.diagnostics.map((d, i) => {
|
|
132
|
+
if (!preExisting.has(i))
|
|
133
|
+
return { ...d, origin: 'edited' };
|
|
134
|
+
const defect = d.level === 'error' && d.class === 'lowering';
|
|
135
|
+
return {
|
|
136
|
+
...d,
|
|
137
|
+
level: 'warning',
|
|
138
|
+
message: defect ? `${d.message}\n ${PRE_EXISTING_LOWERING_NOTE}` : d.message,
|
|
139
|
+
origin: 'pre-existing',
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
if (edited.stoppedAt) {
|
|
143
|
+
const at = edited.stoppedAt.code !== undefined ? `[${edited.stoppedAt.code}] ` : '';
|
|
144
|
+
out.push({
|
|
145
|
+
level: 'error', code: 'CHECK_INCOMPLETE',
|
|
146
|
+
message: `check stopped at ${at}${edited.stoppedAt.message} Nothing after that refusal was checked, so the edit is not judged. `
|
|
147
|
+
+ 'Fix it, even if the original has it too, and run check again.',
|
|
148
|
+
class: refusalClass('CHECK_INCOMPLETE'), origin: 'edited',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
/** The `"quoted"` names in a finding's message, in order: what it is about, apart from its wording. */
|
|
154
|
+
function quotedSubjects(message) {
|
|
155
|
+
return JSON.stringify(message.match(/"[^"]*"/g) ?? []);
|
|
50
156
|
}
|
|
157
|
+
/** What a pre-existing lowering finding adds to its message ({@link scopeToEdit}). */
|
|
158
|
+
const PRE_EXISTING_LOWERING_NOTE = 'This node is unchanged from the baseline, so this is probably an SDK defect: '
|
|
159
|
+
+ 'the SDK cannot read what the designer wrote. `compile` refuses this node; `merge --baseline-from` keeps it as saved. '
|
|
160
|
+
+ 'Report it: https://github.com/UiPath/builder-sdk/issues';
|
|
51
161
|
/** The error-level diagnostics a custom node's own `diagnostics()` reported (see `serializerRefusals`). */
|
|
52
162
|
const OWN_NODE_ERRORS = new WeakSet();
|
|
53
163
|
/**
|
|
@@ -67,13 +177,20 @@ const OWN_NODE_ERRORS = new WeakSet();
|
|
|
67
177
|
*/
|
|
68
178
|
function serializerRefusals(built, compile, found) {
|
|
69
179
|
const collected = [];
|
|
180
|
+
let flow;
|
|
181
|
+
let stoppedAt;
|
|
70
182
|
try {
|
|
71
|
-
serialize(built, { ...compile, collect: (r) => collected.push(r) });
|
|
183
|
+
flow = serialize(built, { ...compile, collect: (r) => collected.push(r) });
|
|
184
|
+
// Collection stopped: the refusal that stopped it is the last one reported.
|
|
185
|
+
const stop = collected[collected.length - 1];
|
|
186
|
+
if (flow === undefined && stop)
|
|
187
|
+
stoppedAt = { code: stop.code, message: stop.message };
|
|
72
188
|
}
|
|
73
189
|
catch (error) {
|
|
74
190
|
// LIBRARY_REQUIRED is dropped below, so it does not count as something reported.
|
|
75
191
|
if (!found.some((d) => d.level === 'error') && !collected.some((r) => r.code !== 'LIBRARY_REQUIRED'))
|
|
76
192
|
throw error;
|
|
193
|
+
stoppedAt = { message: error instanceof Error ? error.message : String(error) };
|
|
77
194
|
}
|
|
78
195
|
const out = [];
|
|
79
196
|
const seen = new Set();
|
|
@@ -100,9 +217,10 @@ function serializerRefusals(built, compile, found) {
|
|
|
100
217
|
level: 'error', code: r.code, message,
|
|
101
218
|
...(step !== undefined ? { step } : {}),
|
|
102
219
|
...(r.suggestion !== undefined ? { suggestion: r.suggestion } : {}),
|
|
220
|
+
...(r.nodeId !== undefined ? { nodeId: r.nodeId } : {}),
|
|
103
221
|
});
|
|
104
222
|
}
|
|
105
|
-
return out;
|
|
223
|
+
return { diagnostics: out, flow, ...(stoppedAt ? { stoppedAt } : {}) };
|
|
106
224
|
}
|
|
107
225
|
/**
|
|
108
226
|
* `ancestors` is the chain of child flows currently being checked, innermost
|
|
@@ -23,14 +23,16 @@ export function registerFlowCommands(program) {
|
|
|
23
23
|
.option('--library <dir>', 'connector library directory')
|
|
24
24
|
.option('--bindings <file>', 'bindings.json path')
|
|
25
25
|
.option('--connectors-local <dir>', 'connection-resolved connector overlay')
|
|
26
|
-
.option('--no-check', '
|
|
26
|
+
.option('--no-check', 'deprecated, does nothing: compile no longer runs check')
|
|
27
27
|
.action(async (source, options) => {
|
|
28
28
|
const argv = [source];
|
|
29
29
|
addValue(argv, '-o', options.output);
|
|
30
30
|
addValue(argv, '--library', options.library);
|
|
31
31
|
addValue(argv, '--bindings', options.bindings);
|
|
32
32
|
addValue(argv, '--connectors-local', options.connectorsLocal);
|
|
33
|
-
|
|
33
|
+
// Passed on only when given, so compile prints its deprecation warning.
|
|
34
|
+
// Emit-only mode needs nothing here: compile does not run check.
|
|
35
|
+
if (options.check === false)
|
|
34
36
|
argv.push('--no-check');
|
|
35
37
|
await invoke('flow compile', () => import('../../compile-cli.js'), argv);
|
|
36
38
|
});
|
|
@@ -40,7 +42,10 @@ export function registerFlowCommands(program) {
|
|
|
40
42
|
.option('--source', 'check authored TypeScript')
|
|
41
43
|
.addOption(retiredCompiledOption())
|
|
42
44
|
.option('--library <dir>', 'connector library directory')
|
|
43
|
-
.option('--connectors-local <dir>', 'connection-resolved connector overlay')
|
|
45
|
+
.option('--connectors-local <dir>', 'connection-resolved connector overlay')
|
|
46
|
+
.option('--bindings <file>', 'bindings.json path')
|
|
47
|
+
.option('--baseline <file>', 'the original .flow the source was decompiled from (what merge --baseline-from takes, not the compiled baseline merge --baseline takes): findings on nodes the edit did not touch, which the original has too, become warnings')
|
|
48
|
+
.option('--package <name...>', 'trust and load this node-type package for --baseline (repeatable)');
|
|
44
49
|
// Hidden, not removed: scripts that pass these keep parsing.
|
|
45
50
|
for (const [flags, , description] of RETIRED_COMPILED_CHECK_FLAGS) {
|
|
46
51
|
check.addOption(new Option(flags, description).hideHelp());
|
|
@@ -69,6 +74,10 @@ export function registerFlowCommands(program) {
|
|
|
69
74
|
const argv = [input];
|
|
70
75
|
addValue(argv, '--library', options.library);
|
|
71
76
|
addValue(argv, '--connectors-local', options.connectorsLocal);
|
|
77
|
+
addValue(argv, '--bindings', options.bindings);
|
|
78
|
+
addValue(argv, '--baseline', options.baseline);
|
|
79
|
+
for (const name of options.package ?? [])
|
|
80
|
+
argv.push('--package', name);
|
|
72
81
|
await invoke('flow check', () => import('../../check-cli.js'), argv);
|
|
73
82
|
return;
|
|
74
83
|
}
|
package/dist/cli-result.d.ts
CHANGED
|
@@ -24,30 +24,34 @@ export declare function reportResult(fields: Record<string, unknown>): void;
|
|
|
24
24
|
* @param text - the result as text; a trailing newline is added.
|
|
25
25
|
*/
|
|
26
26
|
export declare function printResult(text: string): void;
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
* shape every family's check (and compile's check pass) uses.
|
|
30
|
-
*/
|
|
31
|
-
export declare function reportDiagnostics(diags: ReadonlyArray<{
|
|
27
|
+
/** A diagnostic as `reportDiagnostics` and `diagnosticLine` read it: any family's, with the fields it has. */
|
|
28
|
+
interface ReportedDiagnostic {
|
|
32
29
|
level: string;
|
|
33
30
|
code: string;
|
|
34
31
|
message: string;
|
|
35
32
|
step?: string;
|
|
36
33
|
suggestion?: string;
|
|
37
|
-
|
|
34
|
+
nodeId?: string;
|
|
35
|
+
class?: string;
|
|
36
|
+
origin?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Report a check's diagnostics as `{ errors, warnings, diagnostics }`, the one
|
|
40
|
+
* shape every family's check (and compile's check pass) uses. Each diagnostic
|
|
41
|
+
* keeps the optional fields it has: `step`, `suggestion`, and Flow's `nodeId`,
|
|
42
|
+
* `class` and (with `check --baseline`) `origin`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function reportDiagnostics(diags: ReadonlyArray<ReportedDiagnostic>): void;
|
|
38
45
|
/**
|
|
39
46
|
* One diagnostic as `check` and `compile` print it: its mark and code, the
|
|
40
|
-
* message, the step it is on (unless the message already names it),
|
|
41
|
-
* suggestion. For instance
|
|
42
|
-
* `✗ [STEP_SWITCH_EXIT_UNKNOWN] no exit named "maybe". … (step "route")
|
|
47
|
+
* message, the step it is on (unless the message already names it), the
|
|
48
|
+
* suggestion, and, with `check --baseline`, its origin. For instance
|
|
49
|
+
* `✗ [STEP_SWITCH_EXIT_UNKNOWN] no exit named "maybe". … (step "route")`, or
|
|
50
|
+
* `⚠ [EMPTY_ARM] … (pre-existing, not counted)`. With an origin, a message's
|
|
51
|
+
* later lines (the note on a pre-existing lowering finding) follow the line
|
|
52
|
+
* the origin ends.
|
|
43
53
|
*/
|
|
44
|
-
export declare function diagnosticLine(d:
|
|
45
|
-
level: string;
|
|
46
|
-
code: string;
|
|
47
|
-
message: string;
|
|
48
|
-
step?: string;
|
|
49
|
-
suggestion?: string;
|
|
50
|
-
}): string;
|
|
54
|
+
export declare function diagnosticLine(d: ReportedDiagnostic): string;
|
|
51
55
|
/**
|
|
52
56
|
* The error diagnostic for a throw that ends a compile: the error's own string
|
|
53
57
|
* `code` when it carries one, else `COMPILE_FAILED`.
|
|
@@ -57,3 +61,4 @@ export declare function thrownDiagnostic(error: unknown): {
|
|
|
57
61
|
code: string;
|
|
58
62
|
message: string;
|
|
59
63
|
};
|
|
64
|
+
export {};
|
package/dist/cli-result.js
CHANGED
|
@@ -38,7 +38,9 @@ export function printResult(text) {
|
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Report a check's diagnostics as `{ errors, warnings, diagnostics }`, the one
|
|
41
|
-
* shape every family's check (and compile's check pass) uses.
|
|
41
|
+
* shape every family's check (and compile's check pass) uses. Each diagnostic
|
|
42
|
+
* keeps the optional fields it has: `step`, `suggestion`, and Flow's `nodeId`,
|
|
43
|
+
* `class` and (with `check --baseline`) `origin`.
|
|
42
44
|
*/
|
|
43
45
|
export function reportDiagnostics(diags) {
|
|
44
46
|
reportResult({
|
|
@@ -48,21 +50,34 @@ export function reportDiagnostics(diags) {
|
|
|
48
50
|
level: d.level, code: d.code, message: d.message,
|
|
49
51
|
...(d.step !== undefined ? { step: d.step } : {}),
|
|
50
52
|
...(d.suggestion ? { suggestion: d.suggestion } : {}),
|
|
53
|
+
...(d.nodeId !== undefined ? { nodeId: d.nodeId } : {}),
|
|
54
|
+
...(d.class !== undefined ? { class: d.class } : {}),
|
|
55
|
+
...(d.origin !== undefined ? { origin: d.origin } : {}),
|
|
51
56
|
})),
|
|
52
57
|
});
|
|
53
58
|
}
|
|
54
59
|
/**
|
|
55
60
|
* One diagnostic as `check` and `compile` print it: its mark and code, the
|
|
56
|
-
* message, the step it is on (unless the message already names it),
|
|
57
|
-
* suggestion. For instance
|
|
58
|
-
* `✗ [STEP_SWITCH_EXIT_UNKNOWN] no exit named "maybe". … (step "route")
|
|
61
|
+
* message, the step it is on (unless the message already names it), the
|
|
62
|
+
* suggestion, and, with `check --baseline`, its origin. For instance
|
|
63
|
+
* `✗ [STEP_SWITCH_EXIT_UNKNOWN] no exit named "maybe". … (step "route")`, or
|
|
64
|
+
* `⚠ [EMPTY_ARM] … (pre-existing, not counted)`. With an origin, a message's
|
|
65
|
+
* later lines (the note on a pre-existing lowering finding) follow the line
|
|
66
|
+
* the origin ends.
|
|
59
67
|
*/
|
|
60
68
|
export function diagnosticLine(d) {
|
|
61
69
|
// A step inside a subflow is `<caller> › <step>`; the message names the innermost one.
|
|
62
70
|
const named = d.step !== undefined && d.message.includes(`"${d.step.split(' › ').pop()}"`);
|
|
63
|
-
|
|
64
|
-
+ (d.step !== undefined && !named ? ` (step "${d.step}")` : '')
|
|
71
|
+
const suffix = (d.step !== undefined && !named ? ` (step "${d.step}")` : '')
|
|
65
72
|
+ (d.suggestion ? ` (did you mean "${d.suggestion}"?)` : '');
|
|
73
|
+
const mark = `${d.level === 'error' ? '✗' : '⚠'} [${d.code}]`;
|
|
74
|
+
if (d.origin === undefined)
|
|
75
|
+
return `${mark} ${d.message}${suffix}`;
|
|
76
|
+
// With a baseline the origin ends the finding's first line, where it is seen.
|
|
77
|
+
const origin = d.origin === 'edited' ? ' (edited)'
|
|
78
|
+
: d.class === 'lowering' ? ' (pre-existing)' : ' (pre-existing, not counted)';
|
|
79
|
+
const [first, ...rest] = d.message.split('\n');
|
|
80
|
+
return [`${mark} ${first}${suffix}${origin}`, ...rest].join('\n');
|
|
66
81
|
}
|
|
67
82
|
/**
|
|
68
83
|
* The error diagnostic for a throw that ends a compile: the error's own string
|
package/dist/compile-cli.js
CHANGED
|
@@ -10,12 +10,17 @@
|
|
|
10
10
|
* --library defaults to $FLOW_SDK_LIBRARY_JSON; --bindings defaults to
|
|
11
11
|
* .flow-sdk/bindings.json (or a legacy ./bindings.json) when present. Connector actions require a library.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* `compile` only serializes (docs/CHECK_COMPILE_CONTRACT_DESIGN.md D1, D2). It
|
|
14
|
+
* does not run `check()`'s lint rules: run `flow check` first for those. It
|
|
15
|
+
* stops only when it cannot write a correct `.flow`, a refusal the serializer
|
|
16
|
+
* throws. A draft node (a connector with no connection, an empty required
|
|
17
|
+
* input) the canvas saved is written, with a warning: decompile marks its step
|
|
18
|
+
* `{ draft: true }` and carries its definition in `.definitions([...])`. Any
|
|
19
|
+
* other draft, such as one an agent just wrote (even of an operation the canvas
|
|
20
|
+
* has a draft of), is refused.
|
|
21
|
+
*
|
|
22
|
+
* `--no-check` is a deprecated no-op: it is accepted for one minor version, with
|
|
23
|
+
* a warning, and then removed.
|
|
19
24
|
*/
|
|
20
25
|
import { diagnosticLine, reportDiagnostics, reportResult, thrownDiagnostic } from './cli-result.js';
|
|
21
26
|
import { writeJsonFile } from './core/json-file.js';
|
|
@@ -24,8 +29,7 @@ import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
|
24
29
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
25
30
|
import { serializeWithSidecars } from './serialize.js';
|
|
26
31
|
import { compileOwnsResource } from './core/agent-tool-resources.js';
|
|
27
|
-
import {
|
|
28
|
-
import { flowCheckOpts, openCliLibrary } from './cli-library.js';
|
|
32
|
+
import { openCliLibrary } from './cli-library.js';
|
|
29
33
|
import { Bindings } from './bindings.js';
|
|
30
34
|
import { flagValue, runWhenInvokedDirectly } from './cli-run.js';
|
|
31
35
|
import { connectionResources, inlineAgentResources, mergeBindingsV2, refreshInlineAgentRows } from './bindings-v2.js';
|
|
@@ -35,8 +39,8 @@ import { defaultBindingsFile } from './workdir.js';
|
|
|
35
39
|
import { isWithin } from './path-within.js';
|
|
36
40
|
import { flowSources, resourceSourcesOfOtherFlows } from './project-resource-users.js';
|
|
37
41
|
import { fnv1aX4Uuid } from './core/stable-id.js';
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
42
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
43
|
+
import { cliSpelling } from './core/cli-spelling.js';
|
|
40
44
|
/** An agent folder name: a uuid, any case. */
|
|
41
45
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42
46
|
export async function run(argv) {
|
|
@@ -45,7 +49,9 @@ export async function run(argv) {
|
|
|
45
49
|
return 2;
|
|
46
50
|
}
|
|
47
51
|
const flags = new Set(['-o', '--library', '--bindings', '--connectors-local']);
|
|
48
|
-
|
|
52
|
+
if (argv.includes('--no-check')) {
|
|
53
|
+
console.error(`compile: --no-check is deprecated and does nothing: compile no longer runs check. Run \`${cliSpelling()} flow check\` before \`compile\`; the flag will be removed.`);
|
|
54
|
+
}
|
|
49
55
|
const pos = argv.find((a, i) => !flags.has(a) && !flags.has(argv[i - 1]));
|
|
50
56
|
if (!pos) {
|
|
51
57
|
console.error('compile: missing <flow.ts | BaseName>');
|
|
@@ -66,37 +72,20 @@ export async function run(argv) {
|
|
|
66
72
|
// (e.g. HTTP-only) then compile fine even if the connector cache isn't built.
|
|
67
73
|
// A connector action with no library resolves to a clear error in serialize().
|
|
68
74
|
//
|
|
69
|
-
// Loaded BEFORE check() on purpose: one of check's rules (whether a connector
|
|
70
|
-
// event operation declares any event parameters) can only be answered from the
|
|
71
|
-
// library, and answering it from an assumption instead is what made
|
|
72
|
-
// EVENT_NO_SCOPE an error for HTTP Webhook, whose only correct `where` is empty.
|
|
73
75
|
const library = openCliLibrary(argv);
|
|
74
76
|
const bindFile = flagValue(argv, '--bindings') ?? defaultBindingsFile();
|
|
75
77
|
const bindings = new Bindings(bindFile);
|
|
76
78
|
// One list per run; every exit path reports it (reportResult overwrites keys).
|
|
77
79
|
const diagnostics = [];
|
|
78
|
-
if (!noCheck) {
|
|
79
|
-
// Same options as `flow check`, so the two cannot disagree.
|
|
80
|
-
const diags = check(built, flowCheckOpts(library, bindings));
|
|
81
|
-
const errors = diags.filter((d) => d.level === 'error');
|
|
82
|
-
for (const d of diags)
|
|
83
|
-
console.error(` ${diagnosticLine(d)}`);
|
|
84
|
-
diagnostics.push(...diags);
|
|
85
|
-
if (errors.length) {
|
|
86
|
-
reportDiagnostics(diagnostics);
|
|
87
|
-
console.error(`compile: ${errors.length} error(s); not emitting.`);
|
|
88
|
-
return 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
80
|
// The serializer's own observations — a connection or folder value that can
|
|
92
|
-
// never be a tenant resource key
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
81
|
+
// never be a tenant resource key, a draft node written from its carried
|
|
82
|
+
// definition — printed in the same `⚠ [CODE]` shape as check()'s. Warnings are
|
|
83
|
+
// never fatal: the .flow is still written, exit stays 0. A binding LABEL the
|
|
84
|
+
// serializer refuses (its own name as the resourceKey, or a label a populated
|
|
85
|
+
// bindings.json does not declare) is an error, not a warning, and so is a
|
|
86
|
+
// BYOA-only event on a connection `prepare` never checked
|
|
87
|
+
// (EVENT_BYOA_UNVERIFIED): printed in the same `✗ [CODE]` shape `check` uses
|
|
88
|
+
// for it. Nothing is written.
|
|
100
89
|
const out = flagValue(argv, '-o') ?? `${built.name || basename(file).replace(/\.flow\.ts$|\.ts$/, '')}.flow`;
|
|
101
90
|
const outDir = dirname(resolve(out));
|
|
102
91
|
// ONE FOLDER, ONE FLOW (#1184). An agent's `source` is the name of the folder
|
|
@@ -147,28 +136,37 @@ export async function run(argv) {
|
|
|
147
136
|
const warnings = [];
|
|
148
137
|
let emitted;
|
|
149
138
|
try {
|
|
150
|
-
emitted = serializeWithSidecars(built, {
|
|
139
|
+
emitted = serializeWithSidecars(built, {
|
|
140
|
+
library, bindings, warn: (w) => warnings.push(w), agentSource,
|
|
141
|
+
carriedDrafts: (d) => warnings.push(draftWarning(d)),
|
|
142
|
+
});
|
|
151
143
|
}
|
|
152
144
|
catch (error) {
|
|
153
145
|
for (const w of warnings)
|
|
154
|
-
console.error(`
|
|
146
|
+
console.error(` ${diagnosticLine(serializeWarningDiagnostic(w))}`);
|
|
155
147
|
diagnostics.push(...warnings.map(serializeWarningDiagnostic));
|
|
156
|
-
if (!(error instanceof
|
|
157
|
-
// Any other throw still ends the run through the
|
|
158
|
-
// `error`); the list is reported first.
|
|
148
|
+
if (!(error instanceof CompileRefusal)) {
|
|
149
|
+
// Any other throw (an internal SDK error) still ends the run through the
|
|
150
|
+
// caller (exit 1, `--json` `error`); the list is reported first.
|
|
159
151
|
diagnostics.push(thrownDiagnostic(error));
|
|
160
152
|
reportDiagnostics(diagnostics);
|
|
161
153
|
throw error;
|
|
162
154
|
}
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
// A coded refusal: the serializer cannot write a correct `.flow`. Printed in
|
|
156
|
+
// `check`'s `✗ [CODE]` shape, which `check` reports for it too.
|
|
157
|
+
const refused = {
|
|
158
|
+
level: 'error', code: error.code, message: error.message,
|
|
159
|
+
...(error.suggestion !== undefined ? { suggestion: error.suggestion } : {}),
|
|
160
|
+
};
|
|
161
|
+
console.error(` ${diagnosticLine(refused)}`);
|
|
162
|
+
diagnostics.push(refused);
|
|
165
163
|
reportDiagnostics(diagnostics);
|
|
166
164
|
console.error('compile: 1 error(s); not emitting.');
|
|
167
165
|
return 1;
|
|
168
166
|
}
|
|
169
167
|
const { flow: flowJson, sidecars } = emitted;
|
|
170
168
|
for (const w of warnings)
|
|
171
|
-
console.error(`
|
|
169
|
+
console.error(` ${diagnosticLine(serializeWarningDiagnostic(w))}`);
|
|
172
170
|
diagnostics.push(...warnings.map(serializeWarningDiagnostic));
|
|
173
171
|
// An inline agent is TWO artifacts: the node, and the agent's own configuration
|
|
174
172
|
// at `<source>/agent.json` — which is where flow-v1's serializer points the
|
|
@@ -325,7 +323,19 @@ export async function run(argv) {
|
|
|
325
323
|
return 0;
|
|
326
324
|
}
|
|
327
325
|
function serializeWarningDiagnostic(w) {
|
|
328
|
-
return { level: 'warning', code: w.code, message: w.message };
|
|
326
|
+
return { level: 'warning', code: w.code, message: w.message, ...(w.step !== undefined ? { step: w.step } : {}) };
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* A draft node written from its carried definition, as a warning: the node is
|
|
330
|
+
* in the `.flow` as the canvas saved it, unfinished. For instance
|
|
331
|
+
* `⚠ [BINDING_MISSING] … written as a draft from its carried definition`.
|
|
332
|
+
*/
|
|
333
|
+
function draftWarning(d) {
|
|
334
|
+
return {
|
|
335
|
+
code: String(d.code),
|
|
336
|
+
message: `${d.message} Written as a draft, with the definition the source carries; finish the node before the flow runs.`,
|
|
337
|
+
...(d.step !== undefined ? { step: d.step } : {}),
|
|
338
|
+
};
|
|
329
339
|
}
|
|
330
340
|
runWhenInvokedDirectly(import.meta.url, 'compile', run);
|
|
331
341
|
/** A JSON file's contents, or `undefined` when it is missing or unreadable. */
|
package/dist/core/refusals.d.ts
CHANGED
|
@@ -55,6 +55,7 @@ export declare const REFUSALS: {
|
|
|
55
55
|
readonly CARRIED_DEFINITION_CONFLICT: "lowering";
|
|
56
56
|
readonly CARRIED_DEFINITION_INVALID: "lowering";
|
|
57
57
|
readonly CEQL_UNPARSEABLE: "lowering";
|
|
58
|
+
readonly CHECK_INCOMPLETE: "lowering";
|
|
58
59
|
readonly COMPILE_FAILED: "lowering";
|
|
59
60
|
readonly CONNECTION_CONNECTOR_CONFLICT: "draft";
|
|
60
61
|
readonly CONNECTION_STUB: "lint";
|
package/dist/core/refusals.js
CHANGED
|
@@ -93,6 +93,9 @@ export const REFUSALS = {
|
|
|
93
93
|
CARRIED_DEFINITION_CONFLICT: 'lowering',
|
|
94
94
|
CARRIED_DEFINITION_INVALID: 'lowering',
|
|
95
95
|
CEQL_UNPARSEABLE: 'lowering',
|
|
96
|
+
// Only `check --baseline` reports it: collection stopped at a scope-level
|
|
97
|
+
// refusal, and `compile` (and `merge --baseline-from`) stops at that refusal.
|
|
98
|
+
CHECK_INCOMPLETE: 'lowering',
|
|
96
99
|
// A throw that is no refusal, which `compile` cannot get past: check reports it on its step.
|
|
97
100
|
COMPILE_FAILED: 'lowering',
|
|
98
101
|
CONNECTION_CONNECTOR_CONFLICT: 'draft',
|
package/dist/decompile-cli.js
CHANGED
|
@@ -159,7 +159,7 @@ if (!existsSync(BASELINE)) {
|
|
|
159
159
|
// ── Step 1: capture the baseline = compile of the PRISTINE decompiled code,
|
|
160
160
|
// BEFORE any edit. Run this once right after decompiling, then edit. ──
|
|
161
161
|
mkdirSync(dirname(BASELINE), { recursive: true });
|
|
162
|
-
run('compile', [TS, '-o', BASELINE
|
|
162
|
+
run('compile', [TS, '-o', BASELINE]);
|
|
163
163
|
console.log('\\n>>> Baseline captured. Now EDIT ' + ${JSON.stringify(basename(tsPath))} + ', then re-run this script to compile + merge.');
|
|
164
164
|
} else {
|
|
165
165
|
// ── Step 2: compile your edits, then merge them back into the original. ──
|
|
@@ -182,7 +182,7 @@ if (!existsSync(BASELINE)) {
|
|
|
182
182
|
process.exit(2);
|
|
183
183
|
}
|
|
184
184
|
mkdirSync(dirname(EDITED), { recursive: true });
|
|
185
|
-
run('compile', [TS, '-o', EDITED
|
|
185
|
+
run('compile', [TS, '-o', EDITED]);
|
|
186
186
|
|
|
187
187
|
// ── DIFF GATE (seam) ──────────────────────────────────────────────────────
|
|
188
188
|
// A future step can diff ORIGINAL vs the about-to-be-written MERGED here and
|