@uipath/maestro-builder-sdk 6.18.8 → 6.18.9

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.
@@ -20,4 +20,25 @@ export declare const CONNECTORS_LIBRARY_VERSION_ENV = "CONNECTORS_LIBRARY_VERSIO
20
20
  * rather than guessed at, so a typo cannot fall back to "latest" silently.
21
21
  */
22
22
  export declare function connectorsLibraryUrl(argv: string[], env?: Record<string, string | undefined>): string;
23
+ /**
24
+ * Point `current` at an entry, replacing whatever it named.
25
+ *
26
+ * Nothing to do when `current` already resolves to the entry — the unchanged
27
+ * pull, which is most of them.
28
+ *
29
+ * Otherwise the new link is made under a temp name and renamed over `current`.
30
+ * On POSIX that rename is atomic over an existing symlink, so a concurrent
31
+ * compile never finds the link missing. On Windows `current` is a junction, and
32
+ * a rename onto an existing junction fails with `EPERM` (flow-builder-sdk#945),
33
+ * so there the old link is parked under `current.old-<pid>` first, the new one
34
+ * renamed in, and the parked one removed; a failed rename puts the parked link
35
+ * back. That leaves a moment where `current` does not exist, which Windows
36
+ * gives no way to avoid with a junction.
37
+ *
38
+ * Links are removed with `unlink`, never a recursive `rm`: the link's target is
39
+ * the extracted library, and removing the link must not reach into it.
40
+ *
41
+ * @param platform - Which branch to take; a parameter so both run in tests.
42
+ */
43
+ export declare function pointCurrentAt(root: string, sha256: string, platform?: NodeJS.Platform): Promise<void>;
23
44
  export declare function run(argv: string[]): Promise<number>;
@@ -1,6 +1,6 @@
1
1
  import { reportResult } from '../cli-result.js';
2
2
  import { createHash } from "node:crypto";
3
- import { mkdir, readFile, rename, rm, stat, symlink, writeFile, } from "node:fs/promises";
3
+ import { mkdir, readFile, realpath, rename, rm, stat, symlink, unlink, writeFile, } from "node:fs/promises";
4
4
  import { join, resolve } from "node:path";
5
5
  import { writeJsonFile } from "../core/json-file.js";
6
6
  import { CURRENT_LINK, LIBRARY_JSON_DIR, LIBRARY_MARKDOWN_DIR, cacheEntry, libraryCacheRoot, referencedConnectorKeys, } from "../core/library-cache.js";
@@ -215,16 +215,81 @@ async function writeSource(libraryPath, archive, sha256, archiveSha256, overlayS
215
215
  /**
216
216
  * Point `current` at an entry, replacing whatever it named.
217
217
  *
218
- * Written to a temp name and renamed, so a reader never sees the link missing —
219
- * `rename` over an existing symlink is atomic, while unlink-then-create leaves a
220
- * window where a concurrent compile finds nothing.
218
+ * Nothing to do when `current` already resolves to the entry — the unchanged
219
+ * pull, which is most of them.
220
+ *
221
+ * Otherwise the new link is made under a temp name and renamed over `current`.
222
+ * On POSIX that rename is atomic over an existing symlink, so a concurrent
223
+ * compile never finds the link missing. On Windows `current` is a junction, and
224
+ * a rename onto an existing junction fails with `EPERM` (flow-builder-sdk#945),
225
+ * so there the old link is parked under `current.old-<pid>` first, the new one
226
+ * renamed in, and the parked one removed; a failed rename puts the parked link
227
+ * back. That leaves a moment where `current` does not exist, which Windows
228
+ * gives no way to avoid with a junction.
229
+ *
230
+ * Links are removed with `unlink`, never a recursive `rm`: the link's target is
231
+ * the extracted library, and removing the link must not reach into it.
232
+ *
233
+ * @param platform - Which branch to take; a parameter so both run in tests.
221
234
  */
222
- async function pointCurrentAt(root, sha256) {
235
+ export async function pointCurrentAt(root, sha256, platform = process.platform) {
223
236
  const link = join(root, CURRENT_LINK);
237
+ if (await resolvesTo(link, join(root, sha256)))
238
+ return;
224
239
  const temporary = `${link}.tmp-${process.pid}`;
225
- await rm(temporary, { recursive: true, force: true });
226
- await symlink(sha256, temporary, "junction");
227
- await rename(temporary, link);
240
+ await removeLink(temporary);
241
+ try {
242
+ await symlink(sha256, temporary, "junction");
243
+ if (platform !== "win32") {
244
+ await rename(temporary, link);
245
+ return;
246
+ }
247
+ const parked = `${link}.old-${process.pid}`;
248
+ await removeLink(parked);
249
+ let wasParked = false;
250
+ try {
251
+ await rename(link, parked);
252
+ wasParked = true;
253
+ }
254
+ catch (error) {
255
+ if (error.code !== "ENOENT")
256
+ throw error;
257
+ }
258
+ try {
259
+ await rename(temporary, link);
260
+ }
261
+ catch (error) {
262
+ if (wasParked)
263
+ await rename(parked, link).catch(() => undefined);
264
+ throw error;
265
+ }
266
+ if (wasParked)
267
+ await removeLink(parked);
268
+ }
269
+ finally {
270
+ // Gone already after a successful rename; this is for every way out that
271
+ // is not one, so a failed pull leaves no `current.tmp-<pid>` behind.
272
+ await removeLink(temporary);
273
+ }
274
+ }
275
+ /** Whether `link` and `entry` name the same directory; false when either is missing. */
276
+ async function resolvesTo(link, entry) {
277
+ try {
278
+ return (await realpath(link)) === (await realpath(entry));
279
+ }
280
+ catch {
281
+ return false;
282
+ }
283
+ }
284
+ /** Remove a symlink or junction without following it; a missing one is fine. */
285
+ async function removeLink(path) {
286
+ try {
287
+ await unlink(path);
288
+ }
289
+ catch (error) {
290
+ if (error.code !== "ENOENT")
291
+ throw error;
292
+ }
228
293
  }
229
294
  export async function run(argv) {
230
295
  if (has(argv, "--help") || has(argv, "-h")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.18.8",
3
+ "version": "6.18.9",
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",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "ce161efe130f6a6cdee895d8a7fab2faddcec0fd"
95
+ "gitref": "6f8e3c88230704c5b5bc0e8983be45ebedd135dc"
96
96
  }