@uipath/data-fabric-tool 1.1.0 → 1.196.0
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/tool.js +6246 -5365
- package/package.json +16 -24
- package/src/commands/choice-sets.spec.ts +571 -83
- package/src/commands/choice-sets.ts +561 -147
- package/src/commands/entities.spec.ts +109 -159
- package/src/commands/entities.ts +181 -372
- package/src/commands/files.spec.ts +62 -34
- package/src/commands/files.ts +51 -88
- package/src/commands/records.spec.ts +188 -206
- package/src/commands/records.ts +133 -330
- package/src/tool.ts +5 -1
- package/src/utils/input.spec.ts +127 -0
- package/src/utils/input.ts +30 -1
- package/src/utils/output.spec.ts +91 -0
- package/src/utils/output.ts +69 -0
- package/src/utils/sdk-client.spec.ts +59 -0
- package/src/utils/sdk-client.ts +23 -0
- package/src/utils/pagination.ts +0 -10
package/src/tool.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import pkg from "../package.json" with { type: "json" };
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
registerChoiceSetsCommand,
|
|
5
|
+
registerChoiceSetValuesCommand,
|
|
6
|
+
} from "./commands/choice-sets";
|
|
4
7
|
import { registerEntitiesCommand } from "./commands/entities";
|
|
5
8
|
import { registerFilesCommand } from "./commands/files";
|
|
6
9
|
import { registerRecordsCommand } from "./commands/records";
|
|
@@ -18,4 +21,5 @@ export const registerCommands = async (program: Command): Promise<void> => {
|
|
|
18
21
|
registerRecordsCommand(program);
|
|
19
22
|
registerFilesCommand(program);
|
|
20
23
|
registerChoiceSetsCommand(program);
|
|
24
|
+
registerChoiceSetValuesCommand(program);
|
|
21
25
|
};
|
|
@@ -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
|
+
});
|
package/src/utils/input.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
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 { fail, requireDestructiveConfirmation } from "./output";
|
|
13
|
+
|
|
14
|
+
describe("fail", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.resetAllMocks();
|
|
17
|
+
process.exitCode = undefined;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("emits a failure result and sets a non-zero exit code", () => {
|
|
21
|
+
fail("Something broke", "Try again.");
|
|
22
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
23
|
+
expect.objectContaining({
|
|
24
|
+
Result: "Failure",
|
|
25
|
+
Message: "Something broke",
|
|
26
|
+
Instructions: "Try again.",
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
expect(process.exitCode).toBe(1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("requireDestructiveConfirmation", () => {
|
|
34
|
+
const operation = "delete entity 'abc'";
|
|
35
|
+
const reasonInstruction = "Pass --reason to explain.";
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.resetAllMocks();
|
|
39
|
+
process.exitCode = undefined;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("returns the trimmed reason when --yes and --reason are present", () => {
|
|
43
|
+
const result = requireDestructiveConfirmation(
|
|
44
|
+
{ yes: true, reason: " cleanup " },
|
|
45
|
+
operation,
|
|
46
|
+
reasonInstruction,
|
|
47
|
+
);
|
|
48
|
+
expect(result).toBe("cleanup");
|
|
49
|
+
expect(OutputFormatter.error).not.toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
|
|
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.
|
|
67
|
+
const result = requireDestructiveConfirmation(
|
|
68
|
+
{ reason: "cleanup" },
|
|
69
|
+
operation,
|
|
70
|
+
reasonInstruction,
|
|
71
|
+
);
|
|
72
|
+
expect(result).toBeNull();
|
|
73
|
+
expect(process.exitCode).toBe(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("fails and returns null when --reason is missing or blank", () => {
|
|
77
|
+
const result = requireDestructiveConfirmation(
|
|
78
|
+
{ yes: true, reason: " " },
|
|
79
|
+
operation,
|
|
80
|
+
reasonInstruction,
|
|
81
|
+
);
|
|
82
|
+
expect(result).toBeNull();
|
|
83
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
84
|
+
expect.objectContaining({
|
|
85
|
+
Message: "Reason required for destructive operation",
|
|
86
|
+
Instructions: reasonInstruction,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
expect(process.exitCode).toBe(1);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OutputFormatter,
|
|
3
|
+
processContext,
|
|
4
|
+
RESULTS,
|
|
5
|
+
requireConfirmation,
|
|
6
|
+
warnDeprecatedOptionAlias,
|
|
7
|
+
} from "@uipath/common";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Emit a standard failure result and set a non-zero exit code.
|
|
11
|
+
*
|
|
12
|
+
* Returns `void` so a handler can halt in one line: `return fail(msg, instr);`
|
|
13
|
+
* (`processContext.exit` only sets `process.exitCode`, so the caller must still
|
|
14
|
+
* return to stop execution).
|
|
15
|
+
*/
|
|
16
|
+
export function fail(message: string, instructions: string): void {
|
|
17
|
+
OutputFormatter.error({
|
|
18
|
+
Result: RESULTS.Failure,
|
|
19
|
+
Message: message,
|
|
20
|
+
Instructions: instructions,
|
|
21
|
+
});
|
|
22
|
+
processContext.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
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.
|
|
29
|
+
confirm?: boolean;
|
|
30
|
+
reason?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
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.
|
|
38
|
+
*
|
|
39
|
+
* @param operation - short description of what will happen, e.g.
|
|
40
|
+
* `"delete entity 'abc'"`, surfaced in the confirmation error.
|
|
41
|
+
* @param reasonInstruction - Instruction text shown when `--reason` is missing,
|
|
42
|
+
* e.g. `'Pass --reason "<text>" to record why the choice set is being deleted.'`
|
|
43
|
+
*/
|
|
44
|
+
export function requireDestructiveConfirmation(
|
|
45
|
+
options: DestructiveOptions,
|
|
46
|
+
operation: string,
|
|
47
|
+
reasonInstruction: string,
|
|
48
|
+
): string | null {
|
|
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
|
+
) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const reason = options.reason?.trim();
|
|
63
|
+
if (reason === undefined || reason === "") {
|
|
64
|
+
fail("Reason required for destructive operation", reasonInstruction);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return reason;
|
|
69
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
vi.mock("@uipath/auth", () => ({
|
|
4
|
+
getLoginStatusAsync: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
vi.mock("@uipath/uipath-typescript", () => ({
|
|
8
|
+
UiPath: class {},
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock("@uipath/common", async (importOriginal) => {
|
|
12
|
+
const actual = await importOriginal<typeof import("@uipath/common")>();
|
|
13
|
+
return {
|
|
14
|
+
...actual,
|
|
15
|
+
OutputFormatter: { success: vi.fn(), error: vi.fn() },
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
import { getLoginStatusAsync } from "@uipath/auth";
|
|
20
|
+
import { OutputFormatter } from "@uipath/common";
|
|
21
|
+
import { connectOrFail } from "./sdk-client";
|
|
22
|
+
|
|
23
|
+
describe("connectOrFail", () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.resetAllMocks();
|
|
26
|
+
process.exitCode = undefined;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("returns a client when logged in", async () => {
|
|
30
|
+
vi.mocked(getLoginStatusAsync).mockResolvedValue({
|
|
31
|
+
loginStatus: "Logged in",
|
|
32
|
+
accessToken: "token",
|
|
33
|
+
baseUrl: "https://cloud.uipath.com",
|
|
34
|
+
organizationName: "org",
|
|
35
|
+
tenantName: "tenant",
|
|
36
|
+
} as never);
|
|
37
|
+
|
|
38
|
+
const sdk = await connectOrFail();
|
|
39
|
+
expect(sdk).toBeDefined();
|
|
40
|
+
expect(OutputFormatter.error).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("emits a connection error and returns undefined when not logged in", async () => {
|
|
44
|
+
vi.mocked(getLoginStatusAsync).mockResolvedValue({
|
|
45
|
+
loginStatus: "LoggedOut",
|
|
46
|
+
hint: "Run 'uip login' first.",
|
|
47
|
+
} as never);
|
|
48
|
+
|
|
49
|
+
const sdk = await connectOrFail();
|
|
50
|
+
expect(sdk).toBeUndefined();
|
|
51
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
52
|
+
expect.objectContaining({
|
|
53
|
+
Result: "Failure",
|
|
54
|
+
Message: "Error connecting to Data Fabric",
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
expect(process.exitCode).toBe(1);
|
|
58
|
+
});
|
|
59
|
+
});
|
package/src/utils/sdk-client.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getLoginStatusAsync } from "@uipath/auth";
|
|
2
|
+
import { catchError, extractErrorMessage } from "@uipath/common";
|
|
2
3
|
import { UiPath } from "@uipath/uipath-typescript";
|
|
4
|
+
import { fail } from "./output";
|
|
3
5
|
|
|
4
6
|
export const createDataFabricClient = async (
|
|
5
7
|
tenantOverride?: string,
|
|
@@ -21,3 +23,24 @@ export const createDataFabricClient = async (
|
|
|
21
23
|
tenantName: tenantOverride ?? status.tenantName,
|
|
22
24
|
});
|
|
23
25
|
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a Data Fabric client, or emit a standard connection error and return
|
|
29
|
+
* `undefined`. Lets a handler open with: `const sdk = await connectOrFail(t);
|
|
30
|
+
* if (!sdk) return;`
|
|
31
|
+
*/
|
|
32
|
+
export const connectOrFail = async (
|
|
33
|
+
tenantOverride?: string,
|
|
34
|
+
): Promise<UiPath | undefined> => {
|
|
35
|
+
const [clientError, sdk] = await catchError(
|
|
36
|
+
createDataFabricClient(tenantOverride),
|
|
37
|
+
);
|
|
38
|
+
if (clientError) {
|
|
39
|
+
fail(
|
|
40
|
+
"Error connecting to Data Fabric",
|
|
41
|
+
await extractErrorMessage(clientError),
|
|
42
|
+
);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return sdk;
|
|
46
|
+
};
|
package/src/utils/pagination.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** Normalises an SDK pagination cursor to a plain string for CLI output.
|
|
2
|
-
* The SDK returns `PaginationCursor = { value: string }` but we want
|
|
3
|
-
* to expose just the string so the user can pass it directly to --cursor. */
|
|
4
|
-
export function extractCursorValue(cursor: unknown): string | undefined {
|
|
5
|
-
if (cursor === undefined || cursor === null) return undefined;
|
|
6
|
-
if (typeof cursor === "string") return cursor;
|
|
7
|
-
const c = cursor as Record<string, unknown>;
|
|
8
|
-
if (typeof c.value === "string") return c.value;
|
|
9
|
-
return undefined;
|
|
10
|
-
}
|