@uipath/maestro-builder-sdk 6.53.2 → 6.54.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 +20 -20
- package/dist/api-members.md +124 -123
- package/dist/case/case-sdk.d.ts +35 -2
- package/dist/case/case-sdk.js +72 -4
- package/dist/case/check.js +35 -0
- package/dist/case/decompile.js +8 -3
- package/dist/case/serialize.js +18 -0
- package/dist/registry/curated-overlay.d.ts +25 -1
- package/dist/registry/curated-overlay.js +112 -18
- package/package.json +2 -2
package/dist/case/case-sdk.d.ts
CHANGED
|
@@ -595,9 +595,16 @@ export interface TaskInputBinding {
|
|
|
595
595
|
export interface TaskOutputBinding {
|
|
596
596
|
/** The case-variable name the field feeds (read downstream as `=vars.<var>`). */
|
|
597
597
|
var: string;
|
|
598
|
-
/** The source field expression, e.g. `=response` or `=Error.Message`. */
|
|
598
|
+
/** The source field expression, e.g. `=response` or `=Error.Message`. For a `custom` output, the value assigned instead. */
|
|
599
599
|
source: string;
|
|
600
600
|
type: TypeDesc;
|
|
601
|
+
/**
|
|
602
|
+
* A custom assignment output, written `custom: true` with no `id`, the way
|
|
603
|
+
* Studio Web saves one: when the task completes, `var` is set to `source` (a
|
|
604
|
+
* literal, `=vars.<name>`, or a `=js:` expression), not to a field of the
|
|
605
|
+
* task's result. Set by `.outputs({ <var>: { assign } })`.
|
|
606
|
+
*/
|
|
607
|
+
custom?: boolean;
|
|
601
608
|
}
|
|
602
609
|
export interface BuiltTask {
|
|
603
610
|
/** Task key + display name (also the handle used by `selected-tasks-completed`). */
|
|
@@ -1131,12 +1138,38 @@ declare class TaskBuilder {
|
|
|
1131
1138
|
* task's own (bare) output, with no root entry — until a second task writes
|
|
1132
1139
|
* the same variable, which makes both reassignments.
|
|
1133
1140
|
*
|
|
1134
|
-
*
|
|
1141
|
+
* Pass `{ assign, type }` for a **custom** output: the SDD's `x = expr` row.
|
|
1142
|
+
* When the task completes, the case variable is set to `assign` — a literal,
|
|
1143
|
+
* `=vars.<name>`, or a `=js:` expression — instead of a result field. It is
|
|
1144
|
+
* written as Studio Web saves one: `custom: true`, `elementId: 'root'`, no
|
|
1145
|
+
* `id`. The SDD notation `=metadata.<field>` reads nothing outside `=js:`, so it
|
|
1146
|
+
* is written as `=js:metadata.<field>`. An SDD literal copied with its quotes
|
|
1147
|
+
* (`{ assign: '"literal-assigned"' }`) loses them, as `sdd convert` reads it.
|
|
1148
|
+
* A custom output sees only the outputs listed before it on the same task:
|
|
1149
|
+
* outputs are evaluated in order, so it cannot read its own variable or a
|
|
1150
|
+
* later output's (`check` reports `OUTPUT_SELF_REFERENCE` /
|
|
1151
|
+
* `OUTPUT_FORWARD_READ`).
|
|
1152
|
+
*
|
|
1153
|
+
* @example
|
|
1154
|
+
* **Extract a result field and assign two custom outputs**
|
|
1155
|
+
* ```ts
|
|
1156
|
+
* casePlan('echo').stage('Echo', (s) => s.task('Echo', (t) =>
|
|
1157
|
+
* t.apiWorkflow('Echo', { folder: 'Shared' }).outputs({
|
|
1158
|
+
* renamedResult: '=APIOutput1',
|
|
1159
|
+
* literalResult: { assign: 'literal-assigned' },
|
|
1160
|
+
* resultCopy: { assign: "=js:vars.renamedResult + '-copy'" },
|
|
1161
|
+
* })))
|
|
1162
|
+
* ```
|
|
1163
|
+
*
|
|
1164
|
+
* @param shape - Case-variable names to the result field they take, `{ source, type }`, or a custom `{ assign, type }`.
|
|
1135
1165
|
* @returns This builder, so calls chain.
|
|
1136
1166
|
*/
|
|
1137
1167
|
outputs(shape: Record<string, string | {
|
|
1138
1168
|
source: string;
|
|
1139
1169
|
type?: TypeDesc;
|
|
1170
|
+
} | {
|
|
1171
|
+
assign: string;
|
|
1172
|
+
type?: TypeDesc;
|
|
1140
1173
|
}>): this;
|
|
1141
1174
|
/**
|
|
1142
1175
|
* Add a task-entry condition. Pass a nested array for the complete OR-of-AND grid.
|
package/dist/case/case-sdk.js
CHANGED
|
@@ -489,15 +489,48 @@ class TaskBuilder {
|
|
|
489
489
|
* task's own (bare) output, with no root entry — until a second task writes
|
|
490
490
|
* the same variable, which makes both reassignments.
|
|
491
491
|
*
|
|
492
|
-
*
|
|
492
|
+
* Pass `{ assign, type }` for a **custom** output: the SDD's `x = expr` row.
|
|
493
|
+
* When the task completes, the case variable is set to `assign` — a literal,
|
|
494
|
+
* `=vars.<name>`, or a `=js:` expression — instead of a result field. It is
|
|
495
|
+
* written as Studio Web saves one: `custom: true`, `elementId: 'root'`, no
|
|
496
|
+
* `id`. The SDD notation `=metadata.<field>` reads nothing outside `=js:`, so it
|
|
497
|
+
* is written as `=js:metadata.<field>`. An SDD literal copied with its quotes
|
|
498
|
+
* (`{ assign: '"literal-assigned"' }`) loses them, as `sdd convert` reads it.
|
|
499
|
+
* A custom output sees only the outputs listed before it on the same task:
|
|
500
|
+
* outputs are evaluated in order, so it cannot read its own variable or a
|
|
501
|
+
* later output's (`check` reports `OUTPUT_SELF_REFERENCE` /
|
|
502
|
+
* `OUTPUT_FORWARD_READ`).
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* **Extract a result field and assign two custom outputs**
|
|
506
|
+
* ```ts
|
|
507
|
+
* casePlan('echo').stage('Echo', (s) => s.task('Echo', (t) =>
|
|
508
|
+
* t.apiWorkflow('Echo', { folder: 'Shared' }).outputs({
|
|
509
|
+
* renamedResult: '=APIOutput1',
|
|
510
|
+
* literalResult: { assign: 'literal-assigned' },
|
|
511
|
+
* resultCopy: { assign: "=js:vars.renamedResult + '-copy'" },
|
|
512
|
+
* })))
|
|
513
|
+
* ```
|
|
514
|
+
*
|
|
515
|
+
* @param shape - Case-variable names to the result field they take, `{ source, type }`, or a custom `{ assign, type }`.
|
|
493
516
|
* @returns This builder, so calls chain.
|
|
494
517
|
*/
|
|
495
518
|
outputs(shape) {
|
|
496
519
|
this._outputs ??= [];
|
|
497
520
|
for (const [name, v] of Object.entries(shape)) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
521
|
+
if (typeof v === 'string') {
|
|
522
|
+
this._outputs.push({ var: name, source: v, type: 'string' });
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const type = (v.type ?? 'string');
|
|
526
|
+
if ('assign' in v) {
|
|
527
|
+
if ('source' in v)
|
|
528
|
+
throw new Error(`task "${this._name}": output "${name}" sets both source and assign — use source to extract a result field, assign for a custom output.`);
|
|
529
|
+
this._outputs.push({ var: name, source: customOutputValue(v.assign), type, custom: true });
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
this._outputs.push({ var: name, source: v.source, type });
|
|
533
|
+
}
|
|
501
534
|
}
|
|
502
535
|
return this;
|
|
503
536
|
}
|
|
@@ -543,6 +576,41 @@ class TaskBuilder {
|
|
|
543
576
|
};
|
|
544
577
|
}
|
|
545
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* A custom output's value as the runtime reads it. `=metadata.<field>` (the SDD
|
|
581
|
+
* notation) has no bare path prefix, so it resolves to nothing; only inside
|
|
582
|
+
* `=js:` is `metadata` in scope. The same narrow rewrite as `uip maestro case
|
|
583
|
+
* sdd convert` (`canonicalExpression`).
|
|
584
|
+
*/
|
|
585
|
+
function customOutputValue(value) {
|
|
586
|
+
const unquoted = unquoteLiteral(value);
|
|
587
|
+
return unquoted.startsWith('=metadata.') ? `=js:${unquoted.slice(1)}` : unquoted;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* An SDD cell's quoted literal without its quotes, as the CLI's `sdd convert`
|
|
591
|
+
* reads it (`unquoteLiteral` in `packages/case-tool/src/services/sdd-convert/io.ts`):
|
|
592
|
+
* `"literal-assigned"` → `literal-assigned`, with escaped quotes unescaped.
|
|
593
|
+
* A value not wholly wrapped in one pair of `"…"` or `'…'` (an unescaped quote
|
|
594
|
+
* inside, or an expression like `'a' + 'b'`) is returned unchanged.
|
|
595
|
+
*/
|
|
596
|
+
function unquoteLiteral(cell) {
|
|
597
|
+
const text = cell.trim();
|
|
598
|
+
const quote = text[0];
|
|
599
|
+
if (quote !== '"' && quote !== "'")
|
|
600
|
+
return cell;
|
|
601
|
+
if (text.length < 2 || text[text.length - 1] !== quote)
|
|
602
|
+
return cell;
|
|
603
|
+
const inner = text.slice(1, -1);
|
|
604
|
+
for (let at = 0; at < inner.length; at += 1) {
|
|
605
|
+
if (inner[at] === '\\') {
|
|
606
|
+
at += 1;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
if (inner[at] === quote)
|
|
610
|
+
return cell;
|
|
611
|
+
}
|
|
612
|
+
return inner.replaceAll(`\\${quote}`, quote);
|
|
613
|
+
}
|
|
546
614
|
class StageBuilder {
|
|
547
615
|
_label;
|
|
548
616
|
_exception;
|
package/dist/case/check.js
CHANGED
|
@@ -539,6 +539,32 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
539
539
|
if (isResourceLabelRefusal(resolved))
|
|
540
540
|
diags.push({ level: 'error', code: resolved.code, message: resolved.message, where: t.name });
|
|
541
541
|
}
|
|
542
|
+
// A custom row (`{ assign }`) is evaluated in array order, after the rows
|
|
543
|
+
// before it on the same task: it cannot read itself or a later row's
|
|
544
|
+
// variable (it would see the old value). `uip maestro case validate
|
|
545
|
+
// --strict` reports these as STRICT_OUTPUT_SELF_REFERENCE /
|
|
546
|
+
// STRICT_OUTPUT_FORWARD_READ; they need only this task, so check says so.
|
|
547
|
+
(t.outputs ?? []).forEach((o, i) => {
|
|
548
|
+
if (!o.custom || !o.source.startsWith('='))
|
|
549
|
+
return;
|
|
550
|
+
const later = new Set((t.outputs ?? []).slice(i + 1).map((row) => row.var));
|
|
551
|
+
for (const name of varsRead(o.source)) {
|
|
552
|
+
if (name === o.var)
|
|
553
|
+
diags.push({
|
|
554
|
+
level: 'error',
|
|
555
|
+
code: 'OUTPUT_SELF_REFERENCE',
|
|
556
|
+
message: `Task "${t.name}" custom output "${o.var}" reads vars.${name}, its own variable. A custom output is set once when the task completes; read another variable, or compute the value from the task's result.`,
|
|
557
|
+
where: t.name,
|
|
558
|
+
});
|
|
559
|
+
else if (later.has(name))
|
|
560
|
+
diags.push({
|
|
561
|
+
level: 'error',
|
|
562
|
+
code: 'OUTPUT_FORWARD_READ',
|
|
563
|
+
message: `Task "${t.name}" custom output "${o.var}" reads vars.${name}, which a later output on the same task writes. Outputs are evaluated in the order listed, so it would read the old value. List "${name}" before "${o.var}".`,
|
|
564
|
+
where: t.name,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
});
|
|
542
568
|
if (t.kind === 'business-rule') {
|
|
543
569
|
if (!t.ref || !t.ref.name.trim() || !t.ref.folderPath.trim())
|
|
544
570
|
diags.push({
|
|
@@ -548,6 +574,8 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
548
574
|
where: t.name,
|
|
549
575
|
});
|
|
550
576
|
for (const o of t.outputs ?? []) {
|
|
577
|
+
if (o.custom)
|
|
578
|
+
continue; // an assigned value, not a result field
|
|
551
579
|
const wrapped = /^=\s*(result|output)\./.exec(o.source);
|
|
552
580
|
if (wrapped)
|
|
553
581
|
diags.push({
|
|
@@ -666,3 +694,10 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
666
694
|
});
|
|
667
695
|
}
|
|
668
696
|
}
|
|
697
|
+
/** The case variables an expression reads as `vars.<name>` or `vars["<name>"]`. */
|
|
698
|
+
function varsRead(expression) {
|
|
699
|
+
const names = new Set();
|
|
700
|
+
for (const m of expression.matchAll(/\bvars\.([A-Za-z_$][\w$]*)|\bvars\[\s*(['"])(.+?)\2\s*\]/g))
|
|
701
|
+
names.add(m[1] ?? m[3]);
|
|
702
|
+
return names;
|
|
703
|
+
}
|
package/dist/case/decompile.js
CHANGED
|
@@ -806,9 +806,14 @@ function ioArg(rows, kind) {
|
|
|
806
806
|
const entries = rows.map((row) => {
|
|
807
807
|
const name = kind === 'inputs' ? row.name : row.var;
|
|
808
808
|
const value = tsLiteral(row[valueField] ?? '');
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
809
|
+
// A custom output (`custom: true`) assigns its value; it reads no result field.
|
|
810
|
+
const custom = kind === 'outputs' && row.custom === true;
|
|
811
|
+
const typed = row.type && row.type !== 'string' ? `, type: ${tsLiteral(row.type)}` : '';
|
|
812
|
+
const cell = custom
|
|
813
|
+
? `{ assign: ${value}${typed} }`
|
|
814
|
+
: typed
|
|
815
|
+
? `{ ${valueField}: ${value}${typed} }`
|
|
816
|
+
: value;
|
|
812
817
|
return `${key(String(name ?? ''))}: ${cell}`;
|
|
813
818
|
});
|
|
814
819
|
return `{ ${entries.join(', ')} }`;
|
package/dist/case/serialize.js
CHANGED
|
@@ -1258,6 +1258,8 @@ const OWN_NAME_OUTPUT_ROWS = new WeakSet();
|
|
|
1258
1258
|
* `var` still names the shared case variable.
|
|
1259
1259
|
*/
|
|
1260
1260
|
function emitTaskOutput(o, elementId, ids, seed, resolver) {
|
|
1261
|
+
if (o.custom)
|
|
1262
|
+
return emitCustomOutput(o, resolver);
|
|
1261
1263
|
const path = o.source.replace(/^=/, '');
|
|
1262
1264
|
const field = path.split('.').pop() || o.source;
|
|
1263
1265
|
// A reassigned row registers the readable companion once (declared vars take
|
|
@@ -1291,6 +1293,20 @@ function emitTaskOutput(o, elementId, ids, seed, resolver) {
|
|
|
1291
1293
|
elementId,
|
|
1292
1294
|
};
|
|
1293
1295
|
}
|
|
1296
|
+
/**
|
|
1297
|
+
* A custom assignment output (`.outputs({ x: { assign } })`), as Studio Web and
|
|
1298
|
+
* `uip maestro case sdd convert` (`customOutputFor`) write one: no `id` and no
|
|
1299
|
+
* `originalVar` (it reads no result field), `value` and `source` both the
|
|
1300
|
+
* assigned value, an empty `target` and `body`, and `elementId: 'root'`:
|
|
1301
|
+
* { name: 'literalResult', type: 'string', custom: true, var: 'literalResult',
|
|
1302
|
+
* value: 'literal-assigned', source: 'literal-assigned', target: '', body: '', elementId: 'root' }
|
|
1303
|
+
* Like an extract, it registers the root companion so `=vars.<var>` resolves.
|
|
1304
|
+
*/
|
|
1305
|
+
function emitCustomOutput(o, resolver) {
|
|
1306
|
+
if (!resolver.varCompanions.has(o.var))
|
|
1307
|
+
resolver.varCompanions.set(o.var, { id: o.var, name: o.var, type: o.type, elementId: 'root', custom: true });
|
|
1308
|
+
return { name: o.var, type: o.type, custom: true, var: o.var, value: o.source, source: o.source, target: '', body: '', elementId: 'root' };
|
|
1309
|
+
}
|
|
1294
1310
|
// ─── Business-rule task ───────────────────────────────────────────────────────
|
|
1295
1311
|
/** The version a business-rule task carries: below v3 the engine cannot run the
|
|
1296
1312
|
* `.uirule` package a BusinessRules project deploys (the case converter falls
|
|
@@ -1707,6 +1723,8 @@ function claimOutputId(resolver, base) {
|
|
|
1707
1723
|
}
|
|
1708
1724
|
/** Whether a task output is written in the own-name shape (see `emitTaskOutput`). */
|
|
1709
1725
|
function isOwnNameOutput(o) {
|
|
1726
|
+
if (o.custom)
|
|
1727
|
+
return false;
|
|
1710
1728
|
const path = o.source.replace(/^=/, '');
|
|
1711
1729
|
return !path.includes('.') && keepsOwnName(o.var, path);
|
|
1712
1730
|
}
|
|
@@ -78,6 +78,30 @@ export declare function readOverlayCorrections(overlayPath: string): Promise<Ove
|
|
|
78
78
|
* What was done is reported in one line, and recorded in `.curated-overlay.json`
|
|
79
79
|
* beside the index, so a second application recognises its own patches: the
|
|
80
80
|
* record keeps a sha256 of each patched entry and sidecar, and a correction
|
|
81
|
-
* whose files no longer match it is patched again.
|
|
81
|
+
* whose files no longer match it is patched again. The record also names the
|
|
82
|
+
* overlay that wrote it ({@link curatedOverlayIdentity}). Under a different
|
|
83
|
+
* overlay, such as a newer SDK pulling a published archive, every correction
|
|
84
|
+
* is patched again, so fields a newer correction lists are added.
|
|
82
85
|
*/
|
|
83
86
|
export declare function applyCuratedOverlay(libraryPath: string, overlayPath?: string, reporter?: Reporter): Promise<CuratedOverlayResult>;
|
|
87
|
+
/** What {@link checkCuratedOverlay} found, one line per item. */
|
|
88
|
+
export interface CuratedOverlayCheck {
|
|
89
|
+
/** Parts the overlay adds that are absent: the overlay was not applied, or broke. */
|
|
90
|
+
missing: string[];
|
|
91
|
+
/**
|
|
92
|
+
* Corrections whose target the library no longer serves at the correction's
|
|
93
|
+
* path, or serves without the fields the patch adds: the operation moved,
|
|
94
|
+
* was renamed or re-versioned. `registry pull` only warns about these too.
|
|
95
|
+
*/
|
|
96
|
+
staleCorrections: string[];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check `libraryPath` against the curated overlay. Both lists are empty once
|
|
100
|
+
* {@link applyCuratedOverlay} has run on it, unless a correction went stale.
|
|
101
|
+
* Every overlay identity that is not a correction must be indexed at a file
|
|
102
|
+
* that exists, and each side-table must equal the bundled copy.
|
|
103
|
+
*
|
|
104
|
+
* The connector library pipeline runs this on the archive it publishes,
|
|
105
|
+
* because readers that load that archive directly apply no overlay.
|
|
106
|
+
*/
|
|
107
|
+
export declare function checkCuratedOverlay(libraryPath: string, overlayPath?: string): Promise<CuratedOverlayCheck>;
|
|
@@ -34,6 +34,25 @@ export async function curatedOverlayIdentity(overlayPath = bundledOverlayPath())
|
|
|
34
34
|
function entryIdentity(entry) {
|
|
35
35
|
return `${String(entry.nodeType)}\0${String(entry.version)}`;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Every path the library resolves each identity to. A `GenericExpanded` or
|
|
39
|
+
* per-object entry shares its nodeType with its siblings, so an identity
|
|
40
|
+
* legitimately has more than one.
|
|
41
|
+
*/
|
|
42
|
+
function pathsByIdentity(entries) {
|
|
43
|
+
const resolvedPaths = new Map();
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
if (typeof entry.path !== "string")
|
|
46
|
+
continue;
|
|
47
|
+
const identity = entryIdentity(entry);
|
|
48
|
+
const known = resolvedPaths.get(identity);
|
|
49
|
+
if (known === undefined)
|
|
50
|
+
resolvedPaths.set(identity, [entry.path]);
|
|
51
|
+
else
|
|
52
|
+
known.push(entry.path);
|
|
53
|
+
}
|
|
54
|
+
return resolvedPaths;
|
|
55
|
+
}
|
|
37
56
|
/** The ledger of deliberate patches; read here, never copied into a library. */
|
|
38
57
|
export const CORRECTIONS_FILE = "overlay-corrections.json";
|
|
39
58
|
/**
|
|
@@ -209,7 +228,10 @@ async function patchCorrection(libraryPath, overlayPath, correction) {
|
|
|
209
228
|
* What was done is reported in one line, and recorded in `.curated-overlay.json`
|
|
210
229
|
* beside the index, so a second application recognises its own patches: the
|
|
211
230
|
* record keeps a sha256 of each patched entry and sidecar, and a correction
|
|
212
|
-
* whose files no longer match it is patched again.
|
|
231
|
+
* whose files no longer match it is patched again. The record also names the
|
|
232
|
+
* overlay that wrote it ({@link curatedOverlayIdentity}). Under a different
|
|
233
|
+
* overlay, such as a newer SDK pulling a published archive, every correction
|
|
234
|
+
* is patched again, so fields a newer correction lists are added.
|
|
213
235
|
*/
|
|
214
236
|
export async function applyCuratedOverlay(libraryPath, overlayPath = bundledOverlayPath(),
|
|
215
237
|
// The public materializer API does not forward one, and a warning nobody
|
|
@@ -223,24 +245,13 @@ reporter = consoleReporter) {
|
|
|
223
245
|
const previous = (await exists(recordPath))
|
|
224
246
|
? (await readJson(recordPath))
|
|
225
247
|
: {};
|
|
248
|
+
const overlayIdentity = await curatedOverlayIdentity(overlayPath);
|
|
249
|
+
const sameOverlay = previous.overlayIdentity === overlayIdentity;
|
|
226
250
|
const previouslyAdded = new Set(previous.added ?? []);
|
|
227
251
|
const previousDigests = previous.correctedSha256 ?? {};
|
|
228
252
|
const digests = {};
|
|
229
253
|
const entries = destination.entries ?? [];
|
|
230
|
-
|
|
231
|
-
// per-object entry shares its nodeType with its siblings, so an identity
|
|
232
|
-
// legitimately has more than one.
|
|
233
|
-
const resolvedPaths = new Map();
|
|
234
|
-
for (const entry of entries) {
|
|
235
|
-
if (typeof entry.path !== "string")
|
|
236
|
-
continue;
|
|
237
|
-
const identity = entryIdentity(entry);
|
|
238
|
-
const known = resolvedPaths.get(identity);
|
|
239
|
-
if (known === undefined)
|
|
240
|
-
resolvedPaths.set(identity, [entry.path]);
|
|
241
|
-
else
|
|
242
|
-
known.push(entry.path);
|
|
243
|
-
}
|
|
254
|
+
const resolvedPaths = pathsByIdentity(entries);
|
|
244
255
|
const additions = [];
|
|
245
256
|
const skipped = new Set();
|
|
246
257
|
const added = [];
|
|
@@ -285,15 +296,18 @@ reporter = consoleReporter) {
|
|
|
285
296
|
continue;
|
|
286
297
|
}
|
|
287
298
|
// Our own earlier patch, as long as neither file changed since: a refreshed
|
|
288
|
-
// entry (an `unzip -o` over the same directory, say) is patched again.
|
|
299
|
+
// entry (an `unzip -o` over the same directory, say) is patched again. So
|
|
300
|
+
// is our patch under another overlay, whose correction may list more; when
|
|
301
|
+
// it lists nothing new, the patch is still ours, not upstream catching up.
|
|
289
302
|
const digest = await correctionDigest(libraryPath, row.path);
|
|
290
|
-
|
|
303
|
+
const ownPatch = previousDigests[row.path] === digest;
|
|
304
|
+
if (ownPatch && sameOverlay) {
|
|
291
305
|
corrected.push(row.path);
|
|
292
306
|
digests[row.path] = digest;
|
|
293
307
|
continue;
|
|
294
308
|
}
|
|
295
309
|
const patched = await patchCorrection(libraryPath, overlayPath, correction);
|
|
296
|
-
if (patched.added.length === 0) {
|
|
310
|
+
if (patched.added.length === 0 && !ownPatch) {
|
|
297
311
|
kept.push(row.path);
|
|
298
312
|
reporter.warn([
|
|
299
313
|
` WARN: curated overlay correction no longer needed: ${row.path} (${correction.since})`,
|
|
@@ -338,6 +352,7 @@ reporter = consoleReporter) {
|
|
|
338
352
|
};
|
|
339
353
|
writeJsonFile(recordPath, {
|
|
340
354
|
$comment: "What the SDK's curated overlay did to this library (registry pull). See curated-overlay.ts.",
|
|
355
|
+
overlayIdentity,
|
|
341
356
|
added: result.added,
|
|
342
357
|
corrected: result.corrected,
|
|
343
358
|
correctedSha256: digests,
|
|
@@ -350,3 +365,82 @@ reporter = consoleReporter) {
|
|
|
350
365
|
`, kept ${result.kept.length + result.skipped.length} published definition(s) unchanged.`);
|
|
351
366
|
return result;
|
|
352
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Check `libraryPath` against the curated overlay. Both lists are empty once
|
|
370
|
+
* {@link applyCuratedOverlay} has run on it, unless a correction went stale.
|
|
371
|
+
* Every overlay identity that is not a correction must be indexed at a file
|
|
372
|
+
* that exists, and each side-table must equal the bundled copy.
|
|
373
|
+
*
|
|
374
|
+
* The connector library pipeline runs this on the archive it publishes,
|
|
375
|
+
* because readers that load that archive directly apply no overlay.
|
|
376
|
+
*/
|
|
377
|
+
export async function checkCuratedOverlay(libraryPath, overlayPath = bundledOverlayPath()) {
|
|
378
|
+
const library = (await readJson(join(libraryPath, "index.json")));
|
|
379
|
+
const overlay = (await readJson(join(overlayPath, "index.json")));
|
|
380
|
+
const corrections = await readOverlayCorrections(overlayPath);
|
|
381
|
+
const correctionPaths = new Set(corrections.map((correction) => correction.path));
|
|
382
|
+
const resolvedPaths = pathsByIdentity(library.entries ?? []);
|
|
383
|
+
const overlayRows = new Map();
|
|
384
|
+
const missing = [];
|
|
385
|
+
const staleCorrections = [];
|
|
386
|
+
for (const row of overlay.entries ?? []) {
|
|
387
|
+
if (typeof row.path !== "string")
|
|
388
|
+
continue;
|
|
389
|
+
overlayRows.set(row.path, row);
|
|
390
|
+
if (correctionPaths.has(row.path))
|
|
391
|
+
continue;
|
|
392
|
+
const name = `${String(row.nodeType)}@${String(row.version)}`;
|
|
393
|
+
const paths = resolvedPaths.get(entryIdentity(row));
|
|
394
|
+
if (paths === undefined) {
|
|
395
|
+
missing.push(`definition ${name}: no index row`);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
for (const path of paths) {
|
|
399
|
+
if (!(await exists(join(libraryPath, path))))
|
|
400
|
+
missing.push(`definition ${name}: indexed at ${path}, which is absent`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
for (const correction of corrections) {
|
|
404
|
+
const where = `correction ${correction.path} (${correction.since})`;
|
|
405
|
+
const paths = resolvedPaths.get(entryIdentity(overlayRows.get(correction.path))) ?? [];
|
|
406
|
+
if (!paths.includes(correction.path)) {
|
|
407
|
+
staleCorrections.push(`${where}: the library has no definition at that path${paths.length ? ` (it indexes ${paths.join(", ")})` : ""}`);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (!(await exists(join(libraryPath, correction.path)))) {
|
|
411
|
+
staleCorrections.push(`${where}: the library indexes that path, but the file is absent`);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
const lacks = [];
|
|
415
|
+
const entry = (await readJson(join(libraryPath, correction.path)));
|
|
416
|
+
const entryFields = fieldNames(entry.inputSchema?.fields);
|
|
417
|
+
for (const field of correction.inputFields ?? []) {
|
|
418
|
+
if (!entryFields.has(field))
|
|
419
|
+
lacks.push(`inputSchema field ${field}`);
|
|
420
|
+
}
|
|
421
|
+
const sidecarPath = join(libraryPath, sidecarOf(correction.path));
|
|
422
|
+
if (await exists(sidecarPath)) {
|
|
423
|
+
const sidecar = sidecarBody(await readJson(sidecarPath)) ?? {};
|
|
424
|
+
const sidecarFields = fieldNames(sidecar.inputDefinition?.fields);
|
|
425
|
+
for (const field of correction.inputFields ?? []) {
|
|
426
|
+
if (!sidecarFields.has(field))
|
|
427
|
+
lacks.push(`sidecar inputDefinition field ${field}`);
|
|
428
|
+
}
|
|
429
|
+
for (const key of correction.sidecarKeys ?? []) {
|
|
430
|
+
if (!(key in sidecar))
|
|
431
|
+
lacks.push(`sidecar key ${key}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (lacks.length > 0)
|
|
435
|
+
staleCorrections.push(`${where}: the definition lacks ${lacks.join(", ")}`);
|
|
436
|
+
}
|
|
437
|
+
for (const table of SIDE_TABLES) {
|
|
438
|
+
const target = join(libraryPath, table);
|
|
439
|
+
if (!(await exists(target)))
|
|
440
|
+
missing.push(`side-table ${table}: absent`);
|
|
441
|
+
else if (!(await readFile(target)).equals(await readFile(join(overlayPath, table)))) {
|
|
442
|
+
missing.push(`side-table ${table}: differs from the SDK's copy`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return { missing, staleCorrections };
|
|
446
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.54.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",
|
|
@@ -96,5 +96,5 @@
|
|
|
96
96
|
"@types/node": "^22.7.0",
|
|
97
97
|
"esbuild": "^0.28.1"
|
|
98
98
|
},
|
|
99
|
-
"gitref": "
|
|
99
|
+
"gitref": "5dfee12e621c69cfa2cf1338fc7df299768dbe16"
|
|
100
100
|
}
|