@lovrabet/cli-framework 0.1.1-beta.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/README.md +246 -0
- package/lib/errors.d.ts +121 -0
- package/lib/errors.js +73 -0
- package/lib/framework/build-all-flags.d.ts +39 -0
- package/lib/framework/build-all-flags.js +54 -0
- package/lib/framework/flags.d.ts +37 -0
- package/lib/framework/flags.js +152 -0
- package/lib/framework/help.d.ts +136 -0
- package/lib/framework/help.js +244 -0
- package/lib/framework/output.d.ts +45 -0
- package/lib/framework/output.js +354 -0
- package/lib/framework/response.d.ts +60 -0
- package/lib/framework/response.js +47 -0
- package/lib/framework/runner-shared.d.ts +244 -0
- package/lib/framework/runner-shared.js +240 -0
- package/lib/framework/runner.d.ts +246 -0
- package/lib/framework/runner.js +184 -0
- package/lib/framework/schema-export.d.ts +176 -0
- package/lib/framework/schema-export.js +148 -0
- package/lib/framework/types.d.ts +338 -0
- package/lib/framework/types.js +53 -0
- package/lib/index.d.ts +209 -0
- package/lib/index.js +52 -0
- package/lib/utils/apply-jq-filter.d.ts +33 -0
- package/lib/utils/apply-jq-filter.js +99 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# @lovrabet/cli-framework
|
|
2
|
+
|
|
3
|
+
[](README.md)
|
|
4
|
+
[](README_zh-CN.md)
|
|
5
|
+
[](README_id-ID.md)
|
|
6
|
+
[](README_ja-JP.md)
|
|
7
|
+
[](README_tr-TR.md)
|
|
8
|
+
|
|
9
|
+
A lightweight, type-safe CLI framework for building structured command-line tools with built-in flag parsing, output formatting, risk control, and dry-run support.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Type-Safe Commands** — Define commands with TypeScript interfaces for flags, arguments, and results
|
|
14
|
+
- **Flexible Flag Parsing** — Supports string, boolean, number types with validation, enums, and regex patterns
|
|
15
|
+
- **Multiple Output Formats** — JSON, pretty-printed, and compress modes
|
|
16
|
+
- **Risk Control** — Built-in risk levels (read/write/high-risk-write) with configurable policies
|
|
17
|
+
- **Dry-Run Mode** — Preview commands without executing them
|
|
18
|
+
- **Context-Aware Runtime** — Unified runtime context with helper methods for flags and output
|
|
19
|
+
- **Help Generation** — Automatic help text generation from command definitions
|
|
20
|
+
- **Schema Export** — Export command schemas for tooling and documentation
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bun add @lovrabet/cli-framework
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
`runCommandWithAdapter(def, env, adapter)` needs:
|
|
31
|
+
- `def`: declarative command definition
|
|
32
|
+
- `env`: raw CLI input/environment values
|
|
33
|
+
- `adapter`: CLI-specific bridge implementation (errors, output, prepare, confirmation, risk policy)
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import {
|
|
37
|
+
createCliErrors,
|
|
38
|
+
formatOutput,
|
|
39
|
+
runCommandWithAdapter,
|
|
40
|
+
type CommandDefinition,
|
|
41
|
+
type RunnerAdapter,
|
|
42
|
+
type RunnerEnvBase,
|
|
43
|
+
} from "@lovrabet/cli-framework";
|
|
44
|
+
|
|
45
|
+
const myCommand: CommandDefinition = {
|
|
46
|
+
service: "app",
|
|
47
|
+
command: "list",
|
|
48
|
+
description: "List all applications",
|
|
49
|
+
risk: "read",
|
|
50
|
+
requiresAuth: true,
|
|
51
|
+
flags: [
|
|
52
|
+
{ name: "format", type: "string", description: "Output format", default: "compress" },
|
|
53
|
+
{ name: "page", type: "number", description: "Page number", default: 1 },
|
|
54
|
+
],
|
|
55
|
+
async execute(ctx) {
|
|
56
|
+
const apps = await fetchApps({ page: ctx.num("page") });
|
|
57
|
+
return { ok: true, data: apps };
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const env: RunnerEnvBase = {
|
|
62
|
+
rawFlags: { format: "compress", page: 1 },
|
|
63
|
+
isNonInteractive: true,
|
|
64
|
+
args: [],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const CliErrors = createCliErrors({ programName: "my-cli" });
|
|
68
|
+
|
|
69
|
+
const adapter: RunnerAdapter<RunnerEnvBase> = {
|
|
70
|
+
cliErrors: CliErrors,
|
|
71
|
+
formatOutput,
|
|
72
|
+
getCommandLabel: (def) => `${def.service} ${def.command}`,
|
|
73
|
+
prepare: async () => ({ extras: {} }),
|
|
74
|
+
confirmHighRisk: async () => {},
|
|
75
|
+
riskPolicy: {
|
|
76
|
+
createError: (message) => CliErrors.validation(message),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
await runCommandWithAdapter(myCommand, env, adapter);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Core Concepts
|
|
84
|
+
|
|
85
|
+
### Command Definition
|
|
86
|
+
|
|
87
|
+
A command consists of:
|
|
88
|
+
|
|
89
|
+
| Field | Type | Description |
|
|
90
|
+
|-------|------|-------------|
|
|
91
|
+
| `service` | `string` | Service/namespace grouping |
|
|
92
|
+
| `command` | `string` | Command name |
|
|
93
|
+
| `description` | `string` | Help text description |
|
|
94
|
+
| `risk` | `Risk` | Risk level: `read`, `write`, `high-risk-write` |
|
|
95
|
+
| `flags` | `FlagDef[]` | Flag definitions |
|
|
96
|
+
| `args` | `ArgDef[]` | Positional argument definitions |
|
|
97
|
+
| `validate` | `(ctx) => Promise<void>` | Pre-execution validation |
|
|
98
|
+
| `dryRun` | `(ctx) => Promise<DryRunResult>` | Dry-run implementation |
|
|
99
|
+
| `execute` | `(ctx) => Promise<CommandResult>` | Main execution logic |
|
|
100
|
+
|
|
101
|
+
### Flag Types
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// String flag with enum
|
|
105
|
+
{ name: "format", type: "string", enum: ["json", "pretty", "compress"], default: "compress" }
|
|
106
|
+
|
|
107
|
+
// Boolean flag
|
|
108
|
+
{ name: "dry-run", type: "boolean", description: "Preview without executing" }
|
|
109
|
+
|
|
110
|
+
// Number flag with validation
|
|
111
|
+
{ name: "page", type: "number", default: 1 }
|
|
112
|
+
|
|
113
|
+
// String with regex pattern
|
|
114
|
+
{ name: "appcode", type: "string", pattern: { regex: /^[a-z0-9-]+$/, description: "lowercase alphanumeric with dashes" } }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Output Formats
|
|
118
|
+
|
|
119
|
+
| Format | Description |
|
|
120
|
+
|--------|-------------|
|
|
121
|
+
| `json` | Raw JSON output |
|
|
122
|
+
| `pretty` | Formatted JSON with colors |
|
|
123
|
+
| `compress` | Minified single-line JSON (default) |
|
|
124
|
+
|
|
125
|
+
### Risk Levels
|
|
126
|
+
|
|
127
|
+
| Level | Order | Description |
|
|
128
|
+
|-------|-------|-------------|
|
|
129
|
+
| `read` | 0 | Safe read operations |
|
|
130
|
+
| `write` | 1 | Data modification |
|
|
131
|
+
| `high-risk-write` | 2 | Destructive operations |
|
|
132
|
+
|
|
133
|
+
## API Reference
|
|
134
|
+
|
|
135
|
+
### Types
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Core types
|
|
139
|
+
export type { CommandDefinition, CommandResult, RuntimeContext, FlagDef, Risk }
|
|
140
|
+
|
|
141
|
+
// Runner types
|
|
142
|
+
export type { RunnerAdapter, RunnerEnvBase, RiskPolicy }
|
|
143
|
+
|
|
144
|
+
// Help types
|
|
145
|
+
export type { HelpGeneratorOptions, HelpServiceEntry }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Functions
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Command execution
|
|
152
|
+
runCommandWithAdapter(def, env, adapter)
|
|
153
|
+
|
|
154
|
+
// Flag helpers
|
|
155
|
+
createFlagHelpers(cliErrors) → { parseFlags, validateFlags }
|
|
156
|
+
|
|
157
|
+
// Output formatting
|
|
158
|
+
formatOutput(result, options)
|
|
159
|
+
|
|
160
|
+
// Runtime context builders
|
|
161
|
+
buildRuntimeContext(options)
|
|
162
|
+
resolveOutputConfig(options)
|
|
163
|
+
assertRiskWithinLevel(options)
|
|
164
|
+
runDryRunIfNeeded(options)
|
|
165
|
+
|
|
166
|
+
// Help generation
|
|
167
|
+
createHelpGenerators(options)
|
|
168
|
+
|
|
169
|
+
// Schema export
|
|
170
|
+
buildSchemaPayload(commands, options)
|
|
171
|
+
|
|
172
|
+
// Utilities
|
|
173
|
+
extractList(data) → ListResponse
|
|
174
|
+
extractPaging(data) → Paging
|
|
175
|
+
resolveJqFilter(filter, data)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Error Handling
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { CliError, createCliErrors } from "@lovrabet/cli-framework";
|
|
182
|
+
|
|
183
|
+
const CliErrors = createCliErrors({ programName: "my-cli" });
|
|
184
|
+
|
|
185
|
+
throw CliErrors.flagMissing("appcode", "App code is required");
|
|
186
|
+
throw CliErrors.validation("Invalid format value");
|
|
187
|
+
throw CliErrors.authRequired("Login first with: my-cli auth login");
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Example: Complete CLI Setup
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import {
|
|
194
|
+
runCommandWithAdapter,
|
|
195
|
+
createFlagHelpers,
|
|
196
|
+
formatOutput,
|
|
197
|
+
CliError,
|
|
198
|
+
} from "@lovrabet/cli-framework";
|
|
199
|
+
import type { CommandDefinition, RunnerAdapter } from "@lovrabet/cli-framework";
|
|
200
|
+
|
|
201
|
+
const adapter: RunnerAdapter<any> = {
|
|
202
|
+
cliErrors: createCliErrors({ programName: "myapp" }),
|
|
203
|
+
formatOutput,
|
|
204
|
+
getCommandLabel: (def) => `${def.service} ${def.command}`,
|
|
205
|
+
prepare: async (def, env, flags) => ({
|
|
206
|
+
appCode: flags["appcode"] as string,
|
|
207
|
+
cookie: env.cookie,
|
|
208
|
+
apiDomain: "https://api.example.com",
|
|
209
|
+
apiDir: "/v1",
|
|
210
|
+
}),
|
|
211
|
+
confirmHighRisk: async ({ commandLabel }) => {
|
|
212
|
+
const confirmed = await confirm(`${commandLabel}?`);
|
|
213
|
+
if (!confirmed) throw new CliError("cancelled");
|
|
214
|
+
},
|
|
215
|
+
riskPolicy: {
|
|
216
|
+
createError: (msg) => new Error(msg),
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Define and run commands
|
|
221
|
+
const commands: CommandDefinition[] = [listCommand, createCommand, deleteCommand];
|
|
222
|
+
|
|
223
|
+
for (const def of commands) {
|
|
224
|
+
await runCommandWithAdapter(def, env, adapter);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Install dependencies
|
|
232
|
+
bun install
|
|
233
|
+
|
|
234
|
+
# Type check
|
|
235
|
+
bun run typecheck
|
|
236
|
+
|
|
237
|
+
# Run tests
|
|
238
|
+
bun test
|
|
239
|
+
|
|
240
|
+
# Build
|
|
241
|
+
bun run build
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT
|
package/lib/errors.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Error types, factory interfaces, and creator for CLI-specific errors.
|
|
3
|
+
* All CLI errors are expressed as {@link CliError} instances with a machine-readable
|
|
4
|
+
* `code`, numeric `exitCode`, human-readable `message`, and optional `hint`.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Unified CLI error class thrown by command handlers and caught by the top-level
|
|
8
|
+
* error handler in the runner/adapter.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* throw new CliError("flag_missing", 1, "Missing required flag: --appcode", "Use --appcode=<code>");
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare class CliError extends Error {
|
|
16
|
+
/** Machine-readable error code (e.g. "auth_required", "flag_missing"). */
|
|
17
|
+
readonly code: string;
|
|
18
|
+
/**
|
|
19
|
+
* Numeric exit code to emit on process exit.
|
|
20
|
+
* - `0` = success or user-cancelled
|
|
21
|
+
* - `1` = validation / user error
|
|
22
|
+
* - `2` = network / API / system error
|
|
23
|
+
*/
|
|
24
|
+
readonly exitCode: number;
|
|
25
|
+
/** Optional hint shown after the error message to guide the user. */
|
|
26
|
+
readonly hint?: string;
|
|
27
|
+
/**
|
|
28
|
+
* @param code - Machine-readable error identifier.
|
|
29
|
+
* @param exitCode - Process exit code.
|
|
30
|
+
* @param message - Human-readable error message.
|
|
31
|
+
* @param hint - Optional actionable hint displayed below the message.
|
|
32
|
+
*/
|
|
33
|
+
constructor(code: string, exitCode: number, message: string, hint?: string);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Shape of the error factory returned by {@link createCliErrors}.
|
|
37
|
+
* Each method produces a typed {@link CliError} with a known `code` and `exitCode`.
|
|
38
|
+
*/
|
|
39
|
+
export interface CliErrorsShape {
|
|
40
|
+
/**
|
|
41
|
+
* Creates an error when the command requires authentication but none is present.
|
|
42
|
+
* @param hint - Optional override of the default auth hint.
|
|
43
|
+
*/
|
|
44
|
+
authRequired(hint?: string): CliError;
|
|
45
|
+
/**
|
|
46
|
+
* Creates an error when the configuration file cannot be found.
|
|
47
|
+
* @param hint - Optional override of the default config hint.
|
|
48
|
+
*/
|
|
49
|
+
configMissing(hint?: string): CliError;
|
|
50
|
+
/** Creates an error when the current directory is not inside a project. */
|
|
51
|
+
notInProject(): CliError;
|
|
52
|
+
/**
|
|
53
|
+
* Creates an error for a failed API call.
|
|
54
|
+
* @param message - Raw API error message.
|
|
55
|
+
* @param hint - Optional hint for recovery.
|
|
56
|
+
*/
|
|
57
|
+
apiError(message: string, hint?: string): CliError;
|
|
58
|
+
/**
|
|
59
|
+
* Creates an error for a network-level failure.
|
|
60
|
+
* @param message - Brief description of the network problem.
|
|
61
|
+
*/
|
|
62
|
+
networkError(message: string): CliError;
|
|
63
|
+
/**
|
|
64
|
+
* Creates an error for an unrecognized command or subcommand.
|
|
65
|
+
* @param cmd - The command string that was not recognized.
|
|
66
|
+
*/
|
|
67
|
+
unknownCommand(cmd: string): CliError;
|
|
68
|
+
/**
|
|
69
|
+
* Creates an error when a required flag is missing.
|
|
70
|
+
* @param flagName - Name of the missing flag (without leading `--`).
|
|
71
|
+
* @param hint - Optional hint describing how to provide the flag.
|
|
72
|
+
*/
|
|
73
|
+
flagMissing(flagName: string, hint?: string): CliError;
|
|
74
|
+
/**
|
|
75
|
+
* Creates an error for general business-logic validation failures.
|
|
76
|
+
* @param message - Description of the validation failure.
|
|
77
|
+
* @param hint - Optional hint for correcting the input.
|
|
78
|
+
*/
|
|
79
|
+
validation(message: string, hint?: string): CliError;
|
|
80
|
+
/**
|
|
81
|
+
* Creates an error signalling the user cancelled an interactive prompt (Ctrl+C).
|
|
82
|
+
* Exit code is `0`; the top-level handler treats this as a silent success.
|
|
83
|
+
* @param message - Optional override of the default cancellation message.
|
|
84
|
+
*/
|
|
85
|
+
cancelled(message?: string): CliError;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Configuration options passed to {@link createCliErrors}.
|
|
89
|
+
* These supply the CLI binary name and the default hint messages used when
|
|
90
|
+
* callers do not provide explicit hints.
|
|
91
|
+
*/
|
|
92
|
+
export interface CliErrorFactoryOptions {
|
|
93
|
+
/** CLI binary name used in auto-generated hint messages (e.g. "rabetbase"). */
|
|
94
|
+
cliBinName: string;
|
|
95
|
+
/** Default hint shown when `authRequired()` is called without an explicit hint. */
|
|
96
|
+
authRequiredHint: string;
|
|
97
|
+
/** Default hint shown when `configMissing()` is called without an explicit hint. */
|
|
98
|
+
configMissingHint: string;
|
|
99
|
+
/** Default hint shown when `notInProject()` is called without an explicit hint. */
|
|
100
|
+
notInProjectHint: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a pre-configured {@link CliErrorsShape} instance.
|
|
104
|
+
* The returned factory uses `options.cliBinName` and the default hints from
|
|
105
|
+
* `options` for any method call that does not receive an explicit hint.
|
|
106
|
+
*
|
|
107
|
+
* @param options - Bin name and default hint messages.
|
|
108
|
+
* @returns A fully-populated error factory.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const cliErrors = createCliErrors({
|
|
113
|
+
* cliBinName: "mycli",
|
|
114
|
+
* authRequiredHint: "Run `mycli auth login` first.",
|
|
115
|
+
* configMissingHint: "Run `mycli init` to create a project.",
|
|
116
|
+
* notInProjectHint: " cd into your project directory and try again.",
|
|
117
|
+
* });
|
|
118
|
+
* throw cliErrors.flagMissing("appcode");
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function createCliErrors(options: CliErrorFactoryOptions): CliErrorsShape;
|
package/lib/errors.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Error types, factory interfaces, and creator for CLI-specific errors.
|
|
3
|
+
* All CLI errors are expressed as {@link CliError} instances with a machine-readable
|
|
4
|
+
* `code`, numeric `exitCode`, human-readable `message`, and optional `hint`.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Unified CLI error class thrown by command handlers and caught by the top-level
|
|
8
|
+
* error handler in the runner/adapter.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* throw new CliError("flag_missing", 1, "Missing required flag: --appcode", "Use --appcode=<code>");
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export class CliError extends Error {
|
|
16
|
+
/** Machine-readable error code (e.g. "auth_required", "flag_missing"). */
|
|
17
|
+
code;
|
|
18
|
+
/**
|
|
19
|
+
* Numeric exit code to emit on process exit.
|
|
20
|
+
* - `0` = success or user-cancelled
|
|
21
|
+
* - `1` = validation / user error
|
|
22
|
+
* - `2` = network / API / system error
|
|
23
|
+
*/
|
|
24
|
+
exitCode;
|
|
25
|
+
/** Optional hint shown after the error message to guide the user. */
|
|
26
|
+
hint;
|
|
27
|
+
/**
|
|
28
|
+
* @param code - Machine-readable error identifier.
|
|
29
|
+
* @param exitCode - Process exit code.
|
|
30
|
+
* @param message - Human-readable error message.
|
|
31
|
+
* @param hint - Optional actionable hint displayed below the message.
|
|
32
|
+
*/
|
|
33
|
+
constructor(code, exitCode, message, hint) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "CliError";
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.exitCode = exitCode;
|
|
38
|
+
this.hint = hint;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a pre-configured {@link CliErrorsShape} instance.
|
|
43
|
+
* The returned factory uses `options.cliBinName` and the default hints from
|
|
44
|
+
* `options` for any method call that does not receive an explicit hint.
|
|
45
|
+
*
|
|
46
|
+
* @param options - Bin name and default hint messages.
|
|
47
|
+
* @returns A fully-populated error factory.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const cliErrors = createCliErrors({
|
|
52
|
+
* cliBinName: "mycli",
|
|
53
|
+
* authRequiredHint: "Run `mycli auth login` first.",
|
|
54
|
+
* configMissingHint: "Run `mycli init` to create a project.",
|
|
55
|
+
* notInProjectHint: " cd into your project directory and try again.",
|
|
56
|
+
* });
|
|
57
|
+
* throw cliErrors.flagMissing("appcode");
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function createCliErrors(options) {
|
|
61
|
+
const { cliBinName, authRequiredHint, configMissingHint, notInProjectHint } = options;
|
|
62
|
+
return {
|
|
63
|
+
authRequired: (hint) => new CliError("auth_required", 1, "Authentication required", hint ?? authRequiredHint),
|
|
64
|
+
configMissing: (hint) => new CliError("config_missing", 1, "Configuration file not found", hint ?? configMissingHint),
|
|
65
|
+
notInProject: () => new CliError("not_in_project", 1, `Not in a ${cliBinName} project directory`, notInProjectHint),
|
|
66
|
+
apiError: (message, hint) => new CliError("api_error", 2, message, hint),
|
|
67
|
+
networkError: (message) => new CliError("network_error", 2, `Network error: ${message}`, "Check your internet connection and try again."),
|
|
68
|
+
unknownCommand: (cmd) => new CliError("unknown_command", 1, `Unknown command: ${cmd}`, `Run \`${cliBinName} --help\` to see available commands.`),
|
|
69
|
+
flagMissing: (flagName, hint) => new CliError("flag_missing", 1, `Missing required flag: --${flagName}`, hint),
|
|
70
|
+
validation: (message, hint) => new CliError("validation_error", 1, message, hint),
|
|
71
|
+
cancelled: (message) => new CliError("cancelled", 0, message ?? "Operation cancelled.", undefined),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Builds the complete effective flag list for a command.
|
|
3
|
+
*
|
|
4
|
+
* Injects framework-managed built-in flags (`--dry-run`, `--format`, `--yes`)
|
|
5
|
+
* alongside the command's own flag definitions, so the runner only needs to
|
|
6
|
+
* pass a single merged list to {@link createFlagHelpers}.
|
|
7
|
+
*/
|
|
8
|
+
import type { CommandDefinition, FlagDef } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Options for {@link buildAllFlags}.
|
|
11
|
+
*/
|
|
12
|
+
export interface BuildAllFlagsOptions {
|
|
13
|
+
/**
|
|
14
|
+
* The global default output format, used as the fallback for `--format`
|
|
15
|
+
* when the command does not define its own `defaultOutputFormat`.
|
|
16
|
+
*/
|
|
17
|
+
defaultOutputFormat: "json" | "pretty" | "compress";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds the full effective flag list for a command by appending
|
|
21
|
+
* framework-injected flags when appropriate:
|
|
22
|
+
*
|
|
23
|
+
* - `--dry-run` (boolean) — added only when the command defines a `dryRun` hook.
|
|
24
|
+
* - `--format` (string) — added unless `hasFormat === false`; defaults to
|
|
25
|
+
* `def.defaultOutputFormat ?? options.defaultOutputFormat`.
|
|
26
|
+
* - `--yes` (boolean) — added only for `high-risk-write` commands.
|
|
27
|
+
*
|
|
28
|
+
* @param def - Command definition to extend.
|
|
29
|
+
* @param options - Global options supplying the default format.
|
|
30
|
+
* @returns A new array containing command flags plus any applicable built-in flags.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const allFlags = buildAllFlags(myCommandDef, { defaultOutputFormat: "compress" });
|
|
35
|
+
* const { parseFlags } = createFlagHelpers(cliErrors);
|
|
36
|
+
* const flags = parseFlags(allFlags, rawFlags);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildAllFlags(def: CommandDefinition, options: BuildAllFlagsOptions): FlagDef[];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Builds the complete effective flag list for a command.
|
|
3
|
+
*
|
|
4
|
+
* Injects framework-managed built-in flags (`--dry-run`, `--format`, `--yes`)
|
|
5
|
+
* alongside the command's own flag definitions, so the runner only needs to
|
|
6
|
+
* pass a single merged list to {@link createFlagHelpers}.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Builds the full effective flag list for a command by appending
|
|
10
|
+
* framework-injected flags when appropriate:
|
|
11
|
+
*
|
|
12
|
+
* - `--dry-run` (boolean) — added only when the command defines a `dryRun` hook.
|
|
13
|
+
* - `--format` (string) — added unless `hasFormat === false`; defaults to
|
|
14
|
+
* `def.defaultOutputFormat ?? options.defaultOutputFormat`.
|
|
15
|
+
* - `--yes` (boolean) — added only for `high-risk-write` commands.
|
|
16
|
+
*
|
|
17
|
+
* @param def - Command definition to extend.
|
|
18
|
+
* @param options - Global options supplying the default format.
|
|
19
|
+
* @returns A new array containing command flags plus any applicable built-in flags.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const allFlags = buildAllFlags(myCommandDef, { defaultOutputFormat: "compress" });
|
|
24
|
+
* const { parseFlags } = createFlagHelpers(cliErrors);
|
|
25
|
+
* const flags = parseFlags(allFlags, rawFlags);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function buildAllFlags(def, options) {
|
|
29
|
+
const flags = [...def.flags];
|
|
30
|
+
if (def.dryRun) {
|
|
31
|
+
flags.push({
|
|
32
|
+
name: "dry-run",
|
|
33
|
+
type: "boolean",
|
|
34
|
+
description: "Preview the operation without executing",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (def.hasFormat !== false) {
|
|
38
|
+
flags.push({
|
|
39
|
+
name: "format",
|
|
40
|
+
type: "string",
|
|
41
|
+
default: def.defaultOutputFormat ?? options.defaultOutputFormat,
|
|
42
|
+
enum: ["json", "pretty", "compress"],
|
|
43
|
+
description: "Output format",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (def.risk === "high-risk-write") {
|
|
47
|
+
flags.push({
|
|
48
|
+
name: "yes",
|
|
49
|
+
type: "boolean",
|
|
50
|
+
description: "Skip confirmation prompt",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return flags;
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Flag parsing and validation helpers.
|
|
3
|
+
*
|
|
4
|
+
* Provides the {@link createFlagHelpers} factory which returns `parseFlags`
|
|
5
|
+
* and `validateFlags` — the two-phase pipeline used by the runner adapter.
|
|
6
|
+
*
|
|
7
|
+
* Phase 1 (`parseFlags`): coerce raw CLI inputs into typed values using
|
|
8
|
+
* {@link FlagDef} metadata.
|
|
9
|
+
*
|
|
10
|
+
* Phase 2 (`validateFlags`): enforce `required`, `enum`, and `pattern`
|
|
11
|
+
* constraints and throw typed {@link CliError} on violation.
|
|
12
|
+
*/
|
|
13
|
+
import type { CliErrorsShape } from "../errors.js";
|
|
14
|
+
import type { FlagDef } from "./types.js";
|
|
15
|
+
/**
|
|
16
|
+
* Result of {@link parseFlags}: a flat map of flag names to their coerced
|
|
17
|
+
* typed values. Missing flags with defaults are populated; missing flags
|
|
18
|
+
* without defaults are `undefined`.
|
|
19
|
+
*/
|
|
20
|
+
export type ParsedFlags = Record<string, string | boolean | number | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a paired set of flag helpers used by the framework runner.
|
|
23
|
+
*
|
|
24
|
+
* @param cliErrors - Subset of the error factory providing `flagMissing` and `validation`.
|
|
25
|
+
* @returns An object with `parseFlags` and `validateFlags` functions.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const { parseFlags, validateFlags } = createFlagHelpers(cliErrors);
|
|
30
|
+
* const flags = parseFlags(flagDefs, rawInput);
|
|
31
|
+
* validateFlags(flagDefs, flags, "api list");
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function createFlagHelpers(cliErrors: Pick<CliErrorsShape, "flagMissing" | "validation">): {
|
|
35
|
+
parseFlags: (defs: FlagDef[], rawFlags: Record<string, any>) => ParsedFlags;
|
|
36
|
+
validateFlags: (defs: FlagDef[], parsed: ParsedFlags, commandLabel: string) => void;
|
|
37
|
+
};
|