@uipath/maestro-builder-sdk 6.68.1 → 6.68.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-index.md +36 -36
- package/dist/api-members.md +156 -156
- package/dist/case/case-sdk.d.ts +28 -8
- package/dist/case/case-sdk.js +22 -1
- package/dist/case/check.js +9 -2
- package/dist/case/connector-placeholder.d.ts +61 -0
- package/dist/case/connector-placeholder.js +70 -0
- package/dist/case/decompile.d.ts +1 -0
- package/dist/case/decompile.js +34 -1
- package/dist/case/preserve.d.ts +32 -0
- package/dist/case/preserve.js +41 -0
- package/dist/case/serialize.js +22 -12
- package/dist/core/bindings.d.ts +12 -0
- package/dist/core/bindings.js +25 -3
- package/package.json +2 -2
package/dist/case/case-sdk.d.ts
CHANGED
|
@@ -383,15 +383,23 @@ export interface BuiltCaseExitCondition {
|
|
|
383
383
|
export type TaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'flow-process' | 'function' | 'business-rule' | 'external-agent' | 'external-workflow' | 'action' | 'connector' | 'wait-for-timer' | 'wait-for-connector';
|
|
384
384
|
/**
|
|
385
385
|
* Task kinds that the Case schema permits as unresolved skeletons: the
|
|
386
|
-
* published-resource kinds,
|
|
387
|
-
*
|
|
388
|
-
* `
|
|
386
|
+
* published-resource kinds, `action` (a human task whose Action App or form is
|
|
387
|
+
* not known yet), and the two connector kinds, `connector` (an
|
|
388
|
+
* `execute-connector-activity` task) and `wait-for-connector`, for a connector
|
|
389
|
+
* or event the spec leaves unresolved. Each compiles to a task whose `data` is
|
|
390
|
+
* `{}`, which `uip maestro case validate` reports as a `TASK_NOT_CONFIGURED`
|
|
391
|
+
* warning.
|
|
389
392
|
*/
|
|
390
|
-
export type UnresolvedReferenceTaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'function' | 'action';
|
|
393
|
+
export type UnresolvedReferenceTaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'function' | 'action' | 'connector' | 'wait-for-connector';
|
|
391
394
|
/**
|
|
392
395
|
* A `wait-for-connector` subscription: suspend on an Integration Service event.
|
|
393
|
-
* Omit `connectorKey`/`operation` for a bare **placeholder
|
|
394
|
-
*
|
|
396
|
+
* Omit `connectorKey`/`operation` for a bare **placeholder**, the same task as
|
|
397
|
+
* `.unresolved('wait-for-connector')`: its `data` is `{}`.
|
|
398
|
+
*
|
|
399
|
+
* Setting them is the legacy named form, which `uip maestro case validate`
|
|
400
|
+
* refuses on a task (`CASE_MGMT_CONNECTOR_ACTIVITY_UNRESOLVED`, #1560): for an
|
|
401
|
+
* unresolved event use `.unresolved('wait-for-connector')`, and for a known one
|
|
402
|
+
* a trigger descriptor or `{ connector, event }`.
|
|
395
403
|
*/
|
|
396
404
|
export interface WaitConnectorPlaceholderSpec {
|
|
397
405
|
/** Connector key, e.g. `uipath-microsoft-outlook365`. */
|
|
@@ -875,8 +883,20 @@ declare class TaskBuilder {
|
|
|
875
883
|
* `unresolved('action')` is the human-task skeleton: no Action App, title,
|
|
876
884
|
* recipient or form fields, for an app that is not published yet. Use
|
|
877
885
|
* `.action({ ... })` instead once the form is known.
|
|
886
|
+
* `unresolved('connector')` and `unresolved('wait-for-connector')` are the
|
|
887
|
+
* connector placeholders, for a connector key, activity or event the spec
|
|
888
|
+
* leaves unresolved: do not pick a real connector to stand in for it. Use
|
|
889
|
+
* `.connector(...)` or `.waitForConnector(...)` once it is known.
|
|
890
|
+
*
|
|
891
|
+
* @example
|
|
892
|
+
* **Leave both connector tasks of an SDD with an `<UNRESOLVED>` connector as placeholders**
|
|
893
|
+
* ```ts
|
|
894
|
+
* casePlan('notice').stage('Notice', (s) => s
|
|
895
|
+
* .task('Notify regulator', (t) => t.unresolved('connector'))
|
|
896
|
+
* .task('Await notice', (t) => t.unresolved('wait-for-connector')))
|
|
897
|
+
* ```
|
|
878
898
|
*
|
|
879
|
-
* @param kind - The task kind to preserve: a published-resource family,
|
|
899
|
+
* @param kind - The task kind to preserve: a published-resource family, `'action'` for a human task, or `'connector'` / `'wait-for-connector'` for a connector task.
|
|
880
900
|
* @returns This builder, so calls chain.
|
|
881
901
|
*/
|
|
882
902
|
unresolved(kind: UnresolvedReferenceTaskKind): this;
|
|
@@ -1061,7 +1081,7 @@ declare class TaskBuilder {
|
|
|
1061
1081
|
/**
|
|
1062
1082
|
* A wait-for-connector task in placeholder or stringly-resolved form.
|
|
1063
1083
|
*
|
|
1064
|
-
* @param spec - Omit for a placeholder, pass `connectorKey`/`operation` for the legacy named
|
|
1084
|
+
* @param spec - Omit for a placeholder (`data: {}`, the same task as `.unresolved('wait-for-connector')`), pass `connectorKey`/`operation` for the legacy named form, which `uip maestro case validate` refuses on a task (#1560; for an unresolved event use `.unresolved('wait-for-connector')`),
|
|
1065
1085
|
* or pass a symbolic `{ connector, event, ... }` subscription for library resolution.
|
|
1066
1086
|
* @returns This builder, so calls chain.
|
|
1067
1087
|
*/
|
package/dist/case/case-sdk.js
CHANGED
|
@@ -4,6 +4,7 @@ import { checkCaseExpressions, CaseBuildError } from './case-expr-check.js';
|
|
|
4
4
|
import { CASE_FORMAT_PROFILE, CASE_SCHEMA_VERSIONS, recordAuthoredCaseVersion } from './format-profile.js';
|
|
5
5
|
import { schemaVersionRefusal } from '../schema-version.js';
|
|
6
6
|
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
7
|
+
import { isConnectorPlaceholder } from './connector-placeholder.js';
|
|
7
8
|
export { preserveCaseJson } from './preserve.js';
|
|
8
9
|
export { CaseBuildError } from './case-expr-check.js';
|
|
9
10
|
/**
|
|
@@ -208,8 +209,20 @@ class TaskBuilder {
|
|
|
208
209
|
* `unresolved('action')` is the human-task skeleton: no Action App, title,
|
|
209
210
|
* recipient or form fields, for an app that is not published yet. Use
|
|
210
211
|
* `.action({ ... })` instead once the form is known.
|
|
212
|
+
* `unresolved('connector')` and `unresolved('wait-for-connector')` are the
|
|
213
|
+
* connector placeholders, for a connector key, activity or event the spec
|
|
214
|
+
* leaves unresolved: do not pick a real connector to stand in for it. Use
|
|
215
|
+
* `.connector(...)` or `.waitForConnector(...)` once it is known.
|
|
211
216
|
*
|
|
212
|
-
* @
|
|
217
|
+
* @example
|
|
218
|
+
* **Leave both connector tasks of an SDD with an `<UNRESOLVED>` connector as placeholders**
|
|
219
|
+
* ```ts
|
|
220
|
+
* casePlan('notice').stage('Notice', (s) => s
|
|
221
|
+
* .task('Notify regulator', (t) => t.unresolved('connector'))
|
|
222
|
+
* .task('Await notice', (t) => t.unresolved('wait-for-connector')))
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @param kind - The task kind to preserve: a published-resource family, `'action'` for a human task, or `'connector'` / `'wait-for-connector'` for a connector task.
|
|
213
226
|
* @returns This builder, so calls chain.
|
|
214
227
|
*/
|
|
215
228
|
unresolved(kind) {
|
|
@@ -585,6 +598,14 @@ class TaskBuilder {
|
|
|
585
598
|
throw new CompileRefusal('TASK_IO_UNRESOLVED_REFERENCE', `task "${this._name}": an unresolved reference task cannot declare inputs or outputs.`);
|
|
586
599
|
if (!this._action && !this._ref && this._kind === 'action' && this._outputs)
|
|
587
600
|
throw new CompileRefusal('TASK_IO_UNRESOLVED_REFERENCE', `task "${this._name}": an unresolved action task cannot declare outputs.`);
|
|
601
|
+
// A connector placeholder's `data` must stay `{}` (connector-placeholder.ts):
|
|
602
|
+
// any output row, an `{ assign }` one included, would make it a configured
|
|
603
|
+
// task the CLI refuses for naming no activity.
|
|
604
|
+
if (this._kind === 'connector' && this._outputs?.length && isConnectorPlaceholder({ kind: this._kind, connector: this._connector, waitConnector: this._waitConnector }))
|
|
605
|
+
throw new CompileRefusal('TASK_IO_UNRESOLVED_REFERENCE', `task "${this._name}": an unresolved connector task cannot declare outputs. Remove the outputs, or resolve the connector with .connector(...) first.`);
|
|
606
|
+
if (this._kind === 'wait-for-connector' && this._outputs?.length && !extract
|
|
607
|
+
&& isConnectorPlaceholder({ kind: this._kind, connector: this._connector, waitConnector: this._waitConnector }))
|
|
608
|
+
throw new CompileRefusal('TASK_IO_UNRESOLVED_REFERENCE', `task "${this._name}": an unresolved wait-for-connector task cannot declare outputs. Remove the outputs, or name the event with .waitForConnector(<trigger descriptor>) first.`);
|
|
588
609
|
if (this._kind === 'wait-for-connector' && extract
|
|
589
610
|
&& !(this._waitConnector && 'connector' in this._waitConnector && 'event' in this._waitConnector))
|
|
590
611
|
throw new CompileRefusal('TASK_OUTPUTS_PLACEHOLDER_EVENT', `task "${this._name}": a placeholder wait-for-connector task has no event response to extract outputs from — name the event (.waitForConnector(<trigger descriptor>) or { connector, event }).`);
|
package/dist/case/check.js
CHANGED
|
@@ -3,12 +3,13 @@ import { ruleShapeProblems } from './rule-options.js';
|
|
|
3
3
|
import { types } from '../core/expr.js';
|
|
4
4
|
import { CASE_FORMAT_PROFILE, CASE_SCHEMA_VERSIONS, authoredCaseVersion, caseVersionMismatch } from './format-profile.js';
|
|
5
5
|
import { schemaVersionRefusal } from '../schema-version.js';
|
|
6
|
-
import { caseJsonPreservation, caseJsonPreservationErrors } from './preserve.js';
|
|
6
|
+
import { caseJsonPreservation, caseJsonPreservationErrors, preservedBindings } from './preserve.js';
|
|
7
7
|
import { EQUALS_EXPR_DIALECT, checkBindingLabels, checkConnectorContract, checkConnectorLookups, checkConnectorSchema, } from '../core/connector-checks.js';
|
|
8
8
|
import { checkEventResolution } from '../core/event-checks.js';
|
|
9
9
|
import { EXTERNAL_TASK_DESCRIPTOR_CONTRACT, EXTERNAL_TASK_HYBRID_UNSUPPORTED, externalHybridRefusal, isExternalExecutionMode } from './external.js';
|
|
10
10
|
import { isResourceLabelRefusal, resolveResourceRef } from './resource-identity.js';
|
|
11
11
|
import { taskShapeProblems } from './task-shape.js';
|
|
12
|
+
import { isConnectorPlaceholder } from './connector-placeholder.js';
|
|
12
13
|
const TYPE_NAMES = new Set(Object.values(types));
|
|
13
14
|
// audit-case-coverage: rule-slot table start
|
|
14
15
|
// This runtime table must equal the committed matrix's rejected cells. The audit
|
|
@@ -24,6 +25,11 @@ const ILLEGAL = [];
|
|
|
24
25
|
*/
|
|
25
26
|
export function check(built, opts = {}) {
|
|
26
27
|
const diags = [];
|
|
28
|
+
// The same registry `serialize` resolves against, so the two agree on which
|
|
29
|
+
// labels a decompiled source's preserved binding rows answer.
|
|
30
|
+
const bindings = preservedBindings(opts.bindings, caseJsonPreservation(built));
|
|
31
|
+
if (bindings !== opts.bindings)
|
|
32
|
+
opts = { ...opts, bindings };
|
|
27
33
|
for (const message of caseJsonPreservationErrors(caseJsonPreservation(built)))
|
|
28
34
|
diags.push({
|
|
29
35
|
level: 'error',
|
|
@@ -362,7 +368,8 @@ function checkLibraryConnectors(built, opts, diags) {
|
|
|
362
368
|
rules(stage.exit, `stage "${stage.label}" exit`);
|
|
363
369
|
for (const t of stage.lanes.flat()) {
|
|
364
370
|
rules(t.entry, `task "${t.name}" entry`);
|
|
365
|
-
|
|
371
|
+
// A placeholder (`.unresolved('connector')`) names no operation to resolve.
|
|
372
|
+
if (t.kind === 'connector' && t.connector && !isConnectorPlaceholder(t)) {
|
|
366
373
|
const where = `Connector task "${t.name}"`;
|
|
367
374
|
const spec = t.connector;
|
|
368
375
|
checkConnectorLookups(where, spec, opts, EQUALS_EXPR_DIALECT, reporter(where, t.name));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one definition of a Case **connector placeholder**: a connector task
|
|
3
|
+
* (`execute-connector-activity`) or a wait-for-connector task whose connector
|
|
4
|
+
* is not known yet (#1354).
|
|
5
|
+
*
|
|
6
|
+
* The contract is the CLI's. `uip maestro case validate` treats an empty task
|
|
7
|
+
* `data` as the unresolved placeholder and reports it with the
|
|
8
|
+
* `TASK_NOT_CONFIGURED` warning. Any other connector task `data`, however
|
|
9
|
+
* small, is judged as a configured task, and one without `metadata` and
|
|
10
|
+
* `objectName` context entries is refused with
|
|
11
|
+
* `CASE_MGMT_CONNECTOR_ACTIVITY_UNRESOLVED` (cli
|
|
12
|
+
* `packages/case-tool/src/services/case-validators/connector-activity-validator.ts`,
|
|
13
|
+
* `appliesTo` = connector task type and non-empty `data`).
|
|
14
|
+
*
|
|
15
|
+
* So, for example:
|
|
16
|
+
*
|
|
17
|
+
* t.unresolved('wait-for-connector') -> { type: 'wait-for-connector', data: {} }
|
|
18
|
+
* t.unresolved('connector') -> { type: 'execute-connector-activity', data: {} }
|
|
19
|
+
* t.waitForConnector() -> { type: 'wait-for-connector', data: {} }
|
|
20
|
+
*
|
|
21
|
+
* The SDK once wrote `{ serviceType: 'Intsvc.WaitForEvent' }` for the bare
|
|
22
|
+
* wait-for-connector task, after an older caseplan. The CLI refuses that, so
|
|
23
|
+
* it is only read here (decompile lifts it back to the placeholder) and never
|
|
24
|
+
* written.
|
|
25
|
+
*
|
|
26
|
+
* `serialize` writes the placeholder, `check` skips the library checks for it,
|
|
27
|
+
* the builder refuses outputs on it, and `decompile` reads it back, all through
|
|
28
|
+
* this module. A wait-for-connector *rule* is not a task and keeps its own
|
|
29
|
+
* subscription bag. A task that names its connector without resolving it
|
|
30
|
+
* (`.waitForConnector({ connectorKey, operation })`) is not a placeholder and
|
|
31
|
+
* still writes its `context`; the CLI refuses that too (#1560).
|
|
32
|
+
*/
|
|
33
|
+
import type { BuiltTask, TaskKind } from './case-sdk.js';
|
|
34
|
+
/** The SDK task kinds that have a connector placeholder. */
|
|
35
|
+
export type ConnectorPlaceholderKind = Extract<TaskKind, 'connector' | 'wait-for-connector'>;
|
|
36
|
+
/**
|
|
37
|
+
* Whether a built task is a connector placeholder: a connector task with no
|
|
38
|
+
* connector call, or a wait-for-connector task that names no event (no
|
|
39
|
+
* resolved subscription and no `connectorKey` / `operation`).
|
|
40
|
+
*
|
|
41
|
+
* @param task - The task as the builder recorded it.
|
|
42
|
+
* @returns True when the task compiles to the placeholder `data: {}`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function isConnectorPlaceholder(task: Pick<BuiltTask, 'kind' | 'connector' | 'waitConnector'>): boolean;
|
|
45
|
+
/** The placeholder's `data`: empty, the shape the CLI reads as unresolved. */
|
|
46
|
+
export declare function connectorPlaceholderData(): Record<string, unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* The SDK kind of a caseplan task that is a connector placeholder, or
|
|
49
|
+
* undefined when it is not one.
|
|
50
|
+
*
|
|
51
|
+
* Reads the CLI's placeholder (`data` empty or absent) on both connector task
|
|
52
|
+
* types, and, on a wait-for-connector task, the two shapes older SDKs wrote
|
|
53
|
+
* for it: `{ serviceType: 'Intsvc.WaitForEvent' }`, optionally with empty
|
|
54
|
+
* `inputs` / `outputs` / `bindings`, and that plus a `connectorKey` /
|
|
55
|
+
* `operation` context whose values are both `"placeholder"`.
|
|
56
|
+
*
|
|
57
|
+
* @param type - The task's wire `type`.
|
|
58
|
+
* @param data - The task's `data`.
|
|
59
|
+
* @returns The kind to lift it to with `.unresolved(kind)`, or undefined.
|
|
60
|
+
*/
|
|
61
|
+
export declare function connectorPlaceholderKindOf(type: unknown, data: unknown): ConnectorPlaceholderKind | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { CASE_FORMAT_PROFILE } from './format-profile.js';
|
|
2
|
+
/** The keys an older SDK's wait-for-connector placeholder bag carried. */
|
|
3
|
+
const LEGACY_WAIT_KEYS = new Set(['serviceType', 'context', 'inputs', 'outputs', 'bindings']);
|
|
4
|
+
/**
|
|
5
|
+
* Whether a built task is a connector placeholder: a connector task with no
|
|
6
|
+
* connector call, or a wait-for-connector task that names no event (no
|
|
7
|
+
* resolved subscription and no `connectorKey` / `operation`).
|
|
8
|
+
*
|
|
9
|
+
* @param task - The task as the builder recorded it.
|
|
10
|
+
* @returns True when the task compiles to the placeholder `data: {}`.
|
|
11
|
+
*/
|
|
12
|
+
export function isConnectorPlaceholder(task) {
|
|
13
|
+
if (task.kind === 'connector')
|
|
14
|
+
return task.connector === undefined;
|
|
15
|
+
if (task.kind !== 'wait-for-connector')
|
|
16
|
+
return false;
|
|
17
|
+
const spec = task.waitConnector;
|
|
18
|
+
if (spec === undefined)
|
|
19
|
+
return true;
|
|
20
|
+
if ('connector' in spec && 'event' in spec)
|
|
21
|
+
return false;
|
|
22
|
+
return spec.connectorKey === undefined && spec.operation === undefined;
|
|
23
|
+
}
|
|
24
|
+
/** The placeholder's `data`: empty, the shape the CLI reads as unresolved. */
|
|
25
|
+
export function connectorPlaceholderData() {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The SDK kind of a caseplan task that is a connector placeholder, or
|
|
30
|
+
* undefined when it is not one.
|
|
31
|
+
*
|
|
32
|
+
* Reads the CLI's placeholder (`data` empty or absent) on both connector task
|
|
33
|
+
* types, and, on a wait-for-connector task, the two shapes older SDKs wrote
|
|
34
|
+
* for it: `{ serviceType: 'Intsvc.WaitForEvent' }`, optionally with empty
|
|
35
|
+
* `inputs` / `outputs` / `bindings`, and that plus a `connectorKey` /
|
|
36
|
+
* `operation` context whose values are both `"placeholder"`.
|
|
37
|
+
*
|
|
38
|
+
* @param type - The task's wire `type`.
|
|
39
|
+
* @param data - The task's `data`.
|
|
40
|
+
* @returns The kind to lift it to with `.unresolved(kind)`, or undefined.
|
|
41
|
+
*/
|
|
42
|
+
export function connectorPlaceholderKindOf(type, data) {
|
|
43
|
+
const kind = type === CASE_FORMAT_PROFILE.connectorActivityNodeType ? 'connector'
|
|
44
|
+
: type === 'wait-for-connector' ? 'wait-for-connector'
|
|
45
|
+
: undefined;
|
|
46
|
+
if (!kind)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (data === undefined || data === null)
|
|
49
|
+
return kind;
|
|
50
|
+
if (typeof data !== 'object' || Array.isArray(data))
|
|
51
|
+
return undefined;
|
|
52
|
+
const bag = data;
|
|
53
|
+
const keys = Object.keys(bag);
|
|
54
|
+
if (keys.length === 0)
|
|
55
|
+
return kind;
|
|
56
|
+
if (kind !== 'wait-for-connector' || bag.serviceType !== CASE_FORMAT_PROFILE.waitConnectorServiceType)
|
|
57
|
+
return undefined;
|
|
58
|
+
const emptyList = (value) => value === undefined || (Array.isArray(value) && value.length === 0);
|
|
59
|
+
if (!keys.every((key) => LEGACY_WAIT_KEYS.has(key)))
|
|
60
|
+
return undefined;
|
|
61
|
+
if (!['inputs', 'outputs', 'bindings'].every((key) => emptyList(bag[key])))
|
|
62
|
+
return undefined;
|
|
63
|
+
const context = bag.context;
|
|
64
|
+
if (context === undefined)
|
|
65
|
+
return kind;
|
|
66
|
+
const placeholderContext = Array.isArray(context) && context.length === 2
|
|
67
|
+
&& ['connectorKey', 'operation'].every((name) => context
|
|
68
|
+
.some((row) => row?.name === name && row.value === 'placeholder'));
|
|
69
|
+
return placeholderContext ? kind : undefined;
|
|
70
|
+
}
|
package/dist/case/decompile.d.ts
CHANGED
package/dist/case/decompile.js
CHANGED
|
@@ -7,6 +7,7 @@ import { ownEntry } from '../core/own-entry.js';
|
|
|
7
7
|
import { isUnrestorableSlotId, taskFingerprint } from './preserve.js';
|
|
8
8
|
import { isInSolutionKey, SOLUTION_FOLDER } from './resource-identity.js';
|
|
9
9
|
import { offlineBindingLabel } from './ids.js';
|
|
10
|
+
import { connectorPlaceholderKindOf } from './connector-placeholder.js';
|
|
10
11
|
const TASK_KIND_METHOD = {
|
|
11
12
|
process: 'process',
|
|
12
13
|
agent: 'agent',
|
|
@@ -698,6 +699,12 @@ function externalCall(task, refs, library) {
|
|
|
698
699
|
function emitTaskKind(task, refs, library) {
|
|
699
700
|
const kind = task.type;
|
|
700
701
|
const data = task.data ?? {};
|
|
702
|
+
// A connector placeholder (connector-placeholder.ts): `data: {}`, or an older
|
|
703
|
+
// SDK's serviceType-only wait bag. It names no connector, so it needs no
|
|
704
|
+
// library and lifts to the call that compiles to `{}` again.
|
|
705
|
+
const placeholder = connectorPlaceholderKindOf(kind, task.data);
|
|
706
|
+
if (placeholder)
|
|
707
|
+
return `.unresolved(${tsLiteral(placeholder)})`;
|
|
701
708
|
const method = ownEntry(TASK_KIND_METHOD, kind);
|
|
702
709
|
if (method) {
|
|
703
710
|
if (kind !== 'flow-process' && data.name === undefined && data.folderPath === undefined) {
|
|
@@ -756,7 +763,10 @@ function emitTaskKind(task, refs, library) {
|
|
|
756
763
|
}
|
|
757
764
|
if (kind === 'wait-for-connector') {
|
|
758
765
|
const spec = waitConnectorSpec(data, `wait-for-connector task "${task.displayName ?? task.id ?? '?'}"`, refs);
|
|
759
|
-
|
|
766
|
+
// A bag with no context and no resolved rows is the placeholder, lifted above.
|
|
767
|
+
if (spec === undefined)
|
|
768
|
+
throw new Error(`internal SDK error: wait-for-connector task "${task.displayName ?? task.id ?? '?'}" is a placeholder the lift above missed.`);
|
|
769
|
+
return `.waitForConnector(${spec})`;
|
|
760
770
|
}
|
|
761
771
|
const message = kind === 'business-rule'
|
|
762
772
|
// The converter reads a missing version as v2, whose result shape differs
|
|
@@ -1305,6 +1315,26 @@ function identityPreservation(plan) {
|
|
|
1305
1315
|
])),
|
|
1306
1316
|
};
|
|
1307
1317
|
}
|
|
1318
|
+
/**
|
|
1319
|
+
* The root connection/folder rows a recompile needs to resolve the labels
|
|
1320
|
+
* decompile names connectors by. `bindingLabel` writes such a label as the
|
|
1321
|
+
* row's `id` (`connection: "bOC1"`), and that id means nothing without the row.
|
|
1322
|
+
* Kept: `resource: "Connection"` rows with a key other than their own id. Not
|
|
1323
|
+
* kept: a row `serialize` minted offline (decompile writes its authored label,
|
|
1324
|
+
* and a recompile mints the same row again) and a row keyed by its own id
|
|
1325
|
+
* (a placeholder, which `Bindings` would ignore anyway).
|
|
1326
|
+
*/
|
|
1327
|
+
function preservedBindingRows(plan) {
|
|
1328
|
+
return (plan.bindings ?? []).filter((row) => {
|
|
1329
|
+
const r = row;
|
|
1330
|
+
if (typeof r.id !== 'string' || r.resource !== 'Connection')
|
|
1331
|
+
return false;
|
|
1332
|
+
if (offlineBindingLabel(r) !== undefined)
|
|
1333
|
+
return false;
|
|
1334
|
+
const key = r.resourceKey ?? r.default;
|
|
1335
|
+
return typeof key === 'string' && key !== '' && key !== r.id;
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1308
1338
|
/**
|
|
1309
1339
|
* Everything the ORIGINAL document owns and the typed builder does not model —
|
|
1310
1340
|
* ids, layout, platform metadata, annotation nodes, node order, designer
|
|
@@ -1426,6 +1456,9 @@ export function collectPreservation(plan) {
|
|
|
1426
1456
|
preservation.omitTaskElementIds = omitTaskElementIds;
|
|
1427
1457
|
if (omitStagePositions.length)
|
|
1428
1458
|
preservation.omitStagePositions = omitStagePositions;
|
|
1459
|
+
const bindingRows = preservedBindingRows(plan);
|
|
1460
|
+
if (bindingRows.length)
|
|
1461
|
+
preservation.bindings = bindingRows;
|
|
1429
1462
|
const variableKnown = new Set(['id', 'name', 'type', 'default', 'body', '_jsonSchema', 'elementId', 'var', 'custom']);
|
|
1430
1463
|
const variableFields = {};
|
|
1431
1464
|
const legacyVariableDefaults = {};
|
package/dist/case/preserve.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { BuiltCase } from './case-sdk.js';
|
|
2
|
+
import { Bindings } from '../core/bindings.js';
|
|
3
|
+
import type { BindingsView } from '../core/binding-messages.js';
|
|
2
4
|
export type PreservedVariableLane = 'inputs' | 'outputs' | 'inputOutputs';
|
|
3
5
|
/**
|
|
4
6
|
* A preserved `inputs[]` / `outputs[]` slot id copied verbatim from its name is
|
|
@@ -143,11 +145,41 @@ export interface CaseJsonPreservation {
|
|
|
143
145
|
omitTaskElementIds?: PreservedTaskRef[];
|
|
144
146
|
/** Opaque wire identities aligned to typed constructs by name and position. */
|
|
145
147
|
identities?: CaseIdentityPreservation;
|
|
148
|
+
/**
|
|
149
|
+
* The original plan's connection and folder rows from root `bindings[]`: the
|
|
150
|
+
* map from the binding id decompile names a connector by
|
|
151
|
+
* (`connection: "bOC1"`) to its resource key. Compile reads them only to
|
|
152
|
+
* resolve such a label when bindings.json does not answer it (see
|
|
153
|
+
* {@link preservedBindings}); it emits a row only when a task references it.
|
|
154
|
+
* Without them, compiling the source anywhere but over the original plan
|
|
155
|
+
* wrote `resourceKey: "bOC1"`, and nothing refused it (builder-sdk#1242).
|
|
156
|
+
*/
|
|
157
|
+
bindings?: Record<string, unknown>[];
|
|
146
158
|
}
|
|
147
159
|
/** Attach decompiler-owned, non-authoring wire data to a built case. */
|
|
148
160
|
export declare function preserveCaseJson(built: BuiltCase, payload: CaseJsonPreservation): BuiltCase;
|
|
149
161
|
/** @internal Read the preservation payload attached by `preserveCaseJson`. */
|
|
150
162
|
export declare function caseJsonPreservation(built: BuiltCase): CaseJsonPreservation | undefined;
|
|
163
|
+
/**
|
|
164
|
+
* The bindings registry `check` and `serialize` resolve a decompiled source's
|
|
165
|
+
* connection and folder labels against: `bindings` plus the rows the source
|
|
166
|
+
* preserved ({@link CaseJsonPreservation.bindings}) as fallback rows. A fallback
|
|
167
|
+
* row answers only a label equal to its id, never counts as declared, and loses
|
|
168
|
+
* to bindings.json and to the plan `-o` writes over (the same rules as
|
|
169
|
+
* `Bindings.withFallback`). A row `serialize` minted offline is a placeholder for
|
|
170
|
+
* its label, not a key, so it answers nothing.
|
|
171
|
+
*
|
|
172
|
+
* A `Bindings` comes back as a new `Bindings`. Any other {@link BindingsView}
|
|
173
|
+
* (an SDK caller's own, or a `Bindings` from a second copy of the package) is
|
|
174
|
+
* wrapped, so the preserved rows answer there too instead of being dropped.
|
|
175
|
+
*
|
|
176
|
+
* @param bindings - The registry the caller loaded, if any.
|
|
177
|
+
* @param payload - The source's preservation payload, if any.
|
|
178
|
+
* @returns A registry with the preserved rows, or `bindings` unchanged when
|
|
179
|
+
* there are none.
|
|
180
|
+
*/
|
|
181
|
+
export declare function preservedBindings(bindings: Bindings | undefined, payload: CaseJsonPreservation | undefined): Bindings | undefined;
|
|
182
|
+
export declare function preservedBindings(bindings: BindingsView | undefined, payload: CaseJsonPreservation | undefined): BindingsView | undefined;
|
|
151
183
|
/** @internal Validate that preservation cannot impersonate typed authoring. */
|
|
152
184
|
export declare function caseJsonPreservationErrors(payload: CaseJsonPreservation | undefined): string[];
|
|
153
185
|
export {};
|
package/dist/case/preserve.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { stableId } from '../core/stable-id.js';
|
|
2
|
+
import { Bindings } from '../core/bindings.js';
|
|
3
|
+
import { offlineBindingLabel } from './ids.js';
|
|
2
4
|
/**
|
|
3
5
|
* A preserved `inputs[]` / `outputs[]` slot id copied verbatim from its name is
|
|
4
6
|
* the STRICT_FORMAL_ARG_ID defect, not an identity to keep; the minted id stands.
|
|
@@ -36,6 +38,30 @@ export function preserveCaseJson(built, payload) {
|
|
|
36
38
|
export function caseJsonPreservation(built) {
|
|
37
39
|
return preserved.get(built);
|
|
38
40
|
}
|
|
41
|
+
export function preservedBindings(bindings, payload) {
|
|
42
|
+
const rows = payload?.bindings;
|
|
43
|
+
if (!Array.isArray(rows) || rows.length === 0)
|
|
44
|
+
return bindings;
|
|
45
|
+
const minted = (row) => offlineBindingLabel(row) !== undefined;
|
|
46
|
+
if (bindings === undefined || bindings instanceof Bindings)
|
|
47
|
+
return (bindings ?? new Bindings()).withFallbackRows(rows, minted);
|
|
48
|
+
return withPreservedRows(bindings, new Bindings().withFallbackRows(rows, minted));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `view` with `extra`'s fallback rows behind it: `view` answers first, and a
|
|
52
|
+
* label only `extra` answers is a fallback label (`fromFallback`), exactly as
|
|
53
|
+
* `Bindings` treats its own fallback rows.
|
|
54
|
+
*/
|
|
55
|
+
function withPreservedRows(view, extra) {
|
|
56
|
+
const primary = (name) => view.resolve(name) !== undefined || view.entry?.(name) !== undefined
|
|
57
|
+
|| (name !== undefined && (view.declared?.() ?? []).includes(name));
|
|
58
|
+
return {
|
|
59
|
+
resolve: (name) => view.resolve(name) ?? (primary(name) ? undefined : extra.resolve(name)),
|
|
60
|
+
...(view.declared ? { declared: () => view.declared() } : {}),
|
|
61
|
+
entry: (name) => view.entry?.(name) ?? (primary(name) ? undefined : extra.entry(name)),
|
|
62
|
+
fromFallback: (name) => view.fromFallback?.(name) === true || (!primary(name) && extra.fromFallback(name)),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
39
65
|
const TOP_LEVEL_KEYS = new Set([
|
|
40
66
|
'id', 'name', 'description', 'version', 'bindings', 'variables', 'metadata', 'nodes', 'edges', 'layout',
|
|
41
67
|
]);
|
|
@@ -123,6 +149,21 @@ export function caseJsonPreservationErrors(payload) {
|
|
|
123
149
|
else if (TASK_KEYS.has(field))
|
|
124
150
|
errors.push(`task "${entry.task}" field "${field}" is owned by the typed Case serializer`);
|
|
125
151
|
}
|
|
152
|
+
if (payload.bindings !== undefined) {
|
|
153
|
+
if (!Array.isArray(payload.bindings))
|
|
154
|
+
errors.push('preserved "bindings" must be an array of root binding rows');
|
|
155
|
+
else
|
|
156
|
+
payload.bindings.forEach((row, index) => {
|
|
157
|
+
const r = row;
|
|
158
|
+
const key = r && typeof r === 'object' ? r.resourceKey ?? r.default : undefined;
|
|
159
|
+
if (!r || typeof r !== 'object' || typeof r.id !== 'string' || r.id === '')
|
|
160
|
+
errors.push(`preserved binding row ${index + 1} has no string id`);
|
|
161
|
+
else if (r.resource !== 'Connection')
|
|
162
|
+
errors.push(`preserved binding row "${r.id}" is not a Connection row (only connection and folder rows are preserved)`);
|
|
163
|
+
else if (typeof key !== 'string' || key === '' || key === r.id)
|
|
164
|
+
errors.push(`preserved binding row "${r.id}" carries no resource key other than its own id`);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
126
167
|
for (const [index, fields] of (payload.triggerDataFields ?? []).entries())
|
|
127
168
|
for (const field of Object.keys(fields))
|
|
128
169
|
if (!PRESERVED_TRIGGER_DATA_KEYS.has(field))
|
package/dist/case/serialize.js
CHANGED
|
@@ -25,13 +25,14 @@ import { isResourceLabelRefusal, resolveResourceRef, resourceIdentity } from './
|
|
|
25
25
|
import { ownEntry } from '../core/own-entry.js';
|
|
26
26
|
import { stableId } from '../core/stable-id.js';
|
|
27
27
|
import { resolveSchemaVersion } from '../schema-version.js';
|
|
28
|
-
import { caseJsonPreservation, caseJsonPreservationErrors, } from './preserve.js';
|
|
28
|
+
import { caseJsonPreservation, caseJsonPreservationErrors, preservedBindings, } from './preserve.js';
|
|
29
29
|
import { businessEventProblems } from './business-event-shape.js';
|
|
30
30
|
import { readEventFilterLeaf, eventFilterTreeLeaf } from '../core/event-filters.js';
|
|
31
31
|
import { responseJsonSchema } from '../core/connector-response-schema.js';
|
|
32
32
|
import { ruleShapeProblems } from './rule-options.js';
|
|
33
33
|
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
34
34
|
import { taskShapeProblems } from './task-shape.js';
|
|
35
|
+
import { connectorPlaceholderData, isConnectorPlaceholder } from './connector-placeholder.js';
|
|
35
36
|
/** The ids a serialize pass hashed from element ids, by the document it returned. */
|
|
36
37
|
const ELEMENT_SEEDED_IDS = new WeakMap();
|
|
37
38
|
function elementSeededId(resolver, elementId, suffix) {
|
|
@@ -100,7 +101,9 @@ export function serialize(built, opts = {}) {
|
|
|
100
101
|
stageIdByLabel: new Map(),
|
|
101
102
|
taskIdByKey: new Map(),
|
|
102
103
|
library: opts.library,
|
|
103
|
-
|
|
104
|
+
// A decompiled source carries its plan's connection rows: they answer the
|
|
105
|
+
// binding-id labels decompile wrote (`connection: "bOC1"`).
|
|
106
|
+
bindings: preservedBindings(opts.bindings, caseJsonPreservation(built)),
|
|
104
107
|
bindingSink: new Map(),
|
|
105
108
|
ids,
|
|
106
109
|
offlineBindingIds: new Map(),
|
|
@@ -1195,6 +1198,11 @@ function taskData(task, resolver, taskId, stageId, ids) {
|
|
|
1195
1198
|
const [shape] = taskShapeProblems(task);
|
|
1196
1199
|
if (shape)
|
|
1197
1200
|
throw new CompileRefusal(shape.code, shape.message);
|
|
1201
|
+
// `.unresolved('connector' | 'wait-for-connector')` and a bare
|
|
1202
|
+
// `.waitForConnector()`: `data: {}`, the CLI's placeholder (validate warns
|
|
1203
|
+
// TASK_NOT_CONFIGURED). The builder lets no output reach one.
|
|
1204
|
+
if (isConnectorPlaceholder(task))
|
|
1205
|
+
return connectorPlaceholderData();
|
|
1198
1206
|
switch (task.kind) {
|
|
1199
1207
|
case 'process':
|
|
1200
1208
|
case 'agent':
|
|
@@ -1280,8 +1288,8 @@ function taskData(task, resolver, taskId, stageId, ids) {
|
|
|
1280
1288
|
return withCustomOutputs(data, task, resolver);
|
|
1281
1289
|
}
|
|
1282
1290
|
case 'wait-for-connector':
|
|
1283
|
-
//
|
|
1284
|
-
//
|
|
1291
|
+
// A placeholder returned `{}` above; naming the connector/operation emits
|
|
1292
|
+
// the `context` subscription, and a resolved one the full bag.
|
|
1285
1293
|
return withOutputExtracts(waitConnectorBag(task.waitConnector, { alwaysContext: false }, resolver, `${stageId}-${taskId}`, `Wait-for-connector task "${task.name}"`), task, ids, resolver);
|
|
1286
1294
|
}
|
|
1287
1295
|
}
|
|
@@ -1327,12 +1335,14 @@ function withCustomOutputs(data, task, resolver) {
|
|
|
1327
1335
|
return data;
|
|
1328
1336
|
}
|
|
1329
1337
|
/**
|
|
1330
|
-
* The `wait-for-connector` subscription bag (`data` on
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
* `validate` rejects a bare `uipath` ("connector activity missing").
|
|
1338
|
+
* The `wait-for-connector` subscription bag (`data` on a task that names its
|
|
1339
|
+
* connector, `uipath` on the rule). The bag carries
|
|
1340
|
+
* `serviceType: "Intsvc.WaitForEvent"`; naming the connector adds a two-entry
|
|
1341
|
+
* `context` (connectorKey + operation, defaulting to `"placeholder"`) and empty
|
|
1342
|
+
* `inputs`/`outputs`/`bindings`. A *rule* always carries the context —
|
|
1343
|
+
* `validate` rejects a bare `uipath` ("connector activity missing"). A task
|
|
1344
|
+
* that names no connector never gets here: its `data` is the placeholder `{}`
|
|
1345
|
+
* (connector-placeholder.ts).
|
|
1336
1346
|
*/
|
|
1337
1347
|
function waitConnectorBag(spec, opts, resolver, elementId, where) {
|
|
1338
1348
|
if (isResolvedSubscription(spec))
|
|
@@ -1867,7 +1877,7 @@ function bindingRef(resolver, name, resource, propertyAttribute, displayName, si
|
|
|
1867
1877
|
const refused = bindingLabelRefusal(resolver.bindings, name, site.attribute, site.where, site.target);
|
|
1868
1878
|
if (refused)
|
|
1869
1879
|
throw refused;
|
|
1870
|
-
const entry = resolver.bindings?.entry(name);
|
|
1880
|
+
const entry = resolver.bindings?.entry?.(name);
|
|
1871
1881
|
const resourceKey = entry?.resourceKey ?? entry?.default ?? name;
|
|
1872
1882
|
// Only a label with no bindings.json entry gets a minted id: an entry's
|
|
1873
1883
|
// `resourceKey` is the real key, so its id (or its name) cannot clash with it.
|
|
@@ -1904,7 +1914,7 @@ function folderBindingRef(resolver, name, connection, site) {
|
|
|
1904
1914
|
const refused = bindingLabelRefusal(resolver.bindings, name, site.attribute, site.where, site.target);
|
|
1905
1915
|
if (refused)
|
|
1906
1916
|
throw refused;
|
|
1907
|
-
const entry = resolver.bindings?.entry(name);
|
|
1917
|
+
const entry = resolver.bindings?.entry?.(name);
|
|
1908
1918
|
const baseId = entry
|
|
1909
1919
|
? String(entry.id ?? name)
|
|
1910
1920
|
: offlineBindingId(resolver, offlineBindingSeed('Connection', 'folderKey', name, connection.resourceKey));
|
package/dist/core/bindings.d.ts
CHANGED
|
@@ -26,6 +26,18 @@ export declare class Bindings {
|
|
|
26
26
|
* @returns This registry, for chaining.
|
|
27
27
|
*/
|
|
28
28
|
withFallback(file?: string, isPlaceholder?: (row: Record<string, unknown>) => boolean): this;
|
|
29
|
+
/**
|
|
30
|
+
* A NEW registry: this one's entries and fallback rows, plus `rows` as more
|
|
31
|
+
* fallback rows (same rules as {@link withFallback}; a row whose `id` is
|
|
32
|
+
* already a fallback row is skipped, so earlier fallbacks win). This registry
|
|
33
|
+
* is not changed, so a caller's `Bindings` can be shared across compiles.
|
|
34
|
+
*
|
|
35
|
+
* @param rows - Root binding rows, e.g. the ones a decompiled Case source carries.
|
|
36
|
+
* @param isPlaceholder - As for {@link withFallback}.
|
|
37
|
+
* @returns The new registry.
|
|
38
|
+
*/
|
|
39
|
+
withFallbackRows(rows: readonly unknown[], isPlaceholder?: (row: Record<string, unknown>) => boolean): Bindings;
|
|
40
|
+
private addFallbackRows;
|
|
29
41
|
private primary;
|
|
30
42
|
/**
|
|
31
43
|
* The fallback row whose `id` is `name`. A row whose key is its own id
|
package/dist/core/bindings.js
CHANGED
|
@@ -44,11 +44,33 @@ export class Bindings {
|
|
|
44
44
|
catch {
|
|
45
45
|
return this; // a fallback is optional; an unreadable one adds nothing
|
|
46
46
|
}
|
|
47
|
+
this.addFallbackRows(normalizeBindings(parsed), isPlaceholder);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A NEW registry: this one's entries and fallback rows, plus `rows` as more
|
|
52
|
+
* fallback rows (same rules as {@link withFallback}; a row whose `id` is
|
|
53
|
+
* already a fallback row is skipped, so earlier fallbacks win). This registry
|
|
54
|
+
* is not changed, so a caller's `Bindings` can be shared across compiles.
|
|
55
|
+
*
|
|
56
|
+
* @param rows - Root binding rows, e.g. the ones a decompiled Case source carries.
|
|
57
|
+
* @param isPlaceholder - As for {@link withFallback}.
|
|
58
|
+
* @returns The new registry.
|
|
59
|
+
*/
|
|
60
|
+
withFallbackRows(rows, isPlaceholder) {
|
|
61
|
+
const copy = new Bindings();
|
|
62
|
+
copy.list = this.list;
|
|
63
|
+
copy.fallback = [...this.fallback];
|
|
64
|
+
copy.addFallbackRows(rows, isPlaceholder);
|
|
65
|
+
return copy;
|
|
66
|
+
}
|
|
67
|
+
addFallbackRows(rows, isPlaceholder) {
|
|
47
68
|
const ids = new Set(this.fallback.map((x) => x?.id));
|
|
48
|
-
for (const entry of
|
|
49
|
-
if (typeof entry?.id === 'string' && !ids.has(entry.id) && !isPlaceholder?.(entry))
|
|
69
|
+
for (const entry of rows)
|
|
70
|
+
if (typeof entry?.id === 'string' && !ids.has(entry.id) && !isPlaceholder?.(entry)) {
|
|
50
71
|
this.fallback.push(entry);
|
|
51
|
-
|
|
72
|
+
ids.add(entry.id);
|
|
73
|
+
}
|
|
52
74
|
}
|
|
53
75
|
primary(name) {
|
|
54
76
|
return this.list.find((x) => x.name === name || x.id === name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.68.
|
|
3
|
+
"version": "6.68.3",
|
|
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",
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"@types/node": "^22.7.0",
|
|
101
101
|
"esbuild": "^0.28.1"
|
|
102
102
|
},
|
|
103
|
-
"gitref": "
|
|
103
|
+
"gitref": "7f50084300a8c68552127c4e1ab51a3cb56a5869"
|
|
104
104
|
}
|