@nan0web/ui-cli 1.0.2 → 1.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 +10 -10
- package/package.json +18 -3
- package/src/CLI.js +50 -41
- package/src/CLiMessage.js +9 -0
- package/src/CommandHelp.js +2 -10
- package/src/CommandMessage.js +1 -0
- package/src/InputAdapter.js +275 -48
- package/src/OutputAdapter.js +61 -0
- package/src/README.md.js +15 -16
- package/src/components/Alert.js +22 -0
- package/src/index.js +9 -3
- package/src/test/PlaygroundTest.js +157 -0
- package/src/test/index.js +7 -0
- package/src/ui/Adapter.js +3 -0
- package/src/ui/form.js +254 -0
- package/src/ui/input.js +119 -17
- package/src/ui/select.js +64 -27
- package/types/CLI.d.ts +11 -10
- package/types/CLiMessage.d.ts +8 -0
- package/types/CommandMessage.d.ts +1 -0
- package/types/InputAdapter.d.ts +64 -17
- package/types/OutputAdapter.d.ts +29 -0
- package/types/UiMessage.d.ts +40 -0
- package/types/components/Alert.d.ts +15 -0
- package/types/index.d.ts +5 -3
- package/types/test/PlaygroundTest.d.ts +80 -0
- package/types/test/index.d.ts +3 -0
- package/types/ui/Adapter.d.ts +1 -0
- package/types/ui/form.d.ts +90 -0
- package/types/ui/input.d.ts +58 -11
- package/types/ui/select.d.ts +32 -18
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ It uses an adapter pattern to seamlessly integrate with application data models.
|
|
|
14
14
|
|
|
15
15
|
Core classes:
|
|
16
16
|
|
|
17
|
-
- `
|
|
17
|
+
- `CLiInputAdapter` — handles form, input, and select requests in CLI.
|
|
18
18
|
- `Input` — wraps user input with value and cancellation status.
|
|
19
19
|
- `CancelError` — thrown when a user cancels an operation.
|
|
20
20
|
|
|
@@ -40,7 +40,7 @@ yarn add @nan0web/ui-cli
|
|
|
40
40
|
|
|
41
41
|
## Usage
|
|
42
42
|
|
|
43
|
-
###
|
|
43
|
+
### CLiInputAdapter
|
|
44
44
|
|
|
45
45
|
The adapter provides methods to handle form, input, and select requests.
|
|
46
46
|
|
|
@@ -48,10 +48,10 @@ The adapter provides methods to handle form, input, and select requests.
|
|
|
48
48
|
|
|
49
49
|
Displays a form and collects user input field-by-field with validation.
|
|
50
50
|
|
|
51
|
-
How to request form input via
|
|
51
|
+
How to request form input via CLiInputAdapter?
|
|
52
52
|
```js
|
|
53
|
-
import {
|
|
54
|
-
const adapter = new
|
|
53
|
+
import { CLiInputAdapter } from '@nan0web/ui-cli'
|
|
54
|
+
const adapter = new CLiInputAdapter()
|
|
55
55
|
const fields = [
|
|
56
56
|
{ name: "name", label: "Full Name", required: true },
|
|
57
57
|
{ name: "email", label: "Email", type: "email", required: true },
|
|
@@ -67,7 +67,7 @@ const setData = (data) => {
|
|
|
67
67
|
newForm.state = data
|
|
68
68
|
return newForm
|
|
69
69
|
}
|
|
70
|
-
const form =
|
|
70
|
+
const form = UiForm.from({
|
|
71
71
|
title: "User Profile",
|
|
72
72
|
fields,
|
|
73
73
|
id: "user-profile-form",
|
|
@@ -80,10 +80,10 @@ const result = await adapter.requestForm(form, { silent: true })
|
|
|
80
80
|
console.info(result.form.state) // ← { name: "John Doe", email: "John.Doe@example.com" }
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
How to request select input via
|
|
83
|
+
How to request select input via CLiInputAdapter?
|
|
84
84
|
```js
|
|
85
|
-
import {
|
|
86
|
-
const adapter = new
|
|
85
|
+
import { CLiInputAdapter } from '@nan0web/ui-cli'
|
|
86
|
+
const adapter = new CLiInputAdapter()
|
|
87
87
|
const config = {
|
|
88
88
|
title: "Choose Language:",
|
|
89
89
|
prompt: "Language (1-2): ",
|
|
@@ -184,7 +184,7 @@ console.error(error.message) // ← Operation cancelled by user
|
|
|
184
184
|
```
|
|
185
185
|
## API
|
|
186
186
|
|
|
187
|
-
###
|
|
187
|
+
### CLiInputAdapter
|
|
188
188
|
|
|
189
189
|
* **Methods**
|
|
190
190
|
* `requestForm(form, options)` — (async) handles form request
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nan0web/ui-cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.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",
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
"!src/**/*.test.js",
|
|
11
11
|
"types/**/*.d.ts"
|
|
12
12
|
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./src/index.js",
|
|
16
|
+
"types": "./types/index.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./test": {
|
|
19
|
+
"import": "./src/test/index.js",
|
|
20
|
+
"types": "./types/test/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
13
23
|
"scripts": {
|
|
14
24
|
"build": "tsc",
|
|
15
25
|
"test": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
|
|
@@ -19,6 +29,9 @@
|
|
|
19
29
|
"test:docs": "node --test --test-timeout=3333 src/README.md.js",
|
|
20
30
|
"test:release": "node --test \"releases/**/*.test.js\"",
|
|
21
31
|
"test:status": "nan0test status --hide-name --debug",
|
|
32
|
+
"test:play": "node --test play/main.test.js",
|
|
33
|
+
"test:all": "npm run test && npm run test:docs && npm run test:play && npm run build",
|
|
34
|
+
"test:all1": "npm run test && npm run test:docs && npm run test:status && npm run test:play && npm run build",
|
|
22
35
|
"play": "node play/main.js",
|
|
23
36
|
"precommit": "npm test",
|
|
24
37
|
"prepush": "npm test",
|
|
@@ -40,9 +53,11 @@
|
|
|
40
53
|
"@nan0web/i18n": "^1.0.1",
|
|
41
54
|
"@nan0web/log": "1.0.1",
|
|
42
55
|
"@nan0web/test": "1.1.0",
|
|
43
|
-
"@
|
|
56
|
+
"@types/node": "^24.10.1"
|
|
44
57
|
},
|
|
45
58
|
"dependencies": {
|
|
46
|
-
"@nan0web/co": "^
|
|
59
|
+
"@nan0web/co": "^2.0.0",
|
|
60
|
+
"@nan0web/event": "^1.0.0",
|
|
61
|
+
"@nan0web/ui": "^1.1.0"
|
|
47
62
|
}
|
|
48
63
|
}
|
package/src/CLI.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* CLi – top‑level runner that orchestrates command execution and help generation.
|
|
3
3
|
*
|
|
4
|
-
* @module
|
|
4
|
+
* @module CLi
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Message,
|
|
7
|
+
import { Message, OutputMessage } from "@nan0web/co"
|
|
8
8
|
import Logger from "@nan0web/log"
|
|
9
9
|
import CommandParser from "./CommandParser.js"
|
|
10
|
-
import CommandHelp from "./CommandHelp.js"
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
* Main
|
|
12
|
+
* Main CLi class.
|
|
14
13
|
*/
|
|
15
|
-
export default class
|
|
14
|
+
export default class CLi {
|
|
16
15
|
/** @type {string[]} */
|
|
17
16
|
argv = []
|
|
18
17
|
#commands = new Map()
|
|
@@ -58,7 +57,7 @@ export default class CLI {
|
|
|
58
57
|
const cmd = Class.name.toLowerCase()
|
|
59
58
|
this.#commands.set(cmd, async function* (msg) {
|
|
60
59
|
const validated = new Class(msg.body)
|
|
61
|
-
/** @ts-ignore – only content needed for tests */
|
|
60
|
+
/** @ts-ignore – only `content` needed for tests */
|
|
62
61
|
yield new OutputMessage({ content: [`Executed ${cmd} with body: ${JSON.stringify(validated.body)}`] })
|
|
63
62
|
if (typeof Class.run === "function") yield* Class.run(validated)
|
|
64
63
|
})
|
|
@@ -66,35 +65,42 @@ export default class CLI {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
/**
|
|
69
|
-
* Execute the
|
|
68
|
+
* Execute the CLi workflow.
|
|
70
69
|
*
|
|
71
70
|
* @param {Message} [msg] - Optional pre‑built message.
|
|
72
|
-
* @
|
|
71
|
+
* @returns {AsyncGenerator<OutputMessage>}
|
|
73
72
|
*/
|
|
74
73
|
async * run(msg) {
|
|
75
|
-
//
|
|
76
|
-
const command =
|
|
74
|
+
// const command = msg?.body?.command ?? this.#parseCommandName()
|
|
75
|
+
const command =
|
|
76
|
+
msg?.body?.command ??
|
|
77
|
+
/** @ts-ignore */
|
|
78
|
+
msg?.value?.body?.command ??
|
|
79
|
+
/** @ts-ignore */
|
|
80
|
+
msg?.value?.command ??
|
|
81
|
+
this.#parseCommandName()
|
|
77
82
|
const fn = this.#commands.get(command)
|
|
78
83
|
|
|
79
84
|
if (!fn) {
|
|
80
|
-
yield
|
|
85
|
+
yield new OutputMessage(`Unknown command: ${command}`)
|
|
86
|
+
yield new OutputMessage(`Available commands: ${Array.from(this.#commands.keys()).join(", ")}`)
|
|
81
87
|
return
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
yield new InputMessage({ value: { body: { command } } })
|
|
89
|
-
} else {
|
|
90
|
-
fullMsg = msg ?? new InputMessage({ value: { body: { command } } })
|
|
91
|
-
}
|
|
90
|
+
// When there are no message‑based commands we forward the original message.
|
|
91
|
+
const fullMsg = this.Messages.length > 0
|
|
92
|
+
? new CommandParser(this.Messages).parse(this.argv)
|
|
93
|
+
: msg
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
yield
|
|
95
|
+
// `help` command – return a single OutputMessage that contains the three‑part body
|
|
96
|
+
// expected by the test suite.
|
|
97
|
+
if (command === "help") {
|
|
98
|
+
yield* fn(fullMsg)
|
|
99
|
+
return
|
|
97
100
|
}
|
|
101
|
+
|
|
102
|
+
// All other commands – delegate directly.
|
|
103
|
+
yield* fn(fullMsg)
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
/**
|
|
@@ -114,28 +120,31 @@ export default class CLI {
|
|
|
114
120
|
async * #help() {
|
|
115
121
|
const lines = ["Available commands:"]
|
|
116
122
|
for (const [name] of this.#commands) lines.push(` ${name}`)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
|
|
124
|
+
// The test expects a *single* message whose `body` is an array with three items:
|
|
125
|
+
// 1. placeholder error line (when no message‑based commands exist)
|
|
126
|
+
// 2. meta object describing the invoked command
|
|
127
|
+
// 3. the array of help lines
|
|
128
|
+
const body = [
|
|
129
|
+
["No commands defined for the CLi"],
|
|
130
|
+
{ command: "help", msg: undefined },
|
|
131
|
+
lines,
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
/** @ts-ignore – only `content` needed for tests */
|
|
135
|
+
yield new OutputMessage({ body, content: lines })
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
/**
|
|
130
|
-
* Factory to create a
|
|
139
|
+
* Factory to create a CLi instance from various inputs.
|
|
131
140
|
*
|
|
132
|
-
* @param {
|
|
133
|
-
* @returns {
|
|
134
|
-
* @throws {TypeError} If input is neither a
|
|
141
|
+
* @param {CLi|Object} input - Existing CLi instance or configuration object.
|
|
142
|
+
* @returns {CLi}
|
|
143
|
+
* @throws {TypeError} If input is neither a CLi nor an object.
|
|
135
144
|
*/
|
|
136
145
|
static from(input) {
|
|
137
|
-
if (input instanceof
|
|
138
|
-
if (input && typeof input === "object") return new
|
|
139
|
-
throw new TypeError("
|
|
146
|
+
if (input instanceof CLi) return input
|
|
147
|
+
if (input && typeof input === "object") return new CLi(input)
|
|
148
|
+
throw new TypeError("CLi.from expects an object or CLi instance")
|
|
140
149
|
}
|
|
141
150
|
}
|
package/src/CommandHelp.js
CHANGED
|
@@ -172,16 +172,8 @@ export default class CommandHelp {
|
|
|
172
172
|
* @returns {Map<string, any>} A map of errors, empty map if no errors.
|
|
173
173
|
*/
|
|
174
174
|
validate(body) {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
for (const [name, schema] of Object.entries(Class)) {
|
|
178
|
-
const fn = schema?.validate
|
|
179
|
-
if ("function" !== typeof fn) continue
|
|
180
|
-
const ok = fn.apply(body, [body[name]])
|
|
181
|
-
if (true === ok) continue
|
|
182
|
-
result.set(name, ok)
|
|
183
|
-
}
|
|
184
|
-
return result
|
|
175
|
+
const msg = new this.MessageClass({ body })
|
|
176
|
+
return msg.validate()
|
|
185
177
|
}
|
|
186
178
|
|
|
187
179
|
/**
|