@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/case/decompile.js +5 -1
- package/dist/case/serialize.js +24 -29
- package/dist/check.d.ts +8 -0
- package/dist/check.js +33 -2
- package/dist/cli/commands/flow.js +6 -0
- package/dist/cli-run.d.ts +2 -0
- package/dist/cli-run.js +8 -0
- package/dist/core/connector-configuration.d.ts +29 -2
- package/dist/core/connector-configuration.js +45 -3
- package/dist/core/connector-inputs.d.ts +10 -0
- package/dist/core/connector-inputs.js +16 -0
- package/dist/core/nodes/extensions.d.ts +23 -0
- package/dist/core/nodes/extensions.js +18 -0
- package/dist/core/nodes/family.d.ts +13 -0
- package/dist/core/nodes/node-type.d.ts +19 -1
- package/dist/core/nodes/package-definition.d.ts +82 -0
- package/dist/core/nodes/package-definition.js +162 -0
- package/dist/core/nodes/registry.d.ts +14 -2
- package/dist/core/nodes/registry.js +68 -25
- package/dist/decompile-cli.d.ts +1 -1
- package/dist/decompile-cli.js +14 -4
- package/dist/decompile.d.ts +14 -0
- package/dist/decompile.js +77 -9
- package/dist/extend.d.ts +81 -0
- package/dist/extend.js +56 -0
- package/dist/flow-node-registry.d.ts +7 -0
- package/dist/flow-node-registry.js +14 -0
- package/dist/merge-cli.js +12 -2
- package/dist/merge-compile.js +14 -1
- package/dist/node-packages.d.ts +68 -0
- package/dist/node-packages.js +279 -0
- package/dist/nodes/connector/flow/node-type.js +2 -6
- package/dist/serialize.d.ts +2 -1
- package/dist/serialize.js +10 -3
- package/package.json +6 -2
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;
|
package/dist/merge-compile.js
CHANGED
|
@@ -49,7 +49,12 @@ export async function baselineFromOriginal(originalPath, opts = {}, warn) {
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
});
|
|
52
|
-
|
|
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),
|
package/dist/serialize.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
2403
|
-
throw new Error(`internal SDK error: ${where}:
|
|
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
|
|
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.
|
|
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": "
|
|
99
|
+
"gitref": "d3566b616cb3addce005fe0b6962aa504adb79a3"
|
|
96
100
|
}
|