@uipath/maestro-builder-sdk 6.69.1 → 6.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-index.md +76 -73
- package/dist/api-members.md +231 -227
- package/dist/case/case-sdk.d.ts +37 -8
- package/dist/case/case-sdk.js +6 -1
- package/dist/case/cli-library.js +1 -1
- package/dist/case/compile-cli.js +31 -19
- package/dist/case/connector-saved-config.d.ts +64 -0
- package/dist/case/connector-saved-config.js +168 -0
- package/dist/case/decompile.d.ts +9 -0
- package/dist/case/decompile.js +43 -1
- package/dist/case/preserve.d.ts +22 -0
- package/dist/case/preserve.js +14 -0
- package/dist/case/serialize.js +24 -1
- package/dist/core/carried-connector-fields.d.ts +71 -0
- package/dist/core/carried-connector-fields.js +80 -0
- package/dist/core/refusals.js +3 -3
- package/package.json +2 -2
package/dist/case/serialize.js
CHANGED
|
@@ -13,6 +13,8 @@ import { isUnrestorableSlotId, taskFingerprint } from './preserve.js';
|
|
|
13
13
|
import { v31TaskSets } from './task-sets.js';
|
|
14
14
|
import { camelizeName, defaultActionOutputVar } from './action-output-var.js';
|
|
15
15
|
import { buildConnectorInputs, nestConnectorBody, replayInputsOf, transportHttpMethod } from '../core/connector-inputs.js';
|
|
16
|
+
import { carriedConnectorFieldsRefusal, withCarriedFields } from '../core/carried-connector-fields.js';
|
|
17
|
+
import { connectorTaskIdentity, sameConnectorOperation, withCurrentCaches, writeOptionalConfiguration } from './connector-saved-config.js';
|
|
16
18
|
import { buildEssentialConfiguration } from '../core/connector-configuration.js';
|
|
17
19
|
import { EQUALS_EXPR_DIALECT } from '../core/connector-checks.js';
|
|
18
20
|
import { bindingLabelRefusal } from '../core/binding-messages.js';
|
|
@@ -612,6 +614,20 @@ export function applyPreservation(result, preservation, options = {}) {
|
|
|
612
614
|
task[field] = value;
|
|
613
615
|
}
|
|
614
616
|
}
|
|
617
|
+
for (const entry of preservation.connectorConfigurations ?? []) {
|
|
618
|
+
const stage = stageNodes.find((candidate) => candidate.data?.label === entry.stage);
|
|
619
|
+
const task = stageTask(stage, entry.task);
|
|
620
|
+
// A deleted task, or one the edit pointed at another operation, has no use
|
|
621
|
+
// for the saved half: it gets the essential half only, as a new task does.
|
|
622
|
+
const now = task && connectorTaskIdentity(task);
|
|
623
|
+
if (!task || !now || !sameConnectorOperation(now, entry.connector))
|
|
624
|
+
continue;
|
|
625
|
+
// The Designer's caches describe input values as saved: an entry whose
|
|
626
|
+
// field the edit changed would show the old value, so it is dropped.
|
|
627
|
+
const written = writeOptionalConfiguration(task, withCurrentCaches(task, entry.optionalConfiguration, entry.cachedFieldValues));
|
|
628
|
+
if (written === 'different' && !options.overwrite)
|
|
629
|
+
throw new CompileRefusal('PRESERVATION_FIELD_COLLISION', `preserved connector configuration of task "${entry.stage}/${entry.task}" collides with serializer-owned output.`);
|
|
630
|
+
}
|
|
615
631
|
for (const entry of preservation.omitTaskElementIds ?? []) {
|
|
616
632
|
const stage = stageNodes.find((candidate) => candidate.data?.label === entry.stage);
|
|
617
633
|
const task = stageTask(stage, entry.task);
|
|
@@ -796,6 +812,7 @@ export function followRenames(result, preservation) {
|
|
|
796
812
|
stages,
|
|
797
813
|
},
|
|
798
814
|
...(preservation.taskFields ? { taskFields: preservation.taskFields.map(ref) } : {}),
|
|
815
|
+
...(preservation.connectorConfigurations ? { connectorConfigurations: preservation.connectorConfigurations.map(ref) } : {}),
|
|
799
816
|
...(preservation.omitTaskElementIds ? { omitTaskElementIds: preservation.omitTaskElementIds.map(ref) } : {}),
|
|
800
817
|
...(preservation.omitStagePositions ? { omitStagePositions: preservation.omitStagePositions.map(stageName) } : {}),
|
|
801
818
|
};
|
|
@@ -1901,7 +1918,13 @@ function connectorTaskData(spec, resolver, elementId, where, executionType, even
|
|
|
1901
1918
|
if (!resolver.library)
|
|
1902
1919
|
throw new CompileRefusal('LIBRARY_REQUIRED', `connector task needs the connector library — pass { library } to serialize or use uip maestro case compile --library.`);
|
|
1903
1920
|
const library = resolver.library;
|
|
1904
|
-
|
|
1921
|
+
// A decompiled task's carried fields widen the library's field list with the
|
|
1922
|
+
// plan's own declarations, so a field the Designer saved against a newer
|
|
1923
|
+
// connector than the library compiles instead of being refused (#1362).
|
|
1924
|
+
const carriedRefusal = carriedConnectorFieldsRefusal(spec.carriedFields, where);
|
|
1925
|
+
if (carriedRefusal)
|
|
1926
|
+
throw new CompileRefusal('CARRIED_DEFINITION_INVALID', carriedRefusal);
|
|
1927
|
+
const rc = withCarriedFields(onTask(where, () => resolveOperation(library, spec)), spec.carriedFields);
|
|
1905
1928
|
const op = rc.entry.operation ?? {};
|
|
1906
1929
|
const context = [
|
|
1907
1930
|
io('connectorKey', 'string', { value: rc.connectorKey }),
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector input fields a decompiled task CARRIES — the plan's own field
|
|
3
|
+
* declarations for the ones the connector library does not list (#1362).
|
|
4
|
+
*
|
|
5
|
+
* The published library is a snapshot, and a connector keeps shipping after it
|
|
6
|
+
* is taken. Studio Web saves a connector activity against the LIVE connector,
|
|
7
|
+
* and writes that connector's field list into the activity's configuration
|
|
8
|
+
* (`optionalConfiguration.fieldsContainer.inputFields`). So a Designer plan can
|
|
9
|
+
* use a field the library has never heard of: Outlook `get-email-list` saved at
|
|
10
|
+
* connector 1.47.0 carries `bodyFormat: "html"`, and the library's entry has no
|
|
11
|
+
* `bodyFormat`. With the library as the only field authority, `check` and
|
|
12
|
+
* `compile` refused that untouched field as `unknown input`, and the easy way
|
|
13
|
+
* past the refusal was to delete it: a silent change to the task.
|
|
14
|
+
*
|
|
15
|
+
* Decompile now copies the saved declaration of each field the library lacks
|
|
16
|
+
* into the task's source (`carriedFields`), and `check` and `compile` read the
|
|
17
|
+
* operation's fields as the library's plus the carried ones. This is the same
|
|
18
|
+
* idea as Flow's carried node definitions (`core/carried-definitions.ts`): the
|
|
19
|
+
* file's own definition is the third source, after the SDK and the library.
|
|
20
|
+
*
|
|
21
|
+
* It only ever ADDS a name. A field the library declares keeps the library's
|
|
22
|
+
* type, location and `required`, whatever the carried copy says, so a carried
|
|
23
|
+
* list cannot relax a check the library makes. And a carried field is accepted,
|
|
24
|
+
* never demanded: the library's own fields are the required contract.
|
|
25
|
+
*
|
|
26
|
+
* For instance, with `carriedFields: [{ name: 'bodyFormat', type: 'string',
|
|
27
|
+
* location: 'query' }]`, the input `{ bodyFormat: 'html' }` compiles to
|
|
28
|
+
* `queryParameters.bodyFormat = "html"`; without it, compile refuses it.
|
|
29
|
+
*/
|
|
30
|
+
import type { FieldLocation, ResolvedConnector } from './library.js';
|
|
31
|
+
/** Where a carried field rides on the wire: the request body, the query string or the path. */
|
|
32
|
+
export type CarriedFieldLocation = Extract<FieldLocation, 'body' | 'query' | 'path'>;
|
|
33
|
+
/**
|
|
34
|
+
* One input field a connector task carries from the plan it was decompiled from.
|
|
35
|
+
*
|
|
36
|
+
* Written by decompile, copied from the activity's saved field list. It is not an
|
|
37
|
+
* authoring surface: an input the connector library already declares is checked
|
|
38
|
+
* against the library, whatever is carried.
|
|
39
|
+
*/
|
|
40
|
+
export interface CarriedConnectorField {
|
|
41
|
+
/** The field's name, as the saved field list names it, e.g. `bodyFormat`. */
|
|
42
|
+
name: string;
|
|
43
|
+
/** The wire slot the saved field list names (`fieldLocation`). */
|
|
44
|
+
location: CarriedFieldLocation;
|
|
45
|
+
/** `string`, `number`, `boolean`, `array` or `object`, when the saved field declares one. */
|
|
46
|
+
type?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Why `fields` cannot be carried, or `undefined` when it can.
|
|
50
|
+
*
|
|
51
|
+
* For instance `[{ name: 'bodyFormat', location: 'header' }]` is refused: a
|
|
52
|
+
* header is no slot the connector wire has.
|
|
53
|
+
*/
|
|
54
|
+
export declare function carriedConnectorFieldsRefusal(fields: unknown, where: string): string | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* `rc` with each carried field the library does not declare appended to its
|
|
57
|
+
* fields, as an optional input. A name the library declares keeps the library's
|
|
58
|
+
* declaration. `fields` must have passed {@link carriedConnectorFieldsRefusal}.
|
|
59
|
+
*/
|
|
60
|
+
export declare function withCarriedFields(rc: ResolvedConnector, fields: readonly CarriedConnectorField[] | undefined): ResolvedConnector;
|
|
61
|
+
/**
|
|
62
|
+
* The fields a saved activity configuration declares that `rc` does not, as
|
|
63
|
+
* decompile carries them: from `optionalConfiguration.fieldsContainer.inputFields`,
|
|
64
|
+
* request fields only, in the saved order. Empty when the configuration has no
|
|
65
|
+
* field list (an SDK-compiled plan writes the essential half only), or every
|
|
66
|
+
* saved field is in the library. A saved field on a slot the connector wire has
|
|
67
|
+
* no place for (a `header`) is not carried.
|
|
68
|
+
*
|
|
69
|
+
* @param configuration - The parsed `=jsonString:` configuration blob.
|
|
70
|
+
*/
|
|
71
|
+
export declare function savedFieldsMissingFromLibrary(configuration: unknown, rc: ResolvedConnector): CarriedConnectorField[];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ownEntry } from './own-entry.js';
|
|
2
|
+
const LOCATIONS = { body: 'body', query: 'query', path: 'path' };
|
|
3
|
+
const TYPES = {
|
|
4
|
+
string: 'string', boolean: 'boolean', number: 'number', integer: 'number', array: 'array', object: 'object',
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Why `fields` cannot be carried, or `undefined` when it can.
|
|
8
|
+
*
|
|
9
|
+
* For instance `[{ name: 'bodyFormat', location: 'header' }]` is refused: a
|
|
10
|
+
* header is no slot the connector wire has.
|
|
11
|
+
*/
|
|
12
|
+
export function carriedConnectorFieldsRefusal(fields, where) {
|
|
13
|
+
if (fields === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
if (!Array.isArray(fields))
|
|
16
|
+
return `${where}: carriedFields must be an array of { name, location, type? } fields.`;
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
for (const field of fields) {
|
|
19
|
+
const f = field;
|
|
20
|
+
if (!f || typeof f !== 'object' || typeof f.name !== 'string' || !f.name)
|
|
21
|
+
return `${where}: a carried field must name itself with a non-empty string.`;
|
|
22
|
+
if (typeof f.location !== 'string' || ownEntry(LOCATIONS, f.location) === undefined)
|
|
23
|
+
return `${where}: carried field "${f.name}" has location ${JSON.stringify(f.location)}; expected "body", "query" or "path".`;
|
|
24
|
+
if (f.type !== undefined && (typeof f.type !== 'string' || !Object.values(TYPES).includes(f.type)))
|
|
25
|
+
return `${where}: carried field "${f.name}" has type ${JSON.stringify(f.type)}; expected "string", "number", "boolean", "array" or "object".`;
|
|
26
|
+
const extra = Object.keys(f).find((key) => key !== 'name' && key !== 'location' && key !== 'type');
|
|
27
|
+
if (extra !== undefined)
|
|
28
|
+
return `${where}: carried field "${f.name}" has unsupported key "${extra}".`;
|
|
29
|
+
if (seen.has(f.name))
|
|
30
|
+
return `${where}: carried field "${f.name}" is listed twice.`;
|
|
31
|
+
seen.add(f.name);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* `rc` with each carried field the library does not declare appended to its
|
|
37
|
+
* fields, as an optional input. A name the library declares keeps the library's
|
|
38
|
+
* declaration. `fields` must have passed {@link carriedConnectorFieldsRefusal}.
|
|
39
|
+
*/
|
|
40
|
+
export function withCarriedFields(rc, fields) {
|
|
41
|
+
if (!fields?.length)
|
|
42
|
+
return rc;
|
|
43
|
+
const known = new Set(rc.fields.map((f) => f.name));
|
|
44
|
+
const added = fields
|
|
45
|
+
.filter((f) => !known.has(f.name))
|
|
46
|
+
.map((f) => ({ name: f.name, location: f.location, required: false, ...(f.type !== undefined ? { type: f.type } : {}) }));
|
|
47
|
+
return added.length ? { ...rc, fields: [...rc.fields, ...added] } : rc;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The fields a saved activity configuration declares that `rc` does not, as
|
|
51
|
+
* decompile carries them: from `optionalConfiguration.fieldsContainer.inputFields`,
|
|
52
|
+
* request fields only, in the saved order. Empty when the configuration has no
|
|
53
|
+
* field list (an SDK-compiled plan writes the essential half only), or every
|
|
54
|
+
* saved field is in the library. A saved field on a slot the connector wire has
|
|
55
|
+
* no place for (a `header`) is not carried.
|
|
56
|
+
*
|
|
57
|
+
* @param configuration - The parsed `=jsonString:` configuration blob.
|
|
58
|
+
*/
|
|
59
|
+
export function savedFieldsMissingFromLibrary(configuration, rc) {
|
|
60
|
+
const inputFields = configuration
|
|
61
|
+
?.optionalConfiguration?.fieldsContainer?.inputFields;
|
|
62
|
+
if (!Array.isArray(inputFields))
|
|
63
|
+
return [];
|
|
64
|
+
const known = new Set(rc.fields.map((f) => f.name));
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const raw of inputFields) {
|
|
67
|
+
if (!raw || typeof raw !== 'object')
|
|
68
|
+
continue;
|
|
69
|
+
const f = raw;
|
|
70
|
+
if (typeof f.name !== 'string' || !f.name || known.has(f.name) || f.request === false)
|
|
71
|
+
continue;
|
|
72
|
+
const location = typeof f.fieldLocation === 'string' ? ownEntry(LOCATIONS, f.fieldLocation) : undefined;
|
|
73
|
+
if (!location)
|
|
74
|
+
continue;
|
|
75
|
+
known.add(f.name);
|
|
76
|
+
const type = typeof f.type === 'string' ? ownEntry(TYPES, f.type) : undefined;
|
|
77
|
+
out.push({ name: f.name, location, ...(type !== undefined ? { type } : {}) });
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
package/dist/core/refusals.js
CHANGED
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* they get different codes, so one code never both stops compile and lets it
|
|
22
22
|
* write.
|
|
23
23
|
*
|
|
24
|
-
* Since `compile` no longer runs `check` (#1497), a `lint` code is only right
|
|
24
|
+
* Since `compile` no longer runs `check` (Flow #1497, Case #1446), a `lint` code is only right
|
|
25
25
|
* when the serializer still writes what the source says while the rule fires:
|
|
26
|
-
* otherwise `compile` writes a wrong `.flow` without a word, and the code is
|
|
27
|
-
* `lowering` (the serializer must refuse it). Every Flow `lint` code has a row
|
|
26
|
+
* otherwise `compile` writes a wrong `.flow` (or caseplan) without a word, and the code is
|
|
27
|
+
* `lowering` (the serializer must refuse it). Every Flow, Case and BPMN `lint` code has a row
|
|
28
28
|
* in `tests/lint-lossless/` (#1518): a round-trip fixture, a fixture the
|
|
29
29
|
* serializer refuses, or a written reason. The round trip shows the rule's
|
|
30
30
|
* condition survives compile -> decompile -> compile and that compile is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.70.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",
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"@types/node": "^22.7.0",
|
|
101
101
|
"esbuild": "^0.28.1"
|
|
102
102
|
},
|
|
103
|
-
"gitref": "
|
|
103
|
+
"gitref": "d1531fbce30cd3d2a88997dd8c65be6ef046e5ed"
|
|
104
104
|
}
|