@uipath/maestro-builder-sdk 6.51.3 → 6.52.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/extend.js ADDED
@@ -0,0 +1,56 @@
1
+ import { contributeFamily } from './core/nodes/extensions.js';
2
+ import { builtinActionSpec } from './core/actions.js';
3
+ export { defineNodeDefinition, DEFAULT_PACKAGE_DEFINITION_VERSION, } from './core/nodes/package-definition.js';
4
+ /**
5
+ * Wrap a package factory's spec literal as the `FlowAction` instance `.step()`
6
+ * accepts, keeping the literal's own type. A built-in factory extends the SDK's
7
+ * `FlowActionSpec` union by augmenting an internal interface; a package has no
8
+ * such access, and does not need it: `.step(name, spec)` takes any `FlowAction`,
9
+ * and this is the one way to make one. The returned value IS the spec (the
10
+ * serializer reads `kind` and `inputs` off it) and is `instanceof FlowAction`.
11
+ *
12
+ * @param spec - The spec literal the factory built, `{ kind, inputs }`.
13
+ * @returns The same spec, typed as itself and as a `FlowAction`.
14
+ */
15
+ export function packageActionSpec(spec) {
16
+ return builtinActionSpec(spec);
17
+ }
18
+ /**
19
+ * The version of this extension contract. A package pins the version it was
20
+ * written against in `defineFlowNodeFamily({ contract })`; a mismatch is refused
21
+ * at import time, before any flow is built, with both numbers in the sentence.
22
+ * It moves only when a hook's signature changes.
23
+ */
24
+ export const EXTENSION_CONTRACT_VERSION = 1;
25
+ /**
26
+ * Define and register a node-type package's family with the Flow registry.
27
+ *
28
+ * Returns the family. Refuses, by throwing at import time, a contract version
29
+ * other than this SDK's and a family with no package name. The registry refuses,
30
+ * when it adopts the family (at compile time, before any node is serialized), a
31
+ * kind, type name, prefix or diagnostic code a built-in family or an earlier
32
+ * package owns, and a type that does not declare `definition: 'package'`.
33
+ */
34
+ export function defineFlowNodeFamily(definition) {
35
+ if (definition.contract !== EXTENSION_CONTRACT_VERSION) {
36
+ throw new Error(`node-type package ${definition.package?.name ?? definition.id} was written against extension contract `
37
+ + `${String(definition.contract)}, but this @uipath/maestro-builder-sdk provides ${EXTENSION_CONTRACT_VERSION}. `
38
+ + 'Update the package (or the SDK) so the two agree.');
39
+ }
40
+ if (typeof definition.package?.name !== 'string' || definition.package.name === '') {
41
+ throw new Error(`node-type family "${definition.id}" names no package; defineFlowNodeFamily needs package.name.`);
42
+ }
43
+ const family = {
44
+ id: definition.id,
45
+ title: definition.title,
46
+ types: definition.types,
47
+ diagnostics: definition.diagnostics,
48
+ package: { name: definition.package.name, ...(definition.package.version !== undefined ? { version: definition.package.version } : {}) },
49
+ ...(definition.check !== undefined ? { check: definition.check } : {}),
50
+ };
51
+ // Contributed, not registered: this entry stays light at import (FB009), so it
52
+ // never loads the Flow registry; the registry adopts the family when it loads
53
+ // (`core/nodes/extensions.ts`), and registers it at once if it already has.
54
+ contributeFamily(family);
55
+ return family;
56
+ }
@@ -11,3 +11,10 @@ import type { FlowContexts, FlowNodeFamily } from './flow-node-type.js';
11
11
  /** The families, in no significant order. Adding one is the whole of registering it. */
12
12
  export declare const FLOW_NODE_FAMILIES: readonly FlowNodeFamily[];
13
13
  export declare const FLOW_NODE_TYPES: NodeTypeRegistry<FlowContexts>;
14
+ /**
15
+ * Register a node-type package's family with the Flow registry
16
+ * (`@uipath/maestro-builder-sdk/extend`'s `defineFlowNodeFamily` calls this).
17
+ * Refused, with the package named, when the family collides with a built-in
18
+ * family or with a package registered earlier.
19
+ */
20
+ export declare function registerFlowNodeFamily(family: FlowNodeFamily): FlowNodeFamily;
@@ -7,6 +7,7 @@
7
7
  * is left to port.
8
8
  */
9
9
  import { NodeTypeRegistry } from './core/nodes/registry.js';
10
+ import { adoptContributedFamilies } from './core/nodes/extensions.js';
10
11
  import { hasBundledDefinition } from './flow-definitions.js';
11
12
  import { agentFlowFamily } from './nodes/agent/flow/family.js';
12
13
  import { aiPatternsFlowFamily } from './nodes/ai-patterns/flow/family.js';
@@ -25,3 +26,16 @@ import { transformFlowFamily } from './nodes/transform/flow/family.js';
25
26
  /** The families, in no significant order. Adding one is the whole of registering it. */
26
27
  export const FLOW_NODE_FAMILIES = [agentFlowFamily, aiPatternsFlowFamily, connectorFlowFamily, conversationFlowFamily, dataFabricFlowFamily, delayFlowFamily, documentFlowFamily, hitlFlowFamily, httpFlowFamily, mockFlowFamily, processFlowFamily, queueFlowFamily, scriptFlowFamily, transformFlowFamily];
27
28
  export const FLOW_NODE_TYPES = new NodeTypeRegistry('flow', FLOW_NODE_FAMILIES, { hasBundledDefinition });
29
+ // Node-type packages contribute their families through the light `/extend` entry;
30
+ // adopt the ones already contributed and register every later one as it arrives.
31
+ adoptContributedFamilies((family) => FLOW_NODE_TYPES.register(family));
32
+ /**
33
+ * Register a node-type package's family with the Flow registry
34
+ * (`@uipath/maestro-builder-sdk/extend`'s `defineFlowNodeFamily` calls this).
35
+ * Refused, with the package named, when the family collides with a built-in
36
+ * family or with a package registered earlier.
37
+ */
38
+ export function registerFlowNodeFamily(family) {
39
+ FLOW_NODE_TYPES.register(family);
40
+ return family;
41
+ }
package/dist/merge-cli.js CHANGED
@@ -49,11 +49,12 @@ import { MergeRefusedError, mergeFlowWithSidecars } from './merge.js';
49
49
  import { sidecarSetFromList } from './merge-sidecars.js';
50
50
  import { isWithin } from './path-within.js';
51
51
  import { resourceSourcesOfOtherFlows } from './project-resource-users.js';
52
- import { flagValue, runWhenInvokedDirectly } from './cli-run.js';
52
+ import { flagValue, flagValues, runWhenInvokedDirectly } from './cli-run.js';
53
53
  import { ensureTypeScriptRuntime } from './node-runtime.js';
54
54
  import { flowCheckOpts, openCliLibrary } from './cli-library.js';
55
55
  import { Bindings } from './bindings.js';
56
56
  import { defaultBindingsFile } from './workdir.js';
57
+ import { loadNodePackages } from './node-packages.js';
57
58
  import { baselineFromOriginal, checkEdited, classifyDrafts, compileSourceDraftTolerant, findingCounts, splitUnplaced } from './merge-compile.js';
58
59
  const USAGE = 'usage: flow-merge <original.flow> <edited.flow | Edited.flow.ts> [-o merged.flow] '
59
60
  + '[--baseline <baseline.flow> | --baseline-from <original.flow>] [--library <dir>] [--bindings <file>] [--strict]';
@@ -159,7 +160,7 @@ export async function run(argv) {
159
160
  console.error(USAGE);
160
161
  return 2;
161
162
  }
162
- const flags = new Set(['-o', '--baseline', '--baseline-from', '--library', '--bindings', '--connectors-local']);
163
+ const flags = new Set(['-o', '--baseline', '--baseline-from', '--library', '--bindings', '--connectors-local', '--package']);
163
164
  const positionals = argv.filter((a, i) => !flags.has(a) && !flags.has(argv[i - 1]) && !a.startsWith('-'));
164
165
  const [originalPath, editedPath] = positionals;
165
166
  if (!originalPath || !editedPath) {
@@ -200,6 +201,15 @@ export async function run(argv) {
200
201
  else {
201
202
  edited = JSON.parse(readFileSync(editedPath, 'utf8'));
202
203
  }
204
+ if (baselineFrom) {
205
+ // The baseline is a decompile of the original: load the node-type packages
206
+ // its definitions name, under the project's trust (package.json nodePackages
207
+ // or --package), so the decompiled source imports them rather than printing
208
+ // rawNode(...) — which would differ from the edit and refuse a clean merge.
209
+ const packages = await loadNodePackages(baselineFrom, JSON.parse(readFileSync(baselineFrom, 'utf8')), flagValues(argv, '--package'));
210
+ for (const message of packages.warnings)
211
+ console.error(`flow-merge: warning: ${message}`);
212
+ }
203
213
  const baselineCompile = baselineFrom
204
214
  ? await baselineFromOriginal(baselineFrom, compileOpts, (m) => console.error(`flow-merge: decompile warning: ${m}`))
205
215
  : undefined;
@@ -49,7 +49,12 @@ export async function baselineFromOriginal(originalPath, opts = {}, warn) {
49
49
  }
50
50
  },
51
51
  });
52
- const dir = mkdtempSync(join(tmpdir(), 'flow-merge-baseline-'));
52
+ // Beside the original, not in the system temp dir: the decompiled source imports
53
+ // the SDK, which `useThisSdkForAuthoredSource` resolves from anywhere, and any
54
+ // NODE-TYPE PACKAGE the original was written with, which Node resolves only by
55
+ // walking up from the file to a `node_modules` — the author's project's. A
56
+ // directory that cannot be written to (a read-only mount) falls back to tmp.
57
+ const dir = baselineDirectory(originalPath);
53
58
  try {
54
59
  writeFileSync(join(dir, 'package.json'), '{"type":"module"}\n');
55
60
  const file = join(dir, 'Baseline.flow.ts');
@@ -60,6 +65,14 @@ export async function baselineFromOriginal(originalPath, opts = {}, warn) {
60
65
  rmSync(dir, { recursive: true, force: true });
61
66
  }
62
67
  }
68
+ function baselineDirectory(originalPath) {
69
+ try {
70
+ return mkdtempSync(join(dirname(resolve(originalPath)), '.flow-merge-baseline-'));
71
+ }
72
+ catch {
73
+ return mkdtempSync(join(tmpdir(), 'flow-merge-baseline-'));
74
+ }
75
+ }
63
76
  /**
64
77
  * Sort each finding by what merge does with its node: `refused` when the node
65
78
  * comes from the edited flow (reason `edited`: the author changed or added it;
@@ -0,0 +1,68 @@
1
+ /** The `package.json` key the trust list lives under. */
2
+ export declare const NODE_PACKAGES_MANIFEST_KEY = "maestroBuilderSdk";
3
+ export declare const NODE_PACKAGES_LIST_KEY = "nodePackages";
4
+ export declare function isPackageName(spec: string): boolean;
5
+ export interface TrustedPackages {
6
+ readonly names: ReadonlySet<string>;
7
+ /** Where each trusted name came from: the manifest path, or `--package`. */
8
+ readonly sources: ReadonlyMap<string, string>;
9
+ /** The `package.json` consulted, when one was found above the file. */
10
+ readonly manifest?: string;
11
+ }
12
+ /** The nearest `package.json` at or above `dir`, or undefined. */
13
+ export declare function nearestPackageJson(dir: string): string | undefined;
14
+ /**
15
+ * The packages this run may load: the project's `nodePackages` list plus the
16
+ * `--package` names. An entry that is not a bare package name is refused with
17
+ * the file it came from, so a manifest cannot smuggle a path or a URL in.
18
+ */
19
+ export declare function trustedNodePackages(flowPath: string, flagged?: readonly string[]): TrustedPackages;
20
+ /**
21
+ * The packages a flow's definitions name for node types the registry does not
22
+ * know yet, keyed by package, with the node types each one would explain.
23
+ */
24
+ export declare function provenancePackages(flow: {
25
+ definitions?: unknown;
26
+ }): Map<string, string[]>;
27
+ /**
28
+ * Resolve `pkg` (and an optional `subpath`, `'./runtime'`) the way an ESM
29
+ * `import` from a file in `fromDir` would: walk up `node_modules` directories
30
+ * from there, and read the package's `exports` under import conditions — not
31
+ * `require.resolve`, whose CommonJS conditions refuse an import-only package and
32
+ * can pick a different entry of a dual one. Falls back to `main`, then
33
+ * `index.js`, for a package with no `exports`. Throws a sentence when nothing resolves.
34
+ */
35
+ export declare function resolvePackageEntry(pkg: string, fromDir: string, subpath?: string): string;
36
+ export interface LoadedNodePackages {
37
+ /** Packages imported, with where their trust came from. */
38
+ readonly loaded: ReadonlyArray<{
39
+ readonly package: string;
40
+ readonly trustedBy: string;
41
+ readonly resolved: string;
42
+ }>;
43
+ /** Packages the file names that the project does not trust; their nodes print as `rawNode`. */
44
+ readonly untrusted: ReadonlyArray<{
45
+ readonly package: string;
46
+ readonly nodeTypes: readonly string[];
47
+ }>;
48
+ /** Trusted packages that do not resolve from the file's directory. */
49
+ readonly missing: ReadonlyArray<{
50
+ readonly package: string;
51
+ readonly nodeTypes: readonly string[];
52
+ readonly error: string;
53
+ }>;
54
+ /** The warnings a tool prints, one sentence each. */
55
+ readonly warnings: readonly string[];
56
+ }
57
+ /** How to trust a package, as the warnings say it. */
58
+ export declare function trustHint(pkg: string): string;
59
+ /**
60
+ * Load every trusted package the flow needs (and every `--package`), resolved
61
+ * from the flow's directory. Importing a package registers its family through
62
+ * `/extend`; the registry adopts it at once, so a `decompile` that follows sees
63
+ * the types. Nothing is thrown for an untrusted or missing package: those are
64
+ * reported, and the caller's decompile falls back to `rawNode` for their nodes.
65
+ */
66
+ export declare function loadNodePackages(flowPath: string, flow: {
67
+ definitions?: unknown;
68
+ }, flagged?: readonly string[]): Promise<LoadedNodePackages>;
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Loading NODE-TYPE PACKAGES for a tool that has no author's module to import.
3
+ *
4
+ * `compile` needs none of this: the author's `.flow.ts` imports the package, the
5
+ * package registers its family (`@uipath/maestro-builder-sdk/extend`), and the
6
+ * build runs against a registry that knows the kinds. `decompile` and
7
+ * `merge --baseline-from` read a data-only `.flow`, so they have to learn which
8
+ * packages the file needs and load them — and loading a package runs its code.
9
+ *
10
+ * ## Provenance is discovery, not authorization
11
+ *
12
+ * A package type's definition in `definitions[]` carries `source.package`, the
13
+ * package that wrote it. That names what a node NEEDS; it never, on its own,
14
+ * causes an import. A package is loaded only when the project chose it:
15
+ *
16
+ * - listed under `"maestroBuilderSdk": { "nodePackages": [...] }` in the nearest
17
+ * `package.json` above the `.flow`, or
18
+ * - named with `--package <name>` on the command line.
19
+ *
20
+ * The trust boundary is the one the project already has — `npm install` plus its
21
+ * own manifest — and nothing in a `.flow` can widen it. An untrusted package's
22
+ * nodes are printed as `rawNode(...)`, and the warning names the package and the
23
+ * two ways to trust it. Specifiers are validated as bare package names before
24
+ * anything is resolved: no paths, no URLs.
25
+ *
26
+ * ## Resolution is relative to the workflow, not the tool
27
+ *
28
+ * A globally installed SDK has its own `node_modules`, where the project's
29
+ * packages are not. Every package is resolved from the `.flow`'s own directory
30
+ * (`createRequire` anchored at the file) and the resolved URL is imported. The
31
+ * package's own `@uipath/maestro-builder-sdk/extend` import is redirected to the
32
+ * running SDK (`sdk-resolve-hook.ts`), so its family lands in this registry even
33
+ * when the project has a different copy of the SDK, or none.
34
+ *
35
+ * See docs/EXTENSIBLE_NODE_TYPES_DESIGN.md §3.5.
36
+ */
37
+ import { existsSync, readFileSync } from 'node:fs';
38
+ import { dirname, join, resolve } from 'node:path';
39
+ import { pathToFileURL } from 'node:url';
40
+ import { FLOW_NODE_TYPES } from './flow-node-registry.js';
41
+ import { useThisSdkForAuthoredSource } from './sdk-resolve.js';
42
+ /** The `package.json` key the trust list lives under. */
43
+ export const NODE_PACKAGES_MANIFEST_KEY = 'maestroBuilderSdk';
44
+ export const NODE_PACKAGES_LIST_KEY = 'nodePackages';
45
+ /** A bare npm package name: `name` or `@scope/name`; no path, no URL, no version. */
46
+ const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
47
+ export function isPackageName(spec) {
48
+ return PACKAGE_NAME.test(spec) && spec.length <= 214;
49
+ }
50
+ /** The nearest `package.json` at or above `dir`, or undefined. */
51
+ export function nearestPackageJson(dir) {
52
+ let current = resolve(dir);
53
+ for (;;) {
54
+ const candidate = join(current, 'package.json');
55
+ if (existsSync(candidate))
56
+ return candidate;
57
+ const parent = dirname(current);
58
+ if (parent === current)
59
+ return undefined;
60
+ current = parent;
61
+ }
62
+ }
63
+ /**
64
+ * The packages this run may load: the project's `nodePackages` list plus the
65
+ * `--package` names. An entry that is not a bare package name is refused with
66
+ * the file it came from, so a manifest cannot smuggle a path or a URL in.
67
+ */
68
+ export function trustedNodePackages(flowPath, flagged = []) {
69
+ const names = new Set();
70
+ const sources = new Map();
71
+ for (const name of flagged) {
72
+ if (!isPackageName(name))
73
+ throw new Error(`--package ${JSON.stringify(name)} is not a bare npm package name (expected name or @scope/name).`);
74
+ names.add(name);
75
+ sources.set(name, '--package');
76
+ }
77
+ const manifest = nearestPackageJson(dirname(resolve(flowPath)));
78
+ if (manifest !== undefined) {
79
+ let parsed;
80
+ try {
81
+ parsed = JSON.parse(readFileSync(manifest, 'utf8'));
82
+ }
83
+ catch (error) {
84
+ throw new Error(`${manifest}: cannot read the nodePackages list: ${error.message}`);
85
+ }
86
+ const section = parsed?.[NODE_PACKAGES_MANIFEST_KEY];
87
+ const list = section?.[NODE_PACKAGES_LIST_KEY];
88
+ if (list !== undefined) {
89
+ if (!Array.isArray(list) || !list.every((entry) => typeof entry === 'string')) {
90
+ throw new Error(`${manifest}: "${NODE_PACKAGES_MANIFEST_KEY}"."${NODE_PACKAGES_LIST_KEY}" must be an array of package names.`);
91
+ }
92
+ for (const name of list) {
93
+ if (!isPackageName(name)) {
94
+ throw new Error(`${manifest}: nodePackages entry ${JSON.stringify(name)} is not a bare npm package name (expected name or @scope/name).`);
95
+ }
96
+ if (!names.has(name)) {
97
+ names.add(name);
98
+ sources.set(name, manifest);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ return { names, sources, manifest };
104
+ }
105
+ /**
106
+ * The packages a flow's definitions name for node types the registry does not
107
+ * know yet, keyed by package, with the node types each one would explain.
108
+ */
109
+ export function provenancePackages(flow) {
110
+ const out = new Map();
111
+ const definitions = Array.isArray(flow.definitions) ? flow.definitions : [];
112
+ for (const def of definitions) {
113
+ const nodeType = typeof def?.nodeType === 'string' ? def.nodeType : undefined;
114
+ const source = def?.source;
115
+ const pkg = typeof source?.package === 'string' ? source.package : undefined;
116
+ if (nodeType === undefined || pkg === undefined)
117
+ continue;
118
+ if (FLOW_NODE_TYPES.byTypeName(nodeType) !== undefined)
119
+ continue;
120
+ const list = out.get(pkg) ?? [];
121
+ if (!list.includes(nodeType))
122
+ list.push(nodeType);
123
+ out.set(pkg, list);
124
+ }
125
+ return out;
126
+ }
127
+ /** The conditions an ESM `import` resolves `exports` with, in Node's order. */
128
+ const IMPORT_CONDITIONS = ['node', 'import', 'default'];
129
+ /** `exports` target for `subpath` under import conditions; undefined when the package does not export it. */
130
+ function exportsTarget(exportsField, subpath) {
131
+ const pick = (value) => {
132
+ if (typeof value === 'string')
133
+ return value;
134
+ if (Array.isArray(value)) {
135
+ for (const v of value) {
136
+ const hit = pick(v);
137
+ if (hit !== undefined)
138
+ return hit;
139
+ }
140
+ return undefined;
141
+ }
142
+ if (value !== null && typeof value === 'object') {
143
+ for (const [key, v] of Object.entries(value)) {
144
+ if (IMPORT_CONDITIONS.includes(key)) {
145
+ const hit = pick(v);
146
+ if (hit !== undefined)
147
+ return hit;
148
+ }
149
+ }
150
+ }
151
+ return undefined;
152
+ };
153
+ if (typeof exportsField === 'string' || Array.isArray(exportsField))
154
+ return subpath === '.' ? pick(exportsField) : undefined;
155
+ if (exportsField === null || typeof exportsField !== 'object')
156
+ return undefined;
157
+ const map = exportsField;
158
+ // Either a subpath map ({ ".": …, "./runtime": … }) or bare conditions for ".".
159
+ if (!Object.keys(map).some((k) => k.startsWith('.')))
160
+ return subpath === '.' ? pick(map) : undefined;
161
+ if (Object.hasOwn(map, subpath))
162
+ return pick(map[subpath]);
163
+ // Subpath patterns, as Node resolves them: a key with one `*` matches when the
164
+ // subpath has its prefix and suffix; the longest prefix wins, and the matched
165
+ // part replaces every `*` in the target ("./*": "./dist/*.js" → ./dist/runtime.js).
166
+ let best;
167
+ for (const key of Object.keys(map)) {
168
+ const star = key.indexOf('*');
169
+ if (star < 0 || key.indexOf('*', star + 1) >= 0)
170
+ continue;
171
+ const prefix = key.slice(0, star);
172
+ const suffix = key.slice(star + 1);
173
+ if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix) || subpath.length < prefix.length + suffix.length)
174
+ continue;
175
+ if (best === undefined || prefix.length > best.prefix.length)
176
+ best = { key, prefix, match: subpath.slice(prefix.length, subpath.length - suffix.length) };
177
+ }
178
+ if (best === undefined)
179
+ return undefined;
180
+ const target = pick(map[best.key]);
181
+ return target === undefined ? undefined : target.split('*').join(best.match);
182
+ }
183
+ /**
184
+ * Resolve `pkg` (and an optional `subpath`, `'./runtime'`) the way an ESM
185
+ * `import` from a file in `fromDir` would: walk up `node_modules` directories
186
+ * from there, and read the package's `exports` under import conditions — not
187
+ * `require.resolve`, whose CommonJS conditions refuse an import-only package and
188
+ * can pick a different entry of a dual one. Falls back to `main`, then
189
+ * `index.js`, for a package with no `exports`. Throws a sentence when nothing resolves.
190
+ */
191
+ export function resolvePackageEntry(pkg, fromDir, subpath = '.') {
192
+ let current = resolve(fromDir);
193
+ for (;;) {
194
+ const root = join(current, 'node_modules', ...pkg.split('/'));
195
+ const manifestPath = join(root, 'package.json');
196
+ if (existsSync(manifestPath)) {
197
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
198
+ let target;
199
+ if (manifest.exports !== undefined) {
200
+ target = exportsTarget(manifest.exports, subpath);
201
+ if (target === undefined)
202
+ throw new Error(`${pkg} does not export "${subpath}" for import (${manifestPath}).`);
203
+ }
204
+ else if (subpath === '.') {
205
+ target = typeof manifest.main === 'string' ? manifest.main : 'index.js';
206
+ }
207
+ else {
208
+ target = subpath;
209
+ }
210
+ const file = resolve(root, target);
211
+ if (!existsSync(file))
212
+ throw new Error(`${pkg}'s entry ${target} is missing (${file}).`);
213
+ return file;
214
+ }
215
+ const parent = dirname(current);
216
+ if (parent === current)
217
+ throw new Error(`Cannot find package '${pkg}' in any node_modules above ${fromDir}.`);
218
+ current = parent;
219
+ }
220
+ }
221
+ /** How to trust a package, as the warnings say it. */
222
+ export function trustHint(pkg) {
223
+ return `add it to package.json ("${NODE_PACKAGES_MANIFEST_KEY}": { "${NODE_PACKAGES_LIST_KEY}": [${JSON.stringify(pkg)}] }) or pass --package ${pkg}`;
224
+ }
225
+ /**
226
+ * Load every trusted package the flow needs (and every `--package`), resolved
227
+ * from the flow's directory. Importing a package registers its family through
228
+ * `/extend`; the registry adopts it at once, so a `decompile` that follows sees
229
+ * the types. Nothing is thrown for an untrusted or missing package: those are
230
+ * reported, and the caller's decompile falls back to `rawNode` for their nodes.
231
+ */
232
+ export async function loadNodePackages(flowPath, flow, flagged = []) {
233
+ const trusted = trustedNodePackages(flowPath, flagged);
234
+ const needed = provenancePackages(flow);
235
+ // Every package the project trusts is a candidate, not only the ones provenance
236
+ // names: a hand-written or older .flow whose definition carries no
237
+ // `source.package` is served by listing the package (or passing --package).
238
+ for (const name of trusted.names)
239
+ if (!needed.has(name))
240
+ needed.set(name, []);
241
+ const loaded = [];
242
+ const untrusted = [];
243
+ const missing = [];
244
+ const warnings = [];
245
+ const fromDir = dirname(resolve(flowPath));
246
+ for (const [pkg, nodeTypes] of needed) {
247
+ if (!trusted.names.has(pkg)) {
248
+ untrusted.push({ package: pkg, nodeTypes });
249
+ warnings.push(`node type${nodeTypes.length === 1 ? '' : 's'} ${nodeTypes.map((t) => `"${t}"`).join(', ')} come${nodeTypes.length === 1 ? 's' : ''} from package ${pkg} `
250
+ + `(definitions[].source.package), which this project does not trust; ${trustHint(pkg)}. Its nodes are printed as rawNode(...).`);
251
+ continue;
252
+ }
253
+ if (!isPackageName(pkg)) {
254
+ // A trusted list never holds one of these (trustedNodePackages refuses), so this is provenance lying.
255
+ untrusted.push({ package: pkg, nodeTypes });
256
+ warnings.push(`definitions[].source.package ${JSON.stringify(pkg)} is not a bare npm package name; its nodes are printed as rawNode(...).`);
257
+ continue;
258
+ }
259
+ let resolved;
260
+ try {
261
+ resolved = resolvePackageEntry(pkg, fromDir);
262
+ }
263
+ catch (error) {
264
+ missing.push({ package: pkg, nodeTypes, error: error.message });
265
+ warnings.push(`package ${pkg} is trusted (${trusted.sources.get(pkg)}) but does not resolve from ${fromDir}: ${error.message} Install it beside the flow (npm install ${pkg}); its nodes are printed as rawNode(...).`);
266
+ continue;
267
+ }
268
+ // A package imports `@uipath/maestro-builder-sdk/extend`. Point that at THIS
269
+ // SDK, as compile does for authored source: a second copy (the project's own,
270
+ // under a globally installed tool) would register the family into a registry
271
+ // this run never reads. Installed only now, right before a real import, so a
272
+ // run that falls back to rawNode for every package never needs the hook —
273
+ // a bundled CLI has none (tests/cli-bundle.test.ts).
274
+ useThisSdkForAuthoredSource();
275
+ await import(pathToFileURL(resolved).href);
276
+ loaded.push({ package: pkg, trustedBy: trusted.sources.get(pkg), resolved });
277
+ }
278
+ return { loaded, untrusted, missing, warnings };
279
+ }
@@ -1,7 +1,7 @@
1
1
  import { BindingRefusal, bindingMissingMessage, missingBindingLabels } from '../../../core/binding-messages.js';
2
2
  import { prepareCommand } from '../../../core/cli-spelling.js';
3
3
  import { buildConfiguration } from '../../../core/connector-configuration.js';
4
- import { CONNECTOR_INPUT_MISSING, FLOW_EXPR_DIALECT, buildConnectorInputs, transportHttpMethod } from '../../../core/connector-inputs.js';
4
+ import { CONNECTOR_INPUT_MISSING, FLOW_EXPR_DIALECT, buildConnectorInputs, replayInputsOf, transportHttpMethod } from '../../../core/connector-inputs.js';
5
5
  import { ConnectorResolveError } from '../../../core/connector-resolve-error.js';
6
6
  import { definitionHasErrorPort } from '../../../core/error-port.js';
7
7
  import { Expr } from '../../../core/expr.js';
@@ -141,11 +141,7 @@ function serializeConnector(spec, ctx) {
141
141
  ? library.lookupSpecs(spec.key, spec.action, spec.version, spec.object)
142
142
  : {},
143
143
  });
144
- const replayInputs = {
145
- ...(connectorInputs.bodyParameters ?? {}),
146
- ...(connectorInputs.pathParameters ?? {}),
147
- ...(connectorInputs.queryParameters ?? {}),
148
- };
144
+ const replayInputs = replayInputsOf(connectorInputs);
149
145
  const detail = {
150
146
  connector: rc.connectorKey,
151
147
  method: transportHttpMethod(rc.httpMethod),
@@ -30,7 +30,8 @@ export type DraftCode = BindingRefusalCode | typeof CONNECTOR_INPUT_MISSING | 'C
30
30
  export interface DraftFinding {
31
31
  /** The emitted node's id. */
32
32
  nodeId: string;
33
- code: DraftCode;
33
+ /** A {@link DraftCode}, or a node-type package's own code reported through `ctx.draft`. */
34
+ code: DraftCode | string;
34
35
  /** The sentence the strict compile refuses with. */
35
36
  message: string;
36
37
  }
package/dist/serialize.js CHANGED
@@ -2399,8 +2399,15 @@ export function serialize(built, opts = {}) {
2399
2399
  // makeNode, with the message every definition's mismatch gets.
2400
2400
  def = resolveContributionDefinition(contribution.definition, entry.type.versionedByDefinition ? undefined : version, where);
2401
2401
  }
2402
- else if (impl.definition === 'per-tenant' && resolved.carried === undefined) {
2403
- throw new Error(`internal SDK error: ${where}: per-tenant node type ${impl.typeName}@${version} returned no definition.`);
2402
+ else if (impl.definition !== 'bundled' && resolved.carried === undefined) {
2403
+ throw new Error(`internal SDK error: ${where}: ${impl.definition} node type ${impl.typeName}@${version} returned no definition.`);
2404
+ }
2405
+ // A package definition carries its provenance: which package wrote it, at
2406
+ // which version. Decompile loads the package's authoring half from it, and
2407
+ // the local engine the package's runtime half (docs/EXTENSIBLE_NODE_TYPES_DESIGN.md).
2408
+ if (impl.definition === 'package' && def !== undefined) {
2409
+ const origin = entry.family.package;
2410
+ def = { ...def, source: { package: origin.name, ...(origin.version !== undefined ? { version: origin.version } : {}) } };
2404
2411
  }
2405
2412
  // A type that owns a family of names (`typeNamePrefix`) is placed under the
2406
2413
  // name its definition carries — `uipath.core.function.<key>`, not the prefix.
@@ -2413,7 +2420,7 @@ export function serialize(built, opts = {}) {
2413
2420
  throw new Error(`internal SDK error: ${where}: node type ${impl.typeName}@${version} supplied a definition for `
2414
2421
  + `${JSON.stringify(nodeType)}, which is not a name it owns under "${prefix}".`);
2415
2422
  }
2416
- if (impl.definition === 'per-tenant') {
2423
+ if (impl.definition !== 'bundled') {
2417
2424
  // At a version the flow carries, the carried definition answers: the
2418
2425
  // type's declaration speaks for the versions it registers, and an older
2419
2426
  // tenant definition (an inline agent at 1.1) may have no error handle.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.51.3",
3
+ "version": "6.52.1",
4
4
  "description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.uipath.com/maestro",
@@ -29,6 +29,10 @@
29
29
  "types": "./dist/bpmn/bpmn-sdk.d.ts",
30
30
  "import": "./dist/bpmn/bpmn-sdk.js"
31
31
  },
32
+ "./extend": {
33
+ "types": "./dist/extend.d.ts",
34
+ "import": "./dist/extend.js"
35
+ },
32
36
  "./cli": {
33
37
  "types": "./dist/cli/index.d.ts",
34
38
  "import": "./dist/cli/index.js"
@@ -92,5 +96,5 @@
92
96
  "@types/node": "^22.7.0",
93
97
  "esbuild": "^0.28.1"
94
98
  },
95
- "gitref": "5e87eb230755d35a3c37ebb1921442ab5e196245"
99
+ "gitref": "d3566b616cb3addce005fe0b6962aa504adb79a3"
96
100
  }