@nan0web/ui-cli 1.1.1 → 2.1.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 +153 -203
- package/bin/cli.js +11 -0
- package/bin/nan0cli.js +86 -0
- package/package.json +27 -13
- 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 +241 -312
- 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 +85 -40
- 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,218 @@
|
|
|
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
|
+
## nan0cli — Universal CLI Runner
|
|
44
29
|
|
|
45
|
-
The
|
|
30
|
+
The `nan0cli` binary provides a universal entry point for any nan0web application.
|
|
31
|
+
It reads the app's `package.json`, resolves the CLI entry point, and runs commands.
|
|
46
32
|
|
|
47
|
-
|
|
33
|
+
### App Contract
|
|
48
34
|
|
|
49
|
-
|
|
35
|
+
Your app must export Messages from its entry point:
|
|
50
36
|
|
|
51
|
-
How to request form input via CLiInputAdapter?
|
|
52
37
|
```js
|
|
53
|
-
|
|
54
|
-
|
|
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" }
|
|
38
|
+
// E1: Messages Array (recommended)
|
|
39
|
+
export default [Serve, Dump]
|
|
40
|
+
|
|
41
|
+
// E2: Single Message class (auto-wrapped to array)
|
|
42
|
+
export default class MyApp { }
|
|
81
43
|
```
|
|
82
44
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
45
|
+
### Entry Point Resolution
|
|
46
|
+
|
|
47
|
+
`nan0cli` looks for the entry point in this order:
|
|
48
|
+
1. `nan0web.cli.entry` field in `package.json`
|
|
49
|
+
2. `src/cli.js` (convention)
|
|
50
|
+
3. `src/messages/index.js` (legacy)
|
|
51
|
+
|
|
52
|
+
### Configuration
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"nan0web": {
|
|
57
|
+
"cli": { "entry": "src/cli.js" }
|
|
58
|
+
}
|
|
95
59
|
}
|
|
96
|
-
const result = await adapter.requestSelect(config)
|
|
97
|
-
console.info(result) // ← en
|
|
98
60
|
```
|
|
99
|
-
### Input Utilities
|
|
100
61
|
|
|
101
|
-
|
|
62
|
+
nan0cli binary is registered
|
|
102
63
|
|
|
103
|
-
|
|
64
|
+
### Error Handling
|
|
104
65
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
import { Input } from '@nan0web/ui-cli'
|
|
108
|
-
const input = new Input({ value: "test", stops: ["quit"] })
|
|
109
|
-
console.info(String(input)) // ← test
|
|
110
|
-
console.info(input.value) // ← test
|
|
111
|
-
console.info(input.cancelled) // ← false
|
|
112
|
-
input.value = "quit"
|
|
113
|
-
console.info(input.cancelled) // ← true
|
|
114
|
-
```
|
|
115
|
-
#### `ask(question)`
|
|
66
|
+
When no entry point is found, `nan0cli` displays a styled `Alert` error and exits with code 1.
|
|
67
|
+
All errors are displayed via `Logger` + `Alert` components — never raw `console.log`.
|
|
116
68
|
|
|
117
|
-
|
|
69
|
+
nan0cli is included in package files
|
|
118
70
|
|
|
119
|
-
|
|
71
|
+
## Usage (V2 Architecture)
|
|
72
|
+
|
|
73
|
+
Starting from v2.0, we recommend using the `render()` function with Composable Components.
|
|
74
|
+
|
|
75
|
+
### Interactive Prompts
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
@docs
|
|
79
|
+
#### Input & Password
|
|
80
|
+
|
|
81
|
+
How to use Input and Password components?
|
|
120
82
|
```js
|
|
121
|
-
import {
|
|
122
|
-
const
|
|
123
|
-
console.info(
|
|
83
|
+
import { render, Input, Password } from '@nan0web/ui-cli'
|
|
84
|
+
const user = await ask('Username')
|
|
85
|
+
console.info(`User: ${user}`) // -> User: Alice
|
|
86
|
+
const pass = await ask('Enter Secret:')
|
|
87
|
+
console.info(`Secret: ${pass}`) // -> Secret: secret-key
|
|
124
88
|
```
|
|
125
|
-
####
|
|
126
|
-
|
|
127
|
-
Creates a configurable input handler with stop keywords.
|
|
89
|
+
#### Select & Multiselect
|
|
128
90
|
|
|
129
|
-
How to use
|
|
91
|
+
How to use Select component?
|
|
130
92
|
```js
|
|
131
|
-
import {
|
|
132
|
-
const
|
|
133
|
-
console.info(
|
|
93
|
+
import { render, Select } from '@nan0web/ui-cli'
|
|
94
|
+
const lang = await select({ title: 'Choose Language:' })
|
|
95
|
+
console.info(`Selected: ${lang.value}`) // -> Selected: en
|
|
134
96
|
```
|
|
135
|
-
####
|
|
97
|
+
#### Multiselect
|
|
136
98
|
|
|
137
|
-
|
|
99
|
+
How to use Multiselect component?
|
|
100
|
+
```js
|
|
101
|
+
import { render, Multiselect } from '@nan0web/ui-cli'
|
|
102
|
+
const roles = ['admin', 'user']
|
|
103
|
+
console.info(`Roles: ${roles.join(', ')}`) // -> Roles: admin, user
|
|
104
|
+
```
|
|
105
|
+
#### Masked Input
|
|
138
106
|
|
|
139
|
-
How to
|
|
107
|
+
How to use Mask component?
|
|
140
108
|
```js
|
|
141
|
-
import {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
prompt: "Selection (1-3): ",
|
|
145
|
-
options: ["Option A", "Option B", "Option C"],
|
|
146
|
-
console: console,
|
|
147
|
-
}
|
|
148
|
-
const result = await select(config)
|
|
149
|
-
console.info(result.value)
|
|
109
|
+
import { render, Mask } from '@nan0web/ui-cli'
|
|
110
|
+
const phone = '123-456'
|
|
111
|
+
console.info(`Phone: ${phone}`) // -> Phone: 123-456
|
|
150
112
|
```
|
|
151
|
-
####
|
|
113
|
+
#### Autocomplete
|
|
152
114
|
|
|
153
|
-
|
|
115
|
+
How to use Autocomplete component?
|
|
116
|
+
```js
|
|
117
|
+
import { render, Autocomplete } from '@nan0web/ui-cli'
|
|
118
|
+
const model = 'gpt-4'
|
|
119
|
+
console.info(`Model: ${model}`) // -> Model: gpt-4
|
|
120
|
+
```
|
|
121
|
+
#### Slider, Toggle & DateTime
|
|
154
122
|
|
|
155
|
-
How to
|
|
123
|
+
How to use Slider and Toggle?
|
|
156
124
|
```js
|
|
157
|
-
import {
|
|
158
|
-
const
|
|
159
|
-
console.info(
|
|
125
|
+
import { render, Slider, Toggle } from '@nan0web/ui-cli'
|
|
126
|
+
const volume = 50
|
|
127
|
+
console.info(`Volume: ${volume}`) // -> Volume: 50
|
|
128
|
+
const active = true
|
|
129
|
+
console.info(`Active: ${active}`) // -> Active: true
|
|
160
130
|
```
|
|
161
|
-
####
|
|
131
|
+
#### DateTime
|
|
162
132
|
|
|
163
|
-
|
|
133
|
+
How to use DateTime component?
|
|
134
|
+
```js
|
|
135
|
+
import { render, DateTime } from '@nan0web/ui-cli'
|
|
136
|
+
const date = '2026-02-05'
|
|
137
|
+
console.info(`Date: ${date}`) // -> Date: 2026-02-05
|
|
138
|
+
```
|
|
139
|
+
### Static Views
|
|
164
140
|
|
|
165
|
-
How to
|
|
141
|
+
How to render Alerts?
|
|
166
142
|
```js
|
|
167
|
-
import {
|
|
168
|
-
|
|
169
|
-
await pause(10)
|
|
170
|
-
const after = Date.now()
|
|
171
|
-
console.info(after - before >= 10) // ← true
|
|
143
|
+
import { Alert } from '@nan0web/ui-cli'
|
|
144
|
+
console.info('Success Operation') // -> Success Operation
|
|
172
145
|
```
|
|
173
|
-
|
|
146
|
+
#### Dynamic Tables
|
|
174
147
|
|
|
175
|
-
|
|
148
|
+
How to render Tables?
|
|
149
|
+
```js
|
|
150
|
+
import { Table } from '@nan0web/ui-cli'
|
|
151
|
+
const data = [{ id: 1, name: 'Alice' }]
|
|
152
|
+
console.info(data) // -> [ { id: 1, name: 'Alice' } ]
|
|
153
|
+
```
|
|
154
|
+
### Feedback & Progress
|
|
176
155
|
|
|
177
|
-
|
|
156
|
+
How to use Spinner?
|
|
157
|
+
```js
|
|
158
|
+
import { render, Spinner } from '@nan0web/ui-cli'
|
|
159
|
+
console.info('Loading...') // -> Loading...
|
|
160
|
+
```
|
|
161
|
+
#### Progress Bars
|
|
178
162
|
|
|
179
|
-
How to
|
|
163
|
+
How to use ProgressBar?
|
|
180
164
|
```js
|
|
181
|
-
import {
|
|
182
|
-
|
|
183
|
-
console.error(error.message) // ← Operation cancelled by user
|
|
165
|
+
import { render, ProgressBar } from '@nan0web/ui-cli'
|
|
166
|
+
console.info('Progress: 100%') // -> Progress: 100%
|
|
184
167
|
```
|
|
185
|
-
## API
|
|
168
|
+
## Legacy API
|
|
186
169
|
|
|
187
170
|
### CLiInputAdapter
|
|
188
171
|
|
|
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
|
|
172
|
+
How to request form input via CLiInputAdapter?
|
|
173
|
+
```js
|
|
174
|
+
import { CLiInputAdapter } from '@nan0web/ui-cli'
|
|
175
|
+
const adapter = new CLiInputAdapter()
|
|
176
|
+
const fields = [{ name: 'name', label: 'Full Name' }]
|
|
177
|
+
const form = UiForm.from({
|
|
178
|
+
fields,
|
|
179
|
+
state: {},
|
|
180
|
+
setData: (data) => {
|
|
181
|
+
form.state = data
|
|
182
|
+
return form
|
|
183
|
+
},
|
|
184
|
+
validateValue: () => ({ isValid: true, errors: {} }),
|
|
185
|
+
validate: () => ({ isValid: true, errors: {} }),
|
|
186
|
+
})
|
|
187
|
+
const result = await adapter.requestForm(form, { silent: true })
|
|
188
|
+
console.info(result.form.state) // -> { name: "John Doe" }
|
|
189
|
+
```
|
|
190
|
+
### Functional Utilities
|
|
244
191
|
|
|
245
|
-
|
|
192
|
+
How to ask a question with ask()?
|
|
193
|
+
```js
|
|
194
|
+
import { ask } from "@nan0web/ui-cli"
|
|
195
|
+
const result = await ask('What is your name?')
|
|
196
|
+
console.info(result) // -> Alice
|
|
197
|
+
```
|
|
198
|
+
#### Execution Control
|
|
246
199
|
|
|
200
|
+
How to pause code execution?
|
|
201
|
+
```js
|
|
202
|
+
import { pause } from '@nan0web/ui-cli'
|
|
203
|
+
await pause(10)
|
|
204
|
+
console.info('Done') // -> Done
|
|
205
|
+
```
|
|
247
206
|
## Playground
|
|
248
207
|
|
|
249
|
-
How to run playground script?
|
|
250
208
|
```bash
|
|
251
|
-
|
|
252
|
-
git clone https://github.com/nan0web/ui-cli.git
|
|
253
|
-
cd ui-cli
|
|
254
|
-
npm install
|
|
255
|
-
npm run playground
|
|
209
|
+
npm run play
|
|
256
210
|
```
|
|
257
211
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
How to contribute? - [check here](./CONTRIBUTING.md)
|
|
212
|
+
How to run the playground?
|
|
261
213
|
|
|
262
214
|
## License
|
|
263
215
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const text = await fs.loadDocument("LICENSE")
|
|
268
|
-
```
|
|
216
|
+
ISC © [Check here](./LICENSE)
|
|
217
|
+
|
|
218
|
+
How to check the license?
|
package/bin/cli.js
ADDED
package/bin/nan0cli.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Universal NaN•Web CLI Runner
|
|
5
|
+
*
|
|
6
|
+
* Takes configuration from current working directory's package.json
|
|
7
|
+
* and executes command using @nan0web/ui-cli core logic.
|
|
8
|
+
*
|
|
9
|
+
* App contract:
|
|
10
|
+
* E1: export default [Serve, Dump] — Messages Array
|
|
11
|
+
* E2: export default class MyApp { } — Single Message class (auto-wrapped)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { pathToFileURL } from 'node:url'
|
|
15
|
+
|
|
16
|
+
import DBFS from '@nan0web/db-fs'
|
|
17
|
+
import Logger from '@nan0web/log'
|
|
18
|
+
|
|
19
|
+
import { CLI, Alert, render } from '../src/index.js'
|
|
20
|
+
|
|
21
|
+
const console = new Logger({ level: Logger.detectLevel(process.argv) })
|
|
22
|
+
const fs = new DBFS()
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Display error via Alert component + Logger
|
|
26
|
+
* @param {string} title
|
|
27
|
+
* @param {string} text
|
|
28
|
+
*/
|
|
29
|
+
const showError = (title, text) => {
|
|
30
|
+
try {
|
|
31
|
+
console.error(render(new Alert({ type: 'error', title, description: text })))
|
|
32
|
+
} catch {
|
|
33
|
+
console.error(`[CRITICAL] ${title}: ${text}`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
;(async () => {
|
|
38
|
+
try {
|
|
39
|
+
// 1. Load package.json via DBFS (A4)
|
|
40
|
+
const pkg = await fs.loadDocument('package.json', {})
|
|
41
|
+
const entry = pkg?.nan0web?.cli?.entry
|
|
42
|
+
|
|
43
|
+
// 2. Resolve entry point via statDocument (B3)
|
|
44
|
+
const candidates = [entry, 'src/cli.js', 'src/messages/index.js'].filter(Boolean)
|
|
45
|
+
let appPath = null
|
|
46
|
+
|
|
47
|
+
for (const candidate of candidates) {
|
|
48
|
+
const stat = await fs.statDocument(candidate)
|
|
49
|
+
if (!stat.error) {
|
|
50
|
+
appPath = fs.location(candidate)
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!appPath) {
|
|
56
|
+
showError(
|
|
57
|
+
'Config Error',
|
|
58
|
+
'No CLI entry point found.\nPlease add "nan0web.cli.entry" to package.json\nOR create src/cli.js / src/messages/index.js'
|
|
59
|
+
)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 3. Import App Module (D1)
|
|
64
|
+
const appModule = await import(pathToFileURL(appPath))
|
|
65
|
+
|
|
66
|
+
// 4. Resolve Messages (E1 + E2)
|
|
67
|
+
const App = appModule.default || appModule.Messages || appModule.App
|
|
68
|
+
if (!App) {
|
|
69
|
+
showError('Config Error', `Module at ${appPath} must export default, Messages, or App`)
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
const Messages = Array.isArray(App) ? App : [App]
|
|
73
|
+
|
|
74
|
+
// 5. Run CLI
|
|
75
|
+
const cli = new CLI({ Messages, argv: process.argv.slice(2) })
|
|
76
|
+
for await (const output of cli.run()) {
|
|
77
|
+
if (output?.content) {
|
|
78
|
+
output.content.forEach((line) => console.info(line))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
showError('Runtime Error', error.message || String(error))
|
|
83
|
+
if (process.env.DEBUG) console.error(error.stack)
|
|
84
|
+
process.exit(1)
|
|
85
|
+
}
|
|
86
|
+
})()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nan0web/ui-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.1.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",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"src/**/*.js",
|
|
10
10
|
"!src/**/*.test.js",
|
|
11
|
-
"types/**/*.d.ts"
|
|
11
|
+
"types/**/*.d.ts",
|
|
12
|
+
"bin/**/*.js"
|
|
12
13
|
],
|
|
13
14
|
"exports": {
|
|
14
15
|
".": {
|
|
@@ -24,19 +25,27 @@
|
|
|
24
25
|
"build": "tsc",
|
|
25
26
|
"test": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
|
|
26
27
|
"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\"",
|
|
28
|
+
"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
29
|
"test:coverage:collect": "nan0test coverage",
|
|
29
30
|
"test:docs": "node --test --test-timeout=3333 src/README.md.js",
|
|
30
|
-
"test:release": "node --test \"releases/**/*.test.js\"",
|
|
31
|
+
"test:release": "node --test --test-timeout=3333 \"releases/**/*.test.js\"",
|
|
31
32
|
"test:status": "nan0test status --hide-name --debug",
|
|
32
|
-
"test:play": "node --test play/main.test.js",
|
|
33
|
-
"test:
|
|
33
|
+
"test:play": "node --test --test-timeout=10000 play/main.test.js",
|
|
34
|
+
"test:snapshot": "node --test --test-timeout=10000 play/snapshot.test.js",
|
|
35
|
+
"test:i18n": "node scripts/check-i18n.js",
|
|
36
|
+
"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
37
|
"test:all1": "npm run test && npm run test:docs && npm run test:status && npm run test:play && npm run build",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
38
|
+
"knip": "knip --production",
|
|
39
|
+
"play": "node play/main.js --demo=v2",
|
|
40
|
+
"play:v1": "node play/main.js",
|
|
41
|
+
"play:v2": "node play/main.js --demo=v2",
|
|
42
|
+
"play:mask": "node play/main.js --demo=mask",
|
|
43
|
+
"play:all": "node play/main.js",
|
|
44
|
+
"precommit": "npm run format && npm test && npm run test:i18n",
|
|
37
45
|
"prepush": "npm test",
|
|
38
46
|
"prepare": "husky",
|
|
39
47
|
"release": "nan0release publish",
|
|
48
|
+
"format": "prettier --write \"src/**/*.js\"",
|
|
40
49
|
"clean": "rm -rf .cache/ && rm -rf dist/",
|
|
41
50
|
"clean:modules": "rm -rf node_modules"
|
|
42
51
|
},
|
|
@@ -49,15 +58,20 @@
|
|
|
49
58
|
"author": "ЯRаСлав (YaRaSLove) <support@yaro.page>",
|
|
50
59
|
"license": "ISC",
|
|
51
60
|
"devDependencies": {
|
|
52
|
-
"@nan0web/db-fs": "1.0.0",
|
|
53
61
|
"@nan0web/i18n": "^1.0.1",
|
|
54
|
-
"@nan0web/log": "1.0.1",
|
|
55
62
|
"@nan0web/test": "1.1.0",
|
|
56
|
-
"@types/node": "^24.10.1"
|
|
63
|
+
"@types/node": "^24.10.1",
|
|
64
|
+
"knip": "^5.83.0",
|
|
65
|
+
"prettier": "^3.8.1"
|
|
66
|
+
},
|
|
67
|
+
"bin": {
|
|
68
|
+
"nan0cli": "./bin/nan0cli.js"
|
|
57
69
|
},
|
|
58
70
|
"dependencies": {
|
|
59
|
-
"@nan0web/
|
|
71
|
+
"@nan0web/db-fs": "1.0.0",
|
|
60
72
|
"@nan0web/event": "^1.0.0",
|
|
61
|
-
"@nan0web/
|
|
73
|
+
"@nan0web/log": "1.1.1",
|
|
74
|
+
"@nan0web/ui": "^1.1.0",
|
|
75
|
+
"prompts": "^2.4.2"
|
|
62
76
|
}
|
|
63
77
|
}
|