@nan0web/ui-cli 1.1.0 → 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.
Files changed (115) hide show
  1. package/README.md +114 -207
  2. package/package.json +22 -12
  3. package/src/CLI.js +22 -29
  4. package/src/CLiMessage.js +2 -3
  5. package/src/Command.js +26 -24
  6. package/src/CommandError.js +3 -5
  7. package/src/CommandHelp.js +40 -36
  8. package/src/CommandMessage.js +56 -40
  9. package/src/CommandParser.js +27 -25
  10. package/src/InputAdapter.js +630 -90
  11. package/src/OutputAdapter.js +7 -8
  12. package/src/README.md.js +190 -316
  13. package/src/components/Alert.js +3 -6
  14. package/src/components/prompt/Autocomplete.js +12 -0
  15. package/src/components/prompt/Confirm.js +29 -0
  16. package/src/components/prompt/DateTime.js +26 -0
  17. package/src/components/prompt/Input.js +15 -0
  18. package/src/components/prompt/Mask.js +12 -0
  19. package/src/components/prompt/Multiselect.js +26 -0
  20. package/src/components/prompt/Next.js +8 -0
  21. package/src/components/prompt/Password.js +13 -0
  22. package/src/components/prompt/Pause.js +9 -0
  23. package/src/components/prompt/ProgressBar.js +16 -0
  24. package/src/components/prompt/Select.js +29 -0
  25. package/src/components/prompt/Slider.js +16 -0
  26. package/src/components/prompt/Spinner.js +29 -0
  27. package/src/components/prompt/Toggle.js +13 -0
  28. package/src/components/prompt/Tree.js +17 -0
  29. package/src/components/view/Alert.js +78 -0
  30. package/src/components/view/Badge.js +11 -0
  31. package/src/components/view/Nav.js +23 -0
  32. package/src/components/view/Table.js +12 -0
  33. package/src/components/view/Toast.js +9 -0
  34. package/src/core/Component.js +79 -0
  35. package/src/core/PropValidation.js +138 -0
  36. package/src/core/render.js +37 -0
  37. package/src/index.js +80 -41
  38. package/src/test/PlaygroundTest.js +37 -25
  39. package/src/test/index.js +2 -4
  40. package/src/ui/alert.js +58 -0
  41. package/src/ui/autocomplete.js +86 -0
  42. package/src/ui/badge.js +35 -0
  43. package/src/ui/confirm.js +49 -0
  44. package/src/ui/date-time.js +45 -0
  45. package/src/ui/form.js +120 -55
  46. package/src/ui/index.js +18 -4
  47. package/src/ui/input.js +79 -152
  48. package/src/ui/mask.js +132 -0
  49. package/src/ui/multiselect.js +59 -0
  50. package/src/ui/nav.js +74 -0
  51. package/src/ui/next.js +18 -13
  52. package/src/ui/progress.js +88 -0
  53. package/src/ui/select.js +49 -72
  54. package/src/ui/slider.js +154 -0
  55. package/src/ui/spinner.js +65 -0
  56. package/src/ui/table.js +163 -0
  57. package/src/ui/toast.js +34 -0
  58. package/src/ui/toggle.js +34 -0
  59. package/src/ui/tree.js +393 -0
  60. package/src/utils/parse.js +1 -1
  61. package/types/CLI.d.ts +5 -5
  62. package/types/CLiMessage.d.ts +1 -1
  63. package/types/Command.d.ts +2 -2
  64. package/types/CommandHelp.d.ts +3 -3
  65. package/types/CommandMessage.d.ts +8 -8
  66. package/types/CommandParser.d.ts +3 -3
  67. package/types/InputAdapter.d.ts +149 -15
  68. package/types/OutputAdapter.d.ts +1 -1
  69. package/types/README.md.d.ts +1 -1
  70. package/types/UiMessage.d.ts +31 -29
  71. package/types/components/prompt/Autocomplete.d.ts +6 -0
  72. package/types/components/prompt/Confirm.d.ts +6 -0
  73. package/types/components/prompt/DateTime.d.ts +6 -0
  74. package/types/components/prompt/Input.d.ts +6 -0
  75. package/types/components/prompt/Mask.d.ts +6 -0
  76. package/types/components/prompt/Multiselect.d.ts +6 -0
  77. package/types/components/prompt/Next.d.ts +6 -0
  78. package/types/components/prompt/Password.d.ts +6 -0
  79. package/types/components/prompt/Pause.d.ts +6 -0
  80. package/types/components/prompt/ProgressBar.d.ts +12 -0
  81. package/types/components/prompt/Select.d.ts +18 -0
  82. package/types/components/prompt/Slider.d.ts +6 -0
  83. package/types/components/prompt/Spinner.d.ts +21 -0
  84. package/types/components/prompt/Toggle.d.ts +6 -0
  85. package/types/components/prompt/Tree.d.ts +6 -0
  86. package/types/components/view/Alert.d.ts +21 -0
  87. package/types/components/view/Badge.d.ts +5 -0
  88. package/types/components/view/Nav.d.ts +15 -0
  89. package/types/components/view/Table.d.ts +10 -0
  90. package/types/components/view/Toast.d.ts +5 -0
  91. package/types/core/Component.d.ts +34 -0
  92. package/types/core/PropValidation.d.ts +48 -0
  93. package/types/core/render.d.ts +6 -0
  94. package/types/index.d.ts +47 -15
  95. package/types/test/PlaygroundTest.d.ts +12 -8
  96. package/types/test/index.d.ts +1 -1
  97. package/types/ui/alert.d.ts +14 -0
  98. package/types/ui/autocomplete.d.ts +20 -0
  99. package/types/ui/badge.d.ts +8 -0
  100. package/types/ui/confirm.d.ts +21 -0
  101. package/types/ui/date-time.d.ts +19 -0
  102. package/types/ui/form.d.ts +43 -12
  103. package/types/ui/index.d.ts +17 -2
  104. package/types/ui/input.d.ts +31 -74
  105. package/types/ui/mask.d.ts +29 -0
  106. package/types/ui/multiselect.d.ts +25 -0
  107. package/types/ui/nav.d.ts +27 -0
  108. package/types/ui/progress.d.ts +43 -0
  109. package/types/ui/select.d.ts +25 -64
  110. package/types/ui/slider.d.ts +23 -0
  111. package/types/ui/spinner.d.ts +28 -0
  112. package/types/ui/table.d.ts +28 -0
  113. package/types/ui/toast.d.ts +8 -0
  114. package/types/ui/toggle.d.ts +17 -0
  115. package/types/ui/tree.d.ts +48 -0
package/src/ui/tree.js ADDED
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Tree View Component - Interactive File/Directory Tree.
3
+ *
4
+ * @module ui/tree
5
+ */
6
+
7
+ import process from 'node:process'
8
+ import readline from 'node:readline'
9
+ import Logger from '@nan0web/log'
10
+ import prompts from 'prompts'
11
+ import { CancelError } from '@nan0web/ui/core'
12
+ import { beep } from './input.js'
13
+ import {
14
+ validateString,
15
+ validateFunction,
16
+ validateNumber,
17
+ validateBoolean,
18
+ } from '../core/PropValidation.js'
19
+
20
+ // --- ANSI Utilities ---
21
+ const ESC = '\x1B['
22
+ const HIDE_CURSOR = `${ESC}?25l`
23
+ const SHOW_CURSOR = `${ESC}?25h`
24
+ const UP = (n = 1) => `${ESC}${n}A`
25
+ const ERASE_DOWN = `${ESC}J`
26
+
27
+ /**
28
+ * @typedef {Object} TreeNode
29
+ * @property {string} name
30
+ * @property {'file'|'dir'} type
31
+ * @property {TreeNode[]} [children] -- If undefined, might be loaded async
32
+ * @property {any} [payload] -- Custom data
33
+ * @property {any} [value] -- Result value (usually same as path or name)
34
+ * @property {string} [path] -- File path
35
+ * @property {boolean} [expanded] -- Internal state
36
+ * @property {boolean} [checked] -- Internal state
37
+ * @property {number} [depth] -- Calculated
38
+ */
39
+
40
+ /**
41
+ * Tree Prompt
42
+ */
43
+ export async function tree(config) {
44
+ const {
45
+ message = 'Select:',
46
+ mode = 'file', // 'file', 'dir', 'multi'
47
+ tree = null, // Root node or array of nodes
48
+ loader = null, // async (node) => children[]
49
+ limit = 10,
50
+ initialExpanded = [], // keys or paths
51
+ multiselect = mode === 'multi',
52
+ t = (k) => k,
53
+ } = config
54
+
55
+ // Prop Validation
56
+ validateString(message, 'message', 'Tree')
57
+ validateString(mode, 'mode', 'Tree')
58
+ validateNumber(limit, 'limit', 'Tree')
59
+ validateFunction(loader, 'loader', 'Tree')
60
+ validateFunction(t, 't', 'Tree')
61
+ validateBoolean(multiselect, 'multiselect', 'Tree')
62
+
63
+ // Initialize State
64
+ let roots = Array.isArray(tree) ? tree : tree ? [tree] : []
65
+ // If tree is empty, try to load if loader exists
66
+ if (roots.length === 0 && loader) {
67
+ const res = await loader(null) // Load roots
68
+ roots = res || []
69
+ }
70
+
71
+ // Automated environments support via PLAY_DEMO_SEQUENCE
72
+ if (process.env.PLAY_DEMO_SEQUENCE) {
73
+ const response = await prompts({
74
+ type: 'text',
75
+ name: 'value',
76
+ message: t(message),
77
+ initial: config.initial || roots[0]?.name || 'unknown',
78
+ })
79
+ return { value: response.value, cancelled: response.value === undefined }
80
+ }
81
+
82
+ // Internal State
83
+ const state = {
84
+ roots,
85
+ cursor: 0, // Index in flattened list
86
+ offset: 0, // Scroll offset
87
+ expanded: new Set(), // Set of objects (nodes)
88
+ checked: new Set(), // Set of objects (nodes)
89
+ /** @type {TreeNode[]} */
90
+ flat: [], // Flattened visible nodes
91
+ loading: false,
92
+ message,
93
+ done: false,
94
+ aborted: false,
95
+ }
96
+
97
+ // Helper: Flatten visible nodes
98
+ function flatten() {
99
+ /** @type {TreeNode[]} */
100
+ const list = []
101
+ const traverse = (nodes, depth) => {
102
+ for (const node of nodes) {
103
+ node.depth = depth
104
+ list.push(node)
105
+ if (state.expanded.has(node) && node.children) {
106
+ traverse(node.children, depth + 1)
107
+ }
108
+ }
109
+ }
110
+ traverse(state.roots, 0)
111
+ return list
112
+ }
113
+
114
+ // Load children if needed
115
+ async function toggleExpand(node) {
116
+ if (node.type !== 'dir') return
117
+
118
+ if (state.expanded.has(node)) {
119
+ state.expanded.delete(node)
120
+ } else {
121
+ state.expanded.add(node)
122
+ if (!node.children && loader) {
123
+ state.loading = true
124
+ render() // Show loading
125
+ try {
126
+ const children = await loader(node)
127
+ node.children = children || []
128
+ } catch (e) {
129
+ state.expanded.delete(node) // Rollback
130
+ }
131
+ state.loading = false
132
+ }
133
+ }
134
+ state.flat = flatten()
135
+ }
136
+
137
+ function up() {
138
+ if (state.cursor > 0) {
139
+ state.cursor--
140
+ if (state.cursor < state.offset) {
141
+ state.offset--
142
+ }
143
+ }
144
+ }
145
+
146
+ function down() {
147
+ if (state.cursor < state.flat.length - 1) {
148
+ state.cursor++
149
+ if (state.cursor >= state.offset + limit) {
150
+ state.offset++
151
+ }
152
+ }
153
+ }
154
+
155
+ function left() {
156
+ const node = state.flat[state.cursor]
157
+ if (state.expanded.has(node)) {
158
+ // Collapse
159
+ toggleExpand(node)
160
+ } else {
161
+ // Jump to parent
162
+ if (node.depth && node.depth > 0) {
163
+ // Find parent index: look backwards for node with depth - 1
164
+ for (let i = state.cursor - 1; i >= 0; i--) {
165
+ if (state.flat[i].depth === node.depth - 1) {
166
+ state.cursor = i
167
+ // Update scroll
168
+ if (state.cursor < state.offset) state.offset = state.cursor
169
+ break
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ async function right() {
177
+ const node = state.flat[state.cursor]
178
+ if (node.type === 'dir') {
179
+ if (!state.expanded.has(node)) {
180
+ await toggleExpand(node)
181
+ }
182
+ }
183
+ }
184
+
185
+ function check() {
186
+ if (!multiselect) return
187
+ const node = state.flat[state.cursor]
188
+ if (state.checked.has(node)) state.checked.delete(node)
189
+ else state.checked.add(node)
190
+ }
191
+
192
+ // Initial Flatten
193
+ state.flat = flatten()
194
+
195
+ // Setup Input
196
+ const { stdin, stdout } = process
197
+ if (stdin.isTTY && typeof stdin.setRawMode === 'function') {
198
+ stdin.setRawMode(true)
199
+ }
200
+ stdin.resume()
201
+ readline.emitKeypressEvents(stdin)
202
+
203
+ // Render Loop
204
+ let linesRendered = 0
205
+ let firstRender = true
206
+
207
+ function render() {
208
+ if (!firstRender) {
209
+ // Clear previous output
210
+ stdout.write(UP(linesRendered) + ERASE_DOWN)
211
+ }
212
+ firstRender = false
213
+
214
+ let out = ''
215
+ // Title
216
+ out += Logger.style(`? ${t(state.message)}\n`, { color: Logger.CYAN })
217
+
218
+ // Tree View
219
+ const visible = state.flat.slice(state.offset, state.offset + limit)
220
+
221
+ if (visible.length === 0) {
222
+ out += Logger.style(` ${t('tree.empty')}\n`, { color: Logger.DIM })
223
+ }
224
+
225
+ for (let i = 0; i < visible.length; i++) {
226
+ const node = visible[i]
227
+ const absIndex = state.offset + i
228
+ const isFocused = absIndex === state.cursor
229
+
230
+ // Indent
231
+ let line = ' '.repeat(node.depth || 0)
232
+
233
+ // Prefix (Expandable)
234
+ if (node.type === 'dir') {
235
+ line += state.expanded.has(node) ? '▼ ' : '▶ '
236
+ } else {
237
+ line += ' ' // Align with arrows
238
+ }
239
+
240
+ // Checkbox
241
+ if (multiselect) {
242
+ const isChecked = state.checked.has(node)
243
+ line += isChecked
244
+ ? Logger.style('◉ ', { color: Logger.GREEN })
245
+ : Logger.style('◯ ', { color: Logger.DIM })
246
+ }
247
+
248
+ // Icon + Name
249
+ const icon = node.type === 'dir' ? '📁' : '📄'
250
+ const nameDisplay = `${icon} ${node.name}`
251
+
252
+ if (isFocused) {
253
+ // Use bold/color for focus
254
+ line += Logger.style('> ' + nameDisplay, { color: Logger.CYAN })
255
+ if (state.loading && state.expanded.has(node))
256
+ line += Logger.style(` ${t('tree.loading')}`, { color: Logger.DIM })
257
+ } else {
258
+ line += ' ' + nameDisplay
259
+ }
260
+
261
+ out += line + '\n'
262
+ }
263
+
264
+ // Instructions / Footer
265
+ const helpKey = multiselect ? 'tree.help.multi' : 'tree.help.single'
266
+ out += Logger.style(`\n${t(helpKey)}`, { color: Logger.DIM })
267
+
268
+ stdout.write(out)
269
+ linesRendered = out.split('\n').length - 1 // approximate
270
+ }
271
+
272
+ // Interactive Loop
273
+ return new Promise((resolve, reject) => {
274
+ const onKey = async (str, key) => {
275
+ if (state.done) return
276
+
277
+ // Handle Ctrl+C
278
+ if (key.ctrl && key.name === 'c') {
279
+ cleanup()
280
+ reject(new CancelError())
281
+ return
282
+ }
283
+
284
+ if (state.loading) return // Block input while loading
285
+
286
+ switch (key.name) {
287
+ case 'up':
288
+ up()
289
+ break
290
+ case 'down':
291
+ down()
292
+ break
293
+ case 'left':
294
+ left()
295
+ break // Sync handled?
296
+ case 'right':
297
+ await right()
298
+ break
299
+ case 'space':
300
+ check()
301
+ break
302
+ case 'return':
303
+ case 'enter':
304
+ submit()
305
+ return
306
+ default:
307
+ if (!key.ctrl && !key.meta && str && str.length === 1) {
308
+ const s = str.toLowerCase()
309
+ const idx = state.flat.findIndex(
310
+ (n, i) => i > state.cursor && n.name.toLowerCase().startsWith(s)
311
+ )
312
+ if (idx !== -1) {
313
+ state.cursor = idx
314
+ } else {
315
+ const idx2 = state.flat.findIndex((n) => n.name.toLowerCase().startsWith(s))
316
+ if (idx2 !== -1) state.cursor = idx2
317
+ }
318
+ // Adjust scroll offset
319
+ if (state.cursor < state.offset) state.offset = state.cursor
320
+ if (state.cursor >= state.offset + limit)
321
+ state.offset = Math.max(0, state.cursor - limit + 1)
322
+ }
323
+ break
324
+ }
325
+
326
+ state.flat = flatten() // Re-flatten (redundant if only selection moved, but safe)
327
+ render()
328
+ }
329
+
330
+ function submit() {
331
+ if (multiselect) {
332
+ state.done = true
333
+ cleanup()
334
+ resolve({
335
+ value: Array.from(state.checked).map((n) => n.value || n.path || n.name),
336
+ cancelled: false,
337
+ })
338
+ } else {
339
+ const node = state.flat[state.cursor]
340
+ if (!node) return // Nothing to submit
341
+ // Validation
342
+ if (mode === 'file' && node.type !== 'file') {
343
+ // If Enter on dir in 'file' mode -> toggle expand
344
+ if (node.type === 'dir') {
345
+ toggleExpand(node).then(() => {
346
+ state.flat = flatten()
347
+ render()
348
+ })
349
+ return
350
+ }
351
+ }
352
+ if (mode === 'dir' && node.type !== 'dir') {
353
+ beep()
354
+ return
355
+ }
356
+ state.done = true
357
+ cleanup()
358
+ resolve({ value: node.value || node.path || node.name, cancelled: false, node })
359
+ }
360
+ }
361
+
362
+ function cleanup() {
363
+ stdin.removeListener('keypress', onKey)
364
+ if (process.stdin.isTTY && typeof process.stdin.setRawMode === 'function') {
365
+ process.stdin.setRawMode(false)
366
+ }
367
+ stdin.pause() // Crucial for automated tests to release event loop
368
+
369
+ // Final cleanup message or checkmark?
370
+ if (state.done) {
371
+ stdout.write(UP(linesRendered) + ERASE_DOWN)
372
+ stdout.write(Logger.style(`✔ ${t(state.message)} `, { color: Logger.GREEN }))
373
+ // Show result summary
374
+ if (multiselect) {
375
+ stdout.write(
376
+ Logger.style(`${state.checked.size} ${t('tree.selected')}\n`, { color: Logger.WHITE })
377
+ )
378
+ } else {
379
+ const node = state.flat[state.cursor]
380
+ stdout.write(Logger.style(`${node.name}\n`, { color: Logger.WHITE }))
381
+ }
382
+ } else {
383
+ // Cancelled
384
+ stdout.write('\n')
385
+ }
386
+ stdout.write(SHOW_CURSOR)
387
+ }
388
+
389
+ stdin.on('keypress', onKey)
390
+ stdout.write(HIDE_CURSOR)
391
+ render()
392
+ })
393
+ }
@@ -38,4 +38,4 @@ export function str2argv(str) {
38
38
  }
39
39
  }
40
40
  return parts
41
- }
41
+ }
package/types/CLI.d.ts CHANGED
@@ -22,7 +22,7 @@ export default class CLi {
22
22
  commands?: any;
23
23
  logger?: Logger | undefined;
24
24
  Messages?: Function[] | undefined;
25
- } | undefined);
25
+ });
26
26
  /** @type {string[]} */
27
27
  argv: string[];
28
28
  /** @type {Logger} */
@@ -37,9 +37,9 @@ export default class CLi {
37
37
  * @param {Message} [msg] - Optional pre‑built message.
38
38
  * @returns {AsyncGenerator<OutputMessage>}
39
39
  */
40
- run(msg?: Message | undefined): AsyncGenerator<OutputMessage>;
40
+ run(msg?: Message): AsyncGenerator<OutputMessage>;
41
41
  #private;
42
42
  }
43
- import Logger from "@nan0web/log";
44
- import { Message } from "@nan0web/co";
45
- import { OutputMessage } from "@nan0web/co";
43
+ import Logger from '@nan0web/log';
44
+ import { Message } from '@nan0web/co';
45
+ import { OutputMessage } from '@nan0web/co';
@@ -5,4 +5,4 @@
5
5
  */
6
6
  export default class CLiMessage extends UiMessage {
7
7
  }
8
- import { UiMessage } from "@nan0web/ui";
8
+ import { UiMessage } from '@nan0web/ui';
@@ -68,5 +68,5 @@ export default class Command {
68
68
  */
69
69
  private _applyDefaults;
70
70
  }
71
- import CommandMessage from "./CommandMessage.js";
72
- import { Message } from "@nan0web/co";
71
+ import CommandMessage from './CommandMessage.js';
72
+ import { Message } from '@nan0web/co';
@@ -22,7 +22,7 @@ export default class CommandHelp {
22
22
  * @param {typeof Message} MessageClass - Message class with a schema.
23
23
  * @param {Logger} [logger=new Logger()] - Optional logger.
24
24
  */
25
- constructor(MessageClass: typeof Message, logger?: Logger | undefined);
25
+ constructor(MessageClass: typeof Message, logger?: Logger);
26
26
  /** @type {typeof Message} Message class the help is built for */
27
27
  MessageClass: typeof Message;
28
28
  /** @type {Logger} Logger used for printing */
@@ -81,5 +81,5 @@ export type CommandHelpField = {
81
81
  */
82
82
  pattern?: RegExp | undefined;
83
83
  };
84
- import { Message } from "@nan0web/co";
85
- import Logger from "@nan0web/log";
84
+ import { Message } from '@nan0web/co';
85
+ import Logger from '@nan0web/log';
@@ -12,7 +12,7 @@ export default class CommandMessage extends Message {
12
12
  * @returns {CommandMessage}
13
13
  * @throws {CommandError} If no input is supplied.
14
14
  */
15
- static parse(argv: string | string[], BodyClass?: ObjectConstructor | undefined): CommandMessage;
15
+ static parse(argv: string | string[], BodyClass?: typeof Object): CommandMessage;
16
16
  /**
17
17
  * Convert a raw input into a {@link CommandMessage} instance.
18
18
  *
@@ -34,23 +34,23 @@ export default class CommandMessage extends Message {
34
34
  opts?: any;
35
35
  children?: CommandMessage[] | undefined;
36
36
  body?: any;
37
- } | undefined);
37
+ });
38
38
  /** @param {string} v */
39
- set name(arg: string);
39
+ set name(v: string);
40
40
  /** @returns {string} */
41
41
  get name(): string;
42
42
  /** @param {string[]} v */
43
- set argv(arg: string[]);
43
+ set argv(v: string[]);
44
44
  /** @returns {string[]} */
45
45
  get argv(): string[];
46
46
  /** @param {Object} v */
47
- set opts(arg: any);
47
+ set opts(v: any);
48
48
  /** @returns {Object} */
49
49
  get opts(): any;
50
50
  /** @returns {Array<CommandMessage>} */
51
- get children(): CommandMessage[];
51
+ get children(): Array<CommandMessage>;
52
52
  /** @returns {Array<string>} Full command line (name + args). */
53
- get args(): string[];
53
+ get args(): Array<string>;
54
54
  /** @returns {string} Sub‑command name of the first child, or empty string. */
55
55
  get subCommand(): string;
56
56
  /** @returns {CommandMessage|null} First child message, or null. */
@@ -63,4 +63,4 @@ export default class CommandMessage extends Message {
63
63
  add(msg: CommandMessage | any): void;
64
64
  #private;
65
65
  }
66
- import { Message } from "@nan0web/co";
66
+ import { Message } from '@nan0web/co';
@@ -5,7 +5,7 @@ export default class CommandParser {
5
5
  /**
6
6
  * @param {Array<Function>} [Messages=[]] - Root message classes.
7
7
  */
8
- constructor(Messages?: Function[] | undefined);
8
+ constructor(Messages?: Array<Function>);
9
9
  /** @type {Array<Function>} */
10
10
  Messages: Array<Function>;
11
11
  /**
@@ -15,7 +15,7 @@ export default class CommandParser {
15
15
  * @returns {Message}
16
16
  * @throws {Error} If no command is supplied or unknown root command.
17
17
  */
18
- parse(input?: string | string[] | undefined): Message;
18
+ parse(input?: string | string[]): Message;
19
19
  /**
20
20
  * Generate help text for a given message class.
21
21
  *
@@ -25,4 +25,4 @@ export default class CommandParser {
25
25
  generateHelp(MessageClass: typeof Message): string;
26
26
  #private;
27
27
  }
28
- import { Message } from "@nan0web/co";
28
+ import { Message } from '@nan0web/co';