zinkee 0.1.11 → 0.1.15
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/AGENTS.md +42 -0
- package/README.md +7 -0
- package/dist/index.js +268 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/records.ts +7 -0
- package/src/cli-examples.ts +18 -36
- package/src/cli-runner.ts +10 -2
- package/src/command-registry.ts +7 -0
- package/src/commands/automations.test.ts +5 -5
- package/src/commands/displays.test.ts +2 -2
- package/src/commands/records.test.ts +58 -0
- package/src/commands/records.ts +19 -0
- package/src/index.test.ts +52 -0
- package/src/utils/argv-rewrite.test.ts +149 -0
- package/src/utils/argv-rewrite.ts +269 -0
- package/src/utils/examples.ts +32 -21
- package/src/utils/schema-fields.ts +11 -9
package/package.json
CHANGED
package/src/api/records.ts
CHANGED
|
@@ -133,6 +133,13 @@ export class RecordsApi {
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
clearRecords(schema: NormalizedUuidOrSlug): Promise<Record<string, unknown>> {
|
|
137
|
+
return this.client.requestJson<Record<string, unknown>>(
|
|
138
|
+
`/api/v2/schemas/${selectorValue(schema)}/records:clear`,
|
|
139
|
+
{ method: "DELETE" },
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
136
143
|
private normalizeQueryInput(query: RecordQueryInput): Record<string, unknown> {
|
|
137
144
|
const where = query.where?.map((expression) => ({
|
|
138
145
|
field: expression.field,
|
package/src/cli-examples.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { commandRegistry } from "./command-registry.js";
|
|
2
|
+
import {
|
|
3
|
+
collectOperandTokens,
|
|
4
|
+
createDefaultOptionMetadata,
|
|
5
|
+
findCommandPath,
|
|
6
|
+
type OptionMetadata,
|
|
7
|
+
} from "./utils/argv-rewrite.js";
|
|
2
8
|
import { CliError } from "./utils/errors.js";
|
|
3
9
|
import { getResolvedCommandExamples } from "./utils/examples.js";
|
|
4
10
|
import { buildJsonSuccess } from "./utils/output.js";
|
|
@@ -24,34 +30,6 @@ function getStdout(stdout?: CommandIo): CommandIo {
|
|
|
24
30
|
return stdout ?? process.stdout;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
function resolveExampleCommand(tokens: readonly string[]): {
|
|
28
|
-
commandName: string;
|
|
29
|
-
remainingTokens: readonly string[];
|
|
30
|
-
} {
|
|
31
|
-
const commands = Object.keys(commandRegistry).sort(
|
|
32
|
-
(left, right) => right.split(" ").length - left.split(" ").length,
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
for (const commandName of commands) {
|
|
36
|
-
const commandTokens = commandName.split(" ");
|
|
37
|
-
const matches = commandTokens.every((token, index) => tokens[index] === token);
|
|
38
|
-
|
|
39
|
-
if (matches) {
|
|
40
|
-
return {
|
|
41
|
-
commandName,
|
|
42
|
-
remainingTokens: tokens.slice(commandTokens.length),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const commandTokens = tokens.filter((token) => !token.startsWith("-"));
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
commandName: commandTokens.join(" "),
|
|
51
|
-
remainingTokens: [],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
33
|
function extractExampleFilters(tokens: readonly string[]): { type?: string } {
|
|
56
34
|
const filters: { type?: string } = {};
|
|
57
35
|
|
|
@@ -110,22 +88,26 @@ function extractExampleOverrides(tokens: readonly string[]): {
|
|
|
110
88
|
return overrides;
|
|
111
89
|
}
|
|
112
90
|
|
|
113
|
-
export function extractExampleRequest(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
91
|
+
export function extractExampleRequest(
|
|
92
|
+
argv: readonly string[],
|
|
93
|
+
optionMeta: OptionMetadata = createDefaultOptionMetadata(),
|
|
94
|
+
): ExampleRequest | null {
|
|
95
|
+
if (!argv.includes("--example")) {
|
|
117
96
|
return null;
|
|
118
97
|
}
|
|
119
98
|
|
|
120
|
-
const
|
|
121
|
-
const
|
|
99
|
+
const userArgs = argv.slice(2);
|
|
100
|
+
const operands = collectOperandTokens(userArgs, optionMeta, { skipFlag: "--example" });
|
|
101
|
+
const cmdPath = findCommandPath(operands, commandRegistry);
|
|
122
102
|
|
|
123
|
-
if (
|
|
103
|
+
if (!cmdPath) {
|
|
124
104
|
throw new CliError("invalid_cli_usage", 'Provide a command after "--example".');
|
|
125
105
|
}
|
|
126
106
|
|
|
107
|
+
const remainingTokens = userArgs.slice(cmdPath.lastArgvIndex + 1);
|
|
108
|
+
|
|
127
109
|
return {
|
|
128
|
-
commandName,
|
|
110
|
+
commandName: cmdPath.commandName,
|
|
129
111
|
filters: extractExampleFilters(remainingTokens),
|
|
130
112
|
overrides: extractExampleOverrides(argv),
|
|
131
113
|
json: argv.includes("--json"),
|
package/src/cli-runner.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
|
|
6
6
|
import { extractExampleRequest, showExamples } from "./cli-examples.js";
|
|
7
|
+
import { commandRegistry } from "./command-registry.js";
|
|
7
8
|
import {
|
|
8
9
|
extractCompletionFlag,
|
|
9
10
|
handleCompletionFlag,
|
|
@@ -11,6 +12,10 @@ import {
|
|
|
11
12
|
runCompletionRequest,
|
|
12
13
|
} from "./completion/runtime.js";
|
|
13
14
|
import { buildProgram } from "./program.js";
|
|
15
|
+
import {
|
|
16
|
+
buildOptionMetadata,
|
|
17
|
+
hoistGlobalOptions,
|
|
18
|
+
} from "./utils/argv-rewrite.js";
|
|
14
19
|
import {
|
|
15
20
|
formatCliErrorJson,
|
|
16
21
|
formatCliErrorText,
|
|
@@ -55,7 +60,9 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
55
60
|
return;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
const
|
|
63
|
+
const program = buildProgram();
|
|
64
|
+
const optionMeta = buildOptionMetadata(program);
|
|
65
|
+
const exampleRequest = extractExampleRequest(argv, optionMeta);
|
|
59
66
|
|
|
60
67
|
if (exampleRequest) {
|
|
61
68
|
await showExamples(
|
|
@@ -71,7 +78,8 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
71
78
|
throw new ExampleDisplayed();
|
|
72
79
|
}
|
|
73
80
|
|
|
74
|
-
|
|
81
|
+
const rewrittenArgv = hoistGlobalOptions(argv, optionMeta, commandRegistry);
|
|
82
|
+
await program.parseAsync(rewrittenArgv);
|
|
75
83
|
} catch (error) {
|
|
76
84
|
if (error instanceof ExampleDisplayed) {
|
|
77
85
|
process.exitCode = 0;
|
package/src/command-registry.ts
CHANGED
|
@@ -665,6 +665,13 @@ export const commandRegistry = {
|
|
|
665
665
|
supportsRaw: false,
|
|
666
666
|
output: writeOutput,
|
|
667
667
|
},
|
|
668
|
+
"records clear": {
|
|
669
|
+
command: "records clear",
|
|
670
|
+
accessLevel: "destructive",
|
|
671
|
+
supportsExample: true,
|
|
672
|
+
supportsRaw: false,
|
|
673
|
+
output: writeOutput,
|
|
674
|
+
},
|
|
668
675
|
"comments list": {
|
|
669
676
|
command: "comments list",
|
|
670
677
|
accessLevel: "read",
|
|
@@ -335,7 +335,7 @@ describe("automations commands", () => {
|
|
|
335
335
|
"--target-schema",
|
|
336
336
|
"schema-1",
|
|
337
337
|
"--map-json",
|
|
338
|
-
'owner={"rawValue":null,"source":{"type":"TRIGGER","id":"
|
|
338
|
+
'owner={"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}',
|
|
339
339
|
]);
|
|
340
340
|
|
|
341
341
|
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
@@ -348,7 +348,7 @@ describe("automations commands", () => {
|
|
|
348
348
|
rawValue: null,
|
|
349
349
|
source: {
|
|
350
350
|
type: "TRIGGER",
|
|
351
|
-
id: "
|
|
351
|
+
id: "f1e1d000-0000-0000-0000-000000000005",
|
|
352
352
|
},
|
|
353
353
|
},
|
|
354
354
|
},
|
|
@@ -378,7 +378,7 @@ describe("automations commands", () => {
|
|
|
378
378
|
"--target-schema",
|
|
379
379
|
"schema-1",
|
|
380
380
|
"--condition-json",
|
|
381
|
-
'{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"
|
|
381
|
+
'{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}',
|
|
382
382
|
]);
|
|
383
383
|
|
|
384
384
|
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
@@ -395,7 +395,7 @@ describe("automations commands", () => {
|
|
|
395
395
|
rawValue: null,
|
|
396
396
|
source: {
|
|
397
397
|
type: "TRIGGER",
|
|
398
|
-
id: "
|
|
398
|
+
id: "f1e1d000-0000-0000-0000-000000000005",
|
|
399
399
|
},
|
|
400
400
|
},
|
|
401
401
|
],
|
|
@@ -471,7 +471,7 @@ describe("automations commands", () => {
|
|
|
471
471
|
"--trigger-field",
|
|
472
472
|
"field-1",
|
|
473
473
|
"--condition-json",
|
|
474
|
-
'{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"
|
|
474
|
+
'{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}',
|
|
475
475
|
]),
|
|
476
476
|
).rejects.toThrowError('Expected trigger condition 1 value 1 to use only literal "rawValue" values.');
|
|
477
477
|
|
|
@@ -151,7 +151,7 @@ describe("displays commands", () => {
|
|
|
151
151
|
"--name",
|
|
152
152
|
"Revenue",
|
|
153
153
|
"--chart-type",
|
|
154
|
-
"
|
|
154
|
+
"bars",
|
|
155
155
|
]);
|
|
156
156
|
|
|
157
157
|
const init = fetchMock.mock.calls[0]?.[1];
|
|
@@ -159,7 +159,7 @@ describe("displays commands", () => {
|
|
|
159
159
|
expect(JSON.parse(String(init?.body))).toMatchObject({
|
|
160
160
|
type: "CHART",
|
|
161
161
|
presentation: { name: "Revenue" },
|
|
162
|
-
chart: { chartType: "
|
|
162
|
+
chart: { chartType: "bars" },
|
|
163
163
|
});
|
|
164
164
|
expect(JSON.parse(output.read())).toEqual({
|
|
165
165
|
data: { displayId: "display-1", widgetId: "widget-1" },
|
|
@@ -364,4 +364,62 @@ describe("records commands", () => {
|
|
|
364
364
|
|
|
365
365
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
366
366
|
});
|
|
367
|
+
|
|
368
|
+
it("clears all records in a schema", async () => {
|
|
369
|
+
const configPath = createApiConfig();
|
|
370
|
+
const output = createStdoutBuffer();
|
|
371
|
+
const fetchMock = createFetchMock();
|
|
372
|
+
mockSchemaLookup(fetchMock);
|
|
373
|
+
fetchMock.mockResolvedValueOnce(
|
|
374
|
+
jsonResponse({
|
|
375
|
+
schemaId: "schema-1",
|
|
376
|
+
cleared: true,
|
|
377
|
+
}),
|
|
378
|
+
);
|
|
379
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
380
|
+
|
|
381
|
+
await program.parseAsync([
|
|
382
|
+
"node",
|
|
383
|
+
"zinkee",
|
|
384
|
+
"--json",
|
|
385
|
+
"records",
|
|
386
|
+
"clear",
|
|
387
|
+
"contacts",
|
|
388
|
+
]);
|
|
389
|
+
|
|
390
|
+
const init = fetchMock.mock.calls[1]?.[1];
|
|
391
|
+
|
|
392
|
+
expect(fetchMock.mock.calls[1]?.[0]).toBe(
|
|
393
|
+
"https://api.zinkee.test/api/v2/schemas/schema-1/records:clear",
|
|
394
|
+
);
|
|
395
|
+
expect(init?.method).toBe("DELETE");
|
|
396
|
+
expect(init?.body).toBeUndefined();
|
|
397
|
+
|
|
398
|
+
const payload = JSON.parse(output.read());
|
|
399
|
+
|
|
400
|
+
expect(payload.data).toEqual({
|
|
401
|
+
schemaId: "schema-1",
|
|
402
|
+
cleared: true,
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it("blocks records clear in read-only mode", async () => {
|
|
407
|
+
const configPath = createApiConfig();
|
|
408
|
+
const output = createStdoutBuffer();
|
|
409
|
+
const fetchMock = createFetchMock();
|
|
410
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
411
|
+
|
|
412
|
+
await expect(
|
|
413
|
+
program.parseAsync([
|
|
414
|
+
"node",
|
|
415
|
+
"zinkee",
|
|
416
|
+
"--read-only",
|
|
417
|
+
"records",
|
|
418
|
+
"clear",
|
|
419
|
+
"contacts",
|
|
420
|
+
]),
|
|
421
|
+
).rejects.toThrowError('Command "records clear" is not allowed in read-only mode.');
|
|
422
|
+
|
|
423
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
424
|
+
});
|
|
367
425
|
});
|
package/src/commands/records.ts
CHANGED
|
@@ -723,4 +723,23 @@ export function registerRecordsCommands(
|
|
|
723
723
|
|
|
724
724
|
writeLine(stdout, `Deleted record "${record.value}" from schema "${schema.value}".`);
|
|
725
725
|
});
|
|
726
|
+
|
|
727
|
+
records
|
|
728
|
+
.command("clear")
|
|
729
|
+
.description("Delete all records from a schema's table")
|
|
730
|
+
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
731
|
+
.action(async function action(this: Command, schema: NormalizedUuidOrSlug) {
|
|
732
|
+
const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
|
|
733
|
+
ensureWritable("records clear", readOnly, "destructive");
|
|
734
|
+
const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
|
|
735
|
+
|
|
736
|
+
const data = await api.clearRecords(resolvedSchema);
|
|
737
|
+
|
|
738
|
+
if (json) {
|
|
739
|
+
emitJson(stdout, "records clear", data, filePath);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
writeLine(stdout, `Cleared all records from schema "${schema.value}".`);
|
|
744
|
+
});
|
|
726
745
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -69,6 +69,58 @@ describe("project bootstrap", () => {
|
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it("extracts example requests when --example appears at the end of argv", () => {
|
|
73
|
+
expect(
|
|
74
|
+
extractExampleRequest(["node", "zinkee", "schemas", "list", "--example"]),
|
|
75
|
+
).toEqual({
|
|
76
|
+
commandName: "schemas list",
|
|
77
|
+
filters: {},
|
|
78
|
+
overrides: {},
|
|
79
|
+
json: false,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("extracts example filters when --example appears after the filter flags", () => {
|
|
84
|
+
expect(
|
|
85
|
+
extractExampleRequest([
|
|
86
|
+
"node",
|
|
87
|
+
"zinkee",
|
|
88
|
+
"schemas",
|
|
89
|
+
"fields",
|
|
90
|
+
"create",
|
|
91
|
+
"contacts",
|
|
92
|
+
"--type",
|
|
93
|
+
"option",
|
|
94
|
+
"--example",
|
|
95
|
+
]),
|
|
96
|
+
).toEqual({
|
|
97
|
+
commandName: "schemas fields create",
|
|
98
|
+
filters: { type: "option" },
|
|
99
|
+
overrides: {},
|
|
100
|
+
json: false,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("extracts example overrides regardless of --example position", () => {
|
|
105
|
+
expect(
|
|
106
|
+
extractExampleRequest([
|
|
107
|
+
"node",
|
|
108
|
+
"zinkee",
|
|
109
|
+
"schemas",
|
|
110
|
+
"list",
|
|
111
|
+
"--profile",
|
|
112
|
+
"staging",
|
|
113
|
+
"--example",
|
|
114
|
+
"--json",
|
|
115
|
+
]),
|
|
116
|
+
).toEqual({
|
|
117
|
+
commandName: "schemas list",
|
|
118
|
+
filters: {},
|
|
119
|
+
overrides: { profile: "staging" },
|
|
120
|
+
json: true,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
72
124
|
it("shows field examples filtered by type", async () => {
|
|
73
125
|
let output = "";
|
|
74
126
|
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { commandRegistry } from "../command-registry.js";
|
|
4
|
+
import { buildProgram } from "../program.js";
|
|
5
|
+
import {
|
|
6
|
+
buildOptionMetadata,
|
|
7
|
+
hoistGlobalOptions,
|
|
8
|
+
} from "./argv-rewrite.js";
|
|
9
|
+
|
|
10
|
+
function rewrite(userArgs: readonly string[]): string[] {
|
|
11
|
+
const optionMeta = buildOptionMetadata(buildProgram());
|
|
12
|
+
return hoistGlobalOptions(["node", "zinkee", ...userArgs], optionMeta, commandRegistry);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("hoistGlobalOptions", () => {
|
|
16
|
+
it("moves boolean global flags before the subcommand path", () => {
|
|
17
|
+
expect(rewrite(["schemas", "list", "--json"])).toEqual([
|
|
18
|
+
"node",
|
|
19
|
+
"zinkee",
|
|
20
|
+
"--json",
|
|
21
|
+
"schemas",
|
|
22
|
+
"list",
|
|
23
|
+
]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("moves global value flags together with their value", () => {
|
|
27
|
+
expect(rewrite(["schemas", "list", "--profile", "staging"])).toEqual([
|
|
28
|
+
"node",
|
|
29
|
+
"zinkee",
|
|
30
|
+
"--profile",
|
|
31
|
+
"staging",
|
|
32
|
+
"schemas",
|
|
33
|
+
"list",
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("moves multiple globals preserving their relative order", () => {
|
|
38
|
+
expect(
|
|
39
|
+
rewrite(["schemas", "list", "--json", "--profile", "staging", "--read-only"]),
|
|
40
|
+
).toEqual([
|
|
41
|
+
"node",
|
|
42
|
+
"zinkee",
|
|
43
|
+
"--json",
|
|
44
|
+
"--profile",
|
|
45
|
+
"staging",
|
|
46
|
+
"--read-only",
|
|
47
|
+
"schemas",
|
|
48
|
+
"list",
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("hoists globals that are interleaved between subcommand path tokens", () => {
|
|
53
|
+
expect(rewrite(["schemas", "--json", "fields", "create"])).toEqual([
|
|
54
|
+
"node",
|
|
55
|
+
"zinkee",
|
|
56
|
+
"--json",
|
|
57
|
+
"schemas",
|
|
58
|
+
"fields",
|
|
59
|
+
"create",
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("does not touch global flags that already appear before the subcommand path", () => {
|
|
64
|
+
expect(rewrite(["--json", "schemas", "list"])).toEqual([
|
|
65
|
+
"node",
|
|
66
|
+
"zinkee",
|
|
67
|
+
"--json",
|
|
68
|
+
"schemas",
|
|
69
|
+
"list",
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("preserves --api-key when it appears after profiles add (subcommand-local option)", () => {
|
|
74
|
+
expect(
|
|
75
|
+
rewrite(["profiles", "add", "myProfile", "--api-key", "abc", "--base-url", "https://x"]),
|
|
76
|
+
).toEqual([
|
|
77
|
+
"node",
|
|
78
|
+
"zinkee",
|
|
79
|
+
"profiles",
|
|
80
|
+
"add",
|
|
81
|
+
"myProfile",
|
|
82
|
+
"--api-key",
|
|
83
|
+
"abc",
|
|
84
|
+
"--base-url",
|
|
85
|
+
"https://x",
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("preserves --api-key/--base-url for profiles update while still hoisting other globals", () => {
|
|
90
|
+
expect(
|
|
91
|
+
rewrite(["profiles", "update", "myProfile", "--api-key", "abc", "--json"]),
|
|
92
|
+
).toEqual([
|
|
93
|
+
"node",
|
|
94
|
+
"zinkee",
|
|
95
|
+
"--json",
|
|
96
|
+
"profiles",
|
|
97
|
+
"update",
|
|
98
|
+
"myProfile",
|
|
99
|
+
"--api-key",
|
|
100
|
+
"abc",
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("supports the --foo=bar syntax for global value flags", () => {
|
|
105
|
+
expect(rewrite(["schemas", "list", "--profile=staging"])).toEqual([
|
|
106
|
+
"node",
|
|
107
|
+
"zinkee",
|
|
108
|
+
"--profile=staging",
|
|
109
|
+
"schemas",
|
|
110
|
+
"list",
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("returns argv unchanged when no subcommand path is found", () => {
|
|
115
|
+
expect(rewrite(["--json"])).toEqual(["node", "zinkee", "--json"]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("respects -- and stops hoisting after it", () => {
|
|
119
|
+
expect(
|
|
120
|
+
rewrite(["schemas", "list", "--", "--json", "trailing"]),
|
|
121
|
+
).toEqual([
|
|
122
|
+
"node",
|
|
123
|
+
"zinkee",
|
|
124
|
+
"schemas",
|
|
125
|
+
"list",
|
|
126
|
+
"--",
|
|
127
|
+
"--json",
|
|
128
|
+
"trailing",
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("buildOptionMetadata", () => {
|
|
134
|
+
it("registers local options for leaf subcommands", () => {
|
|
135
|
+
const optionMeta = buildOptionMetadata(buildProgram());
|
|
136
|
+
const profilesAdd = optionMeta.localOptionsByCommand.get("profiles add");
|
|
137
|
+
|
|
138
|
+
expect(profilesAdd?.has("--api-key")).toBe(true);
|
|
139
|
+
expect(profilesAdd?.has("--base-url")).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("recognises global value vs boolean flags", () => {
|
|
143
|
+
const optionMeta = buildOptionMetadata(buildProgram());
|
|
144
|
+
|
|
145
|
+
expect(optionMeta.valueFlags.has("--profile")).toBe(true);
|
|
146
|
+
expect(optionMeta.booleanFlags.has("--json")).toBe(true);
|
|
147
|
+
expect(optionMeta.booleanFlags.has("--read-only")).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
});
|