@uipath/maestro-builder-sdk 6.57.0 → 6.58.1
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/case/subscription.d.ts +4 -2
- package/dist/case/subscription.js +5 -2
- 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.js +2 -1
- package/dist/core/typescript-module.js +2 -1
- package/dist/edit-scope.d.ts +180 -0
- package/dist/edit-scope.js +251 -0
- package/dist/flow-expr-check.d.ts +2 -1
- package/dist/flow-expr-check.js +4 -2
- 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 +7 -6
- package/dist/merge-cli.js +4 -2
- package/dist/merge-compile.d.ts +5 -88
- package/dist/merge-compile.js +8 -138
- package/dist/merge.d.ts +1 -61
- package/dist/merge.js +1 -112
- 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/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/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/load.js
CHANGED
|
@@ -5,6 +5,7 @@ import { statSync } from 'node:fs';
|
|
|
5
5
|
import { useThisSdkForAuthoredSource } from './sdk-resolve.js';
|
|
6
6
|
import { scriptFunctionLowerings } from './core/script-function.js';
|
|
7
7
|
import { scriptFunctionScopeProblem } from './script-function-scope.js';
|
|
8
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
8
9
|
/** Resolve a user argument (a `.ts` path or a BaseName) to a flow source file. */
|
|
9
10
|
export function resolveFlowFile(arg) {
|
|
10
11
|
const candidates = [arg, `${arg}.flow.ts`, `${arg}.ts`];
|
|
@@ -19,7 +20,7 @@ export function resolveFlowFile(arg) {
|
|
|
19
20
|
}
|
|
20
21
|
return resolvedCandidate;
|
|
21
22
|
}
|
|
22
|
-
throw new
|
|
23
|
+
throw new CompileRefusal('FLOW_SOURCE_NOT_FOUND', `flow source not found: tried ${candidates.join(', ')}`);
|
|
23
24
|
}
|
|
24
25
|
export async function loadBuiltFlow(file) {
|
|
25
26
|
useThisSdkForAuthoredSource();
|
|
@@ -33,10 +34,10 @@ export async function loadBuiltFlow(file) {
|
|
|
33
34
|
}
|
|
34
35
|
const exp = mod.default ?? mod.flow ?? mod.workflow;
|
|
35
36
|
if (!exp)
|
|
36
|
-
throw new
|
|
37
|
+
throw new CompileRefusal('FLOW_NO_DEFAULT_EXPORT', `${file} has no default export. Add: export default flow('id')...build();`);
|
|
37
38
|
const built = typeof exp.build === 'function' ? exp.build() : exp;
|
|
38
39
|
if (!built || !Array.isArray(built.steps))
|
|
39
|
-
throw new
|
|
40
|
+
throw new CompileRefusal('FLOW_EXPORT_NOT_BUILT', `${file} default export is not a built flow. Did you call .build()?`);
|
|
40
41
|
// A `script(fn)` function was lowered from its own text, which cannot show a
|
|
41
42
|
// name it takes from its module; this source can (script-function-scope.ts).
|
|
42
43
|
// Only a flow that lowered one pays for the program this builds.
|
|
@@ -45,7 +46,7 @@ export async function loadBuiltFlow(file) {
|
|
|
45
46
|
if (lowered.length > 0) {
|
|
46
47
|
const problem = scriptFunctionScopeProblem(file, lowered);
|
|
47
48
|
if (problem)
|
|
48
|
-
throw new
|
|
49
|
+
throw new CompileRefusal('SCRIPT_FUNCTION_SCOPE', problem);
|
|
49
50
|
}
|
|
50
51
|
return built;
|
|
51
52
|
}
|
|
@@ -96,7 +97,7 @@ export function connectorImportDiagnostic(error) {
|
|
|
96
97
|
return undefined;
|
|
97
98
|
}
|
|
98
99
|
if (parent === 'connectors') {
|
|
99
|
-
return new
|
|
100
|
+
return new CompileRefusal('CONNECTOR_IMPORT_NOT_A_FILE', `connector import "${unresolved}" does not name a file. The generated ` +
|
|
100
101
|
`descriptors are TypeScript, so the specifier keeps its real extension; ` +
|
|
101
102
|
`import "${moduleFile}"`);
|
|
102
103
|
}
|
|
@@ -104,7 +105,7 @@ export function connectorImportDiagnostic(error) {
|
|
|
104
105
|
if (!statSync(descriptorData, { throwIfNoEntry: false })?.isDirectory()) {
|
|
105
106
|
return undefined;
|
|
106
107
|
}
|
|
107
|
-
return new
|
|
108
|
+
return new CompileRefusal('CONNECTOR_IMPORT_NOT_A_FILE', `connector import "${unresolved}" does not name a file; module candidate ` +
|
|
108
109
|
`"${moduleFile}" and descriptor data "${descriptorData}" are separate; ` +
|
|
109
110
|
`import "${moduleFile}"`);
|
|
110
111
|
}
|
package/dist/merge-cli.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* `--baseline-from <original.flow>` makes merge build the baseline itself
|
|
10
10
|
* (decompile + compile, nothing written), and an edited `.flow.ts` makes merge
|
|
11
11
|
* compile it: both compiles tolerate DRAFT nodes, and merge refuses a draft
|
|
12
|
-
* only where it would take the node from the edited flow. See merge-compile.ts
|
|
12
|
+
* only where it would take the node from the edited flow. See merge-compile.ts
|
|
13
|
+
* and edit-scope.ts.
|
|
13
14
|
*
|
|
14
15
|
* `edited.flow` is compile(decompile(original) after your edits). `--baseline`
|
|
15
16
|
* is compile(decompile(original)) BEFORE any edit — supplying it enables the
|
|
@@ -55,7 +56,8 @@ import { flowCheckOpts, openCliLibrary } from './cli-library.js';
|
|
|
55
56
|
import { Bindings } from './bindings.js';
|
|
56
57
|
import { defaultBindingsFile } from './workdir.js';
|
|
57
58
|
import { loadNodePackages } from './node-packages.js';
|
|
58
|
-
import { baselineFromOriginal, checkEdited,
|
|
59
|
+
import { baselineFromOriginal, checkEdited, compileSourceDraftTolerant } from './merge-compile.js';
|
|
60
|
+
import { classifyDrafts, findingCounts, splitUnplaced } from './edit-scope.js';
|
|
59
61
|
const USAGE = 'usage: flow-merge <original.flow> <edited.flow | Edited.flow.ts> [-o merged.flow] '
|
|
60
62
|
+ '[--baseline <baseline.flow> | --baseline-from <original.flow>] [--library <dir>] [--bindings <file>] [--strict]';
|
|
61
63
|
/**
|
package/dist/merge-compile.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type DraftFinding, type SerializeOpts } from './serialize.js';
|
|
2
|
-
import { type CheckOpts
|
|
2
|
+
import { type CheckOpts } from './check.js';
|
|
3
|
+
import { type PlacedDiagnostic } from './edit-scope.js';
|
|
3
4
|
import type { BuiltFlow } from './flow-sdk.js';
|
|
4
5
|
/**
|
|
5
6
|
* A compile's flow, the draft refusals it reported instead of throwing, and the
|
|
@@ -30,94 +31,10 @@ export declare function compileSourceDraftTolerant(file: string, opts?: Omit<Ser
|
|
|
30
31
|
export declare function baselineFromOriginal(originalPath: string, opts?: Omit<SerializeOpts, 'draft'>, warn?: (message: string) => void): Promise<DraftTolerantCompile & {
|
|
31
32
|
built: BuiltFlow;
|
|
32
33
|
}>;
|
|
33
|
-
/**
|
|
34
|
-
* Why a finding is an error: `edited`, its node is one the author changed or
|
|
35
|
-
* added; `subflow`, it sits in a subflow the edit changed, which merge takes
|
|
36
|
-
* whole; `unplaced`, it names no node and the original's own round trip does
|
|
37
|
-
* not report it, so the edit introduced it.
|
|
38
|
-
*/
|
|
39
|
-
export type TakenReason = 'edited' | 'subflow' | 'unplaced';
|
|
40
|
-
/**
|
|
41
|
-
* A finding on a node merge takes from the edited flow, with the reason it
|
|
42
|
-
* takes it: `edited`, the author changed or added the node; `subflow`, the
|
|
43
|
-
* node sits in a subflow the edit changed, which merge takes whole (`subflow`
|
|
44
|
-
* names it).
|
|
45
|
-
*/
|
|
46
|
-
export type Refused<T> = T & {
|
|
47
|
-
reason: TakenReason;
|
|
48
|
-
subflow?: string;
|
|
49
|
-
};
|
|
50
|
-
/** A finding on a node merge restores from the original; `subflow` names the untouched subflow it sits in. */
|
|
51
|
-
export type Kept<T> = T & {
|
|
52
|
-
subflow?: string;
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* Sort each finding by what merge does with its node: `refused` when the node
|
|
56
|
-
* comes from the edited flow (reason `edited`: the author changed or added it;
|
|
57
|
-
* reason `subflow`: it sits in a subflow the edit changed, which merge takes
|
|
58
|
-
* whole), `kept` when merge restores the original node — a root node the edit
|
|
59
|
-
* did not touch, or any node of a subflow the edit did not touch (#1322) — and
|
|
60
|
-
* `unplaced` when the finding names no node merge knows (an unnamed End's
|
|
61
|
-
* `.return`, a flow-level finding). The caller sorts those again with
|
|
62
|
-
* {@link splitUnplaced}: one the original's own round trip reports too is
|
|
63
|
-
* printed, one the edit introduced is refused.
|
|
64
|
-
*
|
|
65
|
-
* For instance a Slack node with no connection that the author did not touch
|
|
66
|
-
* is `kept`; the same node with a new message text is `refused` as `edited`.
|
|
67
|
-
*/
|
|
68
|
-
export declare function classifyDrafts<T extends {
|
|
69
|
-
nodeId: string;
|
|
70
|
-
subflow?: string;
|
|
71
|
-
}>(original: any, edited: any, baseline: any, findings: readonly T[]): {
|
|
72
|
-
kept: Kept<T>[];
|
|
73
|
-
refused: Refused<T>[];
|
|
74
|
-
unplaced: T[];
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* A `check()` diagnostic, placed on the node its step emitted (`''` when it
|
|
78
|
-
* names no node); `subflow` is the calling step when the finding is inside a
|
|
79
|
-
* subflow, which then decides how it is classified.
|
|
80
|
-
*/
|
|
81
|
-
export type PlacedDiagnostic = Diagnostic & {
|
|
82
|
-
nodeId: string;
|
|
83
|
-
subflow?: string;
|
|
84
|
-
};
|
|
85
34
|
/**
|
|
86
35
|
* Run `check()` on the edited source, as `compile` does, and place each
|
|
87
|
-
* diagnostic on
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* segment per level) on the deepest step the path resolves to, with `subflow`
|
|
91
|
-
* set to the deepest subflow on that path, which is what decides; a named End
|
|
92
|
-
* on its End node (#1322). A diagnostic with no step, or one no node answers
|
|
93
|
-
* to, gets nodeId `''`.
|
|
36
|
+
* diagnostic on its node ({@link placeDiagnostics}). The `check()` call stays
|
|
37
|
+
* here, not in `edit-scope.ts`, so `check.ts` can import that module without a
|
|
38
|
+
* load-time cycle.
|
|
94
39
|
*/
|
|
95
40
|
export declare function checkEdited(built: BuiltFlow, editedFlow: any, opts: CheckOpts): PlacedDiagnostic[];
|
|
96
|
-
/** The identity of a finding for the introduced-or-not comparison: its code and message. */
|
|
97
|
-
export declare function findingKey(f: {
|
|
98
|
-
code: string;
|
|
99
|
-
message: string;
|
|
100
|
-
}): string;
|
|
101
|
-
/** How many times each {@link findingKey} occurs, for {@link splitUnplaced}'s baseline. */
|
|
102
|
-
export declare function findingCounts(findings: ReadonlyArray<{
|
|
103
|
-
code: string;
|
|
104
|
-
message: string;
|
|
105
|
-
}>): Map<string, number>;
|
|
106
|
-
/**
|
|
107
|
-
* Sort findings merge could not place on a node by whether the edit introduced
|
|
108
|
-
* them: `known` when the ORIGINAL's own round trip (`baseline`, the decompiled
|
|
109
|
-
* original compiled and checked, as {@link findingCounts}) reports the same
|
|
110
|
-
* code and message — once per occurrence, so a second copy of a finding the
|
|
111
|
-
* original has once is `introduced` — so the finding predates the edit and
|
|
112
|
-
* only prints; `introduced` otherwise, which is refused like any error on an
|
|
113
|
-
* edited node (#1322). With no baseline findings to compare against (a
|
|
114
|
-
* precompiled `--baseline` file), every one is `introduced`, as merge treated
|
|
115
|
-
* them before.
|
|
116
|
-
*/
|
|
117
|
-
export declare function splitUnplaced<T extends {
|
|
118
|
-
code: string;
|
|
119
|
-
message: string;
|
|
120
|
-
}>(unplaced: readonly T[], baseline: ReadonlyMap<string, number> | undefined): {
|
|
121
|
-
known: T[];
|
|
122
|
-
introduced: T[];
|
|
123
|
-
};
|
package/dist/merge-compile.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Both compiles tolerate draft nodes (`SerializeOpts.draft`). A draft on a node
|
|
5
5
|
* merge keeps from the original is a warning; a draft or `check()` error on a
|
|
6
6
|
* node merge takes from the edited flow is an error. Nothing draft-tolerant is
|
|
7
|
-
* written to disk.
|
|
7
|
+
* written to disk. Which nodes count as edited, and how each finding is sorted,
|
|
8
|
+
* is edit-scope.ts's.
|
|
8
9
|
*/
|
|
9
10
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
10
11
|
import { tmpdir } from 'node:os';
|
|
@@ -12,9 +13,9 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
12
13
|
import { decompile } from './decompile.js';
|
|
13
14
|
import { loadBuiltFlow } from './load.js';
|
|
14
15
|
import { serialize } from './serialize.js';
|
|
15
|
-
import { check } from './check.js';
|
|
16
|
-
import { nodesTakenFromEdited, subflowsTakenFromEdited } from './merge.js';
|
|
17
16
|
import { PACKAGE_NAME } from './package-name.js';
|
|
17
|
+
import { check } from './check.js';
|
|
18
|
+
import { placeDiagnostics } from './edit-scope.js';
|
|
18
19
|
/** Compile `built` with draft tolerance. Every other refusal still throws. */
|
|
19
20
|
export function compileDraftTolerant(built, opts = {}) {
|
|
20
21
|
const drafts = [];
|
|
@@ -73,143 +74,12 @@ function baselineDirectory(originalPath) {
|
|
|
73
74
|
return mkdtempSync(join(tmpdir(), 'flow-merge-baseline-'));
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Sort each finding by what merge does with its node: `refused` when the node
|
|
78
|
-
* comes from the edited flow (reason `edited`: the author changed or added it;
|
|
79
|
-
* reason `subflow`: it sits in a subflow the edit changed, which merge takes
|
|
80
|
-
* whole), `kept` when merge restores the original node — a root node the edit
|
|
81
|
-
* did not touch, or any node of a subflow the edit did not touch (#1322) — and
|
|
82
|
-
* `unplaced` when the finding names no node merge knows (an unnamed End's
|
|
83
|
-
* `.return`, a flow-level finding). The caller sorts those again with
|
|
84
|
-
* {@link splitUnplaced}: one the original's own round trip reports too is
|
|
85
|
-
* printed, one the edit introduced is refused.
|
|
86
|
-
*
|
|
87
|
-
* For instance a Slack node with no connection that the author did not touch
|
|
88
|
-
* is `kept`; the same node with a new message text is `refused` as `edited`.
|
|
89
|
-
*/
|
|
90
|
-
export function classifyDrafts(original, edited, baseline, findings) {
|
|
91
|
-
const fromEdited = new Set(nodesTakenFromEdited(original, edited, baseline));
|
|
92
|
-
const takenSubflows = new Set(subflowsTakenFromEdited(original, edited, baseline));
|
|
93
|
-
const rootIds = new Set((edited?.nodes ?? []).map((n) => n.id));
|
|
94
|
-
/** A subflow's own node ids → the key (the calling step) of the subflow holding them. */
|
|
95
|
-
const subflowOfNode = new Map();
|
|
96
|
-
for (const [key, sf] of Object.entries(edited?.subflows ?? {})) {
|
|
97
|
-
for (const n of sf?.nodes ?? [])
|
|
98
|
-
subflowOfNode.set(n.id, key);
|
|
99
|
-
}
|
|
100
|
-
const kept = [];
|
|
101
|
-
const refused = [];
|
|
102
|
-
const unplaced = [];
|
|
103
|
-
for (const f of findings) {
|
|
104
|
-
// A finding inside a subflow — placed on the calling step by `checkEdited`
|
|
105
|
-
// (`subflow` set), or a draft on one of the subflow's own nodes — is judged
|
|
106
|
-
// by the SUBFLOW: taken whole when the edit changed it, else the original's.
|
|
107
|
-
const subflow = f.subflow ?? subflowOfNode.get(f.nodeId);
|
|
108
|
-
if (subflow !== undefined) {
|
|
109
|
-
if (takenSubflows.has(subflow))
|
|
110
|
-
refused.push({ ...f, reason: 'subflow', subflow });
|
|
111
|
-
else
|
|
112
|
-
kept.push({ ...f, subflow });
|
|
113
|
-
}
|
|
114
|
-
else if (rootIds.has(f.nodeId)) {
|
|
115
|
-
if (fromEdited.has(f.nodeId))
|
|
116
|
-
refused.push({ ...f, reason: 'edited' });
|
|
117
|
-
else
|
|
118
|
-
kept.push(f);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
unplaced.push(f);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return { kept, refused, unplaced };
|
|
125
|
-
}
|
|
126
77
|
/**
|
|
127
78
|
* Run `check()` on the edited source, as `compile` does, and place each
|
|
128
|
-
* diagnostic on
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* segment per level) on the deepest step the path resolves to, with `subflow`
|
|
132
|
-
* set to the deepest subflow on that path, which is what decides; a named End
|
|
133
|
-
* on its End node (#1322). A diagnostic with no step, or one no node answers
|
|
134
|
-
* to, gets nodeId `''`.
|
|
79
|
+
* diagnostic on its node ({@link placeDiagnostics}). The `check()` call stays
|
|
80
|
+
* here, not in `edit-scope.ts`, so `check.ts` can import that module without a
|
|
81
|
+
* load-time cycle.
|
|
135
82
|
*/
|
|
136
83
|
export function checkEdited(built, editedFlow, opts) {
|
|
137
|
-
|
|
138
|
-
for (const n of editedFlow?.nodes ?? []) {
|
|
139
|
-
const label = n?.display?.label;
|
|
140
|
-
if (typeof label === 'string' && !byStep.has(label))
|
|
141
|
-
byStep.set(label, n.id);
|
|
142
|
-
}
|
|
143
|
-
const ids = new Set((editedFlow?.nodes ?? []).map((n) => n.id));
|
|
144
|
-
/** `name` as a node id in `nodes`: by label, then by id; `''` when neither answers. */
|
|
145
|
-
const resolveIn = (nodes, name) => {
|
|
146
|
-
const byLabel = nodes.find((n) => n?.display?.label === name);
|
|
147
|
-
if (byLabel)
|
|
148
|
-
return byLabel.id;
|
|
149
|
-
return nodes.some((n) => n?.id === name) ? name : '';
|
|
150
|
-
};
|
|
151
|
-
// `subflows` is flat, keyed by each caller's id at every depth, and check
|
|
152
|
-
// prefixes a finding at every level (`outer › inner › x`): the subflow that
|
|
153
|
-
// decides is the DEEPEST one on the path, since merge keeps or takes each
|
|
154
|
-
// subflow on its own (an edit inside `inner` leaves `outer` kept).
|
|
155
|
-
const place = (step) => {
|
|
156
|
-
if (step === undefined)
|
|
157
|
-
return { nodeId: '' };
|
|
158
|
-
const segments = step.split(SUBFLOW_STEP_SEPARATOR);
|
|
159
|
-
let scope = editedFlow?.nodes ?? [];
|
|
160
|
-
let subflow;
|
|
161
|
-
let nodeId = '';
|
|
162
|
-
for (const [i, segment] of segments.entries()) {
|
|
163
|
-
const id = i === 0 ? (byStep.get(segment) ?? (ids.has(segment) ? segment : '')) : resolveIn(scope, segment);
|
|
164
|
-
if (id === '')
|
|
165
|
-
break;
|
|
166
|
-
nodeId = id;
|
|
167
|
-
const child = i < segments.length - 1 ? editedFlow?.subflows?.[id] : undefined;
|
|
168
|
-
if (child === undefined)
|
|
169
|
-
break;
|
|
170
|
-
subflow = id;
|
|
171
|
-
scope = child.nodes ?? [];
|
|
172
|
-
}
|
|
173
|
-
return subflow !== undefined ? { nodeId: nodeId === '' ? subflow : nodeId, subflow } : { nodeId };
|
|
174
|
-
};
|
|
175
|
-
return check(built, opts).map((d) => ({ ...d, ...place(d.step) }));
|
|
176
|
-
}
|
|
177
|
-
/** The identity of a finding for the introduced-or-not comparison: its code and message. */
|
|
178
|
-
export function findingKey(f) {
|
|
179
|
-
return `${f.code}\n${f.message}`;
|
|
180
|
-
}
|
|
181
|
-
/** How many times each {@link findingKey} occurs, for {@link splitUnplaced}'s baseline. */
|
|
182
|
-
export function findingCounts(findings) {
|
|
183
|
-
const counts = new Map();
|
|
184
|
-
for (const f of findings)
|
|
185
|
-
counts.set(findingKey(f), (counts.get(findingKey(f)) ?? 0) + 1);
|
|
186
|
-
return counts;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Sort findings merge could not place on a node by whether the edit introduced
|
|
190
|
-
* them: `known` when the ORIGINAL's own round trip (`baseline`, the decompiled
|
|
191
|
-
* original compiled and checked, as {@link findingCounts}) reports the same
|
|
192
|
-
* code and message — once per occurrence, so a second copy of a finding the
|
|
193
|
-
* original has once is `introduced` — so the finding predates the edit and
|
|
194
|
-
* only prints; `introduced` otherwise, which is refused like any error on an
|
|
195
|
-
* edited node (#1322). With no baseline findings to compare against (a
|
|
196
|
-
* precompiled `--baseline` file), every one is `introduced`, as merge treated
|
|
197
|
-
* them before.
|
|
198
|
-
*/
|
|
199
|
-
export function splitUnplaced(unplaced, baseline) {
|
|
200
|
-
const known = [];
|
|
201
|
-
const introduced = [];
|
|
202
|
-
const left = new Map(baseline ?? []);
|
|
203
|
-
for (const f of unplaced) {
|
|
204
|
-
const n = left.get(findingKey(f)) ?? 0;
|
|
205
|
-
if (n > 0) {
|
|
206
|
-
left.set(findingKey(f), n - 1);
|
|
207
|
-
known.push(f);
|
|
208
|
-
}
|
|
209
|
-
else
|
|
210
|
-
introduced.push(f);
|
|
211
|
-
}
|
|
212
|
-
return { known, introduced };
|
|
84
|
+
return placeDiagnostics(check(built, opts), editedFlow);
|
|
213
85
|
}
|
|
214
|
-
/** How `check` joins a subflow's calling step and the step inside it in a diagnostic's `step`. */
|
|
215
|
-
const SUBFLOW_STEP_SEPARATOR = ' › ';
|
package/dist/merge.d.ts
CHANGED
|
@@ -87,50 +87,9 @@
|
|
|
87
87
|
* original plus the author's change to the node. See merge-embedded-agent.ts.
|
|
88
88
|
*/
|
|
89
89
|
import { type MergeSidecarSources, type SidecarWrite } from './merge-sidecars.js';
|
|
90
|
+
import { type AnyFlow } from './edit-scope.js';
|
|
90
91
|
export { MergeRefusedError, type MergeFinding } from './merge-roundtrip.js';
|
|
91
92
|
export type { MergeSidecarSources, SidecarSet, SidecarWrite } from './merge-sidecars.js';
|
|
92
|
-
interface AnyNode {
|
|
93
|
-
id: string;
|
|
94
|
-
type: string;
|
|
95
|
-
typeVersion?: string;
|
|
96
|
-
parentId?: string;
|
|
97
|
-
ui?: unknown;
|
|
98
|
-
display?: unknown;
|
|
99
|
-
inputs?: Record<string, unknown>;
|
|
100
|
-
outputs?: Record<string, unknown>;
|
|
101
|
-
[k: string]: unknown;
|
|
102
|
-
}
|
|
103
|
-
interface AnyEdge {
|
|
104
|
-
id?: string;
|
|
105
|
-
sourceNodeId: string;
|
|
106
|
-
sourcePort: string;
|
|
107
|
-
targetNodeId: string;
|
|
108
|
-
targetPort: string;
|
|
109
|
-
[k: string]: unknown;
|
|
110
|
-
}
|
|
111
|
-
interface AnyFlow {
|
|
112
|
-
id?: string;
|
|
113
|
-
name?: string;
|
|
114
|
-
version?: string;
|
|
115
|
-
nodes?: AnyNode[];
|
|
116
|
-
edges?: AnyEdge[];
|
|
117
|
-
definitions?: Array<{
|
|
118
|
-
nodeType?: string;
|
|
119
|
-
[k: string]: unknown;
|
|
120
|
-
}>;
|
|
121
|
-
variables?: unknown;
|
|
122
|
-
subflows?: Record<string, unknown>;
|
|
123
|
-
bindings?: Array<{
|
|
124
|
-
id?: string;
|
|
125
|
-
[k: string]: unknown;
|
|
126
|
-
}>;
|
|
127
|
-
metadata?: unknown;
|
|
128
|
-
solutionId?: unknown;
|
|
129
|
-
projectId?: unknown;
|
|
130
|
-
layout?: unknown;
|
|
131
|
-
runtime?: unknown;
|
|
132
|
-
[k: string]: unknown;
|
|
133
|
-
}
|
|
134
93
|
export interface MergeOptions {
|
|
135
94
|
/**
|
|
136
95
|
* compile(decompile(original)) captured BEFORE the user's edits. Enables the
|
|
@@ -172,25 +131,6 @@ export interface MergeOptions {
|
|
|
172
131
|
*/
|
|
173
132
|
strict?: boolean;
|
|
174
133
|
}
|
|
175
|
-
/**
|
|
176
|
-
* The ids of the root nodes {@link mergeFlow} takes from `edited` rather than
|
|
177
|
-
* restoring from `original`: every node that is new, or that differs from its
|
|
178
|
-
* `baseline` counterpart. With no baseline, that is every node.
|
|
179
|
-
*
|
|
180
|
-
* For instance, with a baseline equal to `edited` and every node present in
|
|
181
|
-
* `original`, the answer is `[]` — a no-edit merge keeps them all.
|
|
182
|
-
*/
|
|
183
|
-
export declare function nodesTakenFromEdited(original: AnyFlow, edited: AnyFlow, baseline?: AnyFlow): string[];
|
|
184
|
-
/**
|
|
185
|
-
* The keys of the subflows {@link mergeFlow} takes from `edited` rather than
|
|
186
|
-
* restoring from `original`: every subflow that is new, that the original does
|
|
187
|
-
* not have, or that differs from its `baseline` counterpart in any way the
|
|
188
|
-
* author can edit — a node's inputs, output sources or label, an edge, the
|
|
189
|
-
* scope's `variables` or `layout` (`subflowUnchanged`, #1322). With no
|
|
190
|
-
* baseline, that is every subflow. A kept subflow is the original's whole: its
|
|
191
|
-
* nodes, edges, variables and layout.
|
|
192
|
-
*/
|
|
193
|
-
export declare function subflowsTakenFromEdited(original: AnyFlow, edited: AnyFlow, baseline?: AnyFlow): string[];
|
|
194
134
|
export declare function mergeFlow(original: AnyFlow, edited: AnyFlow, options?: MergeOptions): AnyFlow;
|
|
195
135
|
/**
|
|
196
136
|
* `mergeFlow`, plus the agent sidecar files the merged flow needs written beside
|
package/dist/merge.js
CHANGED
|
@@ -95,119 +95,8 @@ import { placeAddedNodes } from './node-placement.js';
|
|
|
95
95
|
import { mergeAgentSidecars } from './merge-sidecars.js';
|
|
96
96
|
import { embeddedAgentFindings, isEmbeddedAgent, mergeEmbeddedAgentNode } from './merge-embedded-agent.js';
|
|
97
97
|
import { mergeObject, mergeVariables, stableJson } from './merge-variables.js';
|
|
98
|
+
import { byId, isKept, nodeUnchanged, subflowsTakenFromEdited } from './edit-scope.js';
|
|
98
99
|
export { MergeRefusedError } from './merge-roundtrip.js';
|
|
99
|
-
function byId(nodes) {
|
|
100
|
-
return new Map((nodes ?? []).map((n) => [n.id, n]));
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Whether two nodes are the SAME as far as the author is concerned — same type,
|
|
104
|
-
* same inputs and same output mappings. Layout (`ui`), `display`, `typeVersion`,
|
|
105
|
-
* and the derived parts of `outputs` (type, schema, description) are ignored,
|
|
106
|
-
* because those are exactly the parts decompile does not carry through. An
|
|
107
|
-
* output's `source` is authored, not derived: it is what an End returns, so an
|
|
108
|
-
* edit to `.return({ ... })` changes nothing else on the node. Used only to
|
|
109
|
-
* decide "did the user edit this node?" against the baseline.
|
|
110
|
-
*/
|
|
111
|
-
function nodeUnchanged(a, b) {
|
|
112
|
-
if (!a || !b)
|
|
113
|
-
return false;
|
|
114
|
-
if (a.type !== b.type)
|
|
115
|
-
return false;
|
|
116
|
-
return JSON.stringify(a.inputs ?? {}) === JSON.stringify(b.inputs ?? {})
|
|
117
|
-
&& JSON.stringify(outputSources(a)) === JSON.stringify(outputSources(b));
|
|
118
|
-
}
|
|
119
|
-
/** Each output's `source` by output name; outputs without one are left out. */
|
|
120
|
-
function outputSources(n) {
|
|
121
|
-
const sources = {};
|
|
122
|
-
for (const [name, output] of Object.entries(n.outputs ?? {})) {
|
|
123
|
-
if (output && typeof output === 'object' && 'source' in output)
|
|
124
|
-
sources[name] = output.source;
|
|
125
|
-
}
|
|
126
|
-
return sources;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* The ids of the root nodes {@link mergeFlow} takes from `edited` rather than
|
|
130
|
-
* restoring from `original`: every node that is new, or that differs from its
|
|
131
|
-
* `baseline` counterpart. With no baseline, that is every node.
|
|
132
|
-
*
|
|
133
|
-
* For instance, with a baseline equal to `edited` and every node present in
|
|
134
|
-
* `original`, the answer is `[]` — a no-edit merge keeps them all.
|
|
135
|
-
*/
|
|
136
|
-
export function nodesTakenFromEdited(original, edited, baseline) {
|
|
137
|
-
const origNodes = byId(original.nodes);
|
|
138
|
-
const baseNodes = byId(baseline?.nodes);
|
|
139
|
-
return (edited.nodes ?? []).filter((e) => !isKept(e, origNodes, baseNodes, baseline)).map((e) => e.id);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* The keys of the subflows {@link mergeFlow} takes from `edited` rather than
|
|
143
|
-
* restoring from `original`: every subflow that is new, that the original does
|
|
144
|
-
* not have, or that differs from its `baseline` counterpart in any way the
|
|
145
|
-
* author can edit — a node's inputs, output sources or label, an edge, the
|
|
146
|
-
* scope's `variables` or `layout` (`subflowUnchanged`, #1322). With no
|
|
147
|
-
* baseline, that is every subflow. A kept subflow is the original's whole: its
|
|
148
|
-
* nodes, edges, variables and layout.
|
|
149
|
-
*/
|
|
150
|
-
export function subflowsTakenFromEdited(original, edited, baseline) {
|
|
151
|
-
return Object.keys(edited.subflows ?? {}).filter((key) => {
|
|
152
|
-
const orig = original.subflows?.[key];
|
|
153
|
-
const base = baseline?.subflows?.[key];
|
|
154
|
-
return !baseline || orig === undefined || base === undefined
|
|
155
|
-
|| !subflowUnchanged(edited.subflows[key], base);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
/** A node's canvas label, when it has one. */
|
|
159
|
-
const labelOf = (n) => n?.display?.label;
|
|
160
|
-
/**
|
|
161
|
-
* Whether an edited subflow equals its baseline: the same node ids, each node
|
|
162
|
-
* unchanged (`nodeUnchanged`) and under the same label, the same edges by
|
|
163
|
-
* their endpoints and handles (ids are compile's own), and the same
|
|
164
|
-
* `variables` and `layout` byte for byte. The scope's variables carry what the
|
|
165
|
-
* nodes do not — a step's `{ updates }` row, a child `.input()` type, a `.var()`
|
|
166
|
-
* default — and a kept subflow is the original's whole, so any of them changing
|
|
167
|
-
* has to make the subflow a taken one.
|
|
168
|
-
*/
|
|
169
|
-
function subflowUnchanged(e, b) {
|
|
170
|
-
const eNodes = byId(e.nodes);
|
|
171
|
-
const bNodes = byId(b.nodes);
|
|
172
|
-
if (eNodes.size !== bNodes.size)
|
|
173
|
-
return false;
|
|
174
|
-
for (const [id, n] of eNodes) {
|
|
175
|
-
const base = bNodes.get(id);
|
|
176
|
-
if (!nodeUnchanged(n, base) || labelOf(n) !== labelOf(base))
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
if (JSON.stringify(e.variables ?? null) !== JSON.stringify(b.variables ?? null))
|
|
180
|
-
return false;
|
|
181
|
-
// `layout.edges` is keyed by edge id, and ids come from one file-wide
|
|
182
|
-
// counter: a step added before the call shifts every id in the subflow
|
|
183
|
-
// without touching it. So routes are compared by the endpoints the id names
|
|
184
|
-
// (as the edges themselves are), and `layout.nodes` as it is.
|
|
185
|
-
if (JSON.stringify(layoutByEndpoints(e)) !== JSON.stringify(layoutByEndpoints(b)))
|
|
186
|
-
return false;
|
|
187
|
-
const eEdges = (e.edges ?? []).map(edgeKey).sort();
|
|
188
|
-
const bEdges = (b.edges ?? []).map(edgeKey).sort();
|
|
189
|
-
return eEdges.length === bEdges.length && eEdges.every((k, i) => k === bEdges[i]);
|
|
190
|
-
}
|
|
191
|
-
/** `<source>@<port>-><target>@<port>`: an edge's identity apart from its id. */
|
|
192
|
-
const edgeKey = (x) => `${x.sourceNodeId}@${x.sourcePort}->${x.targetNodeId}@${x.targetPort}`;
|
|
193
|
-
/** A scope's layout with its `edges` re-keyed by endpoints, so an id shift does not read as a change. */
|
|
194
|
-
function layoutByEndpoints(sf) {
|
|
195
|
-
const layout = sf.layout;
|
|
196
|
-
if (!layout || typeof layout !== 'object')
|
|
197
|
-
return layout ?? null;
|
|
198
|
-
const byId = new Map((sf.edges ?? []).map((x) => [x.id, edgeKey(x)]));
|
|
199
|
-
const { edges, ...rest } = layout;
|
|
200
|
-
const routes = Object.entries(edges ?? {}).map(([id, route]) => [byId.get(id) ?? id, route]).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
201
|
-
return { ...rest, edges: Object.fromEntries(routes) };
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Whether merge restores `e`'s ORIGINAL node instead of taking `e`: there is a
|
|
205
|
-
* baseline, the original has the node, and `e` equals its baseline counterpart.
|
|
206
|
-
* {@link mergeFlow}'s node loop and {@link nodesTakenFromEdited} both ask this.
|
|
207
|
-
*/
|
|
208
|
-
function isKept(e, origNodes, baseNodes, baseline) {
|
|
209
|
-
return !!baseline && origNodes.has(e.id) && nodeUnchanged(e, baseNodes.get(e.id));
|
|
210
|
-
}
|
|
211
100
|
/**
|
|
212
101
|
* Give an edited node the original's position, by id.
|
|
213
102
|
*
|
|
@@ -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);
|