@levu/snap 0.1.1 → 0.2.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/CHANGELOG.md +41 -0
- package/README.md +26 -2
- package/dist/dx/terminal/index.d.ts +2 -1
- package/dist/dx/terminal/index.js +3 -1
- package/dist/dx/terminal/intro-outro.d.ts +4 -0
- package/dist/dx/terminal/intro-outro.js +44 -0
- package/dist/dx/terminal/output.d.ts +13 -1
- package/dist/dx/terminal/output.js +43 -2
- package/dist/dx/tui/index.d.ts +12 -0
- package/dist/dx/tui/index.js +12 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/tui/component-adapters/autocomplete.d.ts +15 -0
- package/dist/tui/component-adapters/autocomplete.js +34 -0
- package/dist/tui/component-adapters/note.d.ts +7 -0
- package/dist/tui/component-adapters/note.js +23 -0
- package/dist/tui/component-adapters/password.d.ts +7 -0
- package/dist/tui/component-adapters/password.js +24 -0
- package/dist/tui/component-adapters/progress.d.ts +7 -0
- package/dist/tui/component-adapters/progress.js +44 -0
- package/dist/tui/component-adapters/spinner.d.ts +10 -0
- package/dist/tui/component-adapters/spinner.js +48 -0
- package/dist/tui/component-adapters/tasks.d.ts +9 -0
- package/dist/tui/component-adapters/tasks.js +31 -0
- package/docs/component-reference.md +474 -0
- package/docs/getting-started.md +242 -0
- package/docs/help-contract-spec.md +29 -0
- package/docs/integration-examples.md +677 -0
- package/docs/module-authoring-guide.md +156 -0
- package/docs/snap-args.md +323 -0
- package/docs/snap-help.md +372 -0
- package/docs/snap-runtime.md +394 -0
- package/docs/snap-terminal.md +410 -0
- package/docs/snap-tui.md +529 -0
- package/package.json +4 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Module Authoring Guide
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Define actions once with full triad contract:
|
|
6
|
+
|
|
7
|
+
- `tui` flow steps (legacy `steps` or structured `flow`)
|
|
8
|
+
- `commandline` required/optional args
|
|
9
|
+
- `help` metadata
|
|
10
|
+
|
|
11
|
+
## Minimal Module Shape
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type { ModuleContract } from '../core/contracts/module-contract.js';
|
|
15
|
+
import { ExitCode } from '../core/errors/framework-errors.js';
|
|
16
|
+
|
|
17
|
+
const moduleContract: ModuleContract = {
|
|
18
|
+
moduleId: 'example',
|
|
19
|
+
description: 'Example module',
|
|
20
|
+
actions: [
|
|
21
|
+
{
|
|
22
|
+
actionId: 'run',
|
|
23
|
+
description: 'Run example action',
|
|
24
|
+
tui: { steps: ['collect-input', 'confirm'] },
|
|
25
|
+
commandline: { requiredArgs: ['name'] },
|
|
26
|
+
help: {
|
|
27
|
+
summary: 'Run example with one name argument.',
|
|
28
|
+
args: [{ name: 'name', required: true, description: 'Target name' }],
|
|
29
|
+
examples: ['hub example run --name=alice'],
|
|
30
|
+
useCases: [{ name: 'default', description: 'Basic usage', command: 'hub example run --name=alice' }],
|
|
31
|
+
keybindings: ['Enter confirm', 'Esc cancel']
|
|
32
|
+
},
|
|
33
|
+
run: async (context) => ({
|
|
34
|
+
ok: true,
|
|
35
|
+
mode: context.mode,
|
|
36
|
+
exitCode: ExitCode.SUCCESS,
|
|
37
|
+
data: `hello ${String(context.args.name ?? '')}`
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default moduleContract;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Structured TUI Flow (Optional)
|
|
47
|
+
|
|
48
|
+
Use `tui.flow` when you need explicit component/step definitions:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
tui: {
|
|
52
|
+
flow: {
|
|
53
|
+
entryStepId: 'operation',
|
|
54
|
+
steps: [
|
|
55
|
+
{
|
|
56
|
+
stepId: 'operation',
|
|
57
|
+
title: 'Choose operation',
|
|
58
|
+
components: [
|
|
59
|
+
{
|
|
60
|
+
componentId: 'op',
|
|
61
|
+
type: 'select',
|
|
62
|
+
label: 'Operation',
|
|
63
|
+
arg: 'op',
|
|
64
|
+
required: true,
|
|
65
|
+
options: [
|
|
66
|
+
{ value: 'list', label: 'List profiles' },
|
|
67
|
+
{ value: 'upsert', label: 'Create/update profile' }
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`tui.steps` remains supported and backward compatible.
|
|
78
|
+
|
|
79
|
+
## Custom TUI Components
|
|
80
|
+
|
|
81
|
+
For advanced prompts (for example custom searchable selectors inspired by Clack patterns), declare custom components in flow metadata:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { SnapTui } from '../src/index.js';
|
|
85
|
+
|
|
86
|
+
const flow = SnapTui.defineTuiFlow({
|
|
87
|
+
entryStepId: 'choose',
|
|
88
|
+
steps: [
|
|
89
|
+
{
|
|
90
|
+
stepId: 'choose',
|
|
91
|
+
title: 'Choose target',
|
|
92
|
+
components: [
|
|
93
|
+
SnapTui.defineCustomTuiComponent({
|
|
94
|
+
componentId: 'target',
|
|
95
|
+
label: 'Target',
|
|
96
|
+
arg: 'target',
|
|
97
|
+
renderer: 'searchable-select',
|
|
98
|
+
config: { maxItems: 8, allowCustomValue: true }
|
|
99
|
+
})
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`renderer` identifies your adapter implementation while preserving a typed, framework-level contract.
|
|
107
|
+
|
|
108
|
+
## Register Module
|
|
109
|
+
|
|
110
|
+
Add module to registry bootstrapping in `/Users/khang/Documents/repo/snap/src/cli-entry.ts`.
|
|
111
|
+
|
|
112
|
+
## CLI Bootstrapping Helpers
|
|
113
|
+
|
|
114
|
+
Use Snap CLI helpers so module/tool authors do not re-implement argv parsing and dispatch:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { createRegistry, runMultiModuleCli, runSingleModuleCli, runSubmoduleCli } from '../src/index.js';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- `runMultiModuleCli`: standard `tool <module> <action> --args`.
|
|
121
|
+
- `runSingleModuleCli`: dedicated tool package mapped to one module, with optional default action.
|
|
122
|
+
- `runSubmoduleCli`: dedicated tool package with multiple sub-modules (`tool <submodule> ...`) plus optional default sub-module.
|
|
123
|
+
- Supports `-h/--help`.
|
|
124
|
+
- Parses `--key=value`, `--key value`, and boolean flags.
|
|
125
|
+
- Routes help and action dispatch through framework runtime.
|
|
126
|
+
|
|
127
|
+
## DX Helpers
|
|
128
|
+
|
|
129
|
+
Snap exposes optional helpers so action authors avoid low-level terminal and argv plumbing:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { SnapArgs, SnapHelp, SnapRuntime, SnapTui } from '../src/index.js';
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- `SnapArgs`: typed argument readers and parsers (`readStringArg`, `readBooleanArg`, `collectUpperSnakeCaseEnvArgs`).
|
|
136
|
+
- `SnapHelp`: help/commandline builder from arg schema (`defineArgSchema`, `buildHelpFromArgSchema`, `commandlineFromArgSchema`).
|
|
137
|
+
- `SnapRuntime`: action result helpers (`runActionSafely`, standardized success/error envelopes).
|
|
138
|
+
- `SnapTui`: typed TUI flow/component definitions (`defineTuiFlow`, `defineCustomTuiComponent`).
|
|
139
|
+
|
|
140
|
+
Action runtime context already includes friendly APIs:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
context.prompts.text(...)
|
|
144
|
+
context.prompts.select(...)
|
|
145
|
+
context.terminal.line(...)
|
|
146
|
+
context.flow.next()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Interactive prompt calls are rendered with Clack components through Snap adapters, including option hints and cancellation handling.
|
|
150
|
+
|
|
151
|
+
No direct `process.stdout` / state-machine wiring is required in module actions.
|
|
152
|
+
|
|
153
|
+
## Validation Rules
|
|
154
|
+
|
|
155
|
+
Registration fails if any action misses triad data (`tui`, `commandline`, `help`).
|
|
156
|
+
A valid `tui` must provide either non-empty `steps` or non-empty `flow.steps`.
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# SnapArgs - Type-Safe Argument Reading
|
|
2
|
+
|
|
3
|
+
`SnapArgs` provides typed helper functions for reading and parsing command-line arguments and environment variables.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import * as SnapArgs from 'snap-framework';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API Reference
|
|
12
|
+
|
|
13
|
+
### `readStringArg(args, ...keys)`
|
|
14
|
+
|
|
15
|
+
Reads a string argument from multiple possible keys, returning the first non-empty value.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const name = SnapArgs.readStringArg(context.args, 'name', 'username', 'user');
|
|
19
|
+
// Returns: string | undefined
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Usage Example:**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
run: async (context) => {
|
|
26
|
+
const name = SnapArgs.readStringArg(context.args, 'name', 'username');
|
|
27
|
+
if (!name) {
|
|
28
|
+
return { ok: false, errorMessage: 'Name is required' };
|
|
29
|
+
}
|
|
30
|
+
return { ok: true, data: `Hello, ${name}` };
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `readRequiredStringArg(args, key, message?)`
|
|
35
|
+
|
|
36
|
+
Reads a required string argument, throwing an error if not present.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const name = SnapArgs.readRequiredStringArg(context.args, 'name', 'Name is required');
|
|
40
|
+
// Returns: string (throws if missing)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Usage Example:**
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
run: async (context) => {
|
|
47
|
+
try {
|
|
48
|
+
const name = SnapArgs.readRequiredStringArg(context.args, 'name');
|
|
49
|
+
return { ok: true, data: `Hello, ${name}` };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
errorMessage: error instanceof Error ? error.message : 'Unknown error'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### `readBooleanArg(args, ...keys)`
|
|
60
|
+
|
|
61
|
+
Reads a boolean argument, parsing common string representations.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const verbose = SnapArgs.readBooleanArg(context.args, 'verbose', 'v');
|
|
65
|
+
// Returns: boolean | undefined
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Supported string values:**
|
|
69
|
+
- **True**: `'1'`, `'true'`, `'yes'`, `'y'`, `'on'`
|
|
70
|
+
- **False**: `'0'`, `'false'`, `'no'`, `'n'`, `'off'`
|
|
71
|
+
|
|
72
|
+
**Usage Example:**
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
run: async (context) => {
|
|
76
|
+
const verbose = SnapArgs.readBooleanArg(context.args, 'verbose', 'v');
|
|
77
|
+
const debug = SnapArgs.readBooleanArg(context.args, 'debug');
|
|
78
|
+
|
|
79
|
+
if (verbose) {
|
|
80
|
+
context.terminal.line('Verbose mode enabled');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { ok: true, data: { debug, verbose } };
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `parseBooleanLike(value)`
|
|
88
|
+
|
|
89
|
+
Parses a boolean-like string value into a boolean.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const isTrue = SnapArgs.parseBooleanLike('yes'); // true
|
|
93
|
+
const isFalse = SnapArgs.parseBooleanLike('0'); // false
|
|
94
|
+
const isUndefined = SnapArgs.parseBooleanLike(undefined); // undefined
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Usage Example:**
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
run: async (context) => {
|
|
101
|
+
const forceValue = context.args.force;
|
|
102
|
+
|
|
103
|
+
if (forceValue !== undefined) {
|
|
104
|
+
const force = SnapArgs.parseBooleanLike(forceValue);
|
|
105
|
+
// Use force boolean
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { ok: true, data: {} };
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `collectUpperSnakeCaseEnvArgs(args, prefix)`
|
|
113
|
+
|
|
114
|
+
Collects environment variables with a given prefix into a typed object.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const envArgs = SnapArgs.collectUpperSnakeCaseEnvArgs(context.args, 'MYAPP_');
|
|
118
|
+
// Returns: Partial<Record<UpperSnakeCaseKey, string>>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Usage Example:**
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// With environment variables: MYAPP_API_KEY, MYAPP_TIMEOUT
|
|
125
|
+
run: async (context) => {
|
|
126
|
+
const envArgs = SnapArgs.collectUpperSnakeCaseEnvArgs(context.args, 'MYAPP_');
|
|
127
|
+
|
|
128
|
+
const apiKey = envArgs.MYAPP_API_KEY;
|
|
129
|
+
const timeout = envArgs.MYAPP_TIMEOUT;
|
|
130
|
+
|
|
131
|
+
return { ok: true, data: { apiKey, timeout } };
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Complete Example Module
|
|
136
|
+
|
|
137
|
+
Here's a complete module using `SnapArgs` helpers:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import type { ModuleContract } from 'snap-framework';
|
|
141
|
+
import { ExitCode } from 'snap-framework';
|
|
142
|
+
import * as SnapArgs from 'snap-framework';
|
|
143
|
+
|
|
144
|
+
const deployModule: ModuleContract = {
|
|
145
|
+
moduleId: 'deploy',
|
|
146
|
+
description: 'Deployment management',
|
|
147
|
+
actions: [
|
|
148
|
+
{
|
|
149
|
+
actionId: 'start',
|
|
150
|
+
description: 'Start a deployment',
|
|
151
|
+
tui: {
|
|
152
|
+
steps: ['collect-environment', 'collect-options', 'confirm'],
|
|
153
|
+
flow: {
|
|
154
|
+
entryStepId: 'collect-environment',
|
|
155
|
+
steps: [
|
|
156
|
+
{
|
|
157
|
+
stepId: 'collect-environment',
|
|
158
|
+
title: 'Select Environment',
|
|
159
|
+
components: [
|
|
160
|
+
{
|
|
161
|
+
componentId: 'env',
|
|
162
|
+
type: 'select',
|
|
163
|
+
label: 'Environment',
|
|
164
|
+
arg: 'environment',
|
|
165
|
+
required: true,
|
|
166
|
+
options: [
|
|
167
|
+
{ value: 'development', label: 'Development' },
|
|
168
|
+
{ value: 'staging', label: 'Staging' },
|
|
169
|
+
{ value: 'production', label: 'Production' }
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
stepId: 'collect-options',
|
|
176
|
+
title: 'Deployment Options',
|
|
177
|
+
components: [
|
|
178
|
+
{
|
|
179
|
+
componentId: 'verbose',
|
|
180
|
+
type: 'confirm',
|
|
181
|
+
label: 'Enable verbose logging',
|
|
182
|
+
arg: 'verbose'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
componentId: 'force',
|
|
186
|
+
type: 'confirm',
|
|
187
|
+
label: 'Force deployment (skip checks)',
|
|
188
|
+
arg: 'force'
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
stepId: 'confirm',
|
|
194
|
+
title: 'Confirm Deployment'
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
commandline: {
|
|
200
|
+
requiredArgs: ['environment'],
|
|
201
|
+
optionalArgs: ['verbose', 'force']
|
|
202
|
+
},
|
|
203
|
+
help: {
|
|
204
|
+
summary: 'Deploy application to the specified environment.',
|
|
205
|
+
args: [
|
|
206
|
+
{
|
|
207
|
+
name: 'environment',
|
|
208
|
+
required: true,
|
|
209
|
+
description: 'Target deployment environment',
|
|
210
|
+
example: '--environment=production'
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'verbose',
|
|
214
|
+
required: false,
|
|
215
|
+
description: 'Enable verbose output',
|
|
216
|
+
example: '--verbose=true'
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'force',
|
|
220
|
+
required: false,
|
|
221
|
+
description: 'Skip pre-deployment checks',
|
|
222
|
+
example: '--force=yes'
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
examples: [
|
|
226
|
+
'mytool deploy start --environment=production',
|
|
227
|
+
'mytool deploy start --environment=staging --verbose=true'
|
|
228
|
+
],
|
|
229
|
+
useCases: [
|
|
230
|
+
{
|
|
231
|
+
name: 'production',
|
|
232
|
+
description: 'Deploy to production',
|
|
233
|
+
command: 'mytool deploy start --environment=production'
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
keybindings: ['Enter confirm', 'Esc cancel']
|
|
237
|
+
},
|
|
238
|
+
run: async (context) => {
|
|
239
|
+
// Read required environment
|
|
240
|
+
const environment = SnapArgs.readRequiredStringArg(
|
|
241
|
+
context.args,
|
|
242
|
+
'environment',
|
|
243
|
+
'Environment is required'
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
// Read optional boolean flags
|
|
247
|
+
const verbose = SnapArgs.readBooleanArg(context.args, 'verbose') ?? false;
|
|
248
|
+
const force = SnapArgs.readBooleanArg(context.args, 'force') ?? false;
|
|
249
|
+
|
|
250
|
+
if (verbose) {
|
|
251
|
+
context.terminal.line(`Deploying to ${environment}...`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (force) {
|
|
255
|
+
context.terminal.line('Warning: Skipping pre-deployment checks!');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Perform deployment logic here...
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
ok: true,
|
|
262
|
+
mode: context.mode,
|
|
263
|
+
exitCode: ExitCode.SUCCESS,
|
|
264
|
+
data: `Deployed to ${environment}`
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export default deployModule;
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Environment Variable Integration
|
|
275
|
+
|
|
276
|
+
Combine commandline args with environment variables:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
run: async (context) => {
|
|
280
|
+
// Try commandline first, fall back to environment
|
|
281
|
+
const apiKey = SnapArgs.readStringArg(
|
|
282
|
+
context.args,
|
|
283
|
+
'api-key',
|
|
284
|
+
'apiKey',
|
|
285
|
+
'API_KEY'
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
if (!apiKey) {
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
errorMessage: 'API key must be provided via --api-key or API_KEY env var'
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return { ok: true, data: { apiKey } };
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Best Practices
|
|
300
|
+
|
|
301
|
+
1. **Always validate required args**: Use `readRequiredStringArg` for critical arguments
|
|
302
|
+
2. **Provide fallback values**: Use `??` operator for optional defaults
|
|
303
|
+
3. **Support multiple key aliases**: Use variadic args for flexible naming
|
|
304
|
+
4. **Parse booleans safely**: Always use `readBooleanArg` for flag arguments
|
|
305
|
+
5. **Collect environment prefixes**: Use `collectUpperSnakeCaseEnvArgs` for env var grouping
|
|
306
|
+
|
|
307
|
+
## Type Safety
|
|
308
|
+
|
|
309
|
+
`SnapArgs` provides full TypeScript type safety:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import type { CliArgs } from 'snap-framework';
|
|
313
|
+
|
|
314
|
+
const args: CliArgs = {
|
|
315
|
+
name: 'Alice',
|
|
316
|
+
verbose: 'true',
|
|
317
|
+
count: '42'
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// TypeScript knows these return types
|
|
321
|
+
const name: string | undefined = SnapArgs.readStringArg(args, 'name');
|
|
322
|
+
const verbose: boolean | undefined = SnapArgs.readBooleanArg(args, 'verbose');
|
|
323
|
+
```
|