@uipath/maestro-builder-sdk 6.14.5 → 6.15.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 (46) hide show
  1. package/dist/api-index.md +71 -71
  2. package/dist/api-members.md +324 -323
  3. package/dist/bpmn/bpmn-sdk.d.ts +16 -0
  4. package/dist/bpmn/bpmn-sdk.js +2 -0
  5. package/dist/bpmn/check-cli.js +22 -5
  6. package/dist/bpmn/check.d.ts +15 -1
  7. package/dist/bpmn/check.js +69 -2
  8. package/dist/bpmn/compile-cli.js +24 -14
  9. package/dist/bpmn/connector.js +29 -14
  10. package/dist/bpmn/decompile-cli.js +3 -3
  11. package/dist/bpmn/intsvc.js +5 -2
  12. package/dist/bpmn/serialize.d.ts +2 -0
  13. package/dist/bpmn/serialize.js +1 -1
  14. package/dist/case/compile-cli.js +6 -9
  15. package/dist/case/decompile-cli.js +2 -2
  16. package/dist/case/serialize.js +1 -1
  17. package/dist/check-cli.js +6 -35
  18. package/dist/check.d.ts +16 -148
  19. package/dist/check.js +33 -709
  20. package/dist/cli/commands/bpmn.js +15 -0
  21. package/dist/cli-library.d.ts +6 -25
  22. package/dist/cli-library.js +6 -73
  23. package/dist/core/cli-library.d.ts +47 -0
  24. package/dist/core/cli-library.js +150 -0
  25. package/dist/core/connector-checks.d.ts +226 -0
  26. package/dist/core/connector-checks.js +518 -0
  27. package/dist/core/connector-inputs.d.ts +80 -0
  28. package/dist/core/connector-inputs.js +453 -0
  29. package/dist/core/event-checks.d.ts +137 -0
  30. package/dist/core/event-checks.js +321 -0
  31. package/dist/decompile-cli.js +2 -2
  32. package/dist/decompile.js +1 -1
  33. package/dist/registry/generators/markdown/sections/example.d.ts +7 -1
  34. package/dist/registry/generators/markdown/sections/example.js +70 -5
  35. package/dist/registry/generators/markdown/sections/lookups.js +29 -8
  36. package/dist/registry/prepare/messages.js +6 -1
  37. package/dist/registry/prepare/source-import.d.ts +3 -3
  38. package/dist/registry/prepare/source-import.js +15 -4
  39. package/dist/registry/prepare/source-labels.js +31 -4
  40. package/dist/serialize.d.ts +11 -11
  41. package/dist/serialize.js +27 -420
  42. package/dist/workdir.d.ts +10 -0
  43. package/dist/workdir.js +14 -1
  44. package/package.json +2 -2
  45. /package/dist/{event-filters.d.ts → core/event-filters.d.ts} +0 -0
  46. /package/dist/{event-filters.js → core/event-filters.js} +0 -0
@@ -46,7 +46,15 @@ import { GUID_RE } from "../../core/binding-messages.js";
46
46
  import { relativePosix } from "../compat/paths.js";
47
47
  import { canonicalPath, flowSources, isRelativeSpecifier } from "./source-import.js";
48
48
  import { inWorkDir } from "../../workdir.js";
49
- const CALLEES = new Set(["connector", "onEvent", "waitForEvent"]);
49
+ const CALLEES = new Set([
50
+ "connector", "onEvent", "waitForEvent", "eventTrigger", "dataService",
51
+ ]);
52
+ /**
53
+ * Data Fabric's connector key, spelled here rather than imported from
54
+ * `bpmn/data-service.ts` — `registry/` does not depend on a family's emitter.
55
+ * `.dataService()` is the one call that binds a connection without naming one.
56
+ */
57
+ const DATA_SERVICE_KEY = "uipath-uipath-dataservice";
50
58
  function isFile(path) {
51
59
  try {
52
60
  return statSync(path).isFile();
@@ -81,7 +89,8 @@ export function sourceBindingLabels(options) {
81
89
  catch {
82
90
  continue; // unreadable: it cannot be the author's working source
83
91
  }
84
- readFile(file, relativePosix(root, file), text, options.connectorKey, moduleDirs, result);
92
+ const family = file.endsWith(".bpmn.ts") ? "bpmn" : "flow";
93
+ readFile(file, relativePosix(root, file), text, options.connectorKey, moduleDirs, result, family);
85
94
  }
86
95
  return result;
87
96
  }
@@ -115,7 +124,7 @@ function propertyName(name) {
115
124
  return name.text;
116
125
  return undefined;
117
126
  }
118
- function readFile(file, shown, text, connectorKey, moduleDirs, result) {
127
+ function readFile(file, shown, text, connectorKey, moduleDirs, result, family) {
119
128
  const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
120
129
  // `parseDiagnostics` is where the scanner records syntax problems (see core/expr-check.ts).
121
130
  const problems = sf.parseDiagnostics;
@@ -316,6 +325,24 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result) {
316
325
  unplaceable(at, args.why);
317
326
  else if (args.length === 0)
318
327
  unplaceable(at, "spreads an empty array");
328
+ else if (family === "bpmn") {
329
+ // Every BPMN call leads with the ELEMENT ID, so the key is one further
330
+ // along than Flow's and the options are last. `.dataService()` names no
331
+ // connector at all — there is only one it can mean.
332
+ if (callee === "dataService") {
333
+ record(DATA_SERVICE_KEY, args[1], at);
334
+ }
335
+ else if (callee === "connector" || callee === "eventTrigger" || callee === "waitForEvent") {
336
+ const key = constString(args[1]);
337
+ // A computed key is not a reason to distrust the whole file here: a
338
+ // BPMN options object is a literal at the call site, and the label
339
+ // this reads is the author's own name for a binding, not a fact the
340
+ // emitter depends on. Flow's `unplaceable` exists because its first
341
+ // argument may be a DESCRIPTOR whose key decides everything.
342
+ if (key !== undefined)
343
+ record(key, args[callee === "connector" ? 4 : 3], at);
344
+ }
345
+ }
319
346
  else if (callee === "connector") {
320
347
  const [first] = args;
321
348
  const literalKey = constString(first);
@@ -325,7 +352,7 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result) {
325
352
  else
326
353
  record(key, args[literalKey !== undefined ? 3 : 2], at);
327
354
  }
328
- else {
355
+ else if (callee === "onEvent" || callee === "waitForEvent") {
329
356
  const [first] = args;
330
357
  const subscription = constObject(first);
331
358
  if (subscription) {
@@ -1,7 +1,16 @@
1
1
  import type { BuiltFlow } from './flow-sdk.js';
2
- import type { Library, ResolvedConnector } from './library.js';
2
+ import type { Library } from './library.js';
3
3
  import type { Bindings } from './bindings.js';
4
- import { type LookupResolutions, type LookupSpec } from './core/lookups.js';
4
+ import { connectorInputErrors as coreConnectorInputErrors } from './core/connector-inputs.js';
5
+ /**
6
+ * What `compile` would refuse these connector inputs with, or `undefined` when it
7
+ * would accept them — Flow's binding of the shared validator.
8
+ *
9
+ * `render` is not optional here: `renderDeep` is what REFUSES a lookup token in a
10
+ * position no lookup is resolved from (inside an opaque `object` input, say), and
11
+ * dropping it would make `check` quietly accept what `compile` throws on.
12
+ */
13
+ export declare function connectorInputErrors(rc: Parameters<typeof coreConnectorInputErrors>[0], inputs: Record<string, unknown>, resolutions?: Parameters<typeof coreConnectorInputErrors>[2], lookups?: Parameters<typeof coreConnectorInputErrors>[3]): string | undefined;
5
14
  /**
6
15
  * A compile-time observation that does not stop emission. `check()` has its
7
16
  * diagnostics; this is the serializer's own channel for things it can only see
@@ -82,12 +91,3 @@ export declare function serialize(built: BuiltFlow, opts?: SerializeOpts): any;
82
91
  * placeholder and the step that carries it.
83
92
  */
84
93
  export declare function assertBindingPlaceholdersResolve(flow: any): void;
85
- /**
86
- * What `compile` would refuse these connector inputs with, or `undefined` when it
87
- * would accept them — the SAME code path `buildConnectorInputs` runs at emit time,
88
- * so `check` and `compile` cannot disagree about a missing required field, an
89
- * unknown or mistyped input, or a container part passed as a value. Unresolved
90
- * lookups are the one thing skipped: `check` already reports them as
91
- * LOOKUP_UNRESOLVED with the resolution recipe.
92
- */
93
- export declare function connectorInputErrors(rc: ResolvedConnector, inputs: Record<string, unknown>, resolutions?: LookupResolutions, lookups?: Readonly<Record<string, LookupSpec>>): string | undefined;
package/dist/serialize.js CHANGED
@@ -23,7 +23,23 @@ import { declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
23
23
  import { assertNodeEnvelope } from './core/node-envelope.js';
24
24
  import { buildConfiguration, transportHttpMethod } from './config.js';
25
25
  import { stableId } from './core/stable-id.js';
26
- import { readEventFilter, eventFilterProblem, eventFilterJmes, eventFilterTreeLeaf } from './event-filters.js';
26
+ import { readEventFilter, eventFilterProblem, eventFilterJmes, eventFilterTreeLeaf } from './core/event-filters.js';
27
+ import { FLOW_EXPR_DIALECT } from './core/connector-checks.js';
28
+ import { buildConnectorInputs, connectorInputErrors as coreConnectorInputErrors, multipartBodyFieldName } from './core/connector-inputs.js';
29
+ /**
30
+ * What `compile` would refuse these connector inputs with, or `undefined` when it
31
+ * would accept them — Flow's binding of the shared validator.
32
+ *
33
+ * `render` is not optional here: `renderDeep` is what REFUSES a lookup token in a
34
+ * position no lookup is resolved from (inside an opaque `object` input, say), and
35
+ * dropping it would make `check` quietly accept what `compile` throws on.
36
+ */
37
+ export function connectorInputErrors(rc, inputs, resolutions = {}, lookups = {}) {
38
+ return coreConnectorInputErrors(rc, inputs, resolutions, lookups, {
39
+ dialect: FLOW_EXPR_DIALECT,
40
+ render: (value) => renderDeep(value, NO_RENAME),
41
+ });
42
+ }
27
43
  import { isLookupToken, resolvedValue, unresolvedLookupMessage, } from './core/lookups.js';
28
44
  import { declaredScriptShape, inferScriptShape } from './core/script-return.js';
29
45
  import { migrateToSchemaVersion } from './migrate.js';
@@ -4654,12 +4670,16 @@ export function serialize(built, opts = {}) {
4654
4670
  const { entries: connBindings } = connectionResourceBindings({ def: rc.v1def, connectorKey: rc.connectorKey, connectionId: conn, folderKey: folder }, resourceBindings);
4655
4671
  for (const b of connBindings)
4656
4672
  resourceBindings.set(b.id, b);
4657
- const connectorInputs = buildConnectorInputs(rc, spec.inputs, scope.rename, opts.library.lookupResolutions ?? {},
4658
- // Only for the missing-required hint; a test double that is not a full
4659
- // Library simply gets the plain message.
4660
- typeof opts.library.lookupSpecs === 'function'
4661
- ? opts.library.lookupSpecs(spec.key, spec.action, spec.version, spec.object)
4662
- : {});
4673
+ const connectorInputs = buildConnectorInputs(rc, spec.inputs, {
4674
+ dialect: FLOW_EXPR_DIALECT,
4675
+ render: (value) => renderDeep(value, scope.rename),
4676
+ resolutions: opts.library.lookupResolutions ?? {},
4677
+ // Only for the missing-required hint; a test double that is not a full
4678
+ // Library simply gets the plain message.
4679
+ lookups: typeof opts.library.lookupSpecs === 'function'
4680
+ ? opts.library.lookupSpecs(spec.key, spec.action, spec.version, spec.object)
4681
+ : {},
4682
+ });
4663
4683
  const replayInputs = {
4664
4684
  ...(connectorInputs.bodyParameters ?? {}),
4665
4685
  ...(connectorInputs.pathParameters ?? {}),
@@ -6615,419 +6635,6 @@ export function assertBindingPlaceholdersResolve(flow) {
6615
6635
  + `"invalid GUID <bindings.…>") and refuses to publish. This is a serializer defect, not an authoring `
6616
6636
  + `error; report it with the flow source.`);
6617
6637
  }
6618
- /**
6619
- * Validate a connector's activity inputs against its declared field list and
6620
- * return them ROUTED to the wire slots the platform reads. Catches the three
6621
- * connector-input mistakes — missing required, unknown key, type mismatch — at
6622
- * compile, where the author can act on them.
6623
- *
6624
- * Generated descriptors accept natural nested objects and arrays; the legacy
6625
- * string form also accepts exact flat registry field ids. Routing remains the
6626
- * library's business because a field's location is a property of the operation.
6627
- * Each projected registry field lands in the slot its declaration names:
6628
- *
6629
- * ```jsonc
6630
- * // connector('uipath-atlassian-jira','get-issue', { issueId: …, project: 'IN', issuetype: 'Task' })
6631
- * "inputs": { "detail": {
6632
- * "endpoint": "/curated_get_issue/{issueId}",
6633
- * "pathParameters": { "issueId": "IN-28" },
6634
- * "queryParameters": { "project": "IN", "issuetype": "Task" },
6635
- * "bodyParameters": {} } }
6636
- * ```
6637
- *
6638
- * That is the platform's own encoding, measured on the deployed corpus: 1,283
6639
- * connector nodes in the nightly artifacts, 384 carrying `pathParameters` and
6640
- * 576 carrying `queryParameters`, always as a FLAT map keyed by the parameter's
6641
- * DECLARED name (`send_as`, `issuetype`, `channel_id` keep their spelling — no
6642
- * camelCasing) and always as a SIBLING of `bodyParameters` rather than as a
6643
- * location-tagged entry inside it.
6644
- */
6645
- /** For a missing required lookup field: what it sends and how to resolve it. Empty for a plain field. */
6646
- function missingLookupHint(field, lookup) {
6647
- if (!lookup)
6648
- return '';
6649
- const target = `${lookup.objectName ?? lookup.path}.${lookup.value}`;
6650
- const alias = Object.keys(lookup.aliases)[0];
6651
- const call = alias
6652
- ? `lookup(<Descriptor>, '${field}').${alias}('<value>')`
6653
- : `lookup(<Descriptor>, '${field}').by('${lookup.by[0] ?? 'id'}', '<value>')`;
6654
- return ` — a lookup id (\`${target}\`), not a name or address: ${call}`;
6655
- }
6656
- /**
6657
- * What `compile` would refuse these connector inputs with, or `undefined` when it
6658
- * would accept them — the SAME code path `buildConnectorInputs` runs at emit time,
6659
- * so `check` and `compile` cannot disagree about a missing required field, an
6660
- * unknown or mistyped input, or a container part passed as a value. Unresolved
6661
- * lookups are the one thing skipped: `check` already reports them as
6662
- * LOOKUP_UNRESOLVED with the resolution recipe.
6663
- */
6664
- export function connectorInputErrors(rc, inputs, resolutions = {}, lookups = {}) {
6665
- try {
6666
- buildConnectorInputs(rc, inputs, NO_RENAME, resolutions, lookups, { unresolvedLookups: 'ignore' });
6667
- return undefined;
6668
- }
6669
- catch (e) {
6670
- return e instanceof Error ? e.message : String(e);
6671
- }
6672
- }
6673
- function buildConnectorInputs(rc, inputs, rename = NO_RENAME, resolutions = {}, lookups = {}, policy = {}) {
6674
- const fields = (rc.fields ?? []);
6675
- const byName = new Map(fields.map((f) => [f.name, f]));
6676
- const errs = validateNestedConnectorKeys(fields, inputs, byName);
6677
- const routed = {
6678
- bodyParameters: {}, pathParameters: {}, queryParameters: {},
6679
- };
6680
- // The author's values for FILE parts, by part name. The part LIST itself comes
6681
- // from the library (`rc.multipartParts`): a string-typed part is a transport
6682
- // container the runtime composes from `bodyParameters`, so it is listed without
6683
- // a value and is never an author input — see `readFields` in core/library.ts.
6684
- const partValues = new Map();
6685
- for (const f of fields) {
6686
- const flat = Object.prototype.hasOwnProperty.call(inputs, f.name);
6687
- let nested = flat
6688
- ? { found: true, value: inputs[f.name], projectedArrays: 0 }
6689
- : readConnectorPath(inputs, f.name);
6690
- if (!nested.found) {
6691
- // A required LOOKUP field is missing far more often because the author does
6692
- // not know what to put in it than because they forgot it exists: name the
6693
- // id it sends and the helper that finds one, so "(string)" is not read as
6694
- // "any string" — an e-mail into `fields.reporter.id` is exactly that misreading.
6695
- if (f.required)
6696
- errs.push(`missing required input "${f.name}" (${f.type ?? 'value'})${missingLookupHint(f.name, lookups[f.name])}`);
6697
- continue;
6698
- }
6699
- // An unresolved lookup is swapped for its recorded id, or reported. It is
6700
- // NOT rendered as a placeholder: emitting something for a value nobody
6701
- // resolved would produce a flow that compiles and then talks to the wrong
6702
- // record, which is exactly the failure the lookup surface exists to remove.
6703
- //
6704
- // A token is swapped wherever the field's value can hold one: the whole
6705
- // value, or an element of an array at any depth. A multiselect is
6706
- // `users: [lookup(…), lookup(…)]` — the form `ConnectorLookupValue`'s array
6707
- // branch types and the generated `## Example` prints — and until this swapped
6708
- // only the whole-value case, so every token inside the array reached
6709
- // validation as an object ("expected string, got object") with every lookup
6710
- // recorded. An unresolved element under 'ignore' becomes `null`, which
6711
- // validation skips, so `check` still type-checks the resolved siblings.
6712
- const unresolved = [];
6713
- const swapped = swapLookupTokens(nested.value, resolutions, unresolved);
6714
- if (unresolved.length) {
6715
- if (policy.unresolvedLookups !== 'ignore') {
6716
- for (const token of unresolved)
6717
- errs.push(unresolvedLookupMessage(token));
6718
- continue;
6719
- }
6720
- if (isLookupToken(nested.value))
6721
- continue;
6722
- }
6723
- nested = { ...nested, value: swapped };
6724
- // A FLAT key whose wire name ends in `[*]` names one array: an array value
6725
- // there is checked element by element, as the nested form is. A flat scalar
6726
- // keeps depth 0 and is emitted as the scalar it is — the product wraps it
6727
- // into a one-element list (flow-v1 `convertDotNotationToNested`).
6728
- const depth = flat
6729
- ? (f.name.endsWith('[*]') && Array.isArray(nested.value) ? 1 : 0)
6730
- : nested.projectedArrays;
6731
- validateConnectorFieldValue(f, nested.value, depth, errs);
6732
- if (f.location === 'multipart') {
6733
- partValues.set(f.name, renderDeep(nested.value, rename));
6734
- continue;
6735
- }
6736
- const slot = SLOT_FOR_LOCATION[f.location ?? 'body'] ?? 'bodyParameters';
6737
- routed[slot][f.name] = renderDeep(nested.value, rename);
6738
- }
6739
- if (errs.length) {
6740
- // Name the location of each required field: a "missing required input" on a
6741
- // path parameter reads very differently from one on a body field, and the
6742
- // author's fix is the same either way (add it to the same flat `inputs` map).
6743
- const req = fields
6744
- .filter((f) => f.required)
6745
- .map((f) => (f.location && f.location !== 'body' ? `${f.name} (${f.location})` : f.name))
6746
- .join(', ') || '(none required)';
6747
- // Phase 1 propagates connector discovery capability to every activity. When
6748
- // one of those activities rejects an unknown field, point at the live-schema
6749
- // preparation path; fully static activities on the same connector still
6750
- // compile normally and never reach this hint.
6751
- const schemaDynamic = !!rc.entry?.runtime?.requiresConnectionForSchema;
6752
- const hasUnknown = errs.some((e) => e.startsWith('unknown input'));
6753
- let hint = '';
6754
- if (schemaDynamic && hasUnknown) {
6755
- const prefix = `uipath.connector.${rc.connectorKey}.`;
6756
- const action = String(rc.nodeType).startsWith(prefix)
6757
- ? String(rc.nodeType).slice(prefix.length)
6758
- // `.pop()` on a split is never empty at runtime, but its type admits
6759
- // `undefined`; the node type itself is the only sane fallback.
6760
- : String(rc.nodeType).split('.').pop() ?? String(rc.nodeType);
6761
- hint =
6762
- ` This connector supports connection-specific object or field discovery, and the ` +
6763
- `static library does not contain this input. Run ` +
6764
- `\`${prepareCommand(rc.connectorKey, action)}\` ` +
6765
- `to load them ` +
6766
- `(writes ./.flow-sdk/connectors-local/, which the compiler auto-detects). If that still ` +
6767
- `does not carry this input, the operation resolves its fields from the VALUES of ` +
6768
- `parent inputs: resolve every parent and re-run with ` +
6769
- `\`-f <parent-field>=<value>\` for each — too few parents is refused, and a ` +
6770
- `wrong value resolves back to just the parents. ` +
6771
- `See the connector guide's parent-field loop.`;
6772
- }
6773
- throw new Error(`connector ${rc.nodeType}: ${errs.join('; ')}. Required fields: ${req}.${hint}`);
6774
- }
6775
- // Every declared part, in declaration order, as the designer lists them: the
6776
- // container part valueless, a file part with the author's value when given. A
6777
- // test double that resolves no `multipartParts` falls back to its multipart
6778
- // fields, which are all file parts by construction.
6779
- const declaredParts = rc.multipartParts
6780
- ?? fields.filter((f) => f.location === 'multipart').map((f) => ({ name: f.name, dataType: f.type ?? 'string' }));
6781
- const multipartParameters = declaredParts.map((part) => (partValues.has(part.name)
6782
- ? { name: part.name, dataType: part.dataType, value: partValues.get(part.name) }
6783
- : { name: part.name, dataType: part.dataType }));
6784
- // A FIXED slot order, so a recompile is byte-identical no matter which order
6785
- // the author listed the inputs in.
6786
- const slots = multipartParameters.length === 0 || Object.keys(routed.bodyParameters).length > 0
6787
- ? { bodyParameters: routed.bodyParameters }
6788
- : {};
6789
- if (Object.keys(routed.pathParameters).length)
6790
- slots.pathParameters = routed.pathParameters;
6791
- if (Object.keys(routed.queryParameters).length)
6792
- slots.queryParameters = routed.queryParameters;
6793
- if (multipartParameters.length) {
6794
- slots.multipartParameters = multipartParameters;
6795
- slots.inputMetadata = { type: 'multipart', multipart: { bodyFieldName: multipartBodyFieldName(declaredParts) } };
6796
- }
6797
- return slots;
6798
- }
6799
- /**
6800
- * `value` with every lookup token swapped for its recorded id: the value itself,
6801
- * or the elements of an array at any depth. An unrecorded token is pushed to
6802
- * `unresolved` and stands as `null`, so an array keeps its length.
6803
- *
6804
- * Objects are NOT descended: a lookup's place is a field's value or an element
6805
- * of an array field (`ConnectorLookupValue`), and a nested path such as
6806
- * `contacts[*].id` reaches here already projected to its leaf array. A token
6807
- * anywhere else is refused by {@link renderDeep}.
6808
- */
6809
- function swapLookupTokens(value, resolutions, unresolved) {
6810
- if (isLookupToken(value)) {
6811
- const resolved = resolvedValue(value, resolutions);
6812
- if (resolved === undefined) {
6813
- unresolved.push(value);
6814
- return null;
6815
- }
6816
- return resolved;
6817
- }
6818
- if (Array.isArray(value))
6819
- return value.map((item) => swapLookupTokens(item, resolutions, unresolved));
6820
- return value;
6821
- }
6822
- /**
6823
- * `inputMetadata.multipart.bodyFieldName` names the CONTAINER part — the one the
6824
- * runtime composes from `bodyParameters`. Measured on the designer's own nodes in
6825
- * the eval archive: `body` ×413, `RagRequest` ×1 (GenAI `content-generation`, whose
6826
- * string part is named that), and `body` on 91 of 93 file-only operations. A
6827
- * mis-named container is fatal at dispatch — hardcoding `body` for GenAI made
6828
- * Integration Service answer `400 The request body is empty` (verified live
6829
- * 2026-09-02) — so it is the string part's own name, with `body` only as the
6830
- * default for an operation that has no string part.
6831
- */
6832
- function multipartBodyFieldName(parts) {
6833
- const containers = parts.filter((p) => p.dataType.toLowerCase() !== 'file');
6834
- return containers.length === 1 ? containers[0].name : 'body';
6835
- }
6836
- const SLOT_FOR_LOCATION = {
6837
- body: 'bodyParameters',
6838
- query: 'queryParameters',
6839
- path: 'pathParameters',
6840
- };
6841
- function connectorPath(name) {
6842
- return name.split('.').map((raw) => ({
6843
- key: raw.endsWith('[*]') ? raw.slice(0, -3) : raw,
6844
- array: raw.endsWith('[*]'),
6845
- }));
6846
- }
6847
- /** Read a natural nested authoring value using a registry path such as
6848
- * `contacts[*].id`. Wildcards project one value per array element so the Flow
6849
- * bodyParameters map can retain the registry's exact field ids. */
6850
- function readConnectorPath(inputs, name) {
6851
- const parts = connectorPath(name);
6852
- const read = (value, index) => {
6853
- if (index >= parts.length)
6854
- return { found: true, value, projectedArrays: 0 };
6855
- if (!value || typeof value !== 'object' || value instanceof Expr || Array.isArray(value))
6856
- return { found: false, projectedArrays: 0 };
6857
- const part = parts[index];
6858
- const object = value;
6859
- if (!Object.prototype.hasOwnProperty.call(object, part.key))
6860
- return { found: false, projectedArrays: 0 };
6861
- const next = object[part.key];
6862
- if (!part.array)
6863
- return read(next, index + 1);
6864
- if (!Array.isArray(next))
6865
- return { found: false, projectedArrays: 0 };
6866
- if (index === parts.length - 1)
6867
- return { found: true, value: next, projectedArrays: 1 };
6868
- const projected = next.map((item) => read(item, index + 1));
6869
- if (!projected.some((item) => item.found))
6870
- return { found: false, projectedArrays: 0 };
6871
- return {
6872
- found: true,
6873
- value: projected.map((item) => item.found ? item.value : null),
6874
- projectedArrays: 1 + Math.max(0, ...projected.map((item) => item.projectedArrays)),
6875
- };
6876
- };
6877
- return read(inputs, 0);
6878
- }
6879
- function buildConnectorInputTrie(fields) {
6880
- const root = { children: new Map(), array: false };
6881
- for (const field of fields) {
6882
- let cursor = root;
6883
- for (const part of connectorPath(field.name)) {
6884
- let child = cursor.children.get(part.key);
6885
- if (!child) {
6886
- child = { children: new Map(), array: part.array };
6887
- cursor.children.set(part.key, child);
6888
- }
6889
- child.array ||= part.array;
6890
- cursor = child;
6891
- }
6892
- cursor.terminal = field;
6893
- }
6894
- return root;
6895
- }
6896
- /** Reject nested typos with the same unknown-key message as flat keys. */
6897
- function validateNestedConnectorKeys(fields, inputs, exact) {
6898
- const root = buildConnectorInputTrie(fields);
6899
- const errs = [];
6900
- const visit = (value, node, path) => {
6901
- if (value instanceof Expr)
6902
- return;
6903
- if (node.array) {
6904
- if (!Array.isArray(value)) {
6905
- errs.push(`input "${path}" expected array, got ${typeof value}`);
6906
- return;
6907
- }
6908
- for (const item of value)
6909
- visit(item, { ...node, array: false }, `${path}[*]`);
6910
- return;
6911
- }
6912
- // An opaque object/array terminal owns its complete value; there is no
6913
- // child metadata with which to validate its internal keys.
6914
- if (node.terminal && node.children.size === 0)
6915
- return;
6916
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
6917
- if (node.children.size > 0)
6918
- errs.push(`input "${path}" expected object, got ${Array.isArray(value) ? 'array' : typeof value}`);
6919
- return;
6920
- }
6921
- for (const [key, childValue] of Object.entries(value)) {
6922
- const child = node.children.get(key);
6923
- const childPath = path ? `${path}.${key}` : key;
6924
- if (!child) {
6925
- const near = nearestField(childPath, fields.map((f) => f.name));
6926
- errs.push(`unknown input "${childPath}"${near ? ` — did you mean "${near}"?` : ''}`);
6927
- }
6928
- else {
6929
- visit(childValue, child, childPath);
6930
- }
6931
- }
6932
- };
6933
- for (const [key, value] of Object.entries(inputs)) {
6934
- if (exact.has(key))
6935
- continue; // backwards-compatible flat authoring
6936
- const node = root.children.get(key);
6937
- if (!node) {
6938
- const near = nearestField(key, [...exact.keys()]);
6939
- errs.push(`unknown input "${key}"${near ? ` — did you mean "${near}"?` : ''}`);
6940
- }
6941
- else {
6942
- visit(value, node, key);
6943
- }
6944
- }
6945
- return errs;
6946
- }
6947
- function validateConnectorFieldValue(field, value, projectedArrays, errs) {
6948
- if (projectedArrays > 0) {
6949
- if (!Array.isArray(value)) {
6950
- errs.push(`input "${field.name}" expected array projection, got ${typeof value}`);
6951
- return;
6952
- }
6953
- for (const item of value) {
6954
- if (item !== null && item !== undefined)
6955
- validateConnectorFieldValue(field, item, projectedArrays - 1, errs);
6956
- }
6957
- return;
6958
- }
6959
- if (value instanceof Expr || value === null || value === undefined)
6960
- return;
6961
- if (field.type === 'array') {
6962
- if (!Array.isArray(value))
6963
- errs.push(`input "${field.name}" expected array, got ${typeof value}`);
6964
- return;
6965
- }
6966
- if (field.type === 'object') {
6967
- if (typeof value !== 'object' || Array.isArray(value))
6968
- errs.push(`input "${field.name}" expected object, got ${Array.isArray(value) ? 'array' : typeof value}`);
6969
- return;
6970
- }
6971
- if (field.type === 'string' || field.type === 'number' || field.type === 'boolean') {
6972
- const actual = typeof value;
6973
- const ok = actual === field.type ||
6974
- (field.type === 'number' && actual === 'string' && value !== '' && !isNaN(Number(value)));
6975
- if (!ok)
6976
- errs.push(`input "${field.name}" expected ${field.type}, got ${actual}`);
6977
- }
6978
- }
6979
- /**
6980
- * The closest declared field name to `key`, for the "did you mean …?" half of an
6981
- * unknown-input error.
6982
- *
6983
- * Three rules, tried in order: an exact case-insensitive match (`pagesize` →
6984
- * `pageSize`), a substring either way (`summary` → `fields.summary`), and — the
6985
- * one added for query/path parameters — a small EDIT DISTANCE. The first two miss
6986
- * every substitution typo: `projekt` and `project` share no containment, so the
6987
- * suggestion was simply absent, and it is absent exactly where it is needed most.
6988
- * Parameter names are unfamiliar (`issuetype`, `nextPage`, `expansionLevel`), so
6989
- * the author has no memory to fall back on.
6990
- *
6991
- * The threshold scales with the name's length (⌈len/4⌉, at least 1, at most 3), so
6992
- * a short name needs a near-exact miss and a long one may be off by a few — and
6993
- * two genuinely different fields are never conflated.
6994
- */
6995
- function nearestField(key, names) {
6996
- const lk = key.toLowerCase();
6997
- const exact = names.find((n) => n.toLowerCase() === lk);
6998
- if (exact)
6999
- return exact;
7000
- const contained = names.find((n) => n.toLowerCase().includes(lk) || lk.includes(n.toLowerCase()));
7001
- if (contained)
7002
- return contained;
7003
- const budget = Math.min(3, Math.max(1, Math.ceil(lk.length / 4)));
7004
- let best;
7005
- let bestD = budget + 1;
7006
- for (const n of names) {
7007
- const d = editDistance(lk, n.toLowerCase());
7008
- if (d < bestD) {
7009
- bestD = d;
7010
- best = n;
7011
- }
7012
- }
7013
- return bestD <= budget ? best : undefined;
7014
- }
7015
- /** Levenshtein distance, two rows — enough for field-name suggestions. */
7016
- function editDistance(a, b) {
7017
- if (a === b)
7018
- return 0;
7019
- if (Math.abs(a.length - b.length) > 3)
7020
- return 99; // cheap bail: beyond any budget
7021
- let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
7022
- for (let i = 1; i <= a.length; i++) {
7023
- const row = [i];
7024
- for (let j = 1; j <= b.length; j++) {
7025
- row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
7026
- }
7027
- prev = row;
7028
- }
7029
- return prev[b.length];
7030
- }
7031
6638
  /**
7032
6639
  * Emit a transform's `inputs` in the canonical shape the node's runtime reads:
7033
6640
  * `collection` as a BARE variable path (not `=js:`-prefixed — it is a path
package/dist/workdir.d.ts CHANGED
@@ -33,3 +33,13 @@ export declare function ensureWorkDir(cwd?: string): void;
33
33
  export declare function workDirBeside(path: string): string;
34
34
  /** The default path of a decompiled source named `file`, creating the work directory. */
35
35
  export declare function defaultSourceOut(file: string): string;
36
+ /**
37
+ * Write a decompiled source to `path`, creating its folder first if it is
38
+ * missing (`recursive: true`: an existing folder is not an error).
39
+ *
40
+ * `-o .flow-sdk/Order.flow.ts` on a fresh workspace has no `.flow-sdk/` yet;
41
+ * only the default path ({@link defaultSourceOut}) used to create it, so the
42
+ * documented explicit form failed with ENOENT and the author had to
43
+ * `mkdir -p` and retry.
44
+ */
45
+ export declare function writeSource(path: string, text: string): void;
package/dist/workdir.js CHANGED
@@ -24,7 +24,7 @@
24
24
  * rather than split across two places. When both exist, `.flow-sdk/` wins and
25
25
  * stderr says the root one is ignored.
26
26
  */
27
- import { existsSync, mkdirSync } from 'node:fs';
27
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
28
28
  import { basename, dirname, join } from 'node:path';
29
29
  /** The hidden directory, relative to the current directory. */
30
30
  export const WORK_DIR = '.flow-sdk';
@@ -94,3 +94,16 @@ export function defaultSourceOut(file) {
94
94
  ensureWorkDir();
95
95
  return inWorkDir(file);
96
96
  }
97
+ /**
98
+ * Write a decompiled source to `path`, creating its folder first if it is
99
+ * missing (`recursive: true`: an existing folder is not an error).
100
+ *
101
+ * `-o .flow-sdk/Order.flow.ts` on a fresh workspace has no `.flow-sdk/` yet;
102
+ * only the default path ({@link defaultSourceOut}) used to create it, so the
103
+ * documented explicit form failed with ENOENT and the author had to
104
+ * `mkdir -p` and retry.
105
+ */
106
+ export function writeSource(path, text) {
107
+ mkdirSync(dirname(path), { recursive: true });
108
+ writeFileSync(path, text);
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.14.5",
3
+ "version": "6.15.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",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "b72c8363cd37e75fdd992cfa007d6c6c77c67766"
95
+ "gitref": "a3707b159a49ed3b6611c55bed66622cb6dd1972"
96
96
  }