@uipath/data-fabric-tool 1.195.0 → 1.197.0-preview.59

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.
@@ -9,9 +9,9 @@ import {
9
9
  } from "@uipath/common";
10
10
  import { getFileSystem } from "@uipath/filesystem";
11
11
  import type { EntityRecord } from "@uipath/uipath-typescript";
12
- import type { Command } from "commander";
12
+ import { type Command, Option } from "commander";
13
13
  import { readFileBinary, readJsonInput } from "../utils/input";
14
- import { fail } from "../utils/output";
14
+ import { fail, requireDestructiveConfirmation } from "../utils/output";
15
15
  import { connectOrFail } from "../utils/sdk-client";
16
16
 
17
17
  interface ListOptions {
@@ -19,26 +19,34 @@ interface ListOptions {
19
19
  limit: string;
20
20
  offset?: string;
21
21
  cursor?: string;
22
+ folderKey?: string;
22
23
  }
23
24
 
24
25
  interface GetOptions {
25
26
  tenant?: string;
27
+ folderKey?: string;
26
28
  }
27
29
 
28
30
  interface InsertOptions {
29
31
  tenant?: string;
30
32
  file?: string;
31
33
  body?: string;
34
+ folderKey?: string;
32
35
  }
33
36
 
34
37
  interface UpdateOptions {
35
38
  tenant?: string;
36
39
  file?: string;
37
40
  body?: string;
41
+ folderKey?: string;
38
42
  }
39
43
 
40
44
  interface DeleteOptions {
41
45
  tenant?: string;
46
+ yes?: boolean;
47
+ confirm?: boolean;
48
+ reason?: string;
49
+ folderKey?: string;
42
50
  }
43
51
 
44
52
  interface QueryOptions {
@@ -48,11 +56,13 @@ interface QueryOptions {
48
56
  limit: string;
49
57
  offset?: string;
50
58
  cursor?: string;
59
+ folderKey?: string;
51
60
  }
52
61
 
53
62
  interface ImportOptions {
54
63
  tenant?: string;
55
64
  file?: string;
65
+ folderKey?: string;
56
66
  }
57
67
 
58
68
  interface BatchResult {
@@ -195,6 +205,10 @@ export const registerRecordsCommand = (program: Command) => {
195
205
  "--cursor <cursor>",
196
206
  "Pagination cursor from a previous response to fetch the next page",
197
207
  )
208
+ .option(
209
+ "--folder-key <key>",
210
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
211
+ )
198
212
  .examples(RECORDS_LIST_EXAMPLES)
199
213
  .trackedAction(
200
214
  processContext,
@@ -232,13 +246,18 @@ export const registerRecordsCommand = (program: Command) => {
232
246
  const sdk = await connectOrFail(options.tenant);
233
247
  if (!sdk) return;
234
248
 
249
+ const listPaginationOptions =
250
+ options.cursor !== undefined
251
+ ? { pageSize, cursor: { value: options.cursor } }
252
+ : jumpToPage !== undefined
253
+ ? { pageSize, jumpToPage }
254
+ : { pageSize };
235
255
  const [listError, result] = await catchError(
236
256
  sdk.entities.getAllRecords(entityId, {
237
- pageSize,
238
- ...(options.cursor !== undefined && {
239
- cursor: { value: options.cursor },
257
+ ...listPaginationOptions,
258
+ ...(options.folderKey !== undefined && {
259
+ folderKey: options.folderKey,
240
260
  }),
241
- ...(jumpToPage !== undefined && { jumpToPage }),
242
261
  }),
243
262
  );
244
263
 
@@ -265,6 +284,10 @@ export const registerRecordsCommand = (program: Command) => {
265
284
  .addOption(
266
285
  createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
267
286
  )
287
+ .option(
288
+ "--folder-key <key>",
289
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
290
+ )
268
291
  .examples(RECORDS_GET_EXAMPLES)
269
292
  .trackedAction(
270
293
  processContext,
@@ -273,7 +296,13 @@ export const registerRecordsCommand = (program: Command) => {
273
296
  if (!sdk) return;
274
297
 
275
298
  const [getError, record] = await catchError(
276
- sdk.entities.getRecordById(entityId, recordId),
299
+ sdk.entities.getRecordById(
300
+ entityId,
301
+ recordId,
302
+ options.folderKey !== undefined
303
+ ? { folderKey: options.folderKey }
304
+ : undefined,
305
+ ),
277
306
  );
278
307
 
279
308
  if (getError) {
@@ -311,7 +340,11 @@ export const registerRecordsCommand = (program: Command) => {
311
340
  )
312
341
  .option(
313
342
  "--body <json>",
314
- "Inline JSON record data (object or array of objects)",
343
+ "Inline JSON record data (object or array of objects; use `-` to read from stdin)",
344
+ )
345
+ .option(
346
+ "--folder-key <key>",
347
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
315
348
  )
316
349
  .examples(RECORDS_INSERT_EXAMPLES)
317
350
  .trackedAction(
@@ -333,9 +366,18 @@ export const registerRecordsCommand = (program: Command) => {
333
366
  | Record<string, unknown>[];
334
367
  const recordsList = Array.isArray(data) ? data : [data];
335
368
 
369
+ const folderOptions =
370
+ options.folderKey !== undefined
371
+ ? { folderKey: options.folderKey }
372
+ : undefined;
373
+
336
374
  if (recordsList.length === 1) {
337
375
  const [insertError, result] = await catchError(
338
- sdk.entities.insertRecordById(entityId, recordsList[0]),
376
+ sdk.entities.insertRecordById(
377
+ entityId,
378
+ recordsList[0],
379
+ folderOptions,
380
+ ),
339
381
  );
340
382
 
341
383
  if (insertError) {
@@ -352,7 +394,11 @@ export const registerRecordsCommand = (program: Command) => {
352
394
  });
353
395
  } else {
354
396
  const [insertError, result] = await catchError(
355
- sdk.entities.insertRecordsById(entityId, recordsList),
397
+ sdk.entities.insertRecordsById(
398
+ entityId,
399
+ recordsList,
400
+ folderOptions,
401
+ ),
356
402
  );
357
403
 
358
404
  if (insertError) {
@@ -394,7 +440,11 @@ export const registerRecordsCommand = (program: Command) => {
394
440
  )
395
441
  .option(
396
442
  "--body <json>",
397
- "Inline JSON record data (must include Id field)",
443
+ "Inline JSON record data (must include Id field; use `-` to read from stdin)",
444
+ )
445
+ .option(
446
+ "--folder-key <key>",
447
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
398
448
  )
399
449
  .examples(RECORDS_UPDATE_EXAMPLES)
400
450
  .trackedAction(
@@ -416,6 +466,11 @@ export const registerRecordsCommand = (program: Command) => {
416
466
  | Record<string, unknown>[];
417
467
  const recordsList = Array.isArray(data) ? data : [data];
418
468
 
469
+ const folderOptions =
470
+ options.folderKey !== undefined
471
+ ? { folderKey: options.folderKey }
472
+ : undefined;
473
+
419
474
  if (recordsList.length === 1) {
420
475
  const record = recordsList[0];
421
476
  const recordId =
@@ -432,6 +487,7 @@ export const registerRecordsCommand = (program: Command) => {
432
487
  entityId,
433
488
  String(recordId),
434
489
  record,
490
+ folderOptions,
435
491
  ),
436
492
  );
437
493
 
@@ -462,6 +518,7 @@ export const registerRecordsCommand = (program: Command) => {
462
518
  sdk.entities.updateRecordsById(
463
519
  entityId,
464
520
  recordsList as EntityRecord[],
521
+ folderOptions,
465
522
  ),
466
523
  );
467
524
 
@@ -509,7 +566,7 @@ export const registerRecordsCommand = (program: Command) => {
509
566
  )
510
567
  .option(
511
568
  "--body <json>",
512
- "Inline JSON query options (filterGroup, selectedFields, sortOptions, aggregates, groupBy)",
569
+ "Inline JSON query options (filterGroup, selectedFields, sortOptions, aggregates, groupBy; use `-` to read from stdin)",
513
570
  )
514
571
  .option(
515
572
  "-l, --limit <number>",
@@ -524,6 +581,10 @@ export const registerRecordsCommand = (program: Command) => {
524
581
  "--cursor <cursor>",
525
582
  "Pagination cursor from a previous response to fetch the next page",
526
583
  )
584
+ .option(
585
+ "--folder-key <key>",
586
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
587
+ )
527
588
  .trackedAction(
528
589
  processContext,
529
590
  async (entityId: string, options: QueryOptions) => {
@@ -588,14 +649,19 @@ export const registerRecordsCommand = (program: Command) => {
588
649
  const sdk = await connectOrFail(options.tenant);
589
650
  if (!sdk) return;
590
651
 
652
+ const queryPaginationOptions =
653
+ options.cursor !== undefined
654
+ ? { pageSize, cursor: { value: options.cursor } }
655
+ : jumpToPage !== undefined
656
+ ? { pageSize, jumpToPage }
657
+ : { pageSize };
591
658
  const [queryError, result] = await catchError(
592
659
  sdk.entities.queryRecordsById(entityId, {
593
660
  ...(queryBody !== null && queryBody),
594
- pageSize,
595
- ...(options.cursor !== undefined && {
596
- cursor: { value: options.cursor },
661
+ ...queryPaginationOptions,
662
+ ...(options.folderKey !== undefined && {
663
+ folderKey: options.folderKey,
597
664
  }),
598
- ...(jumpToPage !== undefined && { jumpToPage }),
599
665
  }),
600
666
  );
601
667
 
@@ -622,6 +688,10 @@ export const registerRecordsCommand = (program: Command) => {
622
688
  createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
623
689
  )
624
690
  .option("-f, --file <path>", "Path to the CSV file to import")
691
+ .option(
692
+ "--folder-key <key>",
693
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
694
+ )
625
695
  .trackedAction(
626
696
  processContext,
627
697
  async (entityId: string, options: ImportOptions) => {
@@ -651,7 +721,13 @@ export const registerRecordsCommand = (program: Command) => {
651
721
  { type: "text/csv" },
652
722
  );
653
723
  const [importError, result] = await catchError(
654
- sdk.entities.importRecordsById(entityId, csvFile),
724
+ sdk.entities.importRecordsById(
725
+ entityId,
726
+ csvFile,
727
+ options.folderKey !== undefined
728
+ ? { folderKey: options.folderKey }
729
+ : undefined,
730
+ ),
655
731
  );
656
732
 
657
733
  if (importError) {
@@ -689,6 +765,18 @@ export const registerRecordsCommand = (program: Command) => {
689
765
  .addOption(
690
766
  createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
691
767
  )
768
+ .option("-y, --yes", "Acknowledge this is an irreversible operation")
769
+ .addOption(
770
+ new Option("--confirm", "Deprecated alias for --yes").hideHelp(),
771
+ )
772
+ .option(
773
+ "--reason <reason>",
774
+ "Reason for the deletion — echoed back in the response so the caller can log it",
775
+ )
776
+ .option(
777
+ "--folder-key <key>",
778
+ "Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
779
+ )
692
780
  .examples(RECORDS_DELETE_EXAMPLES)
693
781
  .trackedAction(
694
782
  processContext,
@@ -697,11 +785,24 @@ export const registerRecordsCommand = (program: Command) => {
697
785
  recordIds: string[],
698
786
  options: DeleteOptions,
699
787
  ) => {
788
+ const reason = requireDestructiveConfirmation(
789
+ options,
790
+ `delete ${recordIds.length} record(s) from entity '${entityId}'`,
791
+ 'Pass --reason "<text>" to record why the records are being deleted.',
792
+ );
793
+ if (reason === null) return;
794
+
700
795
  const sdk = await connectOrFail(options.tenant);
701
796
  if (!sdk) return;
702
797
 
703
798
  const [deleteError, result] = await catchError(
704
- sdk.entities.deleteRecordsById(entityId, recordIds),
799
+ sdk.entities.deleteRecordsById(
800
+ entityId,
801
+ recordIds,
802
+ options.folderKey !== undefined
803
+ ? { folderKey: options.folderKey }
804
+ : undefined,
805
+ ),
705
806
  );
706
807
 
707
808
  if (deleteError) {
@@ -721,6 +822,7 @@ export const registerRecordsCommand = (program: Command) => {
721
822
  FailureCount: failureCount,
722
823
  SuccessRecords: r.successRecords ?? [],
723
824
  FailureRecords: r.failureRecords ?? [],
825
+ Reason: reason,
724
826
  },
725
827
  });
726
828
  if (failureCount > 0) {
@@ -0,0 +1,127 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ const readFile = vi.fn();
5
+
6
+ vi.mock("@uipath/filesystem", () => ({
7
+ getFileSystem: () => ({ readFile }),
8
+ }));
9
+
10
+ import { readJsonInput } from "./input";
11
+
12
+ const realStdin = process.stdin;
13
+ const realPlatform = process.platform;
14
+
15
+ // Replaces process.stdin with a fake stream. Pass null to simulate a TTY (no
16
+ // piped input); pass a string to simulate piped data.
17
+ function mockStdin(data: string | null): void {
18
+ const stream = new EventEmitter() as unknown as NodeJS.ReadStream;
19
+ (stream as unknown as { isTTY: boolean }).isTTY = data === null;
20
+ stream.setEncoding = vi.fn().mockReturnValue(stream);
21
+ Object.defineProperty(process, "stdin", {
22
+ value: stream,
23
+ configurable: true,
24
+ });
25
+ if (data !== null) {
26
+ queueMicrotask(() => {
27
+ stream.emit("data", data);
28
+ stream.emit("end");
29
+ });
30
+ }
31
+ }
32
+
33
+ function setPlatform(platform: string): void {
34
+ Object.defineProperty(process, "platform", {
35
+ value: platform,
36
+ configurable: true,
37
+ });
38
+ }
39
+
40
+ describe("readJsonInput", () => {
41
+ beforeEach(() => {
42
+ vi.clearAllMocks();
43
+ });
44
+
45
+ afterEach(() => {
46
+ Object.defineProperty(process, "stdin", {
47
+ value: realStdin,
48
+ configurable: true,
49
+ });
50
+ Object.defineProperty(process, "platform", {
51
+ value: realPlatform,
52
+ configurable: true,
53
+ });
54
+ });
55
+
56
+ it("parses inline JSON", async () => {
57
+ await expect(readJsonInput(undefined, '{"a":1}')).resolves.toEqual({
58
+ a: 1,
59
+ });
60
+ });
61
+
62
+ it("reads and parses a JSON file", async () => {
63
+ readFile.mockResolvedValue('{"fromFile":true}');
64
+ await expect(readJsonInput("payload.json")).resolves.toEqual({
65
+ fromFile: true,
66
+ });
67
+ expect(readFile).toHaveBeenCalledWith("payload.json", "utf-8");
68
+ });
69
+
70
+ it("throws the missing-input message when nothing is provided", async () => {
71
+ await expect(readJsonInput(undefined, undefined)).rejects.toThrow(
72
+ "Provide either --file <path> or inline data.",
73
+ );
74
+ });
75
+
76
+ it("uses a custom missing-input message", async () => {
77
+ await expect(
78
+ readJsonInput(undefined, undefined, "Provide --body or --file."),
79
+ ).rejects.toThrow("Provide --body or --file.");
80
+ });
81
+
82
+ it("reads JSON from stdin when --body is `-` (UV-14637)", async () => {
83
+ // Payload contains `&`, the char that cmd.exe mangles on the CLI line.
84
+ mockStdin('[{"name":"A&B"}]');
85
+ await expect(readJsonInput(undefined, "-")).resolves.toEqual([
86
+ { name: "A&B" },
87
+ ]);
88
+ expect(readFile).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it("reads JSON from stdin when --file is `-`", async () => {
92
+ mockStdin('{"piped":true}');
93
+ await expect(readJsonInput("-")).resolves.toEqual({ piped: true });
94
+ expect(readFile).not.toHaveBeenCalled();
95
+ });
96
+
97
+ it("errors when `-` is given but stdin is a TTY", async () => {
98
+ mockStdin(null);
99
+ await expect(readJsonInput(undefined, "-")).rejects.toThrow(
100
+ "Expected JSON on stdin but got none",
101
+ );
102
+ });
103
+
104
+ it("errors when `-` is given but stdin is empty", async () => {
105
+ mockStdin(" ");
106
+ await expect(readJsonInput(undefined, "-")).rejects.toThrow(
107
+ "Expected JSON on stdin but got none",
108
+ );
109
+ });
110
+
111
+ it("reports invalid JSON without a Windows hint on non-Windows", async () => {
112
+ setPlatform("linux");
113
+ await expect(readJsonInput(undefined, "{bad")).rejects.toThrow(
114
+ /^Invalid JSON input:/,
115
+ );
116
+ await expect(readJsonInput(undefined, "{bad")).rejects.not.toThrow(
117
+ /cmd\/PowerShell/,
118
+ );
119
+ });
120
+
121
+ it("appends a shell-quoting hint on Windows", async () => {
122
+ setPlatform("win32");
123
+ await expect(readJsonInput(undefined, "{bad")).rejects.toThrow(
124
+ /cmd\/PowerShell.*--file.*--body -/s,
125
+ );
126
+ });
127
+ });
@@ -1,5 +1,8 @@
1
+ import { readStdin } from "@uipath/common";
1
2
  import { getFileSystem } from "@uipath/filesystem";
2
3
 
4
+ const STDIN_SENTINEL = "-";
5
+
3
6
  export async function readFileBinary(filePath: string): Promise<Uint8Array> {
4
7
  const fs = getFileSystem();
5
8
  const content = await fs.readFile(filePath);
@@ -17,6 +20,19 @@ export async function readJsonInput(
17
20
  if (!filePath && !inline) {
18
21
  throw new Error(missingMsg);
19
22
  }
23
+
24
+ // `--body -` / `--file -` reads the JSON payload from stdin. Stdin bypasses
25
+ // the shell, so characters like `&` are never mangled by cmd.exe (UV-14637).
26
+ if (inline === STDIN_SENTINEL || filePath === STDIN_SENTINEL) {
27
+ const stdinData = await readStdin();
28
+ if (stdinData === null) {
29
+ throw new Error(
30
+ "Expected JSON on stdin but got none. Pipe data in, e.g. `... --body - < payload.json`.",
31
+ );
32
+ }
33
+ return parseJson(stdinData);
34
+ }
35
+
20
36
  let raw: string;
21
37
  if (filePath) {
22
38
  const fs = getFileSystem();
@@ -28,5 +44,18 @@ export async function readJsonInput(
28
44
  } else {
29
45
  raw = inline as string;
30
46
  }
31
- return JSON.parse(raw);
47
+ return parseJson(raw);
48
+ }
49
+
50
+ function parseJson(raw: string): unknown {
51
+ try {
52
+ return JSON.parse(raw);
53
+ } catch (err: unknown) {
54
+ const detail = err instanceof Error ? err.message : String(err);
55
+ const hint =
56
+ process.platform === "win32"
57
+ ? " On Windows cmd/PowerShell, characters like & | < > ^ split the command — use `--file <path>` or pipe JSON via `--body -`."
58
+ : "";
59
+ throw new Error(`Invalid JSON input: ${detail}.${hint}`);
60
+ }
32
61
  }
@@ -31,6 +31,7 @@ describe("fail", () => {
31
31
  });
32
32
 
33
33
  describe("requireDestructiveConfirmation", () => {
34
+ const operation = "delete entity 'abc'";
34
35
  const reasonInstruction = "Pass --reason to explain.";
35
36
 
36
37
  beforeEach(() => {
@@ -38,32 +39,44 @@ describe("requireDestructiveConfirmation", () => {
38
39
  process.exitCode = undefined;
39
40
  });
40
41
 
41
- it("returns the trimmed reason when --confirm and --reason are present", () => {
42
+ it("returns the trimmed reason when --yes and --reason are present", () => {
42
43
  const result = requireDestructiveConfirmation(
43
- { confirm: true, reason: " cleanup " },
44
+ { yes: true, reason: " cleanup " },
45
+ operation,
44
46
  reasonInstruction,
45
47
  );
46
48
  expect(result).toBe("cleanup");
47
49
  expect(OutputFormatter.error).not.toHaveBeenCalled();
48
50
  });
49
51
 
50
- it("fails and returns null when --confirm is missing", () => {
52
+ it("accepts the deprecated --confirm alias for --yes", () => {
53
+ const result = requireDestructiveConfirmation(
54
+ { confirm: true, reason: "cleanup" },
55
+ operation,
56
+ reasonInstruction,
57
+ );
58
+ expect(result).toBe("cleanup");
59
+ expect(OutputFormatter.error).not.toHaveBeenCalled();
60
+ });
61
+
62
+ it("fails and returns null when neither --yes nor --confirm is present", () => {
63
+ // The confirmation error is emitted by @uipath/common's
64
+ // requireConfirmation (covered by confirmation.spec.ts); it routes
65
+ // through common's own OutputFormatter, which this suite's module-level
66
+ // mock doesn't intercept. Assert the observable contract here.
51
67
  const result = requireDestructiveConfirmation(
52
68
  { reason: "cleanup" },
69
+ operation,
53
70
  reasonInstruction,
54
71
  );
55
72
  expect(result).toBeNull();
56
- expect(OutputFormatter.error).toHaveBeenCalledWith(
57
- expect.objectContaining({
58
- Message: "Confirmation required for destructive operation",
59
- }),
60
- );
61
73
  expect(process.exitCode).toBe(1);
62
74
  });
63
75
 
64
76
  it("fails and returns null when --reason is missing or blank", () => {
65
77
  const result = requireDestructiveConfirmation(
66
- { confirm: true, reason: " " },
78
+ { yes: true, reason: " " },
79
+ operation,
67
80
  reasonInstruction,
68
81
  );
69
82
  expect(result).toBeNull();
@@ -1,4 +1,10 @@
1
- import { OutputFormatter, processContext, RESULTS } from "@uipath/common";
1
+ import {
2
+ OutputFormatter,
3
+ processContext,
4
+ RESULTS,
5
+ requireConfirmation,
6
+ warnDeprecatedOptionAlias,
7
+ } from "@uipath/common";
2
8
 
3
9
  /**
4
10
  * Emit a standard failure result and set a non-zero exit code.
@@ -17,27 +23,39 @@ export function fail(message: string, instructions: string): void {
17
23
  }
18
24
 
19
25
  export interface DestructiveOptions {
26
+ yes?: boolean;
27
+ // Legacy hidden alias for `yes`. The canonical flag is `--yes`; `--confirm`
28
+ // keeps working (with a deprecation warning) so existing scripts don't break.
20
29
  confirm?: boolean;
21
30
  reason?: string;
22
31
  }
23
32
 
24
33
  /**
25
- * Validate the `--confirm`/`--reason` flags required for a destructive
26
- * operation. On failure, emits the appropriate error, sets the exit code, and
27
- * returns `null`. On success, returns the trimmed reason.
34
+ * Validate the `--yes`/`--reason` flags required for a destructive operation.
35
+ * `--confirm` is accepted as a deprecated alias for `--yes`. On failure, emits
36
+ * the appropriate structured error, sets the exit code, and returns `null`. On
37
+ * success, returns the trimmed reason.
28
38
  *
39
+ * @param operation - short description of what will happen, e.g.
40
+ * `"delete entity 'abc'"`, surfaced in the confirmation error.
29
41
  * @param reasonInstruction - Instruction text shown when `--reason` is missing,
30
42
  * e.g. `'Pass --reason "<text>" to record why the choice set is being deleted.'`
31
43
  */
32
44
  export function requireDestructiveConfirmation(
33
45
  options: DestructiveOptions,
46
+ operation: string,
34
47
  reasonInstruction: string,
35
48
  ): string | null {
36
- if (options.confirm !== true) {
37
- fail(
38
- "Confirmation required for destructive operation",
39
- "Pass --confirm to acknowledge this is irreversible.",
40
- );
49
+ // --confirm is a deprecated alias for the canonical --yes.
50
+ if (options.confirm === true && options.yes !== true) {
51
+ warnDeprecatedOptionAlias("--confirm", "--yes");
52
+ }
53
+ if (
54
+ !requireConfirmation(
55
+ { yes: options.yes === true || options.confirm === true },
56
+ operation,
57
+ )
58
+ ) {
41
59
  return null;
42
60
  }
43
61