@uipath/maestro-builder-sdk 6.70.5 → 6.70.7
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 +71 -70
- package/dist/api-members.md +216 -216
- package/dist/case/case-expr-check.js +81 -11
- package/dist/case/case-sdk.d.ts +44 -3
- package/dist/case/case-sdk.js +47 -3
- package/dist/case/decompile.js +31 -0
- package/dist/case/serialize.js +117 -7
- package/dist/case/task-output-ref.d.ts +111 -0
- package/dist/case/task-output-ref.js +212 -0
- package/dist/compile-cli.js +16 -3
- package/dist/core/refusals.d.ts +1 -0
- package/dist/core/refusals.js +1 -0
- package/dist/path-within.d.ts +15 -17
- package/dist/path-within.js +44 -0
- package/package.json +2 -2
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading another task's output in place: the SDD's `vars.$xref('<stage>',
|
|
3
|
+
* '<task>', '<output>')` marker, and `taskOutput()`, the typed way to write it
|
|
4
|
+
* (builder-sdk#1241).
|
|
5
|
+
*
|
|
6
|
+
* A task's own output is a variable of its own: the Case Designer saves it as
|
|
7
|
+
* `{ name: 'AgentExpenseRequest', id: 'agentExpenseRequest', var:
|
|
8
|
+
* 'agentExpenseRequest' }`, with no root `variables` entry, and a later task
|
|
9
|
+
* reads it as `=vars.agentExpenseRequest`. The id is the plan's, not the
|
|
10
|
+
* author's: it is unique across the plan, so a second task's `Action` output
|
|
11
|
+
* is `action2`. An SDD therefore names an output by where it is produced, and
|
|
12
|
+
* `uip maestro case sdd convert` lowers each marker to `vars.<output id>`
|
|
13
|
+
* (`sdd-convert/xrefs.ts`). Without the lowering here, an author had to copy
|
|
14
|
+
* the value into a case variable the SDD does not declare, and `validate
|
|
15
|
+
* --strict --sdd` refuses each one (`STRICT_SDD_VARIABLE_EXTRA`).
|
|
16
|
+
*
|
|
17
|
+
* The rule lives here so `check` (at `build()`) and `serialize` read a marker
|
|
18
|
+
* the same way. `build()` cannot see the ids, which `serialize` mints, so it
|
|
19
|
+
* resolves the stage and the task, and the output where the task's rows are
|
|
20
|
+
* all authored (a reference-mode task). `serialize` resolves every marker
|
|
21
|
+
* against the rows it wrote and refuses one that names no output.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* One argument, read by its own delimiter: `'…'`, `"…"`, `` `…` `` or the curly
|
|
25
|
+
* pair, the four `sdd convert` accepts. A curly pair is two code points, so it
|
|
26
|
+
* gets its own branch instead of a backreference.
|
|
27
|
+
*/
|
|
28
|
+
const QUOTED = String.raw `(?:'((?:\\.|[^'\\])*)'|"((?:\\.|[^"\\])*)"|` +
|
|
29
|
+
'`((?:\\\\.|[^`\\\\])*)`' +
|
|
30
|
+
String.raw `|\u201c([^\u201d]*)\u201d)`;
|
|
31
|
+
/** A `vars.$xref(stage, task, output)` marker. Global: use with `replace`/`matchAll` only. */
|
|
32
|
+
const XREF_CALL = new RegExp(String.raw `vars\.\$xref\(\s*${QUOTED}\s*,\s*${QUOTED}\s*,\s*${QUOTED}\s*\)`, 'g');
|
|
33
|
+
/**
|
|
34
|
+
* A property read glued to the marker (`vars.$xref(…).unusualClauses`). The
|
|
35
|
+
* producer may not have run when the reader does, so inside a `=js:`
|
|
36
|
+
* expression the lowering writes the optional chain
|
|
37
|
+
* `vars.<id>?.unusualClauses`, as `sdd convert` does. Only there: `?.` is
|
|
38
|
+
* JavaScript, and nothing shows that the platform's non-js `=vars.<path>`
|
|
39
|
+
* binding reads it, so any other value keeps the plain `.`.
|
|
40
|
+
*/
|
|
41
|
+
const DOTTED_READ = /^\.[A-Za-z_$]/;
|
|
42
|
+
const unescape = (value) => value.replace(/\\(.)/g, '$1');
|
|
43
|
+
/** Each marker in `text`, in order, with where it sits. */
|
|
44
|
+
export function taskOutputRefs(text) {
|
|
45
|
+
if (!text.includes('$xref'))
|
|
46
|
+
return [];
|
|
47
|
+
return [...text.matchAll(XREF_CALL)].map((m) => {
|
|
48
|
+
const arg = (from) => unescape(m.slice(from, from + 4).find((v) => v !== undefined) ?? '');
|
|
49
|
+
return { stage: arg(1), task: arg(5), output: arg(9), whole: m[0], index: m.index ?? 0 };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Replace every marker in `text` with what `lower` returns for it. In a `=js:`
|
|
54
|
+
* expression, a marker followed by a property read gets the optional chain
|
|
55
|
+
* (`vars.<id>?.field`); any other value keeps the plain read (`vars.<id>.field`).
|
|
56
|
+
*
|
|
57
|
+
* @param text - The expression.
|
|
58
|
+
* @param lower - The variable a marker reads (`agentExpenseRequest`), or `undefined` to keep the marker.
|
|
59
|
+
*/
|
|
60
|
+
export function lowerTaskOutputRefs(text, lower) {
|
|
61
|
+
if (!text.includes('$xref'))
|
|
62
|
+
return text;
|
|
63
|
+
const js = /^\s*=js:/.test(text);
|
|
64
|
+
let out = '';
|
|
65
|
+
let at = 0;
|
|
66
|
+
for (const ref of taskOutputRefs(text)) {
|
|
67
|
+
const after = text.slice(ref.index + ref.whole.length);
|
|
68
|
+
const variable = lower(ref);
|
|
69
|
+
// `undefined` keeps the marker as written (a reference to a stand-in; see `resolveInPlan`).
|
|
70
|
+
out += variable === undefined ? text.slice(at, ref.index) + ref.whole : `${text.slice(at, ref.index)}vars.${variable}${js && DOTTED_READ.test(after) ? '?' : ''}`;
|
|
71
|
+
at = ref.index + ref.whole.length;
|
|
72
|
+
}
|
|
73
|
+
return out + text.slice(at);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The one candidate `name` picks out: an exact match first, then a
|
|
77
|
+
* case-insensitive one (an SDD addresses a stage or an output in any case).
|
|
78
|
+
* `undefined` when none does; `'ambiguous'` when more than one does.
|
|
79
|
+
*/
|
|
80
|
+
export function pickByName(items, name, names) {
|
|
81
|
+
for (const same of [(a) => a === name, (a) => a.toLowerCase() === name.toLowerCase()]) {
|
|
82
|
+
const hits = [...new Set(items.filter((item) => names(item).some((n) => n !== undefined && same(n))))];
|
|
83
|
+
if (hits.length === 1)
|
|
84
|
+
return hits[0];
|
|
85
|
+
if (hits.length > 1)
|
|
86
|
+
return 'ambiguous';
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The row an output reference names among one task's rows: by the field it
|
|
92
|
+
* reads (`name`) or by the variable it writes (`var`).
|
|
93
|
+
*/
|
|
94
|
+
export function pickOutputRow(rows, output) {
|
|
95
|
+
return pickByName(rows, output, (row) => [row.name, row.var]);
|
|
96
|
+
}
|
|
97
|
+
/** The refusal message for a marker that resolves to nothing. */
|
|
98
|
+
export function unresolvedTaskOutputRef(ref, why) {
|
|
99
|
+
return `taskOutput(${JSON.stringify(ref.stage)}, ${JSON.stringify(ref.task)}, ${JSON.stringify(ref.output)}) ${why}. ` +
|
|
100
|
+
'A task-output reference names the stage label, the task name and the output (the result field it reads, or the key it has in `.outputs()`).';
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The marker for a reference: `vars.$xref('<stage>','<task>','<output>')`,
|
|
104
|
+
* each argument single-quoted with its quotes and backslashes escaped. What
|
|
105
|
+
* `taskOutput()` returns, and what decompile writes back.
|
|
106
|
+
*/
|
|
107
|
+
export function taskOutputMarker(stage, task, output) {
|
|
108
|
+
const quote = (value) => `'${value.replace(/[\\']/g, '\\$&')}'`;
|
|
109
|
+
return `vars.$xref(${quote(stage)},${quote(task)},${quote(output)})`;
|
|
110
|
+
}
|
|
111
|
+
/** Every stage task's output rows in a written caseplan, in document order. */
|
|
112
|
+
export function planTaskOutputs(plan) {
|
|
113
|
+
const found = [];
|
|
114
|
+
const nodes = plan?.nodes;
|
|
115
|
+
for (const node of Array.isArray(nodes) ? nodes : []) {
|
|
116
|
+
const data = node?.data;
|
|
117
|
+
if (typeof data?.label !== 'string' || !Array.isArray(data.tasks))
|
|
118
|
+
continue;
|
|
119
|
+
for (const lane of data.tasks)
|
|
120
|
+
for (const task of Array.isArray(lane) ? lane : []) {
|
|
121
|
+
if (typeof task?.displayName !== 'string')
|
|
122
|
+
continue;
|
|
123
|
+
const rows = Array.isArray(task.data?.outputs) ? task.data.outputs : [];
|
|
124
|
+
found.push({
|
|
125
|
+
stage: data.label,
|
|
126
|
+
task: task.displayName,
|
|
127
|
+
rows: rows.filter((row) => row && typeof row === 'object'),
|
|
128
|
+
...(task.data?.standIn === true ? { standIn: true } : {}),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return found;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* What a reference reads in a written plan: the variable of the row it names
|
|
136
|
+
* (`vars.<id>`, or a custom row's `var`, which has no id), or why it names none.
|
|
137
|
+
*/
|
|
138
|
+
export function resolveInPlan(tasks, ref) {
|
|
139
|
+
const stage = pickByName([...new Set(tasks.map((t) => t.stage))], ref.stage, (label) => [label]);
|
|
140
|
+
if (stage === undefined)
|
|
141
|
+
return { why: `names no stage labelled ${JSON.stringify(ref.stage)}` };
|
|
142
|
+
if (stage === 'ambiguous')
|
|
143
|
+
return { why: `names a stage label that more than one stage answers to` };
|
|
144
|
+
const task = pickByName(tasks.filter((t) => t.stage === stage), ref.task, (t) => [t.task]);
|
|
145
|
+
if (task === undefined)
|
|
146
|
+
return { why: `names no task ${JSON.stringify(ref.task)} in stage ${JSON.stringify(stage)}` };
|
|
147
|
+
if (task === 'ambiguous')
|
|
148
|
+
return { why: `names a task that more than one task in stage ${JSON.stringify(stage)} answers to` };
|
|
149
|
+
// The named task was refused and stood in (collect mode). Its refusal is the
|
|
150
|
+
// finding; its outputs were never written, so there is nothing to judge the
|
|
151
|
+
// reference against here (`build()` already judged it against the authored ones).
|
|
152
|
+
if (task.standIn)
|
|
153
|
+
return { standIn: true };
|
|
154
|
+
const row = pickOutputRow(task.rows, ref.output);
|
|
155
|
+
const offered = [...new Set(task.rows.map((r) => r.name).filter((n) => typeof n === 'string'))];
|
|
156
|
+
if (row === undefined)
|
|
157
|
+
return { why: `names no output of task ${JSON.stringify(task.task)}, whose outputs are ${offered.length ? offered.map((n) => JSON.stringify(n)).join(', ') : 'none'}` };
|
|
158
|
+
if (row === 'ambiguous')
|
|
159
|
+
return { why: `names more than one output of task ${JSON.stringify(task.task)}; name it by the variable it writes instead` };
|
|
160
|
+
const variable = typeof row.id === 'string' && row.id ? row.id : row.var;
|
|
161
|
+
if (typeof variable !== 'string' || !variable)
|
|
162
|
+
return { why: `names an output of task ${JSON.stringify(task.task)} that writes no variable` };
|
|
163
|
+
return { variable };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Turn each `vars.<output id>` read in `text` that only a task-output reference
|
|
167
|
+
* can say back into `vars.$xref(…)`, for decompile. `byId` maps such an id to
|
|
168
|
+
* the reference that names it.
|
|
169
|
+
*/
|
|
170
|
+
export function raiseTaskOutputReads(text, byId) {
|
|
171
|
+
if (!byId.size || !text.includes('vars.'))
|
|
172
|
+
return text;
|
|
173
|
+
return text.replace(/(?<![\w$.])vars\.([A-Za-z_$][\w$]*)(?![\w$])/g, (whole, id) => {
|
|
174
|
+
const ref = byId.get(id);
|
|
175
|
+
return ref ? taskOutputMarker(ref.stage, ref.task, ref.output) : whole;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The output ids a decompiled source can only read back by reference: a row
|
|
180
|
+
* whose id is not the variable it writes (a reassigned extract, a Designer row
|
|
181
|
+
* such as `{ id: 'oA1b2C3d4', var: 'AgentExpenseRequest' }`). Recompiling mints
|
|
182
|
+
* that id afresh, and no declaration makes `vars.<id>` readable, so a
|
|
183
|
+
* `vars.<id>` read in the plan goes back to the source as the reference that
|
|
184
|
+
* resolves to the same row. A read of an id that is also a variable (an
|
|
185
|
+
* own-name row, a root variable) is left as written. A row the reference
|
|
186
|
+
* could not name uniquely is left as written too.
|
|
187
|
+
*/
|
|
188
|
+
export function referableOutputIds(plan, rootNames) {
|
|
189
|
+
const tasks = planTaskOutputs(plan);
|
|
190
|
+
const vars = new Set(rootNames);
|
|
191
|
+
for (const task of tasks)
|
|
192
|
+
for (const row of task.rows)
|
|
193
|
+
if (typeof row.var === 'string')
|
|
194
|
+
vars.add(row.var);
|
|
195
|
+
const byId = new Map();
|
|
196
|
+
for (const task of tasks)
|
|
197
|
+
for (const row of task.rows) {
|
|
198
|
+
if (typeof row.id !== 'string' || !row.id || row.id === row.var || vars.has(row.id))
|
|
199
|
+
continue;
|
|
200
|
+
for (const output of [row.name, row.var]) {
|
|
201
|
+
if (typeof output !== 'string' || !output)
|
|
202
|
+
continue;
|
|
203
|
+
const ref = { stage: task.stage, task: task.task, output };
|
|
204
|
+
const resolved = resolveInPlan(tasks, ref);
|
|
205
|
+
if ('variable' in resolved && resolved.variable === row.id) {
|
|
206
|
+
byId.set(row.id, ref);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return byId;
|
|
212
|
+
}
|
package/dist/compile-cli.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { diagnosticLine, reportDiagnostics, reportResult, thrownDiagnostic } from './cli-result.js';
|
|
26
26
|
import { writeJsonFile } from './core/json-file.js';
|
|
27
|
-
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
27
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
28
28
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
29
29
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
30
30
|
import { serializeWithSidecars } from './serialize.js';
|
|
@@ -36,7 +36,7 @@ import { connectionResources, inlineAgentResources, mergeBindingsV2, refreshInli
|
|
|
36
36
|
import { readBindingsV2, readInlineAgentFiles } from './bindings-v2-files.js';
|
|
37
37
|
import { ensureTypeScriptRuntime } from './node-runtime.js';
|
|
38
38
|
import { defaultBindingsFile } from './workdir.js';
|
|
39
|
-
import { isWithin } from './path-within.js';
|
|
39
|
+
import { isWithin, physicalPath } from './path-within.js';
|
|
40
40
|
import { flowSources, resourceSourcesOfOtherFlows } from './project-resource-users.js';
|
|
41
41
|
import { fnv1aX4Uuid } from './core/stable-id.js';
|
|
42
42
|
import { CompileRefusal } from './core/compile-refusal.js';
|
|
@@ -86,7 +86,20 @@ export async function run(argv) {
|
|
|
86
86
|
// BYOA-only event on a connection `prepare` never checked
|
|
87
87
|
// (EVENT_BYOA_UNVERIFIED): printed in the same `✗ [CODE]` shape `check` uses
|
|
88
88
|
// for it. Nothing is written.
|
|
89
|
-
const
|
|
89
|
+
const explicitOut = flagValue(argv, '-o');
|
|
90
|
+
const out = explicitOut ?? `${built.name || basename(file).replace(/\.flow\.ts$|\.ts$/, '')}.flow`;
|
|
91
|
+
// The default is named after the flow, which decompile copies from the input: keep it inside cwd.
|
|
92
|
+
if (explicitOut === undefined) {
|
|
93
|
+
const target = physicalPath(resolve(out));
|
|
94
|
+
if (target === undefined || !isWithin(realpathSync.native(process.cwd()), target)) {
|
|
95
|
+
const message = `refusing to write ${out} outside the working directory: without -o the output is named after`
|
|
96
|
+
+ ` the flow (${JSON.stringify(built.name)}). Pass -o <file>.flow to choose where it is written`;
|
|
97
|
+
console.error(`compile: ${message}; not emitting.`);
|
|
98
|
+
diagnostics.push({ level: 'error', code: 'FLOW_NAME_OUTSIDE_WORKING_DIR', message });
|
|
99
|
+
reportDiagnostics(diagnostics);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
90
103
|
const outDir = dirname(resolve(out));
|
|
91
104
|
// ONE FOLDER, ONE FLOW (#1184). An agent's `source` is the name of the folder
|
|
92
105
|
// beside the `.flow` that holds its `agent.json` and `resources/`. Decompile
|
package/dist/core/refusals.d.ts
CHANGED
|
@@ -640,6 +640,7 @@ export declare const REFUSALS: {
|
|
|
640
640
|
readonly TASK_OUTPUTS_NO_RESPONSE: "lowering";
|
|
641
641
|
readonly TASK_OUTPUTS_PLACEHOLDER_EVENT: "lowering";
|
|
642
642
|
readonly TASK_OUTPUTS_UNSUPPORTED: "lowering";
|
|
643
|
+
readonly TASK_OUTPUT_REF_UNRESOLVED: "lowering";
|
|
643
644
|
readonly TASK_OUTPUT_SOURCE_AND_ASSIGN: "lowering";
|
|
644
645
|
readonly TASK_REENTRY_IGNORED: "lint";
|
|
645
646
|
readonly TASK_TIMER_OUT_OF_RANGE: "lint";
|
package/dist/core/refusals.js
CHANGED
|
@@ -692,6 +692,7 @@ export const REFUSALS = {
|
|
|
692
692
|
TASK_OUTPUTS_NO_RESPONSE: 'lowering',
|
|
693
693
|
TASK_OUTPUTS_PLACEHOLDER_EVENT: 'lowering',
|
|
694
694
|
TASK_OUTPUTS_UNSUPPORTED: 'lowering',
|
|
695
|
+
TASK_OUTPUT_REF_UNRESOLVED: 'lowering',
|
|
695
696
|
TASK_OUTPUT_SOURCE_AND_ASSIGN: 'lowering',
|
|
696
697
|
TASK_REENTRY_IGNORED: 'lint',
|
|
697
698
|
TASK_TIMER_OUT_OF_RANGE: 'lint',
|
package/dist/path-within.d.ts
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* path-within — the one path-containment test for anything that writes files.
|
|
3
|
-
*
|
|
4
|
-
* "Is `child` inside `parent`?" looks like a string-prefix question and is not
|
|
5
|
-
* one. `child.startsWith(parent + '/')` is correct only where the separator is
|
|
6
|
-
* `/`: on Windows `path.resolve` returns `C:\proj\P\x`, the check refuses every
|
|
7
|
-
* path, and compile refused every inline agent's `<source>/agent.json`
|
|
8
|
-
* (flow-builder-sdk#946). A prefix test without a trailing separator has the
|
|
9
|
-
* opposite bug: it accepts the sibling `C:\proj\P2\x` as inside `C:\proj\P`.
|
|
10
|
-
*
|
|
11
|
-
* So the test is `relative(parent, child)` instead, which gets both right on
|
|
12
|
-
* every platform, and Windows' case-insensitive drive letters and
|
|
13
|
-
* cross-drive paths (`relative` returns the absolute `D:\x` there) with them.
|
|
14
|
-
*
|
|
15
|
-
* The path implementation is a parameter so the Windows rules can be tested on
|
|
16
|
-
* a Linux runner (`isWithin(a, b, path.win32)`). Callers leave it out.
|
|
17
|
-
*/
|
|
18
1
|
import * as nodePath from 'node:path';
|
|
19
2
|
/** The slice of `node:path` the test needs; `path.win32` and `path.posix` both fit. */
|
|
20
3
|
export type PathApi = Pick<typeof nodePath, 'relative' | 'isAbsolute' | 'sep'>;
|
|
@@ -31,3 +14,18 @@ export type PathApi = Pick<typeof nodePath, 'relative' | 'isAbsolute' | 'sep'>;
|
|
|
31
14
|
* isWithin('C:\\proj\\P', 'D:\\agent.json', path.win32) // false — other drive
|
|
32
15
|
*/
|
|
33
16
|
export declare function isWithin(parent: string, child: string, path?: PathApi): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Where `dir` lands on disk: the real path of its deepest ancestor that
|
|
19
|
+
* exists, every link on the way followed, then the rest of `dir`, which does
|
|
20
|
+
* not exist yet and so holds no link. `undefined` when that ancestor's real
|
|
21
|
+
* path cannot be read: a link to nothing, or a loop of links. A caller treats
|
|
22
|
+
* `undefined` as outside.
|
|
23
|
+
*
|
|
24
|
+
* The agent SDK's CLI keeps a copy of this function for its default output
|
|
25
|
+
* folder, so both CLIs follow links the same way.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // ./work/sw-basic/l is a link to /elsewhere, and X does not exist:
|
|
29
|
+
* physicalPath('/cwd/work/sw-basic/l/X') // '/elsewhere/X'
|
|
30
|
+
*/
|
|
31
|
+
export declare function physicalPath(dir: string): string | undefined;
|
package/dist/path-within.js
CHANGED
|
@@ -12,10 +12,16 @@
|
|
|
12
12
|
* every platform, and Windows' case-insensitive drive letters and
|
|
13
13
|
* cross-drive paths (`relative` returns the absolute `D:\x` there) with them.
|
|
14
14
|
*
|
|
15
|
+
* `isWithin` reads only the spelling of its paths, so a link inside `parent`
|
|
16
|
+
* that points out of it passes. A check that guards a write compares
|
|
17
|
+
* `physicalPath`s instead.
|
|
18
|
+
*
|
|
15
19
|
* The path implementation is a parameter so the Windows rules can be tested on
|
|
16
20
|
* a Linux runner (`isWithin(a, b, path.win32)`). Callers leave it out.
|
|
17
21
|
*/
|
|
22
|
+
import { lstatSync, realpathSync } from 'node:fs';
|
|
18
23
|
import * as nodePath from 'node:path';
|
|
24
|
+
import { basename, dirname, join } from 'node:path';
|
|
19
25
|
/**
|
|
20
26
|
* `child` is `parent` itself or somewhere beneath it.
|
|
21
27
|
*
|
|
@@ -39,3 +45,41 @@ export function isWithin(parent, child, path = nodePath) {
|
|
|
39
45
|
// Windows: a child on another drive has no relative spelling.
|
|
40
46
|
return !path.isAbsolute(rel);
|
|
41
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Where `dir` lands on disk: the real path of its deepest ancestor that
|
|
50
|
+
* exists, every link on the way followed, then the rest of `dir`, which does
|
|
51
|
+
* not exist yet and so holds no link. `undefined` when that ancestor's real
|
|
52
|
+
* path cannot be read: a link to nothing, or a loop of links. A caller treats
|
|
53
|
+
* `undefined` as outside.
|
|
54
|
+
*
|
|
55
|
+
* The agent SDK's CLI keeps a copy of this function for its default output
|
|
56
|
+
* folder, so both CLIs follow links the same way.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // ./work/sw-basic/l is a link to /elsewhere, and X does not exist:
|
|
60
|
+
* physicalPath('/cwd/work/sw-basic/l/X') // '/elsewhere/X'
|
|
61
|
+
*/
|
|
62
|
+
export function physicalPath(dir) {
|
|
63
|
+
const rest = [];
|
|
64
|
+
let at = dir;
|
|
65
|
+
for (;;) {
|
|
66
|
+
try {
|
|
67
|
+
lstatSync(at);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Missing: its parent, then.
|
|
72
|
+
}
|
|
73
|
+
const parent = dirname(at);
|
|
74
|
+
if (parent === at)
|
|
75
|
+
break;
|
|
76
|
+
rest.unshift(basename(at));
|
|
77
|
+
at = parent;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return join(realpathSync.native(at), ...rest);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.70.
|
|
3
|
+
"version": "6.70.7",
|
|
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": "31471f814e8ca706a02e96e3dbceb39641584de6"
|
|
104
104
|
}
|