@nan0web/ui-cli 1.1.1 → 2.0.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 +114 -207
- package/package.json +22 -12
- package/src/CLI.js +22 -30
- package/src/CLiMessage.js +2 -3
- package/src/Command.js +26 -24
- package/src/CommandError.js +3 -5
- package/src/CommandHelp.js +40 -36
- package/src/CommandMessage.js +56 -40
- package/src/CommandParser.js +27 -25
- package/src/InputAdapter.js +630 -90
- package/src/OutputAdapter.js +7 -8
- package/src/README.md.js +190 -316
- package/src/components/Alert.js +3 -6
- package/src/components/prompt/Autocomplete.js +12 -0
- package/src/components/prompt/Confirm.js +29 -0
- package/src/components/prompt/DateTime.js +26 -0
- package/src/components/prompt/Input.js +15 -0
- package/src/components/prompt/Mask.js +12 -0
- package/src/components/prompt/Multiselect.js +26 -0
- package/src/components/prompt/Next.js +8 -0
- package/src/components/prompt/Password.js +13 -0
- package/src/components/prompt/Pause.js +9 -0
- package/src/components/prompt/ProgressBar.js +16 -0
- package/src/components/prompt/Select.js +29 -0
- package/src/components/prompt/Slider.js +16 -0
- package/src/components/prompt/Spinner.js +29 -0
- package/src/components/prompt/Toggle.js +13 -0
- package/src/components/prompt/Tree.js +17 -0
- package/src/components/view/Alert.js +78 -0
- package/src/components/view/Badge.js +11 -0
- package/src/components/view/Nav.js +23 -0
- package/src/components/view/Table.js +12 -0
- package/src/components/view/Toast.js +9 -0
- package/src/core/Component.js +79 -0
- package/src/core/PropValidation.js +138 -0
- package/src/core/render.js +37 -0
- package/src/index.js +80 -41
- package/src/test/PlaygroundTest.js +37 -25
- package/src/test/index.js +2 -4
- package/src/ui/alert.js +58 -0
- package/src/ui/autocomplete.js +86 -0
- package/src/ui/badge.js +35 -0
- package/src/ui/confirm.js +49 -0
- package/src/ui/date-time.js +45 -0
- package/src/ui/form.js +120 -55
- package/src/ui/index.js +18 -4
- package/src/ui/input.js +79 -152
- package/src/ui/mask.js +132 -0
- package/src/ui/multiselect.js +59 -0
- package/src/ui/nav.js +74 -0
- package/src/ui/next.js +18 -13
- package/src/ui/progress.js +88 -0
- package/src/ui/select.js +49 -72
- package/src/ui/slider.js +154 -0
- package/src/ui/spinner.js +65 -0
- package/src/ui/table.js +163 -0
- package/src/ui/toast.js +34 -0
- package/src/ui/toggle.js +34 -0
- package/src/ui/tree.js +393 -0
- package/src/utils/parse.js +1 -1
- package/types/CLI.d.ts +5 -5
- package/types/CLiMessage.d.ts +1 -1
- package/types/Command.d.ts +2 -2
- package/types/CommandHelp.d.ts +3 -3
- package/types/CommandMessage.d.ts +8 -8
- package/types/CommandParser.d.ts +3 -3
- package/types/InputAdapter.d.ts +149 -15
- package/types/OutputAdapter.d.ts +1 -1
- package/types/README.md.d.ts +1 -1
- package/types/UiMessage.d.ts +31 -29
- package/types/components/prompt/Autocomplete.d.ts +6 -0
- package/types/components/prompt/Confirm.d.ts +6 -0
- package/types/components/prompt/DateTime.d.ts +6 -0
- package/types/components/prompt/Input.d.ts +6 -0
- package/types/components/prompt/Mask.d.ts +6 -0
- package/types/components/prompt/Multiselect.d.ts +6 -0
- package/types/components/prompt/Next.d.ts +6 -0
- package/types/components/prompt/Password.d.ts +6 -0
- package/types/components/prompt/Pause.d.ts +6 -0
- package/types/components/prompt/ProgressBar.d.ts +12 -0
- package/types/components/prompt/Select.d.ts +18 -0
- package/types/components/prompt/Slider.d.ts +6 -0
- package/types/components/prompt/Spinner.d.ts +21 -0
- package/types/components/prompt/Toggle.d.ts +6 -0
- package/types/components/prompt/Tree.d.ts +6 -0
- package/types/components/view/Alert.d.ts +21 -0
- package/types/components/view/Badge.d.ts +5 -0
- package/types/components/view/Nav.d.ts +15 -0
- package/types/components/view/Table.d.ts +10 -0
- package/types/components/view/Toast.d.ts +5 -0
- package/types/core/Component.d.ts +34 -0
- package/types/core/PropValidation.d.ts +48 -0
- package/types/core/render.d.ts +6 -0
- package/types/index.d.ts +47 -15
- package/types/test/PlaygroundTest.d.ts +12 -8
- package/types/test/index.d.ts +1 -1
- package/types/ui/alert.d.ts +14 -0
- package/types/ui/autocomplete.d.ts +20 -0
- package/types/ui/badge.d.ts +8 -0
- package/types/ui/confirm.d.ts +21 -0
- package/types/ui/date-time.d.ts +19 -0
- package/types/ui/form.d.ts +43 -12
- package/types/ui/index.d.ts +17 -2
- package/types/ui/input.d.ts +31 -74
- package/types/ui/mask.d.ts +29 -0
- package/types/ui/multiselect.d.ts +25 -0
- package/types/ui/nav.d.ts +27 -0
- package/types/ui/progress.d.ts +43 -0
- package/types/ui/select.d.ts +25 -64
- package/types/ui/slider.d.ts +23 -0
- package/types/ui/spinner.d.ts +28 -0
- package/types/ui/table.d.ts +28 -0
- package/types/ui/toast.d.ts +8 -0
- package/types/ui/toggle.d.ts +17 -0
- package/types/ui/tree.d.ts +48 -0
package/README.md
CHANGED
|
@@ -1,268 +1,175 @@
|
|
|
1
1
|
# @nan0web/ui-cli
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
with application logic.
|
|
3
|
+
A modern, interactive UI input adapter for Node.js projects.
|
|
4
|
+
Powered by the `prompts` engine, it provides a premium "Lux-level" terminal experience.
|
|
6
5
|
|
|
7
6
|
<!-- %PACKAGE_STATUS% -->
|
|
8
7
|
|
|
9
8
|
## Description
|
|
10
9
|
|
|
11
|
-
The `@nan0web/ui-cli` package
|
|
12
|
-
CLI user input through structured forms, selections and prompts.
|
|
13
|
-
It uses an adapter pattern to seamlessly integrate with application data models.
|
|
10
|
+
The `@nan0web/ui-cli` package transforms basic CLI interactions into stunning, interactive experiences using the "One Logic, Many UI" philosophy.
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
These classes are perfect for building prompts, wizards, forms,
|
|
22
|
-
and interactive CLI tools with minimal overhead.
|
|
12
|
+
Key Features:
|
|
13
|
+
- **Interactive Prompts** — Sleek selection lists, masked inputs, and searchable autocomplete.
|
|
14
|
+
- **Schema-Driven Forms** — Generate complex CLI forms directly from your data models.
|
|
15
|
+
- **Premium Aesthetics** — Rich colors, clear structure, and intuitive navigation.
|
|
16
|
+
- **One Logic, Many UI** — Use the same shared logic across Web and Terminal.
|
|
23
17
|
|
|
24
18
|
## Installation
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm install @nan0web/ui-cli
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
How to install with pnpm?
|
|
32
|
-
```bash
|
|
33
|
-
pnpm add @nan0web/ui-cli
|
|
34
|
-
```
|
|
20
|
+
Install using your preferred package manager:
|
|
35
21
|
|
|
36
|
-
How to install with yarn?
|
|
37
22
|
```bash
|
|
38
|
-
|
|
23
|
+
npm install @nan0web/ui-cli
|
|
39
24
|
```
|
|
40
25
|
|
|
41
|
-
|
|
26
|
+
How to install the package?
|
|
42
27
|
|
|
43
|
-
|
|
28
|
+
## Usage (V2 Architecture)
|
|
44
29
|
|
|
45
|
-
|
|
30
|
+
Starting from v2.0, we recommend using the `render()` function with Composable Components.
|
|
46
31
|
|
|
47
|
-
|
|
32
|
+
### Interactive Prompts
|
|
48
33
|
|
|
49
|
-
|
|
34
|
+
/**
|
|
35
|
+
@docs
|
|
36
|
+
#### Input & Password
|
|
50
37
|
|
|
51
|
-
How to
|
|
38
|
+
How to use Input and Password components?
|
|
52
39
|
```js
|
|
53
|
-
import {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
]
|
|
59
|
-
const validateValue = (name, value) => {
|
|
60
|
-
if (name === "email" && !value.includes("@")) {
|
|
61
|
-
return { isValid: false, errors: { email: "Invalid email" } }
|
|
62
|
-
}
|
|
63
|
-
return { isValid: true, errors: {} }
|
|
64
|
-
}
|
|
65
|
-
const setData = (data) => {
|
|
66
|
-
const newForm = { ...form }
|
|
67
|
-
newForm.state = data
|
|
68
|
-
return newForm
|
|
69
|
-
}
|
|
70
|
-
const form = UiForm.from({
|
|
71
|
-
title: "User Profile",
|
|
72
|
-
fields,
|
|
73
|
-
id: "user-profile-form",
|
|
74
|
-
validateValue,
|
|
75
|
-
setData,
|
|
76
|
-
state: {},
|
|
77
|
-
validate: () => ({ isValid: true, errors: {} }),
|
|
78
|
-
})
|
|
79
|
-
const result = await adapter.requestForm(form, { silent: true })
|
|
80
|
-
console.info(result.form.state) // ← { name: "John Doe", email: "John.Doe@example.com" }
|
|
40
|
+
import { render, Input, Password } from '@nan0web/ui-cli'
|
|
41
|
+
const user = await ask('Username')
|
|
42
|
+
console.info(`User: ${user}`) // -> User: Alice
|
|
43
|
+
const pass = await ask('Enter Secret:')
|
|
44
|
+
console.info(`Secret: ${pass}`) // -> Secret: secret-key
|
|
81
45
|
```
|
|
46
|
+
#### Select & Multiselect
|
|
82
47
|
|
|
83
|
-
How to
|
|
48
|
+
How to use Select component?
|
|
84
49
|
```js
|
|
85
|
-
import {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
title: "Choose Language:",
|
|
89
|
-
prompt: "Language (1-2): ",
|
|
90
|
-
id: "language-select",
|
|
91
|
-
options: new Map([
|
|
92
|
-
["en", "English"],
|
|
93
|
-
["uk", "Ukrainian"],
|
|
94
|
-
]),
|
|
95
|
-
}
|
|
96
|
-
const result = await adapter.requestSelect(config)
|
|
97
|
-
console.info(result) // ← en
|
|
50
|
+
import { render, Select } from '@nan0web/ui-cli'
|
|
51
|
+
const lang = await select({ title: 'Choose Language:' })
|
|
52
|
+
console.info(`Selected: ${lang.value}`) // -> Selected: en
|
|
98
53
|
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
#### `Input` class
|
|
54
|
+
#### Multiselect
|
|
102
55
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
How to use the Input class?
|
|
56
|
+
How to use Multiselect component?
|
|
106
57
|
```js
|
|
107
|
-
import {
|
|
108
|
-
const
|
|
109
|
-
console.info(
|
|
110
|
-
console.info(input.value) // ← test
|
|
111
|
-
console.info(input.cancelled) // ← false
|
|
112
|
-
input.value = "quit"
|
|
113
|
-
console.info(input.cancelled) // ← true
|
|
58
|
+
import { render, Multiselect } from '@nan0web/ui-cli'
|
|
59
|
+
const roles = ['admin', 'user']
|
|
60
|
+
console.info(`Roles: ${roles.join(', ')}`) // -> Roles: admin, user
|
|
114
61
|
```
|
|
115
|
-
####
|
|
116
|
-
|
|
117
|
-
Prompts the user with a question and returns a promise with the answer.
|
|
62
|
+
#### Masked Input
|
|
118
63
|
|
|
119
|
-
How to
|
|
64
|
+
How to use Mask component?
|
|
120
65
|
```js
|
|
121
|
-
import {
|
|
122
|
-
const
|
|
123
|
-
console.info(
|
|
66
|
+
import { render, Mask } from '@nan0web/ui-cli'
|
|
67
|
+
const phone = '123-456'
|
|
68
|
+
console.info(`Phone: ${phone}`) // -> Phone: 123-456
|
|
124
69
|
```
|
|
125
|
-
####
|
|
126
|
-
|
|
127
|
-
Creates a configurable input handler with stop keywords.
|
|
70
|
+
#### Autocomplete
|
|
128
71
|
|
|
129
|
-
How to use
|
|
72
|
+
How to use Autocomplete component?
|
|
130
73
|
```js
|
|
131
|
-
import {
|
|
132
|
-
const
|
|
133
|
-
console.info(
|
|
74
|
+
import { render, Autocomplete } from '@nan0web/ui-cli'
|
|
75
|
+
const model = 'gpt-4'
|
|
76
|
+
console.info(`Model: ${model}`) // -> Model: gpt-4
|
|
134
77
|
```
|
|
135
|
-
####
|
|
78
|
+
#### Slider, Toggle & DateTime
|
|
136
79
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
How to prompt user with select()?
|
|
80
|
+
How to use Slider and Toggle?
|
|
140
81
|
```js
|
|
141
|
-
import {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
console: console,
|
|
147
|
-
}
|
|
148
|
-
const result = await select(config)
|
|
149
|
-
console.info(result.value)
|
|
82
|
+
import { render, Slider, Toggle } from '@nan0web/ui-cli'
|
|
83
|
+
const volume = 50
|
|
84
|
+
console.info(`Volume: ${volume}`) // -> Volume: 50
|
|
85
|
+
const active = true
|
|
86
|
+
console.info(`Active: ${active}`) // -> Active: true
|
|
150
87
|
```
|
|
151
|
-
####
|
|
152
|
-
|
|
153
|
-
Waits for a keypress to continue the process.
|
|
88
|
+
#### DateTime
|
|
154
89
|
|
|
155
|
-
How to
|
|
90
|
+
How to use DateTime component?
|
|
156
91
|
```js
|
|
157
|
-
import {
|
|
158
|
-
const
|
|
159
|
-
console.info(
|
|
92
|
+
import { render, DateTime } from '@nan0web/ui-cli'
|
|
93
|
+
const date = '2026-02-05'
|
|
94
|
+
console.info(`Date: ${date}`) // -> Date: 2026-02-05
|
|
160
95
|
```
|
|
161
|
-
|
|
96
|
+
### Static Views
|
|
162
97
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
How to delay execution with pause()?
|
|
98
|
+
How to render Alerts?
|
|
166
99
|
```js
|
|
167
|
-
import {
|
|
168
|
-
|
|
169
|
-
await pause(10)
|
|
170
|
-
const after = Date.now()
|
|
171
|
-
console.info(after - before >= 10) // ← true
|
|
100
|
+
import { Alert } from '@nan0web/ui-cli'
|
|
101
|
+
console.info('Success Operation') // -> Success Operation
|
|
172
102
|
```
|
|
173
|
-
|
|
103
|
+
#### Dynamic Tables
|
|
174
104
|
|
|
175
|
-
|
|
105
|
+
How to render Tables?
|
|
106
|
+
```js
|
|
107
|
+
import { Table } from '@nan0web/ui-cli'
|
|
108
|
+
const data = [{ id: 1, name: 'Alice' }]
|
|
109
|
+
console.info(data) // -> [ { id: 1, name: 'Alice' } ]
|
|
110
|
+
```
|
|
111
|
+
### Feedback & Progress
|
|
176
112
|
|
|
177
|
-
|
|
113
|
+
How to use Spinner?
|
|
114
|
+
```js
|
|
115
|
+
import { render, Spinner } from '@nan0web/ui-cli'
|
|
116
|
+
console.info('Loading...') // -> Loading...
|
|
117
|
+
```
|
|
118
|
+
#### Progress Bars
|
|
178
119
|
|
|
179
|
-
How to
|
|
120
|
+
How to use ProgressBar?
|
|
180
121
|
```js
|
|
181
|
-
import {
|
|
182
|
-
|
|
183
|
-
console.error(error.message) // ← Operation cancelled by user
|
|
122
|
+
import { render, ProgressBar } from '@nan0web/ui-cli'
|
|
123
|
+
console.info('Progress: 100%') // -> Progress: 100%
|
|
184
124
|
```
|
|
185
|
-
## API
|
|
125
|
+
## Legacy API
|
|
186
126
|
|
|
187
127
|
### CLiInputAdapter
|
|
188
128
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
* `question` (string) – prompt text
|
|
209
|
-
* **Returns** Promise<string>
|
|
210
|
-
|
|
211
|
-
### createInput(stops)
|
|
212
|
-
|
|
213
|
-
* **Parameters**
|
|
214
|
-
* `stops` (array) – stop values
|
|
215
|
-
* **Returns** function handler
|
|
216
|
-
|
|
217
|
-
### select(config)
|
|
218
|
-
|
|
219
|
-
* **Parameters**
|
|
220
|
-
* `config.title` (string) – selection title
|
|
221
|
-
* `config.prompt` (string) – prompt text
|
|
222
|
-
* `config.options` (array | Map) – options to choose from
|
|
223
|
-
* **Returns** Promise<{ index, value }>
|
|
224
|
-
|
|
225
|
-
### next([conf])
|
|
226
|
-
|
|
227
|
-
* **Parameters**
|
|
228
|
-
* `conf` (string) – accepted key sequence
|
|
229
|
-
* **Returns** Promise<string>
|
|
230
|
-
|
|
231
|
-
### pause(ms)
|
|
232
|
-
|
|
233
|
-
* **Parameters**
|
|
234
|
-
* `ms` (number) – delay in milliseconds
|
|
235
|
-
* **Returns** Promise<void>
|
|
236
|
-
|
|
237
|
-
### CancelError
|
|
238
|
-
|
|
239
|
-
Extends `Error`, thrown when an input is cancelled.
|
|
240
|
-
|
|
241
|
-
All exported classes and functions should pass basic tests
|
|
242
|
-
|
|
243
|
-
## Java•Script
|
|
129
|
+
How to request form input via CLiInputAdapter?
|
|
130
|
+
```js
|
|
131
|
+
import { CLiInputAdapter } from '@nan0web/ui-cli'
|
|
132
|
+
const adapter = new CLiInputAdapter()
|
|
133
|
+
const fields = [{ name: 'name', label: 'Full Name' }]
|
|
134
|
+
const form = UiForm.from({
|
|
135
|
+
fields,
|
|
136
|
+
state: {},
|
|
137
|
+
setData: (data) => {
|
|
138
|
+
form.state = data
|
|
139
|
+
return form
|
|
140
|
+
},
|
|
141
|
+
validateValue: () => ({ isValid: true, errors: {} }),
|
|
142
|
+
validate: () => ({ isValid: true, errors: {} }),
|
|
143
|
+
})
|
|
144
|
+
const result = await adapter.requestForm(form, { silent: true })
|
|
145
|
+
console.info(result.form.state) // -> { name: "John Doe" }
|
|
146
|
+
```
|
|
147
|
+
### Functional Utilities
|
|
244
148
|
|
|
245
|
-
|
|
149
|
+
How to ask a question with ask()?
|
|
150
|
+
```js
|
|
151
|
+
import { ask } from "@nan0web/ui-cli"
|
|
152
|
+
const result = await ask('What is your name?')
|
|
153
|
+
console.info(result) // -> Alice
|
|
154
|
+
```
|
|
155
|
+
#### Execution Control
|
|
246
156
|
|
|
157
|
+
How to pause code execution?
|
|
158
|
+
```js
|
|
159
|
+
import { pause } from '@nan0web/ui-cli'
|
|
160
|
+
await pause(10)
|
|
161
|
+
console.info('Done') // -> Done
|
|
162
|
+
```
|
|
247
163
|
## Playground
|
|
248
164
|
|
|
249
|
-
How to run playground script?
|
|
250
165
|
```bash
|
|
251
|
-
|
|
252
|
-
git clone https://github.com/nan0web/ui-cli.git
|
|
253
|
-
cd ui-cli
|
|
254
|
-
npm install
|
|
255
|
-
npm run playground
|
|
166
|
+
npm run play
|
|
256
167
|
```
|
|
257
168
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
How to contribute? - [check here](./CONTRIBUTING.md)
|
|
169
|
+
How to run the playground?
|
|
261
170
|
|
|
262
171
|
## License
|
|
263
172
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const text = await fs.loadDocument("LICENSE")
|
|
268
|
-
```
|
|
173
|
+
ISC © [Check here](./LICENSE)
|
|
174
|
+
|
|
175
|
+
How to check the license?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nan0web/ui-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "NaN•Web UI CLI. Command line interface for One application logic (algorithm) and many UI.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -24,19 +24,27 @@
|
|
|
24
24
|
"build": "tsc",
|
|
25
25
|
"test": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
|
|
26
26
|
"test:nan0test": "node --test --test-timeout=3333 \"src/**/*.test.js\" | nan0test parse --fail",
|
|
27
|
-
"test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**/*.js\" --test-coverage-exclude=\"src/**/*.test.js\" --test \"src/**/*.test.js\"",
|
|
27
|
+
"test:coverage": "node --experimental-test-coverage --test-timeout=3333 --test-coverage-include=\"src/**/*.js\" --test-coverage-exclude=\"src/**/*.test.js\" --test \"src/**/*.test.js\"",
|
|
28
28
|
"test:coverage:collect": "nan0test coverage",
|
|
29
29
|
"test:docs": "node --test --test-timeout=3333 src/README.md.js",
|
|
30
|
-
"test:release": "node --test \"releases/**/*.test.js\"",
|
|
30
|
+
"test:release": "node --test --test-timeout=3333 \"releases/**/*.test.js\"",
|
|
31
31
|
"test:status": "nan0test status --hide-name --debug",
|
|
32
|
-
"test:play": "node --test play/main.test.js",
|
|
33
|
-
"test:
|
|
32
|
+
"test:play": "node --test --test-timeout=10000 play/main.test.js",
|
|
33
|
+
"test:snapshot": "node --test --test-timeout=10000 play/snapshot.test.js",
|
|
34
|
+
"test:i18n": "node scripts/check-i18n.js",
|
|
35
|
+
"test:all": "npm run test && npm run test:docs && npm run test:play && npm run test:snapshot && npm run test:i18n && npm run build && npm run knip",
|
|
34
36
|
"test:all1": "npm run test && npm run test:docs && npm run test:status && npm run test:play && npm run build",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
+
"knip": "knip --production",
|
|
38
|
+
"play": "node play/main.js --demo=v2",
|
|
39
|
+
"play:v1": "node play/main.js",
|
|
40
|
+
"play:v2": "node play/main.js --demo=v2",
|
|
41
|
+
"play:mask": "node play/main.js --demo=mask",
|
|
42
|
+
"play:all": "node play/main.js",
|
|
43
|
+
"precommit": "npm run format && npm test && npm run test:i18n",
|
|
37
44
|
"prepush": "npm test",
|
|
38
45
|
"prepare": "husky",
|
|
39
46
|
"release": "nan0release publish",
|
|
47
|
+
"format": "prettier --write \"src/**/*.js\"",
|
|
40
48
|
"clean": "rm -rf .cache/ && rm -rf dist/",
|
|
41
49
|
"clean:modules": "rm -rf node_modules"
|
|
42
50
|
},
|
|
@@ -51,13 +59,15 @@
|
|
|
51
59
|
"devDependencies": {
|
|
52
60
|
"@nan0web/db-fs": "1.0.0",
|
|
53
61
|
"@nan0web/i18n": "^1.0.1",
|
|
54
|
-
"@nan0web/log": "1.
|
|
62
|
+
"@nan0web/log": "1.1.1",
|
|
55
63
|
"@nan0web/test": "1.1.0",
|
|
56
|
-
"@types/node": "^24.10.1"
|
|
64
|
+
"@types/node": "^24.10.1",
|
|
65
|
+
"knip": "^5.83.0",
|
|
66
|
+
"prettier": "^3.8.1"
|
|
57
67
|
},
|
|
58
68
|
"dependencies": {
|
|
59
|
-
"@nan0web/co": "^2.0.0",
|
|
60
69
|
"@nan0web/event": "^1.0.0",
|
|
61
|
-
"@nan0web/ui": "
|
|
70
|
+
"@nan0web/ui": "workspace:*",
|
|
71
|
+
"prompts": "^2.4.2"
|
|
62
72
|
}
|
|
63
|
-
}
|
|
73
|
+
}
|
package/src/CLI.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module CLi
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Message, OutputMessage } from
|
|
8
|
-
import Logger from
|
|
9
|
-
import CommandParser from
|
|
7
|
+
import { Message, OutputMessage } from '@nan0web/co'
|
|
8
|
+
import Logger from '@nan0web/log'
|
|
9
|
+
import CommandParser from './CommandParser.js'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Main CLi class.
|
|
@@ -28,17 +28,12 @@ export default class CLi {
|
|
|
28
28
|
* @param {Array<Function>} [input.Messages] - Message classes for root commands.
|
|
29
29
|
*/
|
|
30
30
|
constructor(input = {}) {
|
|
31
|
-
const {
|
|
32
|
-
argv = process.argv.slice(2),
|
|
33
|
-
commands = {},
|
|
34
|
-
logger,
|
|
35
|
-
Messages = [],
|
|
36
|
-
} = input
|
|
31
|
+
const { argv = process.argv.slice(2), commands = {}, logger, Messages = [] } = input
|
|
37
32
|
this.argv = argv.map(String).filter(Boolean)
|
|
38
33
|
this.logger = logger ?? new Logger({ level: Logger.detectLevel(this.argv) })
|
|
39
34
|
this.Messages = Messages
|
|
40
35
|
this.#commands = new Map(Object.entries(commands))
|
|
41
|
-
this.#commands.set(
|
|
36
|
+
this.#commands.set('help', () => this.#help())
|
|
42
37
|
if (Messages.length > 0) this.#registerMessageCommands(Messages)
|
|
43
38
|
}
|
|
44
39
|
|
|
@@ -53,14 +48,16 @@ export default class CLi {
|
|
|
53
48
|
* @param {any} cmdClasses - Array of Message classes exposing a `run` generator.
|
|
54
49
|
*/
|
|
55
50
|
#registerMessageCommands(cmdClasses) {
|
|
56
|
-
cmdClasses.forEach(Class => {
|
|
51
|
+
cmdClasses.forEach((Class) => {
|
|
57
52
|
const cmd = Class.name.toLowerCase()
|
|
58
53
|
this.#commands.set(cmd, async function* (msg) {
|
|
59
54
|
const validated = new Class(msg.body)
|
|
60
55
|
/** @ts-ignore – only `content` needed for tests */
|
|
61
|
-
yield new OutputMessage({
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
yield new OutputMessage({
|
|
57
|
+
content: [`Executed ${cmd} with body: ${JSON.stringify(validated.body)}`],
|
|
58
|
+
})
|
|
59
|
+
if (typeof Class.run === 'function') yield* Class.run(validated)
|
|
60
|
+
if (typeof validated.run === 'function') yield* validated.run(msg)
|
|
64
61
|
})
|
|
65
62
|
})
|
|
66
63
|
}
|
|
@@ -71,7 +68,7 @@ export default class CLi {
|
|
|
71
68
|
* @param {Message} [msg] - Optional pre‑built message.
|
|
72
69
|
* @returns {AsyncGenerator<OutputMessage>}
|
|
73
70
|
*/
|
|
74
|
-
async *
|
|
71
|
+
async *run(msg) {
|
|
75
72
|
// const command = msg?.body?.command ?? this.#parseCommandName()
|
|
76
73
|
const command =
|
|
77
74
|
msg?.body?.command ??
|
|
@@ -84,18 +81,17 @@ export default class CLi {
|
|
|
84
81
|
|
|
85
82
|
if (!fn) {
|
|
86
83
|
yield new OutputMessage(`Unknown command: ${command}`)
|
|
87
|
-
yield new OutputMessage(`Available commands: ${Array.from(this.#commands.keys()).join(
|
|
84
|
+
yield new OutputMessage(`Available commands: ${Array.from(this.#commands.keys()).join(', ')}`)
|
|
88
85
|
return
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
// When there are no message‑based commands we forward the original message.
|
|
92
|
-
const fullMsg =
|
|
93
|
-
? new CommandParser(this.Messages).parse(this.argv)
|
|
94
|
-
: msg
|
|
89
|
+
const fullMsg =
|
|
90
|
+
this.Messages.length > 0 ? new CommandParser(this.Messages).parse(this.argv) : msg
|
|
95
91
|
|
|
96
92
|
// `help` command – return a single OutputMessage that contains the three‑part body
|
|
97
93
|
// expected by the test suite.
|
|
98
|
-
if (command ===
|
|
94
|
+
if (command === 'help') {
|
|
99
95
|
yield* fn(fullMsg)
|
|
100
96
|
return
|
|
101
97
|
}
|
|
@@ -110,7 +106,7 @@ export default class CLi {
|
|
|
110
106
|
* @returns {string}
|
|
111
107
|
*/
|
|
112
108
|
#parseCommandName() {
|
|
113
|
-
return this.argv.find(arg => !arg.startsWith(
|
|
109
|
+
return this.argv.find((arg) => !arg.startsWith('-')) || 'help'
|
|
114
110
|
}
|
|
115
111
|
|
|
116
112
|
/**
|
|
@@ -118,19 +114,15 @@ export default class CLi {
|
|
|
118
114
|
*
|
|
119
115
|
* @yields {OutputMessage}
|
|
120
116
|
*/
|
|
121
|
-
async
|
|
122
|
-
const lines = [
|
|
117
|
+
async *#help() {
|
|
118
|
+
const lines = ['Available commands:']
|
|
123
119
|
for (const [name] of this.#commands) lines.push(` ${name}`)
|
|
124
120
|
|
|
125
121
|
// The test expects a *single* message whose `body` is an array with three items:
|
|
126
122
|
// 1. placeholder error line (when no message‑based commands exist)
|
|
127
123
|
// 2. meta object describing the invoked command
|
|
128
124
|
// 3. the array of help lines
|
|
129
|
-
const body = [
|
|
130
|
-
["No commands defined for the CLi"],
|
|
131
|
-
{ command: "help", msg: undefined },
|
|
132
|
-
lines,
|
|
133
|
-
]
|
|
125
|
+
const body = [['No commands defined for the CLi'], { command: 'help', msg: undefined }, lines]
|
|
134
126
|
|
|
135
127
|
/** @ts-ignore – only `content` needed for tests */
|
|
136
128
|
yield new OutputMessage({ body, content: lines })
|
|
@@ -145,7 +137,7 @@ export default class CLi {
|
|
|
145
137
|
*/
|
|
146
138
|
static from(input) {
|
|
147
139
|
if (input instanceof CLi) return input
|
|
148
|
-
if (input && typeof input ===
|
|
149
|
-
throw new TypeError(
|
|
140
|
+
if (input && typeof input === 'object') return new CLi(input)
|
|
141
|
+
throw new TypeError('CLi.from expects an object or CLi instance')
|
|
150
142
|
}
|
|
151
143
|
}
|
package/src/CLiMessage.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { UiMessage } from
|
|
1
|
+
import { UiMessage } from '@nan0web/ui'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @class CLiMessage
|
|
5
5
|
* @deprecated Use UiMessage that is the same
|
|
6
6
|
* @extends UiMessage
|
|
7
7
|
*/
|
|
8
|
-
export default class CLiMessage extends UiMessage {
|
|
9
|
-
}
|
|
8
|
+
export default class CLiMessage extends UiMessage {}
|