zinkee 0.1.35 → 0.1.37
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/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli-examples.ts +22 -1
- package/src/index.test.ts +26 -0
- package/src/utils/examples.test.ts +16 -0
- package/src/utils/examples.ts +31 -4
package/package.json
CHANGED
package/src/cli-examples.ts
CHANGED
|
@@ -56,7 +56,28 @@ export function extractExampleRequest(
|
|
|
56
56
|
const cmdPath = findCommandPath(operands, commandRegistry);
|
|
57
57
|
|
|
58
58
|
if (!cmdPath) {
|
|
59
|
-
|
|
59
|
+
const typed = operands.map((operand) => operand.token).join(" ");
|
|
60
|
+
|
|
61
|
+
if (typed.length === 0) {
|
|
62
|
+
throw new CliError("invalid_cli_usage", 'Provide a command after "--example".');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The token(s) didn't resolve to a runnable command. If they form the prefix
|
|
66
|
+
// of one or more registered commands, the user named a command group (e.g.
|
|
67
|
+
// "schemas") — list its subcommands instead of the generic error.
|
|
68
|
+
const subcommands = Object.keys(commandRegistry).filter((name) =>
|
|
69
|
+
name.startsWith(`${typed} `),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (subcommands.length > 0) {
|
|
73
|
+
const list = subcommands.map((name) => ` zinkee --example ${name}`).join("\n");
|
|
74
|
+
throw new CliError(
|
|
75
|
+
"invalid_cli_usage",
|
|
76
|
+
`"${typed}" is a command group, not a runnable command. Available subcommands:\n${list}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw new CliError("invalid_cli_usage", `Unknown command "${typed}".`);
|
|
60
81
|
}
|
|
61
82
|
|
|
62
83
|
const remainingTokens = userArgs.slice(cmdPath.lastArgvIndex + 1);
|
package/src/index.test.ts
CHANGED
|
@@ -114,6 +114,32 @@ describe("project bootstrap", () => {
|
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
it("rejects a command group by listing all its subcommands", () => {
|
|
118
|
+
let message = "";
|
|
119
|
+
try {
|
|
120
|
+
extractExampleRequest(["node", "zinkee", "--example", "schemas"]);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
message = (error as Error).message;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
expect(message).toContain('"schemas" is a command group');
|
|
126
|
+
expect(message).toContain("zinkee --example schemas list");
|
|
127
|
+
expect(message).toContain("zinkee --example schemas get");
|
|
128
|
+
expect(message).toContain("zinkee --example schemas fields create");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("reports an unknown command after --example", () => {
|
|
132
|
+
expect(() =>
|
|
133
|
+
extractExampleRequest(["node", "zinkee", "--example", "frobnicate"]),
|
|
134
|
+
).toThrowError('Unknown command "frobnicate".');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("still asks for a command when --example has no operand", () => {
|
|
138
|
+
expect(() => extractExampleRequest(["node", "zinkee", "--example"])).toThrowError(
|
|
139
|
+
'Provide a command after "--example".',
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
117
143
|
it("shows field examples filtered by type", async () => {
|
|
118
144
|
let output = "";
|
|
119
145
|
|
|
@@ -334,6 +334,22 @@ describe("getCommandExamples", () => {
|
|
|
334
334
|
expect(getCommandExamples("config validate")[0]?.response).toContain('"valid": true');
|
|
335
335
|
});
|
|
336
336
|
|
|
337
|
+
it("wraps example responses in the { data, meta } JSON envelope", () => {
|
|
338
|
+
const listExample = getCommandExamples("schemas list")[0];
|
|
339
|
+
const parsed = JSON.parse(listExample?.response ?? "{}");
|
|
340
|
+
|
|
341
|
+
expect(parsed).toHaveProperty("data");
|
|
342
|
+
expect(parsed).toHaveProperty("meta");
|
|
343
|
+
expect(parsed.meta).toMatchObject({ command: "schemas list" });
|
|
344
|
+
expect(Array.isArray(parsed.data)).toBe(true);
|
|
345
|
+
|
|
346
|
+
// get/detail commands wrap a single object under data, not an array
|
|
347
|
+
const getExample = getCommandExamples("schemas get")[0];
|
|
348
|
+
const parsedGet = JSON.parse(getExample?.response ?? "{}");
|
|
349
|
+
expect(Array.isArray(parsedGet.data)).toBe(false);
|
|
350
|
+
expect(parsedGet.data).toHaveProperty("fields");
|
|
351
|
+
});
|
|
352
|
+
|
|
337
353
|
it.each(Object.keys(commandRegistry))(
|
|
338
354
|
"returns at least one example for %s",
|
|
339
355
|
(commandName) => {
|
package/src/utils/examples.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
getSchemaFieldTypeDefinitions,
|
|
3
3
|
resolveSchemaFieldType,
|
|
4
4
|
} from "./schema-fields.js";
|
|
5
|
+
import { buildJsonSuccess } from "./output.js";
|
|
5
6
|
|
|
6
7
|
export interface CommandExample {
|
|
7
8
|
command: string;
|
|
@@ -2243,7 +2244,7 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
2243
2244
|
"required": false,
|
|
2244
2245
|
"system": false,
|
|
2245
2246
|
"config": {
|
|
2246
|
-
"formulaText": "date_format(d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90,
|
|
2247
|
+
"formulaText": "date_format(d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90, \\"AAAA-MM\\")",
|
|
2247
2248
|
"uiFormatKind": "date",
|
|
2248
2249
|
"status": "ACTIVE",
|
|
2249
2250
|
"defaultValue": null,
|
|
@@ -4336,11 +4337,37 @@ export function getCommandExamples(
|
|
|
4336
4337
|
commandName: string,
|
|
4337
4338
|
filters: { type?: string } = {},
|
|
4338
4339
|
): readonly CommandExample[] {
|
|
4339
|
-
|
|
4340
|
-
|
|
4340
|
+
const examples =
|
|
4341
|
+
commandName === "schemas fields create"
|
|
4342
|
+
? buildSchemaFieldCreateExamples(filters.type)
|
|
4343
|
+
: (specificExamples[commandName] ?? fallbackExamples(commandName));
|
|
4344
|
+
|
|
4345
|
+
return examples.map((example) => withResponseEnvelope(example, commandName));
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
// The CLI's --json output wraps every payload in a { data, meta } envelope (see
|
|
4349
|
+
// buildJsonSuccess in ./output). Example "response" bodies are authored as the
|
|
4350
|
+
// bare data payload for readability, so we wrap them here to match what the
|
|
4351
|
+
// command actually prints. Non-JSON responses (if any) are left untouched.
|
|
4352
|
+
function withResponseEnvelope(
|
|
4353
|
+
example: CommandExample,
|
|
4354
|
+
commandName: string,
|
|
4355
|
+
): CommandExample {
|
|
4356
|
+
if (example.response === undefined) {
|
|
4357
|
+
return example;
|
|
4341
4358
|
}
|
|
4342
4359
|
|
|
4343
|
-
|
|
4360
|
+
let payload: unknown;
|
|
4361
|
+
try {
|
|
4362
|
+
payload = JSON.parse(example.response);
|
|
4363
|
+
} catch {
|
|
4364
|
+
return example;
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
return {
|
|
4368
|
+
...example,
|
|
4369
|
+
response: JSON.stringify(buildJsonSuccess(payload, { command: commandName }), null, 2),
|
|
4370
|
+
};
|
|
4344
4371
|
}
|
|
4345
4372
|
|
|
4346
4373
|
export function getResolvedCommandExamples(
|