@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.
@@ -506,7 +506,11 @@ function connectorVersion(context) {
506
506
  const encoded = metadata?.activityPropertyConfiguration?.configuration;
507
507
  if (typeof encoded === 'string' && encoded.startsWith('=jsonString:')) {
508
508
  try {
509
- return JSON.parse(encoded.slice('=jsonString:'.length))?.essentialConfiguration?.packageVersion;
509
+ // The activity's version is `instanceParameters.version`. `packageVersion`
510
+ // is what the platform writes as "1.0.0" on every activity; an SDK plan
511
+ // once wrote the library version there, so it stays the fallback.
512
+ const essential = JSON.parse(encoded.slice('=jsonString:'.length))?.essentialConfiguration;
513
+ return essential?.instanceParameters?.version ?? essential?.packageVersion;
510
514
  }
511
515
  catch {
512
516
  throw new Error('connector task has malformed activity metadata configuration JSON.');
@@ -11,7 +11,8 @@
11
11
  import { IdGen, offlineBindingSeed } from './ids.js';
12
12
  import { isUnrestorableSlotId } from './preserve.js';
13
13
  import { v31TaskSets } from './task-sets.js';
14
- import { buildConnectorInputs, nestConnectorBody, transportHttpMethod } from '../core/connector-inputs.js';
14
+ import { buildConnectorInputs, nestConnectorBody, replayInputsOf, transportHttpMethod } from '../core/connector-inputs.js';
15
+ import { buildEssentialConfiguration } from '../core/connector-configuration.js';
15
16
  import { EQUALS_EXPR_DIALECT } from '../core/connector-checks.js';
16
17
  import { bindingLabelRefusal } from '../core/binding-messages.js';
17
18
  import { resolveEventWhere } from '../core/lookups.js';
@@ -1444,7 +1445,7 @@ function connectorTaskData(spec, resolver, elementId, where, executionType) {
1444
1445
  // from the `body` slot. This used to drop the whole wire and push a filled
1445
1446
  // file part into the body, where the runtime does not look for it (#1420).
1446
1447
  const fileParts = (slots.multipartParameters ?? []).filter((part) => part.dataType.toLowerCase() === 'file');
1447
- context.push(io('metadata', 'json', { body: connectorMetadata(rc, op, slots, executionType) }));
1448
+ context.push(io('metadata', 'json', { body: connectorMetadata(rc, op, slots, elementId, executionType) }));
1448
1449
  const inputs = [
1449
1450
  ioSlot('pathParameters', pathParameters, `${elementId}`),
1450
1451
  ioSlot('queryParameters', queryParameters, `${elementId}`),
@@ -1601,45 +1602,39 @@ function folderBindingRef(resolver, name, connection, site) {
1601
1602
  }
1602
1603
  return { id };
1603
1604
  }
1604
- /** The connector `metadata` json blob: the FUNCTIONAL essentialConfiguration +
1605
+ /**
1606
+ * The connector `metadata` json blob: the FUNCTIONAL essentialConfiguration +
1605
1607
  * activityTypeId, and the multipart wire of a multipart operation. Designer
1606
1608
  * cosmetics (packageId/activityColor/…) are omitted — the caseplan
1607
- * schema-validates without them (see design §C). */
1608
- function connectorMetadata(rc, op, slots, executionType) {
1609
+ * schema-validates without them (see design §C).
1610
+ *
1611
+ * The configuration is the one Flow writes, from the shared
1612
+ * {@link buildEssentialConfiguration}. Case used to build its own, thinner copy:
1613
+ * no `operation` verb, `customFieldsRequestDetails`, `connectorVersion` or
1614
+ * `unifiedTypesCompatible`, a curated-only `instanceParameters` even for a
1615
+ * Generic activity, and the library version where the platform writes
1616
+ * `packageVersion: "1.0.0"` (#1420).
1617
+ */
1618
+ function connectorMetadata(rc, op, slots, seed, executionType) {
1609
1619
  // The part DECLARATIONS only: a file part's value rides on its `file` input.
1610
1620
  const multipartParameters = (slots.multipartParameters ?? []).map(({ name, dataType }) => ({ name, dataType }));
1611
- const instanceParameters = {
1612
- connectorKey: rc.connectorKey,
1613
- objectName: op.objectName ?? rc.nodeType.split('.').pop(),
1614
- httpMethod: rc.httpMethod,
1615
- activityType: op.activityType ?? 'Curated',
1616
- version: rc.version,
1617
- supportsStreaming: op.supportsStreaming ?? false,
1618
- subType: op.subType ?? 'standard',
1619
- };
1620
- const configuration = {
1621
- essentialConfiguration: {
1622
- instanceParameters,
1623
- objectName: instanceParameters.objectName,
1624
- httpMethod: transportHttpMethod(rc.httpMethod),
1625
- path: rc.endpoint,
1626
- packageVersion: rc.version,
1627
- // An external task's object states how it runs; the Designer saves it here
1628
- // and beside the configuration. A plain connector activity has none.
1629
- ...(executionType ? { executionType } : {}),
1630
- },
1631
- };
1621
+ // The configuration replays the authored values (a schema action's parent
1622
+ // fields, a CEQL filter's tree), keyed by the library's own names, as Flow's.
1623
+ const essential = buildEssentialConfiguration(rc.entry, rc.version, replayInputsOf(slots), seed, executionType ?? null);
1624
+ const configuration = `=jsonString:${JSON.stringify({ essentialConfiguration: essential })}`;
1625
+ const instanceParameters = essential.instanceParameters;
1626
+ const objectName = String(op.objectName ?? rc.nodeType.split('.').pop());
1632
1627
  return {
1633
1628
  activityPropertyConfiguration: {
1634
1629
  ...(multipartParameters.length ? { multipartParameters } : {}),
1635
- configuration: `=jsonString:${JSON.stringify(configuration)}`,
1630
+ configuration,
1636
1631
  uiPathActivityTypeId: rc.activityTypeId ?? '',
1637
1632
  errorState: { issues: [] },
1638
1633
  ...(executionType ? { executionType } : {}),
1639
1634
  },
1640
1635
  activityMetadata: {
1641
1636
  activity: {
1642
- displayName: rc.entry.display?.label ?? instanceParameters.objectName,
1637
+ displayName: rc.entry.display?.label ?? objectName,
1643
1638
  description: rc.entry.display?.description ?? '',
1644
1639
  category: rc.connectorName,
1645
1640
  configuration: JSON.stringify(instanceParameters),
@@ -1653,7 +1648,7 @@ function connectorMetadata(rc, op, slots, executionType) {
1653
1648
  telemetryData: {
1654
1649
  connectorKey: rc.connectorKey,
1655
1650
  connectorName: rc.connectorName,
1656
- objectName: instanceParameters.objectName,
1651
+ objectName,
1657
1652
  objectDisplayName: op.objectDisplayName ?? rc.entry.display?.operationLabel ?? '',
1658
1653
  },
1659
1654
  };
package/dist/check.d.ts CHANGED
@@ -52,3 +52,11 @@ export declare const SCHEDULE_PATTERN: string;
52
52
  * and day names, and the Quartz specials `L W LW ? * #`.
53
53
  */
54
54
  export declare const SCHEDULE_CRON_PATTERN: string;
55
+ /**
56
+ * The one thing `check` says about runtimes: a flow with a node from a node-type
57
+ * package does not run on the UiPath platform, which runs exactly its registry.
58
+ * Reported once per flow, naming the steps and their packages, as a WARNING: the
59
+ * flow is still a correct workflow for a runtime that loads the package (the
60
+ * local engine), it just cannot be validated or published through `uip`.
61
+ */
62
+ export declare const PACKAGE_NODES_NOT_PLATFORM = "PACKAGE_NODES_NOT_PLATFORM";
package/dist/check.js CHANGED
@@ -892,6 +892,7 @@ function checkFlow(built, ancestors, opts = {}, ledger = newResourceIdLedger())
892
892
  }
893
893
  reportUnreachableHitlOutcomes(hitlChoiceNoPorts, exprs, diags);
894
894
  checkRegisteredFamilies(registeredSteps, unresolvedSteps, stepNames, diags);
895
+ checkPackageNodes(registeredSteps, diags);
895
896
  // The ids this scope's other nodes get, for the file-wide nodeId check in `check`.
896
897
  for (const n of stepNames)
897
898
  if (!ledger.taken.has(n))
@@ -2819,6 +2820,36 @@ function catalogued(entry, step, code, message, suggestion) {
2819
2820
  // Key order as the per-family rules always wrote it — `check --json` prints it.
2820
2821
  return { level: known.severity, code, step, message, ...(suggestion !== undefined ? { suggestion } : {}) };
2821
2822
  }
2823
+ /**
2824
+ * The one thing `check` says about runtimes: a flow with a node from a node-type
2825
+ * package does not run on the UiPath platform, which runs exactly its registry.
2826
+ * Reported once per flow, naming the steps and their packages, as a WARNING: the
2827
+ * flow is still a correct workflow for a runtime that loads the package (the
2828
+ * local engine), it just cannot be validated or published through `uip`.
2829
+ */
2830
+ export const PACKAGE_NODES_NOT_PLATFORM = 'PACKAGE_NODES_NOT_PLATFORM';
2831
+ function checkPackageNodes(steps, diags) {
2832
+ const byPackage = new Map();
2833
+ let first;
2834
+ for (const [name, placed] of steps) {
2835
+ const origin = placed.entry.family.package;
2836
+ if (origin === undefined)
2837
+ continue;
2838
+ first ??= name;
2839
+ const list = byPackage.get(origin.name) ?? [];
2840
+ list.push(`"${name}" (${placed.impl.typeName})`);
2841
+ byPackage.set(origin.name, list);
2842
+ }
2843
+ if (first === undefined)
2844
+ return;
2845
+ const packages = [...byPackage].map(([pkg, names]) => `${pkg}: ${names.join(', ')}`).join('; ');
2846
+ diags.push({
2847
+ level: 'warning', code: PACKAGE_NODES_NOT_PLATFORM, step: first,
2848
+ message: `This flow uses node types from a node-type package (${packages}). The UiPath platform runs only its own registry, `
2849
+ + 'so `uip maestro flow validate` refuses the file and it cannot be published; it runs on a runtime that loads the package, '
2850
+ + 'such as the local engine (`flow-debug`).',
2851
+ });
2852
+ }
2822
2853
  /** Run each registered family's own rules over all of its placements in this flow. */
2823
2854
  function checkRegisteredFamilies(steps, unresolved, stepNames, diags) {
2824
2855
  const byFamily = new Map();
@@ -2969,7 +3000,7 @@ placed, opts, carried) {
2969
3000
  if (!placed)
2970
3001
  return;
2971
3002
  const { impl } = placed;
2972
- if (impl.definition === 'per-tenant' && placed.carried !== undefined) {
3003
+ if (impl.definition !== 'bundled' && placed.carried !== undefined) {
2973
3004
  // At a carried version the carried definition answers, as serialize asks
2974
3005
  // it: the type's declaration covers only the versions it registers.
2975
3006
  if (!definitionHasErrorPort(placed.carried)) {
@@ -2977,7 +3008,7 @@ placed, opts, carried) {
2977
3008
  return;
2978
3009
  }
2979
3010
  }
2980
- else if (impl.definition === 'per-tenant') {
3011
+ else if (impl.definition !== 'bundled') {
2981
3012
  // The definition serialize will be supplied, when check's options can name
2982
3013
  // it (a connector's, from the library); the type answers without it otherwise.
2983
3014
  const def = impl.checkDefinition?.(placed.spec, opts);
@@ -92,6 +92,7 @@ export function registerFlowCommands(program) {
92
92
  .option('--import <specifier>', 'SDK import specifier in generated source')
93
93
  .option('--strict', 'fail on unsupported constructs')
94
94
  .option('--no-pipeline', 'do not emit a brownfield pipeline helper')
95
+ .option('--package <name...>', 'trust and load this node-type package (repeatable); otherwise package.json "maestroBuilderSdk".nodePackages decides')
95
96
  .action(async (input, options) => {
96
97
  const argv = [input];
97
98
  addValue(argv, '-o', options.output);
@@ -100,6 +101,8 @@ export function registerFlowCommands(program) {
100
101
  argv.push('--strict');
101
102
  if (options.pipeline === false)
102
103
  argv.push('--no-pipeline');
104
+ for (const name of options.package ?? [])
105
+ argv.push('--package', name);
103
106
  await invoke('flow decompile', () => import('../../decompile-cli.js'), argv);
104
107
  });
105
108
  flow.command('merge')
@@ -112,6 +115,7 @@ export function registerFlowCommands(program) {
112
115
  .option('--library <dir>', 'connector library, for compiling with --baseline-from or a .flow.ts edited source')
113
116
  .option('--bindings <file>', 'bindings.json, for the same compiles')
114
117
  .option('--strict', 'refuse when the no-edit round trip (the baseline) differs from the original')
118
+ .option('--package <name...>', 'trust and load this node-type package for --baseline-from (repeatable)')
115
119
  .allowExcessArguments()
116
120
  .action(async (_edited, options, command) => {
117
121
  const pair = namedOrLegacySecond(command, {
@@ -129,6 +133,8 @@ export function registerFlowCommands(program) {
129
133
  addValue(argv, '--bindings', options.bindings);
130
134
  if (options.strict)
131
135
  argv.push('--strict');
136
+ for (const name of options.package ?? [])
137
+ argv.push('--package', name);
132
138
  await invoke('flow merge', () => import('../../merge-cli.js'), argv);
133
139
  });
134
140
  }
package/dist/cli-run.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export type CliExitCode = 0 | 1 | 2;
2
2
  export declare function runWhenInvokedDirectly(moduleUrl: string, label: string, run: (argv: string[]) => Promise<CliExitCode> | CliExitCode): void;
3
3
  /** The value after `name` in `argv`, or `undefined`. */
4
+ /** Every value of a repeatable flag, in order: `--package a --package b` → `['a', 'b']`. */
5
+ export declare function flagValues(argv: readonly string[], flag: string): string[];
4
6
  export declare function flagValue(argv: readonly string[], name: string): string | undefined;
package/dist/cli-run.js CHANGED
@@ -51,6 +51,14 @@ export function runWhenInvokedDirectly(moduleUrl, label, run) {
51
51
  });
52
52
  }
53
53
  /** The value after `name` in `argv`, or `undefined`. */
54
+ /** Every value of a repeatable flag, in order: `--package a --package b` → `['a', 'b']`. */
55
+ export function flagValues(argv, flag) {
56
+ const out = [];
57
+ for (let i = 0; i < argv.length - 1; i++)
58
+ if (argv[i] === flag)
59
+ out.push(argv[i + 1]);
60
+ return out;
61
+ }
54
62
  export function flagValue(argv, name) {
55
63
  const i = argv.indexOf(name);
56
64
  return i >= 0 ? argv[i + 1] : undefined;
@@ -63,6 +63,33 @@
63
63
  *
64
64
  * Neither is cosmetic for a generic node: v1's own `check_generic_dynamic_node.py`
65
65
  * identifies the activity semantically, by `activityType == 'generic'` **and
66
- * `operation == 'list'`**, read from these very keys.
66
+ * `operation == 'list'`**, read from these very keys. *
67
+ * ## `savedJitInputFieldId` is not written, because the library cannot say when
68
+ *
69
+ * The CLI writes `savedJitInputFieldId: "in_<objectName>"` for a create /
70
+ * update / replace whose IS metadata declares a `body` PARAMETER (a
71
+ * just-in-time body). The offline library does not carry that parameter: its
72
+ * `operation.parameters` hold only `query` · `path` · `multipart`. Its body
73
+ * FIELDS do not stand in for it. Measured over the Designer- and CLI-saved
74
+ * create/update/replace nodes in the eval corpus: Slack `create-channel` (2
75
+ * body fields) carries the key, while `join-channel` (1 body field, 0/2) and
76
+ * `create-group-direct-message` (1 body field, 0/89) do not, and Data Service
77
+ * `create-entity-record` (no body fields) does (336/405). Writing it on a
78
+ * guess would be wrong on hundreds of nodes, so it stays out until the library
79
+ * records the body parameter.
80
+ */
81
+ export declare function buildConfiguration(entry: any, version: string, inputValues?: Record<string, unknown>, seed?: string, executionType?: string | null): string;
82
+ /**
83
+ * The `essentialConfiguration` object that {@link buildConfiguration} wraps in
84
+ * its `=jsonString:` envelope. For a caller that also needs a field of it (Case
85
+ * copies its `instanceParameters` into the activity metadata), so it does not
86
+ * parse the string back.
87
+ *
88
+ * @param entry - The connector library entry.
89
+ * @param version - The connector version.
90
+ * @param inputValues - The authored inputs to replay, keyed by the library's names.
91
+ * @param seed - Seed for the saved filter tree ids.
92
+ * @param executionType - An External Agent / Workflow object's run mode, or `null`.
93
+ * @returns The essential configuration, with only Studio Web's essential keys.
67
94
  */
68
- export declare function buildConfiguration(entry: any, version: string, inputValues?: Record<string, unknown>, seed?: string): string;
95
+ export declare function buildEssentialConfiguration(entry: any, version: string, inputValues?: Record<string, unknown>, seed?: string, executionType?: string | null): Record<string, unknown>;
@@ -20,6 +20,14 @@ const ESSENTIAL_DEFAULTS = {
20
20
  executionType: null,
21
21
  operation: 'create',
22
22
  packageVersion: '1.0.0',
23
+ // The CLI's offline builder writes these two on every activity
24
+ // (`buildActivityEssentialConfiguration`, uipath-cli
25
+ // `integrationservice-sdk/src/dap/essential-config.ts`), and so does every
26
+ // Designer save in the eval corpus. `connectorVersion` is a request header
27
+ // the library does not carry; Studio Web fills it from its type cache on
28
+ // open, so offline it is `null`, as the CLI and the Case fixture write it.
29
+ connectorVersion: null,
30
+ unifiedTypesCompatible: true,
23
31
  };
24
32
  /**
25
33
  * Which of an operation's parameters carries a CEQL filter.
@@ -161,9 +169,40 @@ function envelope(flat) {
161
169
  *
162
170
  * Neither is cosmetic for a generic node: v1's own `check_generic_dynamic_node.py`
163
171
  * identifies the activity semantically, by `activityType == 'generic'` **and
164
- * `operation == 'list'`**, read from these very keys.
172
+ * `operation == 'list'`**, read from these very keys. *
173
+ * ## `savedJitInputFieldId` is not written, because the library cannot say when
174
+ *
175
+ * The CLI writes `savedJitInputFieldId: "in_<objectName>"` for a create /
176
+ * update / replace whose IS metadata declares a `body` PARAMETER (a
177
+ * just-in-time body). The offline library does not carry that parameter: its
178
+ * `operation.parameters` hold only `query` · `path` · `multipart`. Its body
179
+ * FIELDS do not stand in for it. Measured over the Designer- and CLI-saved
180
+ * create/update/replace nodes in the eval corpus: Slack `create-channel` (2
181
+ * body fields) carries the key, while `join-channel` (1 body field, 0/2) and
182
+ * `create-group-direct-message` (1 body field, 0/89) do not, and Data Service
183
+ * `create-entity-record` (no body fields) does (336/405). Writing it on a
184
+ * guess would be wrong on hundreds of nodes, so it stays out until the library
185
+ * records the body parameter.
186
+ */
187
+ export function buildConfiguration(entry, version, inputValues, seed = 'node', executionType = null) {
188
+ return '=jsonString:' + JSON.stringify({
189
+ essentialConfiguration: buildEssentialConfiguration(entry, version, inputValues, seed, executionType),
190
+ });
191
+ }
192
+ /**
193
+ * The `essentialConfiguration` object that {@link buildConfiguration} wraps in
194
+ * its `=jsonString:` envelope. For a caller that also needs a field of it (Case
195
+ * copies its `instanceParameters` into the activity metadata), so it does not
196
+ * parse the string back.
197
+ *
198
+ * @param entry - The connector library entry.
199
+ * @param version - The connector version.
200
+ * @param inputValues - The authored inputs to replay, keyed by the library's names.
201
+ * @param seed - Seed for the saved filter tree ids.
202
+ * @param executionType - An External Agent / Workflow object's run mode, or `null`.
203
+ * @returns The essential configuration, with only Studio Web's essential keys.
165
204
  */
166
- export function buildConfiguration(entry, version, inputValues, seed = 'node') {
205
+ export function buildEssentialConfiguration(entry, version, inputValues, seed = 'node', executionType = null) {
167
206
  const op = entry.operation ?? {};
168
207
  const conn = entry.connector ?? {};
169
208
  const at = op.activityType;
@@ -177,6 +216,9 @@ export function buildConfiguration(entry, version, inputValues, seed = 'node') {
177
216
  : ESSENTIAL_DEFAULTS.operation;
178
217
  const composed = {
179
218
  ...ESSENTIAL_DEFAULTS,
219
+ // An External Agent / Workflow object states how it runs; a connector
220
+ // activity has none (`null`, the default).
221
+ executionType,
180
222
  operation: verb,
181
223
  customFieldsRequestDetails: schemaReplayDetails(op, inputValues),
182
224
  savedFilterTrees: savedFilterTrees(op, inputValues, seed),
@@ -202,5 +244,5 @@ export function buildConfiguration(entry, version, inputValues, seed = 'node') {
202
244
  },
203
245
  ...(op.pathTemplate || op.path ? { path: op.pathTemplate || op.path } : {}),
204
246
  };
205
- return '=jsonString:' + JSON.stringify(envelope(composed));
247
+ return envelope(composed).essentialConfiguration;
206
248
  }
@@ -58,6 +58,16 @@ export declare const CONNECTOR_INPUT_MISSING = "CONNECTOR_INPUT_MISSING";
58
58
  */
59
59
  export declare function connectorInputErrors(rc: ResolvedConnector, inputs: Record<string, unknown>, resolutions?: LookupResolutions, lookups?: Readonly<Record<string, LookupSpec>>, wire?: Pick<ConnectorInputOpts, 'dialect' | 'render'>): string | undefined;
60
60
  export declare function buildConnectorInputs(rc: any, inputs: Record<string, unknown>, opts: ConnectorInputOpts): ConnectorSlots;
61
+ /**
62
+ * The authored values a connector's `configuration` replays (a schema action's
63
+ * parent fields, a CEQL filter's tree), keyed by the library's own names: the
64
+ * body, path and query slots, merged. Flow and Case both write the
65
+ * configuration from this, so the two cannot drift.
66
+ *
67
+ * @param slots - The slots {@link buildConnectorInputs} returned.
68
+ * @returns The merged body, path and query values.
69
+ */
70
+ export declare function replayInputsOf(slots: ConnectorSlots): Record<string, unknown>;
61
71
  /**
62
72
  * `inputMetadata.multipart.bodyFieldName` names the CONTAINER part — the one the
63
73
  * runtime composes from `bodyParameters`. Measured on the designer's own nodes in
@@ -253,6 +253,22 @@ function swapLookupTokens(value, resolutions, unresolved) {
253
253
  return value.map((item) => swapLookupTokens(item, resolutions, unresolved));
254
254
  return value;
255
255
  }
256
+ /**
257
+ * The authored values a connector's `configuration` replays (a schema action's
258
+ * parent fields, a CEQL filter's tree), keyed by the library's own names: the
259
+ * body, path and query slots, merged. Flow and Case both write the
260
+ * configuration from this, so the two cannot drift.
261
+ *
262
+ * @param slots - The slots {@link buildConnectorInputs} returned.
263
+ * @returns The merged body, path and query values.
264
+ */
265
+ export function replayInputsOf(slots) {
266
+ return {
267
+ ...(slots.bodyParameters ?? {}),
268
+ ...(slots.pathParameters ?? {}),
269
+ ...(slots.queryParameters ?? {}),
270
+ };
271
+ }
256
272
  /**
257
273
  * `inputMetadata.multipart.bodyFieldName` names the CONTAINER part — the one the
258
274
  * runtime composes from `bodyParameters`. Measured on the designer's own nodes in
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The hand-off between a node-type package and the artifact's registry.
3
+ *
4
+ * `@uipath/maestro-builder-sdk/extend` is a builder entry and stays light at
5
+ * import (FB009): it must not load the Flow registry, which pulls the bundled
6
+ * definitions in. So a package's family is CONTRIBUTED here, and the registry
7
+ * ADOPTS whatever was contributed when it loads — at compile time, before any
8
+ * flow is built — and installs itself as the sink for every later contribution,
9
+ * which it then registers at once. A refusal (a kind a built-in family owns, a
10
+ * type that is not `definition: 'package'`) surfaces at adoption, still before
11
+ * the first node is serialized.
12
+ */
13
+ import type { ArtifactContexts } from './node-type.js';
14
+ import type { NodeFamily } from './family.js';
15
+ type Sink = (family: NodeFamily<ArtifactContexts>) => void;
16
+ /** Hand a package's family to the artifact's registry, now or when it loads. */
17
+ export declare function contributeFamily(family: NodeFamily<ArtifactContexts>): void;
18
+ /**
19
+ * The registry calls this once at load: it receives every family contributed so
20
+ * far and every one contributed after. Families are adopted in contribution order.
21
+ */
22
+ export declare function adoptContributedFamilies(adopt: Sink): void;
23
+ export {};
@@ -0,0 +1,18 @@
1
+ const pending = [];
2
+ let sink;
3
+ /** Hand a package's family to the artifact's registry, now or when it loads. */
4
+ export function contributeFamily(family) {
5
+ if (sink !== undefined)
6
+ sink(family);
7
+ else
8
+ pending.push(family);
9
+ }
10
+ /**
11
+ * The registry calls this once at load: it receives every family contributed so
12
+ * far and every one contributed after. Families are adopted in contribution order.
13
+ */
14
+ export function adoptContributedFamilies(adopt) {
15
+ sink = adopt;
16
+ for (const family of pending.splice(0))
17
+ adopt(family);
18
+ }
@@ -25,12 +25,25 @@ export interface Placement<Spec = unknown> {
25
25
  /** The version the node will be emitted at. */
26
26
  readonly version: string;
27
27
  }
28
+ /** The npm package a node family comes from, when it is not built into the SDK. */
29
+ export interface PackageOrigin {
30
+ /** The package name an author imports, e.g. `'@uipath/flow-local-nodes'`. */
31
+ readonly name: string;
32
+ /** The package's version, when it knows it; written into each definition's provenance. */
33
+ readonly version?: string;
34
+ }
28
35
  export interface NodeFamily<Ctx extends ArtifactContexts> {
29
36
  /** Stable id, e.g. `'data-fabric'`. Unique within the artifact's registry. */
30
37
  readonly id: string;
31
38
  readonly title: string;
32
39
  readonly types: readonly AnyNodeType<Ctx>[];
33
40
  readonly diagnostics: DiagnosticCatalog;
41
+ /**
42
+ * Set on a family registered from a node-type package. Every type of such a
43
+ * family declares `definition: 'package'`, and only such a family may; a
44
+ * built-in family never sets this.
45
+ */
46
+ readonly package?: PackageOrigin;
34
47
  /** Rules over every placement of this family's types, after the per-node checks. */
35
48
  check?(placements: readonly Placement[], ctx: Ctx['familyCheck']): void;
36
49
  }
@@ -25,7 +25,19 @@ import type { TypeDesc } from '../expr.js';
25
25
  * its declared inputs) is read from. `per-tenant`: built per tenant or per call —
26
26
  * there is no bundled entry to ask, so the node type declares those answers itself.
27
27
  */
28
- export type DefinitionSource = 'bundled' | 'per-tenant';
28
+ /**
29
+ * Where a node type's definition comes from.
30
+ *
31
+ * - `bundled`: the SDK's bundled registry snapshot has it, so the node carries no
32
+ * definition and the bundle answers `errorPort`.
33
+ * - `per-tenant`: a connector-shaped type the tenant defines; the type supplies its
34
+ * definition at serialize time and declares `errorPort`.
35
+ * - `package`: a type from a node-type package (`@uipath/maestro-builder-sdk/extend`);
36
+ * the package ships the definition, the type writes it into the file with its
37
+ * provenance, and the UiPath platform does not run it. A built-in family may not
38
+ * declare one — the registry refuses it (see `NodeTypeRegistry`).
39
+ */
40
+ export type DefinitionSource = 'bundled' | 'per-tenant' | 'package';
29
41
  /**
30
42
  * One place a spec carries a value the artifact evaluates against its scope.
31
43
  * `value` is walked deeply for expressions; `code` is a script body whose
@@ -104,6 +116,12 @@ export type OutputContract = {
104
116
  */
105
117
  export interface FactoryCall {
106
118
  readonly factory: string;
119
+ /**
120
+ * The module the factory is imported from, when it is not the SDK: a node-type
121
+ * package's own name. Decompile defaults it to the type's family `package.name`,
122
+ * so a `print` implementation sets it only to import from somewhere else.
123
+ */
124
+ readonly module?: string;
107
125
  readonly positional?: readonly string[];
108
126
  readonly options?: ReadonlyArray<readonly [name: string, source: string | undefined]>;
109
127
  /** A note for the reader, written as a block comment after the call (the artifact escapes it). */
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The node definition a node-type package writes into a compiled `.flow`.
3
+ *
4
+ * A package type has no platform definition — neither the SDK's bundled snapshot
5
+ * nor the tenant has one — so the file has to carry everything a reader needs:
6
+ * the designer's display and handles, the input and output schemas, and the
7
+ * `model.expansion` template the `.flow` → BPMN converter expands.
8
+ *
9
+ * ## Why one-node expansion
10
+ *
11
+ * The product's converter (vendored in `local-engine`) resolves a node's
12
+ * definition as `manifestDef ?? fileDef` and reads a node's per-instance input
13
+ * VALUES only through a definition's `model.expansion`: a `context` entry's
14
+ * `source: 'node.data.inputs.<name>'` is read off each placed node while the
15
+ * template is expanded. So every package definition expands to exactly one
16
+ * `bpmn:ServiceTask` that REPLACES the node under its own id, carries
17
+ * `<uipath:type value="<serviceType>">`, its inputs as context, and its
18
+ * `=response` / `=Error` output rows — the element shape a runtime's dispatch
19
+ * lane reads for a connector. Expansion drops the node's original edges, so the
20
+ * template re-creates the `success` edge and, when the author wired `.onError`,
21
+ * an error boundary with the `error` edge.
22
+ *
23
+ * The input NAMES are the contract with the runtime half of the package: a
24
+ * handler reads them off `TaskContext.inputs`. A `string` context entry is
25
+ * emitted as `value` and a `json` one as a JSON `body`; the converter turns a
26
+ * falsy `string` into `""` and a falsy `json` into `{}`, so a package encodes
27
+ * booleans and numbers as `=js:` expressions rather than relying on either.
28
+ */
29
+ /** One authored input of a package node. */
30
+ export interface PackageInputField {
31
+ readonly type: 'string' | 'boolean' | 'number' | 'array' | 'object';
32
+ readonly description: string;
33
+ readonly required?: boolean;
34
+ readonly enum?: readonly string[];
35
+ readonly default?: unknown;
36
+ }
37
+ /** One field of a package node's `output`. */
38
+ export interface PackageOutputField {
39
+ readonly type: 'string' | 'boolean' | 'number' | 'array' | 'object';
40
+ readonly description: string;
41
+ }
42
+ /** What a package declares about one node; `defineNodeDefinition` does the rest. */
43
+ export interface PackageNodeShape {
44
+ /** The `.flow` node type, e.g. `acme.ledger.post`. */
45
+ readonly nodeType: string;
46
+ /** The BPMN service type a runtime dispatches on, e.g. `Acme.Ledger.Post`. */
47
+ readonly serviceType: string;
48
+ /** The definition version; the node's `typeVersion`. Default `1.0`. */
49
+ readonly version?: string;
50
+ readonly label: string;
51
+ readonly description: string;
52
+ /** A Lucide icon name the designer draws, e.g. `file-text`. */
53
+ readonly icon: string;
54
+ /** The designer's category for the add-node panel. Default `package`. */
55
+ readonly category?: string;
56
+ readonly tags?: readonly string[];
57
+ readonly inputs: Readonly<Record<string, PackageInputField>>;
58
+ readonly outputs: Readonly<Record<string, PackageOutputField>>;
59
+ }
60
+ export declare const DEFAULT_PACKAGE_DEFINITION_VERSION = "1.0";
61
+ /**
62
+ * The definition `defineNodeDefinition` returns: the complete manifest, with the
63
+ * fields a package's node type reads back typed — `version` is the type's
64
+ * `version` and the snapshot reference's, `model.serviceType` is what the runtime
65
+ * half claims.
66
+ */
67
+ export interface PackageNodeDefinition extends Record<string, unknown> {
68
+ readonly nodeType: string;
69
+ readonly version: string;
70
+ readonly model: Record<string, unknown> & {
71
+ readonly type: 'bpmn:ServiceTask';
72
+ readonly serviceType: string;
73
+ };
74
+ }
75
+ /**
76
+ * Build a package node's definition from its shape: the complete manifest a
77
+ * compiled flow carries as a `snapshot` reference, with the one-node expansion
78
+ * described above. Provenance (`source.package`) is stamped at serialize time
79
+ * from the family's `package`, not here, so a shape stays free of the package's
80
+ * own name.
81
+ */
82
+ export declare function defineNodeDefinition(shape: PackageNodeShape): PackageNodeDefinition;