@uipath/maestro-builder-sdk 6.16.14 → 6.17.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.
Files changed (74) hide show
  1. package/README.md +2 -2
  2. package/dist/api-index.md +115 -114
  3. package/dist/api-members.md +295 -291
  4. package/dist/bindings-v2.d.ts +39 -1
  5. package/dist/bindings-v2.js +289 -9
  6. package/dist/bpmn/compile-cli.js +11 -2
  7. package/dist/bpmn/decompile.js +2 -1
  8. package/dist/bpmn/registry-fallback.js +9 -7
  9. package/dist/case/compile-cli.js +16 -6
  10. package/dist/case/decompile-cli.js +2 -1
  11. package/dist/case/decompile.js +3 -2
  12. package/dist/case/merge-cli.js +3 -2
  13. package/dist/check-cli.js +2 -30
  14. package/dist/check.js +278 -55
  15. package/dist/cli-library.d.ts +18 -4
  16. package/dist/cli-library.js +24 -6
  17. package/dist/cli-result.d.ts +9 -0
  18. package/dist/cli-result.js +12 -0
  19. package/dist/compile-cli.js +82 -86
  20. package/dist/core/actions.d.ts +71 -17
  21. package/dist/core/agentic-process-forms.d.ts +51 -0
  22. package/dist/core/agentic-process-forms.js +49 -0
  23. package/dist/core/binding-messages.d.ts +5 -0
  24. package/dist/core/cli-library.d.ts +16 -7
  25. package/dist/core/cli-library.js +53 -19
  26. package/dist/core/comment-text.d.ts +29 -0
  27. package/dist/core/comment-text.js +40 -0
  28. package/dist/core/connector-checks.d.ts +18 -2
  29. package/dist/core/connector-checks.js +15 -5
  30. package/dist/core/contribution-bindings.d.ts +73 -0
  31. package/dist/core/contribution-bindings.js +105 -0
  32. package/dist/core/error-port.d.ts +25 -0
  33. package/dist/core/error-port.js +36 -0
  34. package/dist/core/expr-check.js +7 -2
  35. package/dist/core/expr.d.ts +11 -5
  36. package/dist/core/expr.js +9 -3
  37. package/dist/core/json-file.d.ts +1 -0
  38. package/dist/core/json-file.js +12 -0
  39. package/dist/core/library.d.ts +19 -0
  40. package/dist/core/library.js +22 -6
  41. package/dist/core/script-return.js +4 -1
  42. package/dist/core/typed-raw-node.js +1 -0
  43. package/dist/core/typescript-module.d.ts +2 -0
  44. package/dist/core/typescript-module.js +18 -0
  45. package/dist/core-definitions.json +210 -0
  46. package/dist/decompile-cli.js +2 -1
  47. package/dist/decompile.js +64 -24
  48. package/dist/emit.js +4 -3
  49. package/dist/flow-sdk.d.ts +18 -12
  50. package/dist/flow-sdk.js +19 -8
  51. package/dist/flow-step-tree.d.ts +2 -3
  52. package/dist/flow-step-tree.js +2 -3
  53. package/dist/load.js +1 -1
  54. package/dist/merge-cli.js +3 -2
  55. package/dist/merge.d.ts +2 -1
  56. package/dist/merge.js +0 -0
  57. package/dist/path-within.d.ts +33 -0
  58. package/dist/path-within.js +41 -0
  59. package/dist/registry/archive/extract.js +2 -5
  60. package/dist/registry/curated-overlay.js +3 -2
  61. package/dist/registry/generators/descriptors.js +20 -18
  62. package/dist/registry/generators/triggers.js +4 -4
  63. package/dist/registry/prepare/messages.js +2 -1
  64. package/dist/registry/prepare/source-labels.js +60 -1
  65. package/dist/registry/prepare-cli.js +14 -4
  66. package/dist/registry/pull-cli.js +3 -2
  67. package/dist/registry/uip/client.d.ts +28 -3
  68. package/dist/registry/uip/client.js +95 -34
  69. package/dist/registry/uip/host.d.ts +41 -0
  70. package/dist/registry/uip/host.js +312 -0
  71. package/dist/serialize.d.ts +7 -0
  72. package/dist/serialize.js +414 -277
  73. package/dist/workdir.js +1 -1
  74. package/package.json +3 -3
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { existsSync } from 'node:fs';
18
18
  import { join } from 'node:path';
19
- import { Library } from './library.js';
19
+ import { ConnectorResolveError, Library } from './library.js';
20
20
  import { resolveLibraryDir } from './library-cache.js';
21
21
  import { flagValue } from '../cli-run.js';
22
22
  import { defaultOverlayDir } from '../workdir.js';
@@ -41,19 +41,28 @@ export function openCliLibrary(argv) {
41
41
  * The connector half of a family's check options, from a library the CLI already
42
42
  * opened.
43
43
  *
44
- * Every callback fails OPEN: an entry the library cannot resolve, or no library
45
- * at all, means "unknown", and an unknown fact fires no rule. That is what lets
46
- * `check` stay the fast inner-loop rung — a process with no connectors needs no
47
- * library, and one whose connector is absent from the library is `compile`'s
48
- * business to name, not this pass's to guess at.
44
+ * Every fact callback fails OPEN: no library at all, or an entry the library
45
+ * cannot resolve, means "unknown", and an unknown fact fires no rule. That is
46
+ * what lets `check` stay the fast inner-loop rung — a process with no
47
+ * connectors needs no library. The one exception is the resolution itself:
48
+ * `connectorResolveError` reports the refusal `compile` would throw for an
49
+ * action, the way `eventResolveError` does for an event, so `check` never
50
+ * passes a connector step `compile` refuses (#948).
51
+ *
52
+ * `inputErrors` is the family's field-contract validator. The default speaks
53
+ * the `=`-expression dialect BPMN uses; Flow passes its own, which renders its
54
+ * expressions (see `flowCheckOpts` in `../cli-library.ts`).
49
55
  *
50
56
  * The `resolutions` are the overlay's: without them every lookup already
51
57
  * recorded by `registry prepare --resolve` would be reported as still owed,
52
58
  * telling the author to re-run a prepare they already ran.
53
59
  */
54
- export function connectorCheckOpts(library, bindings) {
60
+ export function connectorCheckOpts(library, bindings, inputErrors = (rc, inputs, resolutions, lookups) => connectorInputErrors(rc, inputs, resolutions, lookups, { dialect: EQUALS_EXPR_DIALECT })) {
55
61
  if (!library)
56
62
  return bindings ? { bindings } : {};
63
+ // One memoized resolve per connector step, shared by `connectorResolveError`
64
+ // and `connectorInputs`, so the second hook does not resolve again.
65
+ const resolve = connectorResolver(library);
57
66
  return {
58
67
  eventResolveError: eventResolveErrorFor(library),
59
68
  ...byoaCheckOpts(library),
@@ -85,26 +94,51 @@ export function connectorCheckOpts(library, bindings) {
85
94
  return undefined;
86
95
  }
87
96
  },
97
+ // The refusal compile throws for an operation it cannot resolve, with its
98
+ // code and text: CONNECTOR_NOT_IN_LIBRARY, CONNECTOR_OBJECT_REQUIRED,
99
+ // CONNECTOR_OBJECT_NOT_GENERIC.
100
+ connectorResolveError: (spec) => {
101
+ const r = resolve(spec);
102
+ return 'error' in r ? r.error : undefined;
103
+ },
88
104
  // The field contract compile enforces, from the same validator, so the inner
89
105
  // loop and the emit rung cannot disagree about a missing required field, an
90
- // unknown key or a mistyped value. An operation the library cannot resolve is
91
- // compile's business to name — here it just means "unknown".
92
- //
93
- // The `=`-expression dialect, because this bag serves the families that spell
94
- // an expression that way. Flow binds its own `connectorInputErrors` in
95
- // `serialize.ts`, which carries `renderDeep` with it, and overrides this.
106
+ // unknown key or a mistyped value. An unresolvable operation is
107
+ // `connectorResolveError`'s to report; here it just means "unknown".
96
108
  connectorInputs: (spec, inputs) => {
97
- try {
98
- const rc = library.resolve(spec.key, spec.action, spec.version, spec.object);
99
- return connectorInputErrors(rc, inputs, library.lookupResolutions ?? {}, library.lookupSpecs(spec.key, spec.action, spec.version, spec.object) ?? {}, { dialect: EQUALS_EXPR_DIALECT });
100
- }
101
- catch {
109
+ const r = resolve(spec);
110
+ if (!('rc' in r))
102
111
  return undefined;
103
- }
112
+ return inputErrors(r.rc, inputs, library.lookupResolutions ?? {}, library.lookupSpecs(spec.key, spec.action, spec.version, spec.object) ?? {});
104
113
  },
105
114
  ...(bindings ? { bindings } : {}),
106
115
  };
107
116
  }
117
+ /**
118
+ * `library.resolve` for one connector step, memoized per step for the life of
119
+ * one check's options. A {@link ConnectorResolveError} is the refusal compile
120
+ * throws and becomes `error`; anything else thrown is not a verdict about the
121
+ * author's source and stays "unknown", as before.
122
+ */
123
+ function connectorResolver(library) {
124
+ const memo = new Map();
125
+ return (spec) => {
126
+ const id = JSON.stringify([spec.key, spec.action, spec.version ?? null, spec.object ?? null]);
127
+ let r = memo.get(id);
128
+ if (!r) {
129
+ try {
130
+ r = { rc: library.resolve(spec.key, spec.action, spec.version, spec.object) };
131
+ }
132
+ catch (error) {
133
+ r = error instanceof ConnectorResolveError
134
+ ? { error: { code: error.code, message: error.message } }
135
+ : { unknown: true };
136
+ }
137
+ memo.set(id, r);
138
+ }
139
+ return r;
140
+ };
141
+ }
108
142
  /**
109
143
  * `check`'s EVENT_UNKNOWN verdict: the message `library.resolveEvent` throws
110
144
  * for a subscription, or `undefined` when it resolves.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Text that is safe inside a generated `//` or `/* … *\/` comment.
3
+ *
4
+ * A decompiler or generator that splices an input string into a comment hands that
5
+ * string the rest of the comment: a line terminator ends a `//` comment, `*\/` ends a
6
+ * block, and whatever follows is CODE in a file `compile` later executes. Those are the
7
+ * only two ways out of a JavaScript comment, so replacing both (line terminators — LF,
8
+ * CR, U+2028, U+2029 — with one space, `*\/` with `* /`) is complete, not a denylist.
9
+ * The replacements are the ones registry/generators already used, so their output keeps
10
+ * its bytes. FB006 holds every comment interpolation to this function.
11
+ *
12
+ * The result is ONE line: a multi-line value is flattened, which is right for a `//`
13
+ * line or a single ` * ` body line. To keep a value's line breaks, use `commentLines`
14
+ * (one escaped, prefixed line per input line) or `docComment` (a whole `/** … *\/`).
15
+ */
16
+ export declare function commentText(value: string | number): string;
17
+ /**
18
+ * `value` split on its line terminators, each line escaped by `commentText` and
19
+ * given `prefix` (`'// '`, `' * '`), trailing whitespace trimmed so a blank line is
20
+ * a bare `//` or ` *`. The lines are joined with `\n` by the caller, so a value's
21
+ * paragraph breaks survive and none of them can end the comment.
22
+ */
23
+ export declare function commentLines(value: string | number, prefix?: string): string[];
24
+ /**
25
+ * A JSDoc comment holding `value`, at `indent`. A one-line value keeps the
26
+ * one-line form, `/** value *\/`, so its bytes do not change; a multi-line value
27
+ * gets an open block with one ` * ` line per input line.
28
+ */
29
+ export declare function docComment(value: string | number, indent?: string): string[];
@@ -0,0 +1,40 @@
1
+ /** The line terminators (LF, CR, CRLF, U+2028, U+2029): the one way out of a `//` comment. */
2
+ const LINE_BREAK = /\r\n?|[\n\u2028\u2029]/g;
3
+ /**
4
+ * Text that is safe inside a generated `//` or `/* … *\/` comment.
5
+ *
6
+ * A decompiler or generator that splices an input string into a comment hands that
7
+ * string the rest of the comment: a line terminator ends a `//` comment, `*\/` ends a
8
+ * block, and whatever follows is CODE in a file `compile` later executes. Those are the
9
+ * only two ways out of a JavaScript comment, so replacing both (line terminators — LF,
10
+ * CR, U+2028, U+2029 — with one space, `*\/` with `* /`) is complete, not a denylist.
11
+ * The replacements are the ones registry/generators already used, so their output keeps
12
+ * its bytes. FB006 holds every comment interpolation to this function.
13
+ *
14
+ * The result is ONE line: a multi-line value is flattened, which is right for a `//`
15
+ * line or a single ` * ` body line. To keep a value's line breaks, use `commentLines`
16
+ * (one escaped, prefixed line per input line) or `docComment` (a whole `/** … *\/`).
17
+ */
18
+ export function commentText(value) {
19
+ return String(value).replace(LINE_BREAK, ' ').replaceAll('*/', '* /');
20
+ }
21
+ /**
22
+ * `value` split on its line terminators, each line escaped by `commentText` and
23
+ * given `prefix` (`'// '`, `' * '`), trailing whitespace trimmed so a blank line is
24
+ * a bare `//` or ` *`. The lines are joined with `\n` by the caller, so a value's
25
+ * paragraph breaks survive and none of them can end the comment.
26
+ */
27
+ export function commentLines(value, prefix = '') {
28
+ return String(value).split(LINE_BREAK).map((line) => `${prefix}${commentText(line)}`.trimEnd());
29
+ }
30
+ /**
31
+ * A JSDoc comment holding `value`, at `indent`. A one-line value keeps the
32
+ * one-line form, `/** value *\/`, so its bytes do not change; a multi-line value
33
+ * gets an open block with one ` * ` line per input line.
34
+ */
35
+ export function docComment(value, indent = '') {
36
+ const body = commentLines(value);
37
+ if (body.length === 1)
38
+ return [`${indent}/** ${commentText(value)} */`];
39
+ return [`${indent}/**`, ...body.map((line) => `${indent} * ${line}`.trimEnd()), `${indent} */`];
40
+ }
@@ -113,6 +113,21 @@ export interface ConnectorCheckOpts {
113
113
  resolutions?: LookupResolutions;
114
114
  /** Whether a connector step's schema is materialized locally. */
115
115
  schemaReadiness?: (connector: string, action: string, version?: string, object?: string) => SchemaReadiness | undefined;
116
+ /**
117
+ * Why `compile` could not resolve this operation at all (`library.resolve`
118
+ * throws), or `undefined` when it resolves. Checked before
119
+ * {@link ConnectorCheckOpts.connectorInputs}, which answers `undefined` for an
120
+ * unresolvable operation and so cannot tell "not in the library" from "no facts".
121
+ */
122
+ connectorResolveError?: (spec: {
123
+ key: string;
124
+ action: string;
125
+ version?: string;
126
+ object?: string;
127
+ }) => {
128
+ code: string;
129
+ message: string;
130
+ } | undefined;
116
131
  /** What `compile` would refuse these inputs with, or `undefined` when it would accept them. */
117
132
  connectorInputs?: (spec: {
118
133
  key: string;
@@ -157,9 +172,10 @@ export declare function checkConnectorLookups(where: string, spec: ConnectorSpec
157
172
  */
158
173
  export declare function checkConnectorSchema(where: string, spec: ConnectorSpec, opts: ConnectorCheckOpts, dialect: ExprDialect, report: ReportConnector): void;
159
174
  /**
160
- * The field contract and the binding names `compile` enforces, reported here first.
175
+ * The operation, its field contract and the binding names `compile` enforces,
176
+ * reported here first.
161
177
  *
162
- * Both rules run only with the facts in hand (a library for the fields, a
178
+ * Each rule runs only with the facts in hand (a library for the operation and its fields, a
163
179
  * bindings.json for the names); without them `check` stays a pure function of the
164
180
  * built model and says nothing, as before.
165
181
  */
@@ -321,18 +321,28 @@ export function checkConnectorSchema(where, spec, opts, dialect, report) {
321
321
  }
322
322
  }
323
323
  /**
324
- * The field contract and the binding names `compile` enforces, reported here first.
324
+ * The operation, its field contract and the binding names `compile` enforces,
325
+ * reported here first.
325
326
  *
326
- * Both rules run only with the facts in hand (a library for the fields, a
327
+ * Each rule runs only with the facts in hand (a library for the operation and its fields, a
327
328
  * bindings.json for the names); without them `check` stays a pure function of the
328
329
  * built model and says nothing, as before.
329
330
  */
330
331
  export function checkConnectorContract(where, spec, opts, report,
331
332
  /** The activity's own id, for {@link connectionConnectorConflicts}; omit to record nothing. */
332
333
  id) {
333
- const inputErr = opts.connectorInputs?.({ key: spec.key, action: spec.action, version: spec.version, object: spec.object }, spec.inputs ?? {});
334
- if (inputErr) {
335
- report({ level: 'error', code: 'CONNECTOR_INPUT', message: `${where}: ${inputErr}` });
334
+ const target = { key: spec.key, action: spec.action, version: spec.version, object: spec.object };
335
+ // The operation itself first: an operation compile cannot resolve has no field
336
+ // contract to check, and `connectorInputs` answers "unknown" for it.
337
+ const unresolved = opts.connectorResolveError?.(target);
338
+ if (unresolved) {
339
+ report({ level: 'error', code: unresolved.code, message: `${where}: ${unresolved.message}` });
340
+ }
341
+ else {
342
+ const inputErr = opts.connectorInputs?.(target, spec.inputs ?? {});
343
+ if (inputErr) {
344
+ report({ level: 'error', code: 'CONNECTOR_INPUT', message: `${where}: ${inputErr}` });
345
+ }
336
346
  }
337
347
  checkBindingLabels(where, spec, opts, (code, message) => report({ level: 'error', code, message }), { key: spec.key, action: spec.action }, id === undefined ? undefined : { id, connector: spec.key });
338
348
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The `bindings[]` entries a contribution-lowered node needs and did not supply
3
+ * (flow-builder-sdk#942).
4
+ *
5
+ * A definition's `model.context` names its resource through `<bindings.NAME>`
6
+ * placeholders, and declares each NAME in `model.bindings.values`. The typed
7
+ * emitters (rpa, api-workflow, queue, ixp, connector) close those names by hand.
8
+ * A `rawNode` or a hand-written `FlowAction` returns a definition and inputs, and
9
+ * usually no `bindings` — so before this, nothing closed them, and compile failed
10
+ * at `BINDING_PLACEHOLDER_UNRESOLVED` while `check` passed.
11
+ *
12
+ * For instance, the manifest `registry get --local` returns for a sibling Flow:
13
+ *
14
+ * ```json
15
+ * "model": { "bindings": { "resource": "process", "resourceSubType": "Flow", "resourceKey": "k1",
16
+ * "values": [{ "name": "name", "default": "Notifier" }, { "name": "folderPath", "default": "" }] },
17
+ * "context": [{ "name": "name", "value": "<bindings.name>" }, { "name": "folderPath", "value": "<bindings.folderPath>" }] }
18
+ * ```
19
+ *
20
+ * needs two `process` entries keyed `k1`: `name` = "Notifier" and `folderPath` = "".
21
+ * {@link planContributionBindings} reads exactly those facts off the definition.
22
+ *
23
+ * `connection` resources are out of scope: a connector definition's entries are
24
+ * keyed by the tenant connection id, which the definition does not carry. A raw
25
+ * connector node is refused for that reason (`connector-raw-node.ts`).
26
+ *
27
+ * Pure: `serialize` lowers with it, and `check` runs it dry so the two agree.
28
+ */
29
+ /** One `bindings[]` entry, as the flow file carries it. */
30
+ export interface PlannedBinding {
31
+ name: string;
32
+ type: string;
33
+ resource: string;
34
+ resourceKey: string;
35
+ default?: string;
36
+ propertyAttribute: string;
37
+ resourceSubType?: string;
38
+ }
39
+ /** What one definition needs: the entries to build, and the names it cannot close. */
40
+ export interface ContributionBindingPlan {
41
+ /** The resource kind (`model.bindings.resource`), when it is one this plan closes. */
42
+ resource?: string;
43
+ /** The resource key every entry carries (`model.bindings.resourceKey`). */
44
+ resourceKey?: string;
45
+ /** One entry per referenced name the contribution did not supply. */
46
+ entries: PlannedBinding[];
47
+ /** One sentence per referenced name that cannot be closed, and why. Empty when every name closes. */
48
+ problems: string[];
49
+ }
50
+ /** The `<bindings.NAME>` names a definition's `model.context` references, in order, once each. */
51
+ export declare function referencedBindingNames(model: any): string[];
52
+ /**
53
+ * Plan the entries a contribution-lowered node needs: for each `<bindings.NAME>`
54
+ * its definition's `model.context` references and `supplied` does not cover, one
55
+ * entry built from the definition — `resource`, `resourceKey`, `resourceSubType`
56
+ * from `model.bindings`, and `name`, `default`, `propertyAttribute` from the
57
+ * matching `model.bindings.values` row.
58
+ *
59
+ * A supplied entry covers a name when it has the same `resource` and `name`: the
60
+ * contributor's own bindings win, as they always did.
61
+ *
62
+ * A name is a problem (not an entry) when the definition declares no
63
+ * `values` row for it, or carries no `resourceKey` — a per-tenant template such
64
+ * as the bundled `core.action.queue.create` (`resourceKey: ""`), which only a
65
+ * typed factory can fill.
66
+ *
67
+ * @param def - The resolved definition.
68
+ * @param supplied - The bindings the contribution returned.
69
+ */
70
+ export declare function planContributionBindings(def: any, supplied?: readonly {
71
+ resource?: string;
72
+ name?: string;
73
+ }[]): ContributionBindingPlan;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The `bindings[]` entries a contribution-lowered node needs and did not supply
3
+ * (flow-builder-sdk#942).
4
+ *
5
+ * A definition's `model.context` names its resource through `<bindings.NAME>`
6
+ * placeholders, and declares each NAME in `model.bindings.values`. The typed
7
+ * emitters (rpa, api-workflow, queue, ixp, connector) close those names by hand.
8
+ * A `rawNode` or a hand-written `FlowAction` returns a definition and inputs, and
9
+ * usually no `bindings` — so before this, nothing closed them, and compile failed
10
+ * at `BINDING_PLACEHOLDER_UNRESOLVED` while `check` passed.
11
+ *
12
+ * For instance, the manifest `registry get --local` returns for a sibling Flow:
13
+ *
14
+ * ```json
15
+ * "model": { "bindings": { "resource": "process", "resourceSubType": "Flow", "resourceKey": "k1",
16
+ * "values": [{ "name": "name", "default": "Notifier" }, { "name": "folderPath", "default": "" }] },
17
+ * "context": [{ "name": "name", "value": "<bindings.name>" }, { "name": "folderPath", "value": "<bindings.folderPath>" }] }
18
+ * ```
19
+ *
20
+ * needs two `process` entries keyed `k1`: `name` = "Notifier" and `folderPath` = "".
21
+ * {@link planContributionBindings} reads exactly those facts off the definition.
22
+ *
23
+ * `connection` resources are out of scope: a connector definition's entries are
24
+ * keyed by the tenant connection id, which the definition does not carry. A raw
25
+ * connector node is refused for that reason (`connector-raw-node.ts`).
26
+ *
27
+ * Pure: `serialize` lowers with it, and `check` runs it dry so the two agree.
28
+ */
29
+ /** The `<bindings.NAME>` names a definition's `model.context` references, in order, once each. */
30
+ export function referencedBindingNames(model) {
31
+ const names = [];
32
+ for (const row of Array.isArray(model?.context) ? model.context : []) {
33
+ const name = typeof row?.value === 'string' ? /^<bindings\.(.+)>$/.exec(row.value)?.[1] : undefined;
34
+ if (name !== undefined && !names.includes(name))
35
+ names.push(name);
36
+ }
37
+ return names;
38
+ }
39
+ /**
40
+ * Plan the entries a contribution-lowered node needs: for each `<bindings.NAME>`
41
+ * its definition's `model.context` references and `supplied` does not cover, one
42
+ * entry built from the definition — `resource`, `resourceKey`, `resourceSubType`
43
+ * from `model.bindings`, and `name`, `default`, `propertyAttribute` from the
44
+ * matching `model.bindings.values` row.
45
+ *
46
+ * A supplied entry covers a name when it has the same `resource` and `name`: the
47
+ * contributor's own bindings win, as they always did.
48
+ *
49
+ * A name is a problem (not an entry) when the definition declares no
50
+ * `values` row for it, or carries no `resourceKey` — a per-tenant template such
51
+ * as the bundled `core.action.queue.create` (`resourceKey: ""`), which only a
52
+ * typed factory can fill.
53
+ *
54
+ * @param def - The resolved definition.
55
+ * @param supplied - The bindings the contribution returned.
56
+ */
57
+ export function planContributionBindings(def, supplied = []) {
58
+ const model = def?.model;
59
+ const names = referencedBindingNames(model);
60
+ const resource = model?.bindings?.resource;
61
+ // Connection (or undeclared) resources are keyed by a tenant connection id the
62
+ // definition does not carry; the connector paths own them.
63
+ if (names.length === 0 || typeof resource !== 'string' || !resource || resource === 'connection') {
64
+ return { entries: [], problems: [] };
65
+ }
66
+ const open = names.filter((n) => !supplied.some((b) => b?.resource === resource && b?.name === n));
67
+ if (open.length === 0)
68
+ return { resource, entries: [], problems: [] };
69
+ const label = `${def?.nodeType}@${def?.version}`;
70
+ const resourceKey = model.bindings.resourceKey;
71
+ if (typeof resourceKey !== 'string' || !resourceKey) {
72
+ return {
73
+ resource,
74
+ entries: [],
75
+ problems: open.map((n) => `${label} references "<bindings.${n}>", and its model.bindings carries no resourceKey, so no `
76
+ + `"${resource}" binding can be built for it. This definition is a per-tenant template, not a resolved `
77
+ + `manifest: use the typed factory for the family, or the manifest \`uip maestro flow registry get `
78
+ + `${def?.nodeType}\` returns for a real resource, or return the bindings from contribute().`),
79
+ };
80
+ }
81
+ const values = Array.isArray(model.bindings.values) ? model.bindings.values : [];
82
+ const subType = model.bindings.resourceSubType;
83
+ const entries = [];
84
+ const problems = [];
85
+ for (const n of open) {
86
+ const row = values.find((r) => r?.name === n);
87
+ if (!row) {
88
+ problems.push(`${label} references "<bindings.${n}>", but its model.bindings.values declares no "${n}" row, so `
89
+ + `there is no default to bind it to. Pass the manifest exactly as \`uip maestro flow registry get\` `
90
+ + `returns it, or return the "${n}" binding from contribute().`);
91
+ continue;
92
+ }
93
+ entries.push({
94
+ name: n,
95
+ type: typeof row.type === 'string' && row.type ? row.type : 'string',
96
+ resource,
97
+ resourceKey,
98
+ ...(typeof row.default === 'string' ? { default: row.default } : {}),
99
+ propertyAttribute: typeof row.propertyAttribute === 'string' && row.propertyAttribute
100
+ ? row.propertyAttribute : n,
101
+ ...(typeof subType === 'string' && subType ? { resourceSubType: subType } : {}),
102
+ });
103
+ }
104
+ return { resource, resourceKey, entries, problems };
105
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Does a node definition have an `error` port? One answer for `check` and
3
+ * `serialize` (flow-builder-sdk#943).
4
+ *
5
+ * The product's rule, read from @uipath/flow-core and @uipath/flow-tool:
6
+ *
7
+ * - the manifest client injects an `error` source handle into every served
8
+ * definition with `supportsErrorHandling: true` that does not declare one
9
+ * (`withErrorHandles` / `nodeSupportsErrorHandling`);
10
+ * - `uip maestro flow validate` does the same when it checks edge handles
11
+ * (`validateEdgeHandlesAgainstCurrentManifests`: `supportsErrorHandling ===
12
+ * true` adds `error` to the node's source handles).
13
+ *
14
+ * So the flag alone grants the port. For instance, the manifest
15
+ * `registry get --local` returns for a sibling Flow or RPA project
16
+ * (`buildNodeManifest`) has `supportsErrorHandling: true` and only `input` +
17
+ * `output` handles, and an `error` edge from it is valid. A definition that
18
+ * declares an `error` source handle without the flag counts too.
19
+ */
20
+ export declare function definitionHasErrorPort(def: any): boolean;
21
+ /**
22
+ * The ONERROR_NO_ERROR_PORT sentence for a definition-backed node, naming its
23
+ * node type. Shared with serialize, which throws it for the same node.
24
+ */
25
+ export declare function errorPortRefusal(stepName: string, def: any): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Does a node definition have an `error` port? One answer for `check` and
3
+ * `serialize` (flow-builder-sdk#943).
4
+ *
5
+ * The product's rule, read from @uipath/flow-core and @uipath/flow-tool:
6
+ *
7
+ * - the manifest client injects an `error` source handle into every served
8
+ * definition with `supportsErrorHandling: true` that does not declare one
9
+ * (`withErrorHandles` / `nodeSupportsErrorHandling`);
10
+ * - `uip maestro flow validate` does the same when it checks edge handles
11
+ * (`validateEdgeHandlesAgainstCurrentManifests`: `supportsErrorHandling ===
12
+ * true` adds `error` to the node's source handles).
13
+ *
14
+ * So the flag alone grants the port. For instance, the manifest
15
+ * `registry get --local` returns for a sibling Flow or RPA project
16
+ * (`buildNodeManifest`) has `supportsErrorHandling: true` and only `input` +
17
+ * `output` handles, and an `error` edge from it is valid. A definition that
18
+ * declares an `error` source handle without the flag counts too.
19
+ */
20
+ export function definitionHasErrorPort(def) {
21
+ if (def?.supportsErrorHandling === true)
22
+ return true;
23
+ const groups = Array.isArray(def?.handleConfiguration) ? def.handleConfiguration : [];
24
+ return groups.some((g) => (Array.isArray(g?.handles) ? g.handles : [])
25
+ .some((h) => h?.id === 'error' && h?.type === 'source'));
26
+ }
27
+ /**
28
+ * The ONERROR_NO_ERROR_PORT sentence for a definition-backed node, naming its
29
+ * node type. Shared with serialize, which throws it for the same node.
30
+ */
31
+ export function errorPortRefusal(stepName, def) {
32
+ return `Step "${stepName}" is a ${def?.nodeType}@${def?.version} node, and its definition has no error port: `
33
+ + `it does not set supportsErrorHandling: true and declares no "error" source handle, so the edge would leave a `
34
+ + `handle the node does not have. Handle the failure downstream instead, or pass the definition exactly as `
35
+ + `\`uip maestro flow registry get\` returns it.`;
36
+ }
@@ -29,7 +29,7 @@
29
29
  * and gets back diagnostics with no location. The caller stamps the location
30
30
  * (which node / task / field) it came from.
31
31
  */
32
- import * as ts from 'typescript';
32
+ import { typescript } from './typescript-module.js';
33
33
  /**
34
34
  * A JSONPath wildcard/filter step — the spelling that reaches for JavaScript and
35
35
  * misses. Used only to add a suggestion; the parse decides.
@@ -47,15 +47,18 @@ const JSONPATH_WILDCARD = /\[\s*(?:\*|\?|'[^']*'|"[^"]*")\s*\]|\.\.[A-Za-z_$]/;
47
47
  * `ts.createSourceFile` is a parse, not an evaluation — nothing in `expr` runs.
48
48
  * TypeScript is already a runtime dependency of this package (see
49
49
  * `core/script-return.ts`, which infers a script's return type the same way), so
50
- * this adds no dependency.
50
+ * this adds no dependency. It is loaded on the first check, not at import
51
+ * (`core/typescript-module.ts`).
51
52
  */
52
53
  function syntaxError(expr) {
54
+ const ts = typescript();
53
55
  const problems = parseExpression(expr).problems;
54
56
  if (problems.length === 0)
55
57
  return undefined;
56
58
  return ts.flattenDiagnosticMessageText(problems[0].messageText, ' ');
57
59
  }
58
60
  function parseExpression(expr) {
61
+ const ts = typescript();
59
62
  const source = ts.createSourceFile('expression.ts', `(${expr}\n)`, ts.ScriptTarget.ES2020,
60
63
  /* setParentNodes */ false);
61
64
  // `parseDiagnostics` is where the scanner records syntax problems. It is not on
@@ -77,6 +80,7 @@ function retype(expr, escapes, keepCooked) {
77
80
  }
78
81
  /** The value a leaf denotes — a name, or a literal's value (a regular expression's is its source). */
79
82
  function leafValue(node) {
83
+ const ts = typescript();
80
84
  if (ts.isIdentifier(node) || ts.isPrivateIdentifier(node))
81
85
  return node.text;
82
86
  // NumericLiteral … TemplateTail: every literal and template piece carries `.text`.
@@ -86,6 +90,7 @@ function leafValue(node) {
86
90
  return undefined;
87
91
  }
88
92
  function children(node) {
93
+ const ts = typescript();
89
94
  const out = [];
90
95
  ts.forEachChild(node, (c) => { out.push(c); });
91
96
  return out;
@@ -145,8 +145,8 @@ export declare function ran(step: string): Expr;
145
145
  * The first five are what every error-capable node's definition declares
146
146
  * (`outputDefinition.error.schema`, `required`, `additionalProperties: false`).
147
147
  * `response` and `element` are NOT declared and are present at run time anyway —
148
- * measured on every family probed (2026-09-10), `element` carrying the id of the
149
- * step that failed. They are typed here because a field that exists should be
148
+ * measured on every family probed (2026-09-10), `element` carrying the label of
149
+ * the step that failed. They are typed here because a field that exists should be
150
150
  * readable; the declared five stay first because those are the contract.
151
151
  */
152
152
  export type ErrorEnvelopeField = 'code' | 'message' | 'detail' | 'category' | 'status' | 'response' | 'element';
@@ -166,15 +166,21 @@ export type ErrorEnvelopeField = 'code' | 'message' | 'detail' | 'category' | 's
166
166
  * | `category` | string | `'User'` |
167
167
  * | `status` | number | `404` |
168
168
  * | `response` | unknown | undeclared; `null` in every probe |
169
- * | `element` | string | undeclared; the id of the step that failed |
169
+ * | `element` | string | undeclared; the failing step's label |
170
170
  *
171
171
  * The last two resolve at run time — measured, a container handler reading
172
- * `element` came back with the failing body step's id — but they are absent from
173
- * the declared schema, so `uip maestro flow validate` reports
172
+ * `element` came back with the failing body step's label — but they are absent
173
+ * from the declared schema, so `uip maestro flow validate` reports
174
174
  * `EXPRESSION_DIAGNOSTIC: Property 'element' does not exist on type
175
175
  * '<Step>_Error'` for them. Valid, and noisy: prefer the declared five unless you
176
176
  * specifically need one of these.
177
177
  *
178
+ * **`element` is the step's canvas label (`display.label`), not its node id.** The
179
+ * label defaults to the step name, so it matches the name (not always the node
180
+ * id) only while no `label` is set: `.step('failA', …, { label: 'Fail A' })`
181
+ * failing gives `element: 'Fail A'`. Route on `code` / `message`, never on
182
+ * `element`.
183
+ *
178
184
  * **Which VARIABLE holds it is per node family, and this helper owns that.**
179
185
  * Measured on the product (2026-09-10), one debug run per family:
180
186
  *
package/dist/core/expr.js CHANGED
@@ -179,15 +179,21 @@ export function ran(step) {
179
179
  * | `category` | string | `'User'` |
180
180
  * | `status` | number | `404` |
181
181
  * | `response` | unknown | undeclared; `null` in every probe |
182
- * | `element` | string | undeclared; the id of the step that failed |
182
+ * | `element` | string | undeclared; the failing step's label |
183
183
  *
184
184
  * The last two resolve at run time — measured, a container handler reading
185
- * `element` came back with the failing body step's id — but they are absent from
186
- * the declared schema, so `uip maestro flow validate` reports
185
+ * `element` came back with the failing body step's label — but they are absent
186
+ * from the declared schema, so `uip maestro flow validate` reports
187
187
  * `EXPRESSION_DIAGNOSTIC: Property 'element' does not exist on type
188
188
  * '<Step>_Error'` for them. Valid, and noisy: prefer the declared five unless you
189
189
  * specifically need one of these.
190
190
  *
191
+ * **`element` is the step's canvas label (`display.label`), not its node id.** The
192
+ * label defaults to the step name, so it matches the name (not always the node
193
+ * id) only while no `label` is set: `.step('failA', …, { label: 'Fail A' })`
194
+ * failing gives `element: 'Fail A'`. Route on `code` / `message`, never on
195
+ * `element`.
196
+ *
191
197
  * **Which VARIABLE holds it is per node family, and this helper owns that.**
192
198
  * Measured on the product (2026-09-10), one debug run per family:
193
199
  *
@@ -0,0 +1 @@
1
+ export declare function writeJsonFile(path: string, value: unknown): void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Write `value` as an emitted JSON artifact: 2-space indent and a trailing newline.
3
+ *
4
+ * Every emitted `.flow`, caseplan, entry-points, bindings_v2 and library file is
5
+ * byte-locked, and twelve call sites each spelled this out by hand; a copy that dropped
6
+ * the `'\n'` would break byte-lock on that one artifact only. FB007 holds pretty-printed
7
+ * JSON writes to this function. Creating the parent directory stays the caller's job.
8
+ */
9
+ import { writeFileSync } from 'node:fs';
10
+ export function writeJsonFile(path, value) {
11
+ writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
12
+ }
@@ -218,6 +218,25 @@ export interface LibraryOpts {
218
218
  */
219
219
  overlayDir?: string;
220
220
  }
221
+ /** Why {@link Library.resolve} refused a connector action. */
222
+ export type ConnectorResolveCode =
223
+ /** The library carries no such operation (or no such connector, or not at that version). */
224
+ 'CONNECTOR_NOT_IN_LIBRARY'
225
+ /** A GENERIC operation (one entry per object) named without an `object`. */
226
+ | 'CONNECTOR_OBJECT_REQUIRED'
227
+ /** An `object` on a single, non-generic operation. */
228
+ | 'CONNECTOR_OBJECT_NOT_GENERIC';
229
+ /**
230
+ * The refusal {@link Library.resolve} throws, with a `code` for each reason.
231
+ *
232
+ * Typed so a caller can tell a refusal from a bug without matching on message
233
+ * text: `check` reports it under this code with this message, and a compile
234
+ * that throws it carries the same code into its `--json` diagnostics (#948).
235
+ */
236
+ export declare class ConnectorResolveError extends Error {
237
+ readonly code: ConnectorResolveCode;
238
+ constructor(code: ConnectorResolveCode, message: string);
239
+ }
221
240
  export declare class Library {
222
241
  private layers;
223
242
  /** The LIST-ENVELOPE LEDGER, overlay first so a prepared entry can add a row. */