@uipath/tasks-tool 1.202.0-preview.140 → 1.202.0-preview.142

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.
Files changed (41) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/{tool-4er8cbf1.js → tool-315h4wtj.js} +5 -2
  3. package/dist/tool.js +1 -1
  4. package/package.json +5 -2
  5. package/browser.json +0 -4
  6. package/src/commands/_shared/catalog-options.spec.ts +0 -114
  7. package/src/commands/_shared/catalog-options.ts +0 -122
  8. package/src/commands/_shared/connect.spec.ts +0 -52
  9. package/src/commands/_shared/connect.ts +0 -31
  10. package/src/commands/_shared/constants.ts +0 -1
  11. package/src/commands/_shared/folder-picker-interactive.spec.ts +0 -92
  12. package/src/commands/_shared/folder-picker-interactive.ts +0 -76
  13. package/src/commands/_shared/folder-picker.spec.ts +0 -159
  14. package/src/commands/_shared/folder-picker.ts +0 -113
  15. package/src/commands/_shared/folder-resolver.spec.ts +0 -152
  16. package/src/commands/_shared/folder-resolver.ts +0 -113
  17. package/src/commands/_shared/parse.spec.ts +0 -69
  18. package/src/commands/_shared/parse.ts +0 -43
  19. package/src/commands/catalogs.ts +0 -281
  20. package/src/commands/comments.ts +0 -160
  21. package/src/commands/data.ts +0 -137
  22. package/src/commands/labels.ts +0 -93
  23. package/src/commands/metadata.ts +0 -139
  24. package/src/commands/tasks.spec.ts +0 -2449
  25. package/src/commands/tasks.ts +0 -631
  26. package/src/index.ts +0 -12
  27. package/src/tool.ts +0 -24
  28. package/src/utils/sdk-client.ts +0 -30
  29. package/tests/assign.e2e.test.ts +0 -166
  30. package/tests/assignable-users.e2e.test.ts +0 -167
  31. package/tests/catalogs.e2e.test.ts +0 -418
  32. package/tests/comments.e2e.test.ts +0 -223
  33. package/tests/complete.e2e.test.ts +0 -207
  34. package/tests/data.e2e.test.ts +0 -187
  35. package/tests/get.e2e.test.ts +0 -103
  36. package/tests/labels.e2e.test.ts +0 -184
  37. package/tests/list.e2e.test.ts +0 -234
  38. package/tests/metadata.e2e.test.ts +0 -186
  39. package/tests/performance.e2e.test.ts +0 -32
  40. package/tsconfig.json +0 -9
  41. package/vitest.config.ts +0 -1
@@ -1,159 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("./folder-picker-interactive", () => ({
4
- pickFolderInteractively: vi.fn(),
5
- }));
6
-
7
- vi.mock("./folder-resolver", () => ({
8
- resolveFolderIdByKey: vi.fn(),
9
- resolveFolderIdByPath: vi.fn(),
10
- }));
11
-
12
- vi.mock("@uipath/common", async (importOriginal) => {
13
- const actual = await importOriginal<typeof import("@uipath/common")>();
14
- return {
15
- ...actual,
16
- OutputFormatter: { success: vi.fn(), error: vi.fn() },
17
- };
18
- });
19
-
20
- import { OutputFormatter } from "@uipath/common";
21
- import { resolveFolderId, resolveFolderScope } from "./folder-picker";
22
- import { pickFolderInteractively } from "./folder-picker-interactive";
23
- import { resolveFolderIdByKey, resolveFolderIdByPath } from "./folder-resolver";
24
-
25
- const FOLDER_KEY = "12345678-1234-1234-1234-123456789abc";
26
-
27
- describe("resolveFolderScope", () => {
28
- beforeEach(() => {
29
- vi.resetAllMocks();
30
- process.exitCode = undefined;
31
- });
32
-
33
- it("parses and returns the id when --folder-id is provided", async () => {
34
- expect(await resolveFolderScope({ folderId: "42" })).toBe(42);
35
- expect(OutputFormatter.error).not.toHaveBeenCalled();
36
- expect(resolveFolderIdByKey).not.toHaveBeenCalled();
37
- expect(resolveFolderIdByPath).not.toHaveBeenCalled();
38
- });
39
-
40
- it("reports an invalid --folder-id", async () => {
41
- expect(await resolveFolderScope({ folderId: "abc" })).toBeNull();
42
- expect(OutputFormatter.error).toHaveBeenCalledWith(
43
- expect.objectContaining({ Message: "Invalid --folder-id: abc" }),
44
- );
45
- });
46
-
47
- it("resolves --folder-key through the folder resolver", async () => {
48
- vi.mocked(resolveFolderIdByKey).mockResolvedValue(55);
49
-
50
- expect(
51
- await resolveFolderScope({ folderKey: FOLDER_KEY, tenant: "t1" }),
52
- ).toBe(55);
53
- expect(resolveFolderIdByKey).toHaveBeenCalledWith(FOLDER_KEY, "t1");
54
- expect(resolveFolderIdByPath).not.toHaveBeenCalled();
55
- });
56
-
57
- it("resolves --folder-path through the folder resolver", async () => {
58
- vi.mocked(resolveFolderIdByPath).mockResolvedValue(77);
59
-
60
- expect(await resolveFolderScope({ folderPath: "Shared/Finance" })).toBe(
61
- 77,
62
- );
63
- expect(resolveFolderIdByPath).toHaveBeenCalledWith(
64
- "Shared/Finance",
65
- undefined,
66
- );
67
- expect(resolveFolderIdByKey).not.toHaveBeenCalled();
68
- });
69
-
70
- it("passes a resolver failure through as null", async () => {
71
- vi.mocked(resolveFolderIdByKey).mockResolvedValue(null);
72
-
73
- expect(await resolveFolderScope({ folderKey: FOLDER_KEY })).toBeNull();
74
- });
75
-
76
- it("returns undefined when no folder option is passed", async () => {
77
- expect(await resolveFolderScope({})).toBeUndefined();
78
- expect(OutputFormatter.error).not.toHaveBeenCalled();
79
- });
80
- });
81
-
82
- describe("resolveFolderId", () => {
83
- const originalTTY = process.stdout.isTTY;
84
-
85
- beforeEach(() => {
86
- vi.resetAllMocks();
87
- process.exitCode = undefined;
88
- });
89
-
90
- afterEach(() => {
91
- process.stdout.isTTY = originalTTY;
92
- });
93
-
94
- it("parses and returns the id when --folder-id is provided", async () => {
95
- expect(await resolveFolderId({ folderId: "42" })).toBe(42);
96
- expect(OutputFormatter.error).not.toHaveBeenCalled();
97
- expect(pickFolderInteractively).not.toHaveBeenCalled();
98
- });
99
-
100
- it("resolves --folder-path without falling back to the picker", async () => {
101
- vi.mocked(resolveFolderIdByPath).mockResolvedValue(77);
102
-
103
- expect(await resolveFolderId({ folderPath: "Shared" })).toBe(77);
104
- expect(pickFolderInteractively).not.toHaveBeenCalled();
105
- });
106
-
107
- it("fails loud when omitted on a non-interactive terminal", async () => {
108
- process.stdout.isTTY = false;
109
-
110
- expect(await resolveFolderId({})).toBeNull();
111
- expect(OutputFormatter.error).toHaveBeenCalledWith(
112
- expect.objectContaining({
113
- Message: "A folder is required",
114
- Instructions: expect.stringContaining("--folder-id <id>"),
115
- }),
116
- );
117
- expect(process.exitCode).toBe(1);
118
- expect(pickFolderInteractively).not.toHaveBeenCalled();
119
- });
120
-
121
- it("uses the caller's instructions when the command's folder options differ", async () => {
122
- process.stdout.isTTY = false;
123
-
124
- expect(
125
- await resolveFolderId({}, "Pass the folder-id argument."),
126
- ).toBeNull();
127
- expect(OutputFormatter.error).toHaveBeenCalledWith(
128
- expect.objectContaining({
129
- Message: "A folder is required",
130
- Instructions: "Pass the folder-id argument.",
131
- }),
132
- );
133
- expect(process.exitCode).toBe(1);
134
- });
135
-
136
- it("delegates to the interactive picker when omitted on a TTY", async () => {
137
- process.stdout.isTTY = true;
138
- vi.mocked(pickFolderInteractively).mockResolvedValue(7);
139
-
140
- expect(await resolveFolderId({})).toBe(7);
141
- expect(pickFolderInteractively).toHaveBeenCalledOnce();
142
- });
143
-
144
- it("passes the tenant to the interactive picker", async () => {
145
- process.stdout.isTTY = true;
146
- vi.mocked(pickFolderInteractively).mockResolvedValue(7);
147
-
148
- expect(await resolveFolderId({ tenant: "my-tenant" })).toBe(7);
149
- expect(pickFolderInteractively).toHaveBeenCalledWith("my-tenant");
150
- });
151
-
152
- it("does not fall back to the picker when resolution fails", async () => {
153
- process.stdout.isTTY = true;
154
- vi.mocked(resolveFolderIdByKey).mockResolvedValue(null);
155
-
156
- expect(await resolveFolderId({ folderKey: FOLDER_KEY })).toBeNull();
157
- expect(pickFolderInteractively).not.toHaveBeenCalled();
158
- });
159
- });
@@ -1,113 +0,0 @@
1
- import { OutputFormatter, processContext, RESULTS } from "@uipath/common";
2
- import { type Command, Option } from "commander";
3
-
4
- import { parseIntArg } from "./parse";
5
-
6
- export interface FolderScopeOptions {
7
- tenant?: string;
8
- folderId?: string;
9
- folderPath?: string;
10
- folderKey?: string;
11
- }
12
-
13
- /**
14
- * Adds the folder scoping options (--folder-id, --folder-path, --folder-key)
15
- * to a command. The three are mutually exclusive; the pairs are declared via
16
- * Commander conflicts so misuse is rejected at parse time.
17
- */
18
- export function addFolderScopeOptions(
19
- command: Command,
20
- folderIdDescription = "Folder ID",
21
- ): Command {
22
- return command
23
- .addOption(new Option("--folder-id <id>", folderIdDescription))
24
- .addOption(
25
- new Option(
26
- "--folder-path <path>",
27
- "Folder path (alternative to --folder-id)",
28
- ).conflicts(["folderId", "folderKey"]),
29
- )
30
- .addOption(
31
- new Option(
32
- "--folder-key <key>",
33
- "Folder key (alternative to --folder-id)",
34
- ).conflicts("folderId"),
35
- );
36
- }
37
-
38
- /**
39
- * Resolves the folder id from whichever folder option was passed:
40
- * - `--folder-id`: parse and use it.
41
- * - `--folder-path` / `--folder-key`: look the folder up in Orchestrator.
42
- * - none: return undefined.
43
- *
44
- * Returns null when resolution fails (after emitting an error + exit), so the
45
- * caller can `return` early.
46
- *
47
- * The path/key lookup lives in a separate module imported lazily, so its
48
- * FoldersApi chunk only loads when one of those flags is passed.
49
- */
50
- export async function resolveFolderScope(
51
- options: FolderScopeOptions,
52
- ): Promise<number | undefined | null> {
53
- if (options.folderId !== undefined) {
54
- return parseIntArg(options.folderId, "--folder-id");
55
- }
56
- if (options.folderKey !== undefined) {
57
- const { resolveFolderIdByKey } = await import("./folder-resolver");
58
- return resolveFolderIdByKey(options.folderKey, options.tenant);
59
- }
60
- if (options.folderPath !== undefined) {
61
- const { resolveFolderIdByPath } = await import("./folder-resolver");
62
- return resolveFolderIdByPath(options.folderPath, options.tenant);
63
- }
64
- return undefined;
65
- }
66
-
67
- /**
68
- * Default guidance for the fail-loud path. Commands whose folder options
69
- * differ (e.g. `tasks users`, where the folder id is a positional argument)
70
- * pass their own text so the guidance never names an option the command
71
- * doesn't have.
72
- */
73
- const DEFAULT_FOLDER_REQUIRED_INSTRUCTIONS =
74
- "Pass --folder-id <id>, --folder-path <path>, or --folder-key <key> (run 'uip or folders list' to find one).";
75
-
76
- /**
77
- * Resolves the folder id for a command that needs one:
78
- * - a folder option passed: resolve it (see resolveFolderScope).
79
- * - none passed on an interactive terminal: show a folder picker.
80
- * - none passed on a non-interactive run (coding agents, CI): fail loud.
81
- *
82
- * Returns null when resolution fails (after emitting an error + exit), so the
83
- * caller can `return` early.
84
- *
85
- * The interactive picker lives in a separate module that pulls Node-only deps
86
- * (@inquirer/prompts). It is imported lazily so those deps stay out of the
87
- * browser bundle's static graph and only load on the TTY path, which never
88
- * runs in the browser.
89
- */
90
- export async function resolveFolderId(
91
- options: FolderScopeOptions,
92
- missingFolderInstructions = DEFAULT_FOLDER_REQUIRED_INSTRUCTIONS,
93
- ): Promise<number | null> {
94
- const resolved = await resolveFolderScope(options);
95
- if (resolved !== undefined) {
96
- return resolved;
97
- }
98
-
99
- if (!process.stdout.isTTY) {
100
- OutputFormatter.error({
101
- Result: RESULTS.Failure,
102
- Message: "A folder is required",
103
- Instructions: missingFolderInstructions,
104
- });
105
- processContext.exit(1);
106
- return null;
107
- }
108
-
109
- const { pickFolderInteractively } = await import(
110
- "./folder-picker-interactive"
111
- );
112
- return pickFolderInteractively(options.tenant);
113
- }
@@ -1,152 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("@uipath/orchestrator-sdk", () => ({
4
- resolveFolder: vi.fn(),
5
- }));
6
-
7
- vi.mock("@uipath/common", async (importOriginal) => {
8
- const actual = await importOriginal<typeof import("@uipath/common")>();
9
- return {
10
- ...actual,
11
- OutputFormatter: { success: vi.fn(), error: vi.fn() },
12
- };
13
- });
14
-
15
- import { OutputFormatter } from "@uipath/common";
16
- import { resolveFolder } from "@uipath/orchestrator-sdk";
17
- import { resolveFolderIdByKey, resolveFolderIdByPath } from "./folder-resolver";
18
-
19
- const FOLDER_KEY = "12345678-1234-1234-1234-123456789abc";
20
-
21
- const RESOLVED = {
22
- id: 42,
23
- key: FOLDER_KEY,
24
- path: "Shared/Finance",
25
- displayName: "Finance",
26
- };
27
-
28
- describe("resolveFolderIdByKey", () => {
29
- beforeEach(() => {
30
- vi.resetAllMocks();
31
- process.exitCode = undefined;
32
- });
33
-
34
- it("rejects a value that is not a UUID without resolving", async () => {
35
- expect(await resolveFolderIdByKey("not-a-guid")).toBeNull();
36
- expect(OutputFormatter.error).toHaveBeenCalledWith(
37
- expect.objectContaining({
38
- Message: "Invalid --folder-key: not-a-guid",
39
- }),
40
- );
41
- expect(process.exitCode).toBe(1);
42
- expect(resolveFolder).not.toHaveBeenCalled();
43
- });
44
-
45
- it("resolves the folder id through the SDK's shared resolveFolder", async () => {
46
- vi.mocked(resolveFolder).mockResolvedValue(RESOLVED);
47
-
48
- expect(await resolveFolderIdByKey(FOLDER_KEY)).toBe(42);
49
- expect(resolveFolder).toHaveBeenCalledWith(
50
- { folderKey: FOLDER_KEY },
51
- { tenant: undefined },
52
- );
53
- expect(OutputFormatter.error).not.toHaveBeenCalled();
54
- });
55
-
56
- it("passes the tenant through", async () => {
57
- vi.mocked(resolveFolder).mockResolvedValue(RESOLVED);
58
-
59
- await resolveFolderIdByKey(FOLDER_KEY, "my-tenant");
60
- expect(resolveFolder).toHaveBeenCalledWith(
61
- { folderKey: FOLDER_KEY },
62
- { tenant: "my-tenant" },
63
- );
64
- });
65
-
66
- it("surfaces the SDK's not-found message as the failure", async () => {
67
- vi.mocked(resolveFolder).mockRejectedValue(
68
- new Error(`Folder not found: '${FOLDER_KEY}'.`),
69
- );
70
-
71
- expect(await resolveFolderIdByKey(FOLDER_KEY)).toBeNull();
72
- expect(OutputFormatter.error).toHaveBeenCalledWith(
73
- expect.objectContaining({
74
- Message: `Folder not found: '${FOLDER_KEY}'.`,
75
- }),
76
- );
77
- expect(process.exitCode).toBe(1);
78
- });
79
-
80
- it("reports a wire failure as an error resolving the key", async () => {
81
- const wireError = Object.assign(new Error("HTTP 500"), {
82
- response: { status: 500 },
83
- });
84
- vi.mocked(resolveFolder).mockRejectedValue(wireError);
85
-
86
- expect(await resolveFolderIdByKey(FOLDER_KEY)).toBeNull();
87
- expect(OutputFormatter.error).toHaveBeenCalledWith(
88
- expect.objectContaining({
89
- Message: `Error resolving folder key '${FOLDER_KEY}'`,
90
- }),
91
- );
92
- expect(process.exitCode).toBe(1);
93
- });
94
- });
95
-
96
- describe("resolveFolderIdByPath", () => {
97
- beforeEach(() => {
98
- vi.resetAllMocks();
99
- process.exitCode = undefined;
100
- });
101
-
102
- it("resolves the folder id for a path", async () => {
103
- vi.mocked(resolveFolder).mockResolvedValue(RESOLVED);
104
-
105
- expect(await resolveFolderIdByPath("Shared/Finance")).toBe(42);
106
- expect(resolveFolder).toHaveBeenCalledWith(
107
- { folderPath: "Shared/Finance" },
108
- { tenant: undefined },
109
- );
110
- expect(OutputFormatter.error).not.toHaveBeenCalled();
111
- });
112
-
113
- it("surfaces the SDK's not-found message as the failure", async () => {
114
- vi.mocked(resolveFolder).mockRejectedValue(
115
- new Error("Folder not found: 'Shared/Nope'."),
116
- );
117
-
118
- expect(await resolveFolderIdByPath("Shared/Nope")).toBeNull();
119
- expect(OutputFormatter.error).toHaveBeenCalledWith(
120
- expect.objectContaining({
121
- Message: "Folder not found: 'Shared/Nope'.",
122
- }),
123
- );
124
- expect(process.exitCode).toBe(1);
125
- });
126
-
127
- it("reports a network failure as an error resolving the path", async () => {
128
- const fetchError = new Error("socket hang up");
129
- fetchError.name = "FetchError";
130
- vi.mocked(resolveFolder).mockRejectedValue(fetchError);
131
-
132
- expect(await resolveFolderIdByPath("Shared/Nope")).toBeNull();
133
- expect(OutputFormatter.error).toHaveBeenCalledWith(
134
- expect.objectContaining({
135
- Message: "Error resolving folder path 'Shared/Nope'",
136
- }),
137
- );
138
- expect(process.exitCode).toBe(1);
139
- });
140
-
141
- it("reports not found when the resolver returns nothing", async () => {
142
- vi.mocked(resolveFolder).mockResolvedValue(undefined);
143
-
144
- expect(await resolveFolderIdByPath("Shared/Nope")).toBeNull();
145
- expect(OutputFormatter.error).toHaveBeenCalledWith(
146
- expect.objectContaining({
147
- Message: "Folder not found: 'Shared/Nope'.",
148
- }),
149
- );
150
- expect(process.exitCode).toBe(1);
151
- });
152
- });
@@ -1,113 +0,0 @@
1
- import {
2
- catchError,
3
- extractErrorMessage,
4
- isGuid,
5
- OutputFormatter,
6
- processContext,
7
- RESULTS,
8
- } from "@uipath/common";
9
- import { resolveFolder } from "@uipath/orchestrator-sdk";
10
-
11
- const FOLDER_LOOKUP_HINT =
12
- "Run 'uip or folders list' to see the folders you can access.";
13
-
14
- /**
15
- * Resolves --folder-path / --folder-key to the numeric folder id the tasks
16
- * API needs, through the SDK's shared resolveFolder — the same helper the
17
- * case/flow/maestro/orchestrator tools use. Its OData lookups work with a
18
- * regular user token (FoldersNavigation/GetFolderByKey does not — it 403s).
19
- * Lives in its own module, imported lazily from folder-picker, so the
20
- * FoldersApi chunk only loads when one of those flags is passed.
21
- *
22
- * Both resolvers return null when resolution fails (after emitting an error +
23
- * exit), so the caller can `return` early.
24
- */
25
-
26
- /**
27
- * resolveFolder throws a plain Error for user-input failures ("Folder not
28
- * found: ...") and ResponseError/FetchError for wire failures — the same
29
- * discrimination flow-tool's debug command uses.
30
- */
31
- function isFolderInputError(error: unknown): boolean {
32
- if (!(error instanceof Error)) return false;
33
- if ("response" in error) return false;
34
- if (error.name === "FetchError") return false;
35
- return true;
36
- }
37
-
38
- async function reportResolutionFailure(
39
- scope: string,
40
- error: unknown,
41
- ): Promise<void> {
42
- if (isFolderInputError(error)) {
43
- OutputFormatter.error({
44
- Result: RESULTS.Failure,
45
- Message: (error as Error).message,
46
- Instructions: FOLDER_LOOKUP_HINT,
47
- });
48
- } else {
49
- OutputFormatter.error({
50
- Result: RESULTS.Failure,
51
- Message: `Error resolving ${scope}`,
52
- Instructions: `${await extractErrorMessage(error)} ${FOLDER_LOOKUP_HINT}`,
53
- });
54
- }
55
- processContext.exit(1);
56
- }
57
-
58
- export async function resolveFolderIdByKey(
59
- folderKey: string,
60
- tenant?: string,
61
- ): Promise<number | null> {
62
- if (!isGuid(folderKey)) {
63
- OutputFormatter.error({
64
- Result: RESULTS.Failure,
65
- Message: `Invalid --folder-key: ${folderKey}`,
66
- Instructions:
67
- "Expected a UUID (e.g. '12345678-1234-1234-1234-123456789abc').",
68
- });
69
- processContext.exit(1);
70
- return null;
71
- }
72
-
73
- const [error, folder] = await catchError(
74
- resolveFolder({ folderKey }, { tenant }),
75
- );
76
- if (error) {
77
- await reportResolutionFailure(`folder key '${folderKey}'`, error);
78
- return null;
79
- }
80
- if (folder?.id == null) {
81
- OutputFormatter.error({
82
- Result: RESULTS.Failure,
83
- Message: `Folder not found: '${folderKey}'.`,
84
- Instructions: FOLDER_LOOKUP_HINT,
85
- });
86
- processContext.exit(1);
87
- return null;
88
- }
89
- return folder.id;
90
- }
91
-
92
- export async function resolveFolderIdByPath(
93
- folderPath: string,
94
- tenant?: string,
95
- ): Promise<number | null> {
96
- const [error, folder] = await catchError(
97
- resolveFolder({ folderPath }, { tenant }),
98
- );
99
- if (error) {
100
- await reportResolutionFailure(`folder path '${folderPath}'`, error);
101
- return null;
102
- }
103
- if (folder?.id == null) {
104
- OutputFormatter.error({
105
- Result: RESULTS.Failure,
106
- Message: `Folder not found: '${folderPath}'.`,
107
- Instructions: FOLDER_LOOKUP_HINT,
108
- });
109
- processContext.exit(1);
110
- return null;
111
- }
112
- return folder.id;
113
- }
@@ -1,69 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- vi.mock("@uipath/common", async (importOriginal) => {
4
- const actual = await importOriginal<typeof import("@uipath/common")>();
5
- return {
6
- ...actual,
7
- OutputFormatter: { success: vi.fn(), error: vi.fn() },
8
- };
9
- });
10
-
11
- import { OutputFormatter } from "@uipath/common";
12
- import { parseIntArg, parsePositiveInt, reportInvalidInt } from "./parse";
13
-
14
- describe("parsePositiveInt", () => {
15
- it("accepts a positive integer", () => {
16
- expect(parsePositiveInt("42", "<id>")).toEqual({
17
- valid: true,
18
- value: 42,
19
- });
20
- });
21
-
22
- it.each(["0", "-1", "abc", "1.5", ""])("rejects %s", (value) => {
23
- const result = parsePositiveInt(value, "--folder-id");
24
- expect(result.valid).toBe(false);
25
- if (!result.valid) {
26
- expect(result.message).toBe(`Invalid --folder-id: ${value}`);
27
- expect(result.instructions).toBe("Must be a positive integer.");
28
- }
29
- });
30
- });
31
-
32
- describe("reportInvalidInt", () => {
33
- beforeEach(() => {
34
- vi.resetAllMocks();
35
- process.exitCode = undefined;
36
- });
37
-
38
- it("emits a failure and exits", () => {
39
- reportInvalidInt({ message: "Invalid <id>: x", instructions: "nope" });
40
- expect(OutputFormatter.error).toHaveBeenCalledWith(
41
- expect.objectContaining({
42
- Result: "Failure",
43
- Message: "Invalid <id>: x",
44
- Instructions: "nope",
45
- }),
46
- );
47
- expect(process.exitCode).toBe(1);
48
- });
49
- });
50
-
51
- describe("parseIntArg", () => {
52
- beforeEach(() => {
53
- vi.resetAllMocks();
54
- process.exitCode = undefined;
55
- });
56
-
57
- it("returns the number for a valid value", () => {
58
- expect(parseIntArg("7", "<id>")).toBe(7);
59
- expect(OutputFormatter.error).not.toHaveBeenCalled();
60
- });
61
-
62
- it("returns null and reports for an invalid value", () => {
63
- expect(parseIntArg("nope", "--folder-id")).toBeNull();
64
- expect(OutputFormatter.error).toHaveBeenCalledWith(
65
- expect.objectContaining({ Message: "Invalid --folder-id: nope" }),
66
- );
67
- expect(process.exitCode).toBe(1);
68
- });
69
- });
@@ -1,43 +0,0 @@
1
- import { OutputFormatter, processContext, RESULTS } from "@uipath/common";
2
-
3
- export type ParseResult =
4
- | { valid: true; value: number }
5
- | { valid: false; message: string; instructions: string };
6
-
7
- export function parsePositiveInt(value: string, label: string): ParseResult {
8
- const parsed = Number(value);
9
- if (!Number.isInteger(parsed) || parsed <= 0) {
10
- return {
11
- valid: false,
12
- message: `Invalid ${label}: ${value}`,
13
- instructions: "Must be a positive integer.",
14
- };
15
- }
16
- return { valid: true, value: parsed };
17
- }
18
-
19
- export function reportInvalidInt(error: {
20
- message: string;
21
- instructions: string;
22
- }): void {
23
- OutputFormatter.error({
24
- Result: RESULTS.Failure,
25
- Message: error.message,
26
- Instructions: error.instructions,
27
- });
28
- processContext.exit(1);
29
- }
30
-
31
- /**
32
- * Parses a positive-integer CLI argument. On an invalid value, emits the
33
- * standard error, exits with code 1, and returns null so the caller can
34
- * `return` early. Valid values are always > 0, so a null check is safe.
35
- */
36
- export function parseIntArg(value: string, label: string): number | null {
37
- const parsed = parsePositiveInt(value, label);
38
- if (!parsed.valid) {
39
- reportInvalidInt(parsed);
40
- return null;
41
- }
42
- return parsed.value;
43
- }