@ollie-shop/cli 0.3.4 → 1.0.1
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/.turbo/turbo-build.log +6 -9
- package/CHANGELOG.md +27 -0
- package/dist/index.js +993 -3956
- package/package.json +15 -37
- package/src/README.md +126 -0
- package/src/cli.tsx +45 -0
- package/src/commands/help.tsx +79 -0
- package/src/commands/login.tsx +92 -0
- package/src/commands/start.tsx +411 -0
- package/src/index.tsx +8 -0
- package/src/utils/auth.ts +218 -21
- package/src/utils/bundle.ts +177 -0
- package/src/utils/config.ts +123 -0
- package/src/utils/esbuild.ts +541 -0
- package/tsconfig.json +10 -15
- package/tsup.config.ts +7 -7
- package/CLAUDE_CLI.md +0 -265
- package/README.md +0 -711
- package/__tests__/mocks/console.ts +0 -22
- package/__tests__/mocks/core.ts +0 -137
- package/__tests__/mocks/index.ts +0 -4
- package/__tests__/mocks/inquirer.ts +0 -16
- package/__tests__/mocks/progress.ts +0 -19
- package/dist/index.d.ts +0 -1
- package/src/__tests__/helpers/cli-test-helper.ts +0 -281
- package/src/__tests__/mocks/index.ts +0 -142
- package/src/actions/component.actions.ts +0 -278
- package/src/actions/function.actions.ts +0 -220
- package/src/actions/project.actions.ts +0 -131
- package/src/actions/version.actions.ts +0 -233
- package/src/commands/__tests__/component-validation.test.ts +0 -250
- package/src/commands/__tests__/component.test.ts +0 -318
- package/src/commands/__tests__/function-validation.test.ts +0 -220
- package/src/commands/__tests__/function.test.ts +0 -286
- package/src/commands/__tests__/store-version-validation.test.ts +0 -414
- package/src/commands/__tests__/store-version.test.ts +0 -402
- package/src/commands/component.ts +0 -178
- package/src/commands/docs.ts +0 -24
- package/src/commands/function.ts +0 -201
- package/src/commands/help.ts +0 -18
- package/src/commands/index.ts +0 -27
- package/src/commands/login.ts +0 -267
- package/src/commands/project.ts +0 -107
- package/src/commands/store-version.ts +0 -242
- package/src/commands/version.ts +0 -51
- package/src/commands/whoami.ts +0 -46
- package/src/index.ts +0 -116
- package/src/prompts/component.prompts.ts +0 -94
- package/src/prompts/function.prompts.ts +0 -168
- package/src/schemas/command.schema.ts +0 -644
- package/src/types/index.ts +0 -183
- package/src/utils/__tests__/command-parser.test.ts +0 -159
- package/src/utils/__tests__/command-suggestions.test.ts +0 -185
- package/src/utils/__tests__/console.test.ts +0 -192
- package/src/utils/__tests__/context-detector.test.ts +0 -258
- package/src/utils/__tests__/enhanced-error-handler.test.ts +0 -137
- package/src/utils/__tests__/error-handler.test.ts +0 -107
- package/src/utils/__tests__/rich-progress.test.ts +0 -181
- package/src/utils/__tests__/validation-error-formatter.test.ts +0 -175
- package/src/utils/__tests__/validation-helpers.test.ts +0 -125
- package/src/utils/cli-progress-reporter.ts +0 -84
- package/src/utils/command-builder.ts +0 -390
- package/src/utils/command-helpers.ts +0 -83
- package/src/utils/command-parser.ts +0 -245
- package/src/utils/command-suggestions.ts +0 -176
- package/src/utils/console.ts +0 -320
- package/src/utils/constants.ts +0 -39
- package/src/utils/context-detector.ts +0 -177
- package/src/utils/deploy-helpers.ts +0 -357
- package/src/utils/enhanced-error-handler.ts +0 -264
- package/src/utils/error-handler.ts +0 -60
- package/src/utils/errors.ts +0 -256
- package/src/utils/interactive-builder.ts +0 -325
- package/src/utils/rich-progress.ts +0 -331
- package/src/utils/store.ts +0 -23
- package/src/utils/validation-error-formatter.ts +0 -337
- package/src/utils/validation-helpers.ts +0 -325
- package/vitest.config.ts +0 -35
- package/vitest.setup.ts +0 -29
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
import type { Command } from "@commander-js/extra-typings";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import type { CLIError, CommandHandler } from "../types";
|
|
4
|
-
import type { EnhancedOption } from "./command-parser";
|
|
5
|
-
import { console as cliConsole } from "./console";
|
|
6
|
-
import {
|
|
7
|
-
displayErrors,
|
|
8
|
-
formatZodError,
|
|
9
|
-
suggestCommand,
|
|
10
|
-
} from "./validation-error-formatter";
|
|
11
|
-
|
|
12
|
-
export interface CommandConfig<T = unknown> {
|
|
13
|
-
name: string;
|
|
14
|
-
description: string;
|
|
15
|
-
aliases?: string[];
|
|
16
|
-
options?: EnhancedOption[];
|
|
17
|
-
schema?: z.ZodSchema<T>;
|
|
18
|
-
examples?: Array<{
|
|
19
|
-
description: string;
|
|
20
|
-
command: string;
|
|
21
|
-
}>;
|
|
22
|
-
handler: CommandHandler<T>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Build an enhanced command with validation and error handling
|
|
27
|
-
*/
|
|
28
|
-
function setupCommandAliases(cmd: Command, aliases?: string[]): void {
|
|
29
|
-
if (!aliases) return;
|
|
30
|
-
for (const alias of aliases) {
|
|
31
|
-
cmd.alias(alias);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isValidDefaultValue(
|
|
36
|
-
value: unknown,
|
|
37
|
-
): value is string | boolean | string[] {
|
|
38
|
-
return (
|
|
39
|
-
typeof value === "string" ||
|
|
40
|
-
typeof value === "boolean" ||
|
|
41
|
-
Array.isArray(value)
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function addOption(cmd: Command, option: EnhancedOption): void {
|
|
46
|
-
if (option.required) {
|
|
47
|
-
cmd.requiredOption(option.flags, option.description);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const hasParser = option.parser !== undefined;
|
|
52
|
-
const hasDefault =
|
|
53
|
-
option.defaultValue !== undefined && option.defaultValue !== null;
|
|
54
|
-
|
|
55
|
-
if (hasParser && hasDefault && option.parser) {
|
|
56
|
-
cmd.option(
|
|
57
|
-
option.flags,
|
|
58
|
-
option.description,
|
|
59
|
-
option.parser,
|
|
60
|
-
option.defaultValue,
|
|
61
|
-
);
|
|
62
|
-
} else if (hasParser && option.parser) {
|
|
63
|
-
cmd.option(option.flags, option.description, option.parser);
|
|
64
|
-
} else if (hasDefault) {
|
|
65
|
-
// Commander.js doesn't accept number as defaultValue, convert to string
|
|
66
|
-
const defaultValue =
|
|
67
|
-
typeof option.defaultValue === "number"
|
|
68
|
-
? String(option.defaultValue)
|
|
69
|
-
: option.defaultValue;
|
|
70
|
-
|
|
71
|
-
if (isValidDefaultValue(defaultValue)) {
|
|
72
|
-
cmd.option(option.flags, option.description, defaultValue);
|
|
73
|
-
} else {
|
|
74
|
-
cmd.option(option.flags, option.description);
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
cmd.option(option.flags, option.description);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function setupCommandOptions(cmd: Command, options?: EnhancedOption[]): void {
|
|
82
|
-
if (!options) return;
|
|
83
|
-
for (const option of options) {
|
|
84
|
-
addOption(cmd, option);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function setupCommandExamples(
|
|
89
|
-
cmd: Command,
|
|
90
|
-
examples?: Array<{ description: string; command: string }>,
|
|
91
|
-
): void {
|
|
92
|
-
if (!examples || examples.length === 0) return;
|
|
93
|
-
cmd.addHelpText("after", "\nExamples:");
|
|
94
|
-
for (const example of examples) {
|
|
95
|
-
cmd.addHelpText("after", ` ${example.description}:`);
|
|
96
|
-
cmd.addHelpText("after", ` $ ${example.command}\n`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function buildCommand<T = unknown>(
|
|
101
|
-
parent: Command,
|
|
102
|
-
config: CommandConfig<T>,
|
|
103
|
-
): Command {
|
|
104
|
-
const cmd = parent.command(config.name).description(config.description);
|
|
105
|
-
|
|
106
|
-
setupCommandAliases(cmd, config.aliases);
|
|
107
|
-
setupCommandOptions(cmd, config.options);
|
|
108
|
-
setupCommandExamples(cmd, config.examples);
|
|
109
|
-
|
|
110
|
-
// Add enhanced action handler with validation
|
|
111
|
-
cmd.action(async (options) => {
|
|
112
|
-
try {
|
|
113
|
-
const validatedOptions = await validateOptions<T>(
|
|
114
|
-
options,
|
|
115
|
-
config,
|
|
116
|
-
parent,
|
|
117
|
-
);
|
|
118
|
-
await config.handler(validatedOptions, cliConsole);
|
|
119
|
-
} catch (error) {
|
|
120
|
-
handleCommandError(error, config as CommandConfig<unknown>, parent);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Override error handling
|
|
125
|
-
cmd.exitOverride();
|
|
126
|
-
cmd.configureOutput({
|
|
127
|
-
writeErr: (str) =>
|
|
128
|
-
handleCommanderError(str, parent, cmd, config as CommandConfig<unknown>),
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
return cmd;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Extract option name from flags string
|
|
136
|
-
*/
|
|
137
|
-
function getOptionName(flags: string): string {
|
|
138
|
-
// Extract long option name from flags like "-n, --name <value>"
|
|
139
|
-
const match = flags.match(/--([a-z-]+)/i);
|
|
140
|
-
return match?.[1] ?? flags;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Validate command options
|
|
145
|
-
*/
|
|
146
|
-
async function validateOptions<T>(
|
|
147
|
-
options: unknown,
|
|
148
|
-
config: CommandConfig<T>,
|
|
149
|
-
parent: Command,
|
|
150
|
-
): Promise<T> {
|
|
151
|
-
let validatedOptions = options as T;
|
|
152
|
-
|
|
153
|
-
// Validate with schema if provided
|
|
154
|
-
if (config.schema) {
|
|
155
|
-
const result = config.schema.safeParse(options);
|
|
156
|
-
if (!result.success) {
|
|
157
|
-
const errors = formatZodError(result.error, parent.name());
|
|
158
|
-
displayErrors(cliConsole, errors, `${parent.name()} ${config.name}`);
|
|
159
|
-
// Throw an error that will be caught by the command handler
|
|
160
|
-
const error = new Error("Validation failed") as CLIError;
|
|
161
|
-
error.exitCode = 1;
|
|
162
|
-
error.isValidationError = true;
|
|
163
|
-
throw error;
|
|
164
|
-
}
|
|
165
|
-
validatedOptions = result.data;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Check for required options
|
|
169
|
-
checkRequiredOptions(options, config, parent);
|
|
170
|
-
|
|
171
|
-
return validatedOptions;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Check required options
|
|
176
|
-
*/
|
|
177
|
-
function checkRequiredOptions<T>(
|
|
178
|
-
options: unknown,
|
|
179
|
-
config: CommandConfig<T>,
|
|
180
|
-
parent: Command,
|
|
181
|
-
): void {
|
|
182
|
-
if (!config.options) return;
|
|
183
|
-
|
|
184
|
-
const missingRequired = config.options
|
|
185
|
-
.filter(
|
|
186
|
-
(opt) =>
|
|
187
|
-
opt.required &&
|
|
188
|
-
!(options as Record<string, unknown>)[getOptionName(opt.flags)],
|
|
189
|
-
)
|
|
190
|
-
.map((opt) => getOptionName(opt.flags));
|
|
191
|
-
|
|
192
|
-
if (missingRequired.length > 0) {
|
|
193
|
-
displayMissingOptions(missingRequired, config, parent);
|
|
194
|
-
// Throw an error that will be caught by the command handler
|
|
195
|
-
const error = new Error("Missing required options") as CLIError;
|
|
196
|
-
error.exitCode = 1;
|
|
197
|
-
error.isValidationError = true;
|
|
198
|
-
throw error;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Display missing required options
|
|
204
|
-
*/
|
|
205
|
-
function displayMissingOptions<T>(
|
|
206
|
-
missingRequired: string[],
|
|
207
|
-
config: CommandConfig<T>,
|
|
208
|
-
parent: Command,
|
|
209
|
-
): void {
|
|
210
|
-
cliConsole.error("❌ Missing required options:");
|
|
211
|
-
|
|
212
|
-
for (const optName of missingRequired) {
|
|
213
|
-
const opt = config.options?.find((o) => getOptionName(o.flags) === optName);
|
|
214
|
-
if (opt) {
|
|
215
|
-
cliConsole.error(` • --${optName}: ${opt.description}`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
cliConsole.log("");
|
|
220
|
-
cliConsole.info(
|
|
221
|
-
`💡 For more help, run: ollieshop ${parent.name()} ${config.name} --help`,
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Handle command errors
|
|
227
|
-
*/
|
|
228
|
-
function handleCommandError(
|
|
229
|
-
error: unknown,
|
|
230
|
-
config: CommandConfig,
|
|
231
|
-
parent: Command,
|
|
232
|
-
): never {
|
|
233
|
-
// Check if this is a validation error that was already displayed
|
|
234
|
-
if (error instanceof Error) {
|
|
235
|
-
const cliError = error as CLIError;
|
|
236
|
-
if (cliError.isValidationError) {
|
|
237
|
-
// Error messages already displayed, just exit
|
|
238
|
-
process.exit(cliError.exitCode || 1);
|
|
239
|
-
}
|
|
240
|
-
} else if (error instanceof z.ZodError) {
|
|
241
|
-
const errors = formatZodError(error, config.name);
|
|
242
|
-
displayErrors(cliConsole, errors, `${parent.name()} ${config.name}`);
|
|
243
|
-
} else {
|
|
244
|
-
cliConsole.error(
|
|
245
|
-
`Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Handle Commander.js errors
|
|
253
|
-
*/
|
|
254
|
-
function handleCommanderError(
|
|
255
|
-
str: string,
|
|
256
|
-
parent: Command,
|
|
257
|
-
cmd: Command,
|
|
258
|
-
config: CommandConfig,
|
|
259
|
-
): void {
|
|
260
|
-
if (str.includes("error: unknown option")) {
|
|
261
|
-
handleUnknownOption(str, parent, cmd, config);
|
|
262
|
-
} else if (str.includes("error: missing required argument")) {
|
|
263
|
-
handleMissingArgument(str, parent, config);
|
|
264
|
-
} else {
|
|
265
|
-
cliConsole.error(str.trim());
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Handle unknown option error
|
|
271
|
-
*/
|
|
272
|
-
function handleUnknownOption(
|
|
273
|
-
str: string,
|
|
274
|
-
parent: Command,
|
|
275
|
-
cmd: Command,
|
|
276
|
-
config: CommandConfig,
|
|
277
|
-
): void {
|
|
278
|
-
const match = str.match(/unknown option '(.+)'/);
|
|
279
|
-
if (!match) return;
|
|
280
|
-
|
|
281
|
-
const unknownOption = match[1] ?? "";
|
|
282
|
-
cliConsole.error(`❌ Unknown option: ${unknownOption}`);
|
|
283
|
-
|
|
284
|
-
// Suggest similar options
|
|
285
|
-
const validOptions =
|
|
286
|
-
config.options?.map((opt) => `--${getOptionName(opt.flags)}`) || [];
|
|
287
|
-
const suggestion = suggestCommand(
|
|
288
|
-
unknownOption.replace(/^-+/, ""),
|
|
289
|
-
validOptions.map((opt) => opt.replace(/^-+/, "")),
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
if (suggestion) {
|
|
293
|
-
cliConsole.log("");
|
|
294
|
-
cliConsole.info(`💡 Did you mean: --${suggestion}?`);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
cliConsole.log("");
|
|
298
|
-
cliConsole.info(
|
|
299
|
-
`💡 For available options, run: ollieshop ${parent.name()} ${cmd.name()} --help`,
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Handle missing argument error
|
|
305
|
-
*/
|
|
306
|
-
function handleMissingArgument(
|
|
307
|
-
str: string,
|
|
308
|
-
parent: Command,
|
|
309
|
-
config: CommandConfig,
|
|
310
|
-
): void {
|
|
311
|
-
const match = str.match(/missing required argument '(.+)'/);
|
|
312
|
-
if (!match) return;
|
|
313
|
-
|
|
314
|
-
cliConsole.error(`❌ Missing required argument: ${match[1]}`);
|
|
315
|
-
cliConsole.log("");
|
|
316
|
-
cliConsole.info(
|
|
317
|
-
`💡 For usage help, run: ollieshop ${parent.name()} ${config.name} --help`,
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Build a command group (like "component" or "function")
|
|
323
|
-
*/
|
|
324
|
-
export function buildCommandGroup(
|
|
325
|
-
program: Command,
|
|
326
|
-
name: string,
|
|
327
|
-
description: string,
|
|
328
|
-
aliases?: string[],
|
|
329
|
-
): Command {
|
|
330
|
-
const cmd = program.command(name).description(description);
|
|
331
|
-
|
|
332
|
-
if (aliases) {
|
|
333
|
-
for (const alias of aliases) {
|
|
334
|
-
cmd.alias(alias);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Add custom error handling
|
|
339
|
-
cmd.exitOverride();
|
|
340
|
-
cmd.configureOutput({
|
|
341
|
-
writeErr: (str) => handleGroupCommandError(str, cmd, name),
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
return cmd;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Handle command group errors
|
|
349
|
-
*/
|
|
350
|
-
function handleGroupCommandError(
|
|
351
|
-
str: string,
|
|
352
|
-
cmd: Command,
|
|
353
|
-
groupName: string,
|
|
354
|
-
): void {
|
|
355
|
-
if (str.includes("is not a") && str.includes("command")) {
|
|
356
|
-
handleUnknownSubcommand(str, cmd, groupName);
|
|
357
|
-
} else {
|
|
358
|
-
cliConsole.error(str.trim());
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Handle unknown subcommand error
|
|
364
|
-
*/
|
|
365
|
-
function handleUnknownSubcommand(
|
|
366
|
-
str: string,
|
|
367
|
-
cmd: Command,
|
|
368
|
-
groupName: string,
|
|
369
|
-
): void {
|
|
370
|
-
const match = str.match(/'([^']+)' is not a/);
|
|
371
|
-
if (!match) return;
|
|
372
|
-
|
|
373
|
-
const unknownCmd = match[1] ?? "";
|
|
374
|
-
cliConsole.error(`❌ Unknown command: ${unknownCmd}`);
|
|
375
|
-
|
|
376
|
-
// Get subcommands
|
|
377
|
-
const subcommands = cmd.commands.map((c) => c.name());
|
|
378
|
-
const suggestion = suggestCommand(unknownCmd, subcommands);
|
|
379
|
-
|
|
380
|
-
if (suggestion) {
|
|
381
|
-
cliConsole.log("");
|
|
382
|
-
cliConsole.info(`💡 Did you mean: ollieshop ${groupName} ${suggestion}?`);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
cliConsole.log("");
|
|
386
|
-
cliConsole.info("💡 Available commands:");
|
|
387
|
-
for (const subcmd of subcommands) {
|
|
388
|
-
cliConsole.log(` • ${subcmd}`);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { console as cliConsole } from "./console";
|
|
3
|
-
import type { CliConsole } from "./console";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Handle errors in a consistent way across all commands
|
|
7
|
-
*/
|
|
8
|
-
export function handleCommandError(error: unknown, console: CliConsole): void {
|
|
9
|
-
if (error instanceof Error) {
|
|
10
|
-
console.error(error.message);
|
|
11
|
-
if (process.env.DEBUG) {
|
|
12
|
-
console.debug(error.stack || "");
|
|
13
|
-
}
|
|
14
|
-
} else {
|
|
15
|
-
console.error("An unexpected error occurred");
|
|
16
|
-
}
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Parse and validate command options using a Zod schema
|
|
22
|
-
*/
|
|
23
|
-
export function parseOptions<T>(
|
|
24
|
-
schema: z.ZodSchema<T>,
|
|
25
|
-
options: Record<string, unknown>,
|
|
26
|
-
): T {
|
|
27
|
-
try {
|
|
28
|
-
return schema.parse(options);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
if (error instanceof z.ZodError) {
|
|
31
|
-
const formattedErrors = error.errors
|
|
32
|
-
.map((err) => `${err.path.join(".")}: ${err.message}`)
|
|
33
|
-
.join("\n");
|
|
34
|
-
throw new Error(`Invalid options:\n${formattedErrors}`);
|
|
35
|
-
}
|
|
36
|
-
throw error;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Create an action handler with schema validation and error handling
|
|
42
|
-
*/
|
|
43
|
-
export function createAction<T>(
|
|
44
|
-
schema: z.ZodSchema<T>,
|
|
45
|
-
action: (options: T, cliConsole: CliConsole) => Promise<void>,
|
|
46
|
-
) {
|
|
47
|
-
return async (firstArg: unknown, options: Record<string, unknown>) => {
|
|
48
|
-
try {
|
|
49
|
-
// Handle both positional args and options
|
|
50
|
-
const combinedOptions = { ...options };
|
|
51
|
-
if (firstArg !== undefined) {
|
|
52
|
-
// Try to determine the first property name from common patterns
|
|
53
|
-
const possibleKeys = ["name", "path", "target"];
|
|
54
|
-
for (const key of possibleKeys) {
|
|
55
|
-
if (!combinedOptions[key]) {
|
|
56
|
-
combinedOptions[key] = firstArg;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const parsedOptions = parseOptions(schema, combinedOptions);
|
|
63
|
-
await action(parsedOptions, cliConsole);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
handleCommandError(error, cliConsole);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Create a simple action handler without schema validation
|
|
72
|
-
*/
|
|
73
|
-
export function createSimpleAction(
|
|
74
|
-
action: (cliConsole: CliConsole) => Promise<void>,
|
|
75
|
-
) {
|
|
76
|
-
return async () => {
|
|
77
|
-
try {
|
|
78
|
-
await action(cliConsole);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
handleCommandError(error, cliConsole);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { InvalidArgumentError } from "@commander-js/extra-typings";
|
|
2
|
-
import { ComponentSlot, FunctionInvocationType } from "@ollie-shop/core";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import {
|
|
5
|
-
ComponentNameSchema,
|
|
6
|
-
FunctionNameSchema,
|
|
7
|
-
PathSchema,
|
|
8
|
-
PrioritySchema,
|
|
9
|
-
StoreIdSchema,
|
|
10
|
-
VersionNameSchema,
|
|
11
|
-
} from "../schemas/command.schema";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Create a Commander option parser that validates with Zod schema
|
|
15
|
-
*/
|
|
16
|
-
export function createOptionParser<T>(
|
|
17
|
-
schema: z.ZodSchema<T>,
|
|
18
|
-
fieldName: string,
|
|
19
|
-
helpText?: string,
|
|
20
|
-
) {
|
|
21
|
-
return (value: string): T => {
|
|
22
|
-
const result = schema.safeParse(value);
|
|
23
|
-
|
|
24
|
-
if (!result.success) {
|
|
25
|
-
const errors = result.error.errors.map((err) => err.message).join(", ");
|
|
26
|
-
|
|
27
|
-
let errorMessage = `Invalid ${fieldName}: ${errors}`;
|
|
28
|
-
|
|
29
|
-
if (helpText) {
|
|
30
|
-
errorMessage += `\n\n${helpText}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
throw new InvalidArgumentError(errorMessage);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return result.data;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Parse component name with validation
|
|
42
|
-
*/
|
|
43
|
-
export const parseComponentName = createOptionParser(
|
|
44
|
-
ComponentNameSchema,
|
|
45
|
-
"component name",
|
|
46
|
-
"Examples: header-nav, shopping-cart, product-list",
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Parse function name with validation
|
|
51
|
-
*/
|
|
52
|
-
export const parseFunctionName = createOptionParser(
|
|
53
|
-
FunctionNameSchema,
|
|
54
|
-
"function name",
|
|
55
|
-
"Examples: validate-order, apply-discount, check-inventory",
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Parse component slot with validation
|
|
60
|
-
*/
|
|
61
|
-
export const parseComponentSlot = createOptionParser(
|
|
62
|
-
ComponentSlot,
|
|
63
|
-
"component slot",
|
|
64
|
-
"Valid slots: header, footer, sidebar, main, checkout-summary, payment-method, shipping-method, customer-info, order-confirmation",
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Parse function invocation with validation
|
|
69
|
-
*/
|
|
70
|
-
export const parseFunctionInvocation = createOptionParser(
|
|
71
|
-
FunctionInvocationType,
|
|
72
|
-
"function invocation",
|
|
73
|
-
"Valid invocations: request, response",
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Parse boolean option with better error handling
|
|
78
|
-
*/
|
|
79
|
-
export function parseBooleanOption(value: string): boolean {
|
|
80
|
-
const normalized = value.toLowerCase().trim();
|
|
81
|
-
|
|
82
|
-
if (["true", "1", "yes", "y"].includes(normalized)) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (["false", "0", "no", "n"].includes(normalized)) {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
throw new InvalidArgumentError(
|
|
91
|
-
`Invalid boolean value: "${value}". Use true/false, yes/no, or 1/0`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Parse path option with validation
|
|
97
|
-
*/
|
|
98
|
-
export const parsePath = createOptionParser(PathSchema, "path");
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Parse UUID with validation
|
|
102
|
-
*/
|
|
103
|
-
export const parseUUID = createOptionParser(
|
|
104
|
-
z
|
|
105
|
-
.string()
|
|
106
|
-
.uuid("Must be a valid UUID (e.g., 123e4567-e89b-12d3-a456-426614174000)"),
|
|
107
|
-
"ID",
|
|
108
|
-
"Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Parse store ID with validation
|
|
113
|
-
*/
|
|
114
|
-
export const parseStoreId = createOptionParser(
|
|
115
|
-
StoreIdSchema,
|
|
116
|
-
"store ID",
|
|
117
|
-
"Example: 123e4567-e89b-12d3-a456-426614174000",
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Parse version name with validation
|
|
122
|
-
*/
|
|
123
|
-
export const parseVersionName = createOptionParser(
|
|
124
|
-
VersionNameSchema,
|
|
125
|
-
"version name",
|
|
126
|
-
"Examples: v1.0.0, summer-sale-2024, black-friday",
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Parse priority with validation
|
|
131
|
-
*/
|
|
132
|
-
export const parsePriority = (value: string): number => {
|
|
133
|
-
const numValue = Number(value);
|
|
134
|
-
if (Number.isNaN(numValue)) {
|
|
135
|
-
throw new InvalidArgumentError(
|
|
136
|
-
`Priority must be a number, received: ${value}`,
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const result = PrioritySchema.safeParse(numValue);
|
|
141
|
-
if (!result.success) {
|
|
142
|
-
const errors = result.error.errors.map((err) => err.message).join(", ");
|
|
143
|
-
throw new InvalidArgumentError(`Invalid priority: ${errors}`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return result.data;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Create an enhanced option configuration with validation
|
|
151
|
-
*/
|
|
152
|
-
export interface EnhancedOption {
|
|
153
|
-
flags: string;
|
|
154
|
-
description: string;
|
|
155
|
-
defaultValue?: unknown;
|
|
156
|
-
parser?: (value: string) => unknown;
|
|
157
|
-
required?: boolean;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Common option configurations
|
|
162
|
-
*/
|
|
163
|
-
export const COMMON_OPTIONS = {
|
|
164
|
-
componentName: {
|
|
165
|
-
flags: "-n, --name <name>",
|
|
166
|
-
description: "Component name (lowercase with hyphens)",
|
|
167
|
-
parser: parseComponentName,
|
|
168
|
-
required: true,
|
|
169
|
-
},
|
|
170
|
-
functionName: {
|
|
171
|
-
flags: "-n, --name <name>",
|
|
172
|
-
description: "Function name (lowercase with hyphens)",
|
|
173
|
-
parser: parseFunctionName,
|
|
174
|
-
required: true,
|
|
175
|
-
},
|
|
176
|
-
componentSlot: {
|
|
177
|
-
flags: "-s, --slot <slot>",
|
|
178
|
-
description:
|
|
179
|
-
"Component slot (header|footer|sidebar|main|checkout-summary|payment-method|shipping-method|customer-info|order-confirmation)",
|
|
180
|
-
parser: parseComponentSlot,
|
|
181
|
-
defaultValue: "main",
|
|
182
|
-
},
|
|
183
|
-
functionInvocation: {
|
|
184
|
-
flags: "-i, --invocation <invocation>",
|
|
185
|
-
description: "Function invocation type (request|response)",
|
|
186
|
-
parser: parseFunctionInvocation,
|
|
187
|
-
defaultValue: "request",
|
|
188
|
-
},
|
|
189
|
-
tests: {
|
|
190
|
-
flags: "--tests",
|
|
191
|
-
description: "Include test files (default: true)",
|
|
192
|
-
defaultValue: true,
|
|
193
|
-
},
|
|
194
|
-
noTests: {
|
|
195
|
-
flags: "--no-tests",
|
|
196
|
-
description: "Skip test file generation",
|
|
197
|
-
},
|
|
198
|
-
path: {
|
|
199
|
-
flags: "-p, --path <path>",
|
|
200
|
-
description: "Path to component or function directory",
|
|
201
|
-
parser: parsePath,
|
|
202
|
-
},
|
|
203
|
-
storeId: {
|
|
204
|
-
flags: "-s, --store <id>",
|
|
205
|
-
description: "Store ID (UUID format)",
|
|
206
|
-
parser: parseStoreId,
|
|
207
|
-
required: true,
|
|
208
|
-
},
|
|
209
|
-
versionName: {
|
|
210
|
-
flags: "-n, --name <name>",
|
|
211
|
-
description: "Version name",
|
|
212
|
-
parser: parseVersionName,
|
|
213
|
-
required: true,
|
|
214
|
-
},
|
|
215
|
-
template: {
|
|
216
|
-
flags: "-t, --template <template>",
|
|
217
|
-
description: "Template to use (default|grocery|sales)",
|
|
218
|
-
defaultValue: "default",
|
|
219
|
-
},
|
|
220
|
-
active: {
|
|
221
|
-
flags: "--active",
|
|
222
|
-
description: "Make version active (default: true)",
|
|
223
|
-
defaultValue: true,
|
|
224
|
-
},
|
|
225
|
-
noActive: {
|
|
226
|
-
flags: "--no-active",
|
|
227
|
-
description: "Create inactive version",
|
|
228
|
-
},
|
|
229
|
-
force: {
|
|
230
|
-
flags: "-f, --force",
|
|
231
|
-
description: "Skip confirmation prompts",
|
|
232
|
-
defaultValue: false,
|
|
233
|
-
},
|
|
234
|
-
watch: {
|
|
235
|
-
flags: "--watch",
|
|
236
|
-
description: "Watch for changes and rebuild automatically",
|
|
237
|
-
defaultValue: false,
|
|
238
|
-
},
|
|
239
|
-
priority: {
|
|
240
|
-
flags: "-p, --priority <priority>",
|
|
241
|
-
description: "Execution priority (0-100)",
|
|
242
|
-
parser: parsePriority,
|
|
243
|
-
defaultValue: 0,
|
|
244
|
-
},
|
|
245
|
-
} satisfies Record<string, EnhancedOption>;
|