@nan0web/ui-cli 1.0.0 → 1.0.2
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 +2 -11
- package/package.json +17 -7
- package/src/CLI.js +141 -0
- package/src/Command.js +210 -0
- package/src/CommandError.js +35 -0
- package/src/CommandHelp.js +204 -0
- package/src/CommandMessage.js +181 -0
- package/src/CommandParser.js +217 -0
- package/src/InputAdapter.js +78 -149
- package/src/README.md.js +7 -6
- package/src/index.js +36 -14
- package/src/ui/index.js +3 -3
- package/src/ui/input.js +68 -11
- package/src/ui/next.js +30 -26
- package/src/ui/select.js +41 -31
- package/src/utils/parse.js +41 -0
- package/types/CLI.d.ts +44 -0
- package/types/Command.d.ts +72 -0
- package/types/CommandError.d.ts +19 -0
- package/types/CommandHelp.d.ts +85 -0
- package/types/CommandMessage.d.ts +65 -0
- package/types/CommandParser.d.ts +28 -0
- package/types/InputAdapter.d.ts +28 -85
- package/types/index.d.ts +12 -9
- package/types/ui/index.d.ts +2 -3
- package/types/ui/input.d.ts +50 -6
- package/types/ui/next.d.ts +11 -8
- package/types/ui/select.d.ts +50 -20
- package/types/utils/parse.d.ts +13 -0
- package/.editorconfig +0 -20
- package/CONTRIBUTING.md +0 -42
- package/docs/uk/README.md +0 -294
- package/playground/forms/addressForm.js +0 -37
- package/playground/forms/ageForm.js +0 -26
- package/playground/forms/profileForm.js +0 -33
- package/playground/forms/userForm.js +0 -36
- package/playground/main.js +0 -81
- package/playground/vocabs/en.js +0 -25
- package/playground/vocabs/index.js +0 -12
- package/playground/vocabs/uk.js +0 -25
- package/src/InputAdapter.test.js +0 -117
- package/src/ui/input.test.js +0 -27
- package/src/ui/select.test.js +0 -34
- package/system.md +0 -99
- package/tsconfig.json +0 -23
- package/types/test/ReadLine.d.ts +0 -1
- package/types/ui/errors.d.ts +0 -3
package/src/README.md.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
1
2
|
import { describe, it, before, beforeEach } from "node:test"
|
|
2
3
|
import assert from "node:assert/strict"
|
|
3
4
|
import FS from "@nan0web/db-fs"
|
|
5
|
+
import Message from "@nan0web/co"
|
|
6
|
+
import { UIForm } from "@nan0web/ui"
|
|
4
7
|
import { NoConsole } from "@nan0web/log"
|
|
5
8
|
import {
|
|
6
9
|
DatasetParser,
|
|
@@ -11,14 +14,12 @@ import {
|
|
|
11
14
|
CLIInputAdapter as BaseCLIInputAdapter,
|
|
12
15
|
CancelError,
|
|
13
16
|
createInput,
|
|
14
|
-
ask as baseAsk,
|
|
15
17
|
Input,
|
|
18
|
+
pause,
|
|
19
|
+
ask as baseAsk,
|
|
16
20
|
select as baseSelect,
|
|
17
21
|
next as baseNext,
|
|
18
|
-
pause,
|
|
19
22
|
} from "./index.js"
|
|
20
|
-
import { UIForm } from "@nan0web/ui"
|
|
21
|
-
import Message from "@nan0web/co"
|
|
22
23
|
|
|
23
24
|
async function ask(question) {
|
|
24
25
|
if ("Full Name *: " === question) return "John Doe"
|
|
@@ -190,8 +191,8 @@ function testRender() {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
const result = await adapter.requestSelect(config)
|
|
193
|
-
console.info(result
|
|
194
|
-
assert.deepStrictEqual(console.output()[0][1],
|
|
194
|
+
console.info(result) // ← en
|
|
195
|
+
assert.deepStrictEqual(console.output()[0][1], "en")
|
|
195
196
|
})
|
|
196
197
|
|
|
197
198
|
/**
|
package/src/index.js
CHANGED
|
@@ -1,25 +1,47 @@
|
|
|
1
|
-
import CLIInputAdapter from
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import CLIInputAdapter from "./InputAdapter.js"
|
|
2
|
+
import { CancelError } from "@nan0web/ui/core"
|
|
3
|
+
import CLI from "./CLI.js"
|
|
4
|
+
import Command from "./Command.js"
|
|
5
|
+
import CommandError from "./CommandError.js"
|
|
6
|
+
import CommandMessage from "./CommandMessage.js"
|
|
7
|
+
import CommandParser from "./CommandParser.js"
|
|
8
|
+
import CommandHelp from "./CommandHelp.js"
|
|
9
|
+
export { str2argv } from "./utils/parse.js"
|
|
4
10
|
|
|
5
|
-
// Export CLI input adapter
|
|
6
11
|
export {
|
|
7
|
-
CLIInputAdapter,
|
|
8
|
-
CancelError,
|
|
9
|
-
createInput,
|
|
10
|
-
ask,
|
|
11
|
-
Input,
|
|
12
12
|
select,
|
|
13
13
|
next,
|
|
14
14
|
pause,
|
|
15
|
+
createInput,
|
|
16
|
+
ask,
|
|
17
|
+
Input,
|
|
18
|
+
} from "./ui/index.js"
|
|
19
|
+
|
|
20
|
+
/** @typedef {import("./CommandHelp.js").CommandHelpField} CommandHelpField */
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
CLI,
|
|
24
|
+
CLIInputAdapter,
|
|
25
|
+
CancelError,
|
|
26
|
+
|
|
27
|
+
/** @deprecated */
|
|
28
|
+
Command,
|
|
29
|
+
/** @deprecated */
|
|
30
|
+
CommandError,
|
|
31
|
+
/** @deprecated */
|
|
32
|
+
CommandMessage,
|
|
33
|
+
|
|
34
|
+
CommandParser,
|
|
35
|
+
CommandHelp,
|
|
15
36
|
}
|
|
16
37
|
|
|
17
|
-
// Export renderers for CLI components
|
|
18
38
|
export const renderers = new Map([
|
|
19
|
-
[
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
[
|
|
40
|
+
"UIProcess",
|
|
41
|
+
data => {
|
|
42
|
+
return `${data.title || "Process"}: ${data.status || "running"}`
|
|
43
|
+
},
|
|
44
|
+
],
|
|
23
45
|
])
|
|
24
46
|
|
|
25
47
|
export default CLIInputAdapter
|
package/src/ui/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export { select } from "./select.js"
|
|
1
|
+
export { Input } from "./input.js"
|
|
2
|
+
export { createInput, ask } from "./input.js"
|
|
3
|
+
export { select, default as baseSelect } from "./select.js"
|
|
4
4
|
export { next, pause } from "./next.js"
|
package/src/ui/input.js
CHANGED
|
@@ -1,33 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input module – provides utilities to read user input from the console.
|
|
3
|
+
*
|
|
4
|
+
* @module ui/input
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { createInterface } from "node:readline"
|
|
2
8
|
import { stdin, stdout } from "node:process"
|
|
3
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Function} InputFn
|
|
12
|
+
* @param {string} question - Prompt displayed to the user.
|
|
13
|
+
* @param {Function|boolean} [loop=false] - Loop control or validator.
|
|
14
|
+
* @param {Function|false} [nextQuestion=false] - Function to compute the next prompt.
|
|
15
|
+
* @returns {Promise<Input>} Resolves with an {@link Input} instance containing the answer.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Represents a line of user input.
|
|
20
|
+
*
|
|
21
|
+
* @class
|
|
22
|
+
* @property {string} value – The raw answer string.
|
|
23
|
+
* @property {string[]} stops – Words that trigger cancellation.
|
|
24
|
+
* @property {boolean} cancelled – True when the answer matches a stop word.
|
|
25
|
+
*/
|
|
4
26
|
export class Input {
|
|
27
|
+
/** @type {string} */
|
|
5
28
|
value = ""
|
|
29
|
+
/** @type {string[]} */
|
|
6
30
|
stops = []
|
|
7
31
|
#cancelled = false
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a new {@link Input} instance.
|
|
35
|
+
*
|
|
36
|
+
* @param {Object} [input={}] - Optional initial values.
|
|
37
|
+
* @param {string} [input.value] - Initial answer string.
|
|
38
|
+
* @param {boolean} [input.cancelled] - Initial cancel flag.
|
|
39
|
+
* @param {string|string[]} [input.stops] - Words that trigger cancellation.
|
|
40
|
+
*/
|
|
8
41
|
constructor(input = {}) {
|
|
9
42
|
const {
|
|
10
43
|
value = this.value,
|
|
11
44
|
cancelled = this.#cancelled,
|
|
12
45
|
stops = [],
|
|
13
46
|
} = input
|
|
47
|
+
|
|
14
48
|
this.value = String(value)
|
|
15
|
-
|
|
49
|
+
|
|
50
|
+
const normalizedStops = Array.isArray(stops) ? stops : [stops].filter(Boolean)
|
|
51
|
+
this.stops = normalizedStops.map(String)
|
|
52
|
+
|
|
16
53
|
this.#cancelled = Boolean(cancelled)
|
|
17
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Returns whether the input has been cancelled either explicitly or via a stop word.
|
|
58
|
+
*
|
|
59
|
+
* @returns {boolean}
|
|
60
|
+
*/
|
|
18
61
|
get cancelled() {
|
|
19
62
|
return this.#cancelled || this.stops.includes(this.value)
|
|
20
63
|
}
|
|
64
|
+
|
|
65
|
+
/** @returns {string} The raw answer value. */
|
|
21
66
|
toString() {
|
|
22
67
|
return this.value
|
|
23
68
|
}
|
|
24
69
|
}
|
|
25
70
|
|
|
26
71
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
72
|
+
* Prompt a question and return the trimmed answer.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} question - Text displayed as a prompt.
|
|
75
|
+
* @returns {Promise<string>} User answer without surrounding whitespace.
|
|
29
76
|
*/
|
|
30
|
-
export function ask(question) {
|
|
77
|
+
export async function ask(question) {
|
|
31
78
|
return new Promise(resolve => {
|
|
32
79
|
const rl = createInterface({ input: stdin, output: stdout, terminal: true })
|
|
33
80
|
rl.question(question, answer => {
|
|
@@ -37,27 +84,37 @@ export function ask(question) {
|
|
|
37
84
|
})
|
|
38
85
|
}
|
|
39
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Factory that creates a reusable async input handler.
|
|
89
|
+
*
|
|
90
|
+
* @param {string[]} [stops=[]] Words that trigger cancellation.
|
|
91
|
+
* @returns {InputFn} Async function that resolves to an {@link Input}.
|
|
92
|
+
*/
|
|
40
93
|
export function createInput(stops = []) {
|
|
41
94
|
const input = new Input({ stops })
|
|
95
|
+
|
|
42
96
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* @param {
|
|
97
|
+
* Internal handler used by the factory.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} question - Prompt displayed to the user.
|
|
100
|
+
* @param {Function|boolean} [loop=false] - Loop‑control flag or validator.
|
|
101
|
+
* @param {Function|false} [nextQuestion=false] - Next prompt generator.
|
|
46
102
|
* @returns {Promise<Input>}
|
|
47
103
|
*/
|
|
48
104
|
async function fn(question, loop = false, nextQuestion = false) {
|
|
49
105
|
while (true) {
|
|
50
106
|
input.value = await ask(question)
|
|
107
|
+
|
|
51
108
|
if (false === loop || input.cancelled) return input
|
|
52
109
|
if (true === loop && input.value) return input
|
|
53
|
-
if ("function"
|
|
110
|
+
if (typeof loop === "function") {
|
|
54
111
|
if (!loop(input)) return input
|
|
55
112
|
}
|
|
56
|
-
if ("string"
|
|
57
|
-
if ("function"
|
|
113
|
+
if (typeof nextQuestion === "string") question = nextQuestion
|
|
114
|
+
if (typeof nextQuestion === "function") question = nextQuestion(input)
|
|
58
115
|
}
|
|
59
116
|
}
|
|
60
117
|
return fn
|
|
61
118
|
}
|
|
62
119
|
|
|
63
|
-
export default createInput
|
|
120
|
+
export default createInput
|
package/src/ui/next.js
CHANGED
|
@@ -1,42 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for handling pauses and key‑press continuations.
|
|
3
|
+
*
|
|
4
|
+
* @module ui/next
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import process from "node:process"
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @
|
|
10
|
+
* Pause execution for a given amount of milliseconds.
|
|
11
|
+
*
|
|
12
|
+
* @param {number} ms - Delay in milliseconds.
|
|
13
|
+
* @returns {Promise<true>} Resolves with `true` after the timeout.
|
|
7
14
|
*/
|
|
8
|
-
|
|
15
|
+
export async function pause(ms) {
|
|
16
|
+
return new Promise(resolve => setTimeout(() => resolve(true), ms))
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @
|
|
20
|
+
* Wait for any key press (or a specific sequence).
|
|
21
|
+
*
|
|
22
|
+
* @param {string|string[]|undefined} [conf] - Expected key or sequence.
|
|
23
|
+
* @returns {Promise<string>} The captured key/sequence.
|
|
24
|
+
* @throws {Error} If stdin is already in raw mode.
|
|
14
25
|
*/
|
|
15
|
-
|
|
26
|
+
export async function next(conf = undefined) {
|
|
16
27
|
return new Promise((resolve, reject) => {
|
|
17
28
|
if (process.stdin.isRaw) {
|
|
18
|
-
reject(new Error(
|
|
29
|
+
reject(new Error("stdin is already in raw mode"))
|
|
19
30
|
return
|
|
20
31
|
}
|
|
32
|
+
let buffer = ""
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const onData = (chunk) => {
|
|
34
|
+
const onData = chunk => {
|
|
25
35
|
const str = chunk.toString()
|
|
26
36
|
buffer += str
|
|
27
37
|
|
|
28
|
-
// Будь-яка клавіша
|
|
29
38
|
if (conf === undefined) {
|
|
30
39
|
cleanup()
|
|
31
40
|
resolve(str)
|
|
32
|
-
}
|
|
33
|
-
else if (typeof conf === 'string') {
|
|
41
|
+
} else if (typeof conf === "string") {
|
|
34
42
|
if (buffer === conf || buffer.endsWith(conf)) {
|
|
35
43
|
cleanup()
|
|
36
44
|
resolve(buffer)
|
|
37
45
|
}
|
|
38
|
-
}
|
|
39
|
-
else if (Array.isArray(conf)) {
|
|
46
|
+
} else if (Array.isArray(conf)) {
|
|
40
47
|
for (const seq of conf) {
|
|
41
48
|
if (buffer === seq || buffer.endsWith(seq)) {
|
|
42
49
|
cleanup()
|
|
@@ -47,24 +54,21 @@ const next = async (conf = undefined) => {
|
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
const errorHandler =
|
|
57
|
+
const errorHandler = err => {
|
|
51
58
|
cleanup()
|
|
52
59
|
reject(err)
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
const cleanup = () => {
|
|
56
|
-
process.stdin.off(
|
|
57
|
-
process.stdin.off(
|
|
63
|
+
process.stdin.off("data", onData)
|
|
64
|
+
process.stdin.off("error", errorHandler)
|
|
58
65
|
process.stdin.setRawMode(false)
|
|
59
66
|
process.stdin.resume()
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
process.stdin.setRawMode(true)
|
|
63
70
|
process.stdin.resume()
|
|
64
|
-
|
|
65
|
-
process.stdin.
|
|
66
|
-
process.stdin.on('data', onData)
|
|
71
|
+
process.stdin.once("error", errorHandler)
|
|
72
|
+
process.stdin.on("data", onData)
|
|
67
73
|
})
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export { next, pause }
|
|
74
|
+
}
|
package/src/ui/select.js
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Select module – renders a numbered list of options and returns the chosen value.
|
|
3
|
+
*
|
|
4
|
+
* @module ui/select
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CancelError } from "@nan0web/ui/core"
|
|
8
|
+
import createInput, { ask as baseAsk } from "./input.js"
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
|
-
*
|
|
6
|
-
* Automatically creates its own input handler.
|
|
11
|
+
* Configuration object for {@link select}.
|
|
7
12
|
*
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
11
|
-
* @
|
|
12
|
-
* @
|
|
13
|
-
* @
|
|
14
|
-
* @
|
|
13
|
+
* @typedef {Object} SelectConfig
|
|
14
|
+
* @property {string} title – Title displayed above the options list.
|
|
15
|
+
* @property {string} prompt – Prompt displayed for the answer.
|
|
16
|
+
* @property {Array|Map} options – Collection of selectable items.
|
|
17
|
+
* @property {Object} console – Console‑like object with an `info` method.
|
|
18
|
+
* @property {string[]} [stops=[]] Words that trigger cancellation.
|
|
19
|
+
* @property {import("./input.js").InputFn} [ask] Custom ask function (defaults to {@link createInput}).
|
|
20
|
+
* @property {string} [invalidPrompt="Invalid choice, try again: "] Message shown on invalid input.
|
|
15
21
|
*
|
|
16
|
-
* @returns {Promise<{
|
|
17
|
-
* @throws {CancelError} if the user cancels
|
|
22
|
+
* @returns {Promise<{index:number,value:any}>} Resolves with the selected index and its value.
|
|
18
23
|
*/
|
|
19
24
|
export async function select({
|
|
20
25
|
title,
|
|
@@ -22,36 +27,41 @@ export async function select({
|
|
|
22
27
|
invalidPrompt = "Invalid choice, try again: ",
|
|
23
28
|
options,
|
|
24
29
|
console,
|
|
25
|
-
|
|
30
|
+
stops = [],
|
|
31
|
+
ask: initAsk,
|
|
26
32
|
}) {
|
|
33
|
+
const ask = initAsk ?? createInput(stops)
|
|
34
|
+
// Normalise Map → Array of {label,value}
|
|
27
35
|
if (options instanceof Map) {
|
|
28
36
|
options = Array.from(options.entries()).map(([value, label]) => ({ label, value }))
|
|
29
37
|
}
|
|
30
38
|
if (!Array.isArray(options) || options.length === 0) {
|
|
31
39
|
throw new Error("Options array is required and must not be empty")
|
|
32
40
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
const list = options.map(el =>
|
|
42
|
+
typeof el === "string" ? { label: el, value: el } : el,
|
|
43
|
+
)
|
|
36
44
|
|
|
37
45
|
console.info(title)
|
|
38
|
-
list.forEach(({ label }, i) => {
|
|
39
|
-
console.info(` ${i + 1}) ${label}`)
|
|
40
|
-
})
|
|
46
|
+
list.forEach(({ label }, i) => console.info(` ${i + 1}) ${label}`))
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
const idx = Number(input.value) - 1
|
|
44
|
-
return idx < 0 || idx >= list.length
|
|
45
|
-
}, invalidPrompt)
|
|
48
|
+
let currentPrompt = prompt
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
while (true) {
|
|
51
|
+
const answer = await ask(currentPrompt)
|
|
52
|
+
|
|
53
|
+
if (answer.cancelled) throw new CancelError()
|
|
54
|
+
|
|
55
|
+
const idx = Number(answer.value) - 1
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
if (isNaN(idx) || idx < 0 || idx >= list.length) {
|
|
58
|
+
if (invalidPrompt) {
|
|
59
|
+
currentPrompt = invalidPrompt
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
throw new Error("Incorrect value provided")
|
|
63
|
+
}
|
|
64
|
+
return { index: idx, value: list[idx].value }
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for parsing command‑line strings.
|
|
3
|
+
*
|
|
4
|
+
* @module utils/parse
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parses a string into an argv array (handles quotes).
|
|
9
|
+
*
|
|
10
|
+
* @param {string} str - Raw command line.
|
|
11
|
+
* @returns {string[]} Tokenized arguments.
|
|
12
|
+
* @throws {Error} If a quote is left unmatched.
|
|
13
|
+
*/
|
|
14
|
+
export function str2argv(str) {
|
|
15
|
+
const parts = []
|
|
16
|
+
let i = 0
|
|
17
|
+
str = String(str).trim()
|
|
18
|
+
|
|
19
|
+
while (i < str.length) {
|
|
20
|
+
while (i < str.length && str[i] === ' ') i++
|
|
21
|
+
if (i >= str.length) break
|
|
22
|
+
|
|
23
|
+
if (str[i] === '"' || str[i] === "'") {
|
|
24
|
+
const quote = str[i]
|
|
25
|
+
i++
|
|
26
|
+
let start = i
|
|
27
|
+
while (i < str.length && str[i] !== quote) i++
|
|
28
|
+
if (i < str.length) {
|
|
29
|
+
parts.push(str.slice(start, i))
|
|
30
|
+
i++
|
|
31
|
+
} else {
|
|
32
|
+
throw new Error(`Unmatched quote in argument: ${str}`)
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
let start = i
|
|
36
|
+
while (i < str.length && str[i] !== ' ') i++
|
|
37
|
+
parts.push(str.slice(start, i))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return parts
|
|
41
|
+
}
|
package/types/CLI.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main CLI class.
|
|
3
|
+
*/
|
|
4
|
+
export default class CLI {
|
|
5
|
+
/**
|
|
6
|
+
* Factory to create a CLI instance from various inputs.
|
|
7
|
+
*
|
|
8
|
+
* @param {CLI|Object} input - Existing CLI instance or configuration object.
|
|
9
|
+
* @returns {CLI}
|
|
10
|
+
* @throws {TypeError} If input is neither a CLI nor an object.
|
|
11
|
+
*/
|
|
12
|
+
static from(input: CLI | any): CLI;
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} [input={}]
|
|
15
|
+
* @param {string[]} [input.argv] - Command‑line arguments (defaults to `process.argv.slice(2)`).
|
|
16
|
+
* @param {Object} [input.commands] - Map of command names to handlers.
|
|
17
|
+
* @param {Logger} [input.logger] - Optional logger instance.
|
|
18
|
+
* @param {Array<Function>} [input.Messages] - Message classes for root commands.
|
|
19
|
+
*/
|
|
20
|
+
constructor(input?: {
|
|
21
|
+
argv?: string[] | undefined;
|
|
22
|
+
commands?: any;
|
|
23
|
+
logger?: Logger | undefined;
|
|
24
|
+
Messages?: Function[] | undefined;
|
|
25
|
+
} | undefined);
|
|
26
|
+
/** @type {string[]} */
|
|
27
|
+
argv: string[];
|
|
28
|
+
/** @type {Logger} */
|
|
29
|
+
logger: Logger;
|
|
30
|
+
/** @type {Array<Function>} */
|
|
31
|
+
Messages: Array<Function>;
|
|
32
|
+
/** @returns {Map<string,Function>} The command map. */
|
|
33
|
+
get commands(): Map<string, Function>;
|
|
34
|
+
/**
|
|
35
|
+
* Execute the CLI workflow.
|
|
36
|
+
*
|
|
37
|
+
* @param {Message} [msg] - Optional pre‑built message.
|
|
38
|
+
* @yields {OutputMessage|InputMessage}
|
|
39
|
+
*/
|
|
40
|
+
run(msg?: Message | undefined): AsyncGenerator<any, void, unknown>;
|
|
41
|
+
#private;
|
|
42
|
+
}
|
|
43
|
+
import Logger from "@nan0web/log";
|
|
44
|
+
import { Message } from "@nan0web/co";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a command definition.
|
|
3
|
+
*
|
|
4
|
+
* @class
|
|
5
|
+
* @deprecated Use CLI instead
|
|
6
|
+
*/
|
|
7
|
+
export default class Command {
|
|
8
|
+
/**
|
|
9
|
+
* @param {Object} config - Command configuration.
|
|
10
|
+
* @param {string} [config.name] - Command name.
|
|
11
|
+
* @param {string} [config.help] - Help description.
|
|
12
|
+
* @param {Object} [config.options] - Options map (`{ flag: [type, default, help] }`).
|
|
13
|
+
* @param {Function} [config.run] - Async generator handling execution.
|
|
14
|
+
* @param {Command[]} [config.children] - Sub‑commands.
|
|
15
|
+
*/
|
|
16
|
+
constructor(config: {
|
|
17
|
+
name?: string | undefined;
|
|
18
|
+
help?: string | undefined;
|
|
19
|
+
options?: any;
|
|
20
|
+
run?: Function | undefined;
|
|
21
|
+
children?: Command[] | undefined;
|
|
22
|
+
});
|
|
23
|
+
/** @type {string} */ name: string;
|
|
24
|
+
/** @type {string} */ help: string;
|
|
25
|
+
/** @type {Object} */ options: any;
|
|
26
|
+
/** @type {Function} */ run: Function;
|
|
27
|
+
/** @type {Command[]} */ children: Command[];
|
|
28
|
+
/**
|
|
29
|
+
* Add a sub‑command.
|
|
30
|
+
*
|
|
31
|
+
* @param {Command} command - Sub‑command instance.
|
|
32
|
+
* @returns {this}
|
|
33
|
+
*/
|
|
34
|
+
addSubcommand(command: Command): this;
|
|
35
|
+
/**
|
|
36
|
+
* Find a sub‑command by name.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} name - Sub‑command name.
|
|
39
|
+
* @returns {Command|null}
|
|
40
|
+
*/
|
|
41
|
+
findSubcommand(name: string): Command | null;
|
|
42
|
+
/**
|
|
43
|
+
* Parse argv into a {@link CommandMessage}.
|
|
44
|
+
*
|
|
45
|
+
* @param {string[]|string} argv - Arguments array or string.
|
|
46
|
+
* @returns {CommandMessage}
|
|
47
|
+
*/
|
|
48
|
+
parse(argv: string[] | string): CommandMessage;
|
|
49
|
+
/**
|
|
50
|
+
* Generate a short help string.
|
|
51
|
+
*
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
generateHelp(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Execute the command's run generator.
|
|
57
|
+
*
|
|
58
|
+
* @param {Message} message - Message passed to the runner.
|
|
59
|
+
* @yields {any}
|
|
60
|
+
* @throws {CommandError}
|
|
61
|
+
*/
|
|
62
|
+
execute(message: Message): AsyncGenerator<any, void, any>;
|
|
63
|
+
/**
|
|
64
|
+
* Apply default values from the options definition to the parsed message.
|
|
65
|
+
*
|
|
66
|
+
* @param {CommandMessage} msg
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
private _applyDefaults;
|
|
70
|
+
}
|
|
71
|
+
import CommandMessage from "./CommandMessage.js";
|
|
72
|
+
import { Message } from "@nan0web/co";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandError – error class representing a failure during command execution.
|
|
3
|
+
*
|
|
4
|
+
* @module CommandError
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* @class
|
|
8
|
+
* @extends Error
|
|
9
|
+
*/
|
|
10
|
+
export default class CommandError extends Error {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a command execution error.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} message - Message that opens the path.
|
|
15
|
+
* @param {Object} [data=null] - Data to help find correct resonance.
|
|
16
|
+
*/
|
|
17
|
+
constructor(message: string, data?: any);
|
|
18
|
+
data: any;
|
|
19
|
+
}
|