@johannes.latzel/terminal 0.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/LICENSE +7 -0
- package/README.md +35 -0
- package/dist/command-arguments.d.ts +120 -0
- package/dist/command-arguments.d.ts.map +1 -0
- package/dist/command-arguments.js +155 -0
- package/dist/command-arguments.js.map +1 -0
- package/dist/command-factory.d.ts +17 -0
- package/dist/command-factory.d.ts.map +1 -0
- package/dist/command-factory.js +43 -0
- package/dist/command-factory.js.map +1 -0
- package/dist/command-tree.d.ts +40 -0
- package/dist/command-tree.d.ts.map +1 -0
- package/dist/command-tree.js +77 -0
- package/dist/command-tree.js.map +1 -0
- package/dist/commands/clear.d.ts +10 -0
- package/dist/commands/clear.d.ts.map +1 -0
- package/dist/commands/clear.js +13 -0
- package/dist/commands/clear.js.map +1 -0
- package/dist/commands/exit.d.ts +10 -0
- package/dist/commands/exit.d.ts.map +1 -0
- package/dist/commands/exit.js +13 -0
- package/dist/commands/exit.js.map +1 -0
- package/dist/commands/help.d.ts +40 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +136 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/completion/completer.d.ts +22 -0
- package/dist/completion/completer.d.ts.map +1 -0
- package/dist/completion/completer.js +55 -0
- package/dist/completion/completer.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -0
- package/dist/hook.d.ts +11 -0
- package/dist/hook.d.ts.map +1 -0
- package/dist/hook.js +16 -0
- package/dist/hook.js.map +1 -0
- package/dist/hooks.d.ts +21 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +17 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/input/args-parser.d.ts +22 -0
- package/dist/input/args-parser.d.ts.map +1 -0
- package/dist/input/args-parser.js +52 -0
- package/dist/input/args-parser.js.map +1 -0
- package/dist/input/parser.d.ts +14 -0
- package/dist/input/parser.d.ts.map +1 -0
- package/dist/input/parser.js +65 -0
- package/dist/input/parser.js.map +1 -0
- package/dist/terminal-hook-builder.d.ts +42 -0
- package/dist/terminal-hook-builder.d.ts.map +1 -0
- package/dist/terminal-hook-builder.js +48 -0
- package/dist/terminal-hook-builder.js.map +1 -0
- package/dist/terminal.d.ts +86 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +274 -0
- package/dist/terminal.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +85 -0
- package/dist/types.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class HookBuilder {
|
|
2
|
+
_reg;
|
|
3
|
+
constructor(_reg) {
|
|
4
|
+
this._reg = _reg;
|
|
5
|
+
}
|
|
6
|
+
do(callback) {
|
|
7
|
+
return this._reg(callback);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Entry point for registering terminal lifecycle hooks.
|
|
12
|
+
*
|
|
13
|
+
* Created by calling {@link Terminal.hook}. Select an event with one of the
|
|
14
|
+
* selector methods, then call {@link do} to register the callback.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const h = term.hook()
|
|
19
|
+
* .beforeExecute()
|
|
20
|
+
* .do((command) => console.log(command.name()));
|
|
21
|
+
* // later: h.dispose();
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class TerminalHookBuilder {
|
|
25
|
+
_regs;
|
|
26
|
+
constructor(_regs) {
|
|
27
|
+
this._regs = _regs;
|
|
28
|
+
}
|
|
29
|
+
beforeParse() {
|
|
30
|
+
return new HookBuilder(this._regs.beforeParse);
|
|
31
|
+
}
|
|
32
|
+
afterParse() {
|
|
33
|
+
return new HookBuilder(this._regs.afterParse);
|
|
34
|
+
}
|
|
35
|
+
beforeExecute() {
|
|
36
|
+
return new HookBuilder(this._regs.beforeExecute);
|
|
37
|
+
}
|
|
38
|
+
afterExecute() {
|
|
39
|
+
return new HookBuilder(this._regs.afterExecute);
|
|
40
|
+
}
|
|
41
|
+
beforeExit() {
|
|
42
|
+
return new HookBuilder(this._regs.beforeExit);
|
|
43
|
+
}
|
|
44
|
+
onError() {
|
|
45
|
+
return new HookBuilder(this._regs.onError);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=terminal-hook-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-hook-builder.js","sourceRoot":"","sources":["../src/terminal-hook-builder.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW;IACO;IAApB,YAAoB,IAAqB;QAArB,SAAI,GAAJ,IAAI,CAAiB;IAAG,CAAC;IAC7C,EAAE,CAAC,QAAW;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;CACJ;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,mBAAmB;IACR;IAApB,YAAoB,KAAwB;QAAxB,UAAK,GAAL,KAAK,CAAmB;IAAG,CAAC;IAEhD,WAAW;QACP,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACnD,CAAC;IAED,UAAU;QACN,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,aAAa;QAOT,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACrD,CAAC;IAED,YAAY;QACR,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACpD,CAAC;IAED,UAAU;QACN,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,OAAO;QACH,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;CACJ"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Command, type TerminalOptions } from './types.js';
|
|
2
|
+
import { TerminalHookBuilder } from './terminal-hook-builder.js';
|
|
3
|
+
/**
|
|
4
|
+
* Interactive terminal/shell that reads input, resolves commands, and
|
|
5
|
+
* dispatches execution through the lifecycle-hook pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Create one, {@link register} commands, then call {@link start}.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const term = new Terminal({ prompt: '> ' });
|
|
12
|
+
* term.register(new MyCommand());
|
|
13
|
+
* await term.start();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare class Terminal {
|
|
17
|
+
private rl;
|
|
18
|
+
private tree;
|
|
19
|
+
private _beforeParseHooks;
|
|
20
|
+
private _afterParseHooks;
|
|
21
|
+
private _beforeExecuteHooks;
|
|
22
|
+
private _afterExecuteHooks;
|
|
23
|
+
private _beforeExitHooks;
|
|
24
|
+
private _onErrorHooks;
|
|
25
|
+
private running;
|
|
26
|
+
private static readonly DEFAULT_OPTIONS;
|
|
27
|
+
private options;
|
|
28
|
+
private ctx;
|
|
29
|
+
private ctxState;
|
|
30
|
+
/**
|
|
31
|
+
* @param options - Prompt, I/O streams, history size, etc.
|
|
32
|
+
* Defaults to stdin/stdout with prompt `"> "`.
|
|
33
|
+
*/
|
|
34
|
+
constructor(options?: TerminalOptions);
|
|
35
|
+
private createContext;
|
|
36
|
+
private registerBuiltins;
|
|
37
|
+
/**
|
|
38
|
+
* Register a command at the root level or as a subcommand of a
|
|
39
|
+
* parent container specified by a dot-separated path.
|
|
40
|
+
*
|
|
41
|
+
* @param command - The command to register.
|
|
42
|
+
* @param parentPath - Optional dot-separated path to a parent
|
|
43
|
+
* container (e.g. `"config.set"`). All
|
|
44
|
+
* intermediate containers must already exist.
|
|
45
|
+
* @throws When the parent path does not resolve to a container.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```
|
|
49
|
+
* term.register(cmd) // root level
|
|
50
|
+
* term.register(cmd, 'config') // under root "config"
|
|
51
|
+
* term.register(cmd, 'config.set') // under config > set
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
register(command: Command, parentPath?: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Create a hook builder to register lifecycle callbacks.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```
|
|
60
|
+
* const h = term.hook()
|
|
61
|
+
* .beforeExecute()
|
|
62
|
+
* .do((command) => console.log(command.name()));
|
|
63
|
+
* // later: h.dispose();
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
hook(): TerminalHookBuilder;
|
|
67
|
+
private _addHook;
|
|
68
|
+
/** Returns all root-level registered commands. */
|
|
69
|
+
getRootCommands(): Command[];
|
|
70
|
+
/**
|
|
71
|
+
* Start the terminal loop. Idempotent — safe to call multiple times.
|
|
72
|
+
*/
|
|
73
|
+
start(): Promise<void>;
|
|
74
|
+
private createReadline;
|
|
75
|
+
private bindEvents;
|
|
76
|
+
/**
|
|
77
|
+
* Stop the terminal loop. Closes the readline interface.
|
|
78
|
+
*/
|
|
79
|
+
stop(): Promise<void>;
|
|
80
|
+
private handleLine;
|
|
81
|
+
private processInput;
|
|
82
|
+
private resolveCommand;
|
|
83
|
+
private executeWithHooks;
|
|
84
|
+
private handleError;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAyC,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAWlG,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAWjE;;;;;;;;;;;;GAYG;AACH,qBAAa,QAAQ;IACjB,OAAO,CAAC,EAAE,CAAmC;IAC7C,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,mBAAmB,CAA2B;IACtD,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAKrC;IAEF,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,QAAQ,CAA+B;IAE/C;;;OAGG;gBACS,OAAO,CAAC,EAAE,eAAe;IAOrC,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,gBAAgB;IAMxB;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAwBrD;;;;;;;;;;OAUG;IACH,IAAI,IAAI,mBAAmB;IAW3B,OAAO,CAAC,QAAQ;IAahB,kDAAkD;IAClD,eAAe,IAAI,OAAO,EAAE;IAI5B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAYb,UAAU;YAqBV,YAAY;IAgB1B,OAAO,CAAC,cAAc;YAaR,gBAAgB;YAYhB,WAAW;CAmB5B"}
|
package/dist/terminal.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import * as readline from 'node:readline';
|
|
2
|
+
import { CommandContainer } from './types.js';
|
|
3
|
+
import { CommandTree } from './command-tree.js';
|
|
4
|
+
import { tokenize } from './input/parser.js';
|
|
5
|
+
import { parseFlags } from './input/args-parser.js';
|
|
6
|
+
import { CommandArguments } from './command-arguments.js';
|
|
7
|
+
import { Completer } from './completion/completer.js';
|
|
8
|
+
import { CommandNotFoundError } from './errors.js';
|
|
9
|
+
import { HelpCommand } from './commands/help.js';
|
|
10
|
+
import { ExitCommand } from './commands/exit.js';
|
|
11
|
+
import { ClearCommand } from './commands/clear.js';
|
|
12
|
+
import { TerminalHookBuilder } from './terminal-hook-builder.js';
|
|
13
|
+
import { TypedHook } from './hooks.js';
|
|
14
|
+
/**
|
|
15
|
+
* Interactive terminal/shell that reads input, resolves commands, and
|
|
16
|
+
* dispatches execution through the lifecycle-hook pipeline.
|
|
17
|
+
*
|
|
18
|
+
* Create one, {@link register} commands, then call {@link start}.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const term = new Terminal({ prompt: '> ' });
|
|
23
|
+
* term.register(new MyCommand());
|
|
24
|
+
* await term.start();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export class Terminal {
|
|
28
|
+
rl = null;
|
|
29
|
+
tree;
|
|
30
|
+
_beforeParseHooks = [];
|
|
31
|
+
_afterParseHooks = [];
|
|
32
|
+
_beforeExecuteHooks = [];
|
|
33
|
+
_afterExecuteHooks = [];
|
|
34
|
+
_beforeExitHooks = [];
|
|
35
|
+
_onErrorHooks = [];
|
|
36
|
+
running = false;
|
|
37
|
+
static DEFAULT_OPTIONS = {
|
|
38
|
+
prompt: '> ',
|
|
39
|
+
stdin: process.stdin,
|
|
40
|
+
stdout: process.stdout,
|
|
41
|
+
historySize: 100
|
|
42
|
+
};
|
|
43
|
+
options;
|
|
44
|
+
ctx;
|
|
45
|
+
ctxState = {};
|
|
46
|
+
/**
|
|
47
|
+
* @param options - Prompt, I/O streams, history size, etc.
|
|
48
|
+
* Defaults to stdin/stdout with prompt `"> "`.
|
|
49
|
+
*/
|
|
50
|
+
constructor(options) {
|
|
51
|
+
this.options = { ...Terminal.DEFAULT_OPTIONS, ...options };
|
|
52
|
+
this.tree = new CommandTree();
|
|
53
|
+
this.ctx = this.createContext();
|
|
54
|
+
this.registerBuiltins();
|
|
55
|
+
}
|
|
56
|
+
createContext() {
|
|
57
|
+
return {
|
|
58
|
+
terminal: this,
|
|
59
|
+
stdout: this.options.stdout,
|
|
60
|
+
stdin: this.options.stdin,
|
|
61
|
+
state: this.ctxState,
|
|
62
|
+
logger: console,
|
|
63
|
+
exit: () => {
|
|
64
|
+
void this.stop();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
registerBuiltins() {
|
|
69
|
+
this.register(new HelpCommand());
|
|
70
|
+
this.register(new ExitCommand());
|
|
71
|
+
this.register(new ClearCommand());
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Register a command at the root level or as a subcommand of a
|
|
75
|
+
* parent container specified by a dot-separated path.
|
|
76
|
+
*
|
|
77
|
+
* @param command - The command to register.
|
|
78
|
+
* @param parentPath - Optional dot-separated path to a parent
|
|
79
|
+
* container (e.g. `"config.set"`). All
|
|
80
|
+
* intermediate containers must already exist.
|
|
81
|
+
* @throws When the parent path does not resolve to a container.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```
|
|
85
|
+
* term.register(cmd) // root level
|
|
86
|
+
* term.register(cmd, 'config') // under root "config"
|
|
87
|
+
* term.register(cmd, 'config.set') // under config > set
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
register(command, parentPath) {
|
|
91
|
+
if (!parentPath) {
|
|
92
|
+
this.tree.add(command);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const segments = parentPath.split('.');
|
|
96
|
+
let container = this.tree;
|
|
97
|
+
let level = this.tree.commands();
|
|
98
|
+
for (const seg of segments) {
|
|
99
|
+
const parent = level.find((c) => c.name() === seg);
|
|
100
|
+
if (!parent || !(parent instanceof CommandContainer)) {
|
|
101
|
+
throw new Error(`Cannot register "${command.name()}": parent path "${parentPath}" does not resolve to a container at segment "${seg}"`);
|
|
102
|
+
}
|
|
103
|
+
container = parent;
|
|
104
|
+
level = parent.commands();
|
|
105
|
+
}
|
|
106
|
+
container.add(command);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create a hook builder to register lifecycle callbacks.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```
|
|
113
|
+
* const h = term.hook()
|
|
114
|
+
* .beforeExecute()
|
|
115
|
+
* .do((command) => console.log(command.name()));
|
|
116
|
+
* // later: h.dispose();
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
hook() {
|
|
120
|
+
return new TerminalHookBuilder({
|
|
121
|
+
beforeParse: (fn) => this._addHook(this._beforeParseHooks, fn),
|
|
122
|
+
afterParse: (fn) => this._addHook(this._afterParseHooks, fn),
|
|
123
|
+
beforeExecute: (fn) => this._addHook(this._beforeExecuteHooks, fn),
|
|
124
|
+
afterExecute: (fn) => this._addHook(this._afterExecuteHooks, fn),
|
|
125
|
+
beforeExit: (fn) => this._addHook(this._beforeExitHooks, fn),
|
|
126
|
+
onError: (fn) => this._addHook(this._onErrorHooks, fn)
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
_addHook(arr, fn) {
|
|
130
|
+
let hook;
|
|
131
|
+
hook = new TypedHook(fn, () => {
|
|
132
|
+
const idx = arr.indexOf(hook);
|
|
133
|
+
arr.splice(idx, 1);
|
|
134
|
+
});
|
|
135
|
+
arr.push(hook);
|
|
136
|
+
return hook;
|
|
137
|
+
}
|
|
138
|
+
/** Returns all root-level registered commands. */
|
|
139
|
+
getRootCommands() {
|
|
140
|
+
return this.tree.getRoots();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Start the terminal loop. Idempotent — safe to call multiple times.
|
|
144
|
+
*/
|
|
145
|
+
async start() {
|
|
146
|
+
if (this.running)
|
|
147
|
+
return;
|
|
148
|
+
this.running = true;
|
|
149
|
+
this.rl = this.createReadline();
|
|
150
|
+
this.bindEvents(this.rl);
|
|
151
|
+
this.rl.prompt();
|
|
152
|
+
}
|
|
153
|
+
createReadline() {
|
|
154
|
+
const completer = new Completer(this.tree);
|
|
155
|
+
return readline.createInterface({
|
|
156
|
+
input: this.options.stdin,
|
|
157
|
+
output: this.options.stdout,
|
|
158
|
+
prompt: this.options.prompt,
|
|
159
|
+
historySize: this.options.historySize,
|
|
160
|
+
completer: (line) => {
|
|
161
|
+
const { matches, partial } = completer.complete(line);
|
|
162
|
+
return [matches, partial];
|
|
163
|
+
},
|
|
164
|
+
terminal: this.options.stdin.isTTY
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
bindEvents(rl) {
|
|
168
|
+
rl.on('line', (line) => {
|
|
169
|
+
void this.handleLine(line);
|
|
170
|
+
});
|
|
171
|
+
rl.on('SIGINT', () => {
|
|
172
|
+
this.options.stdout.write('^C\n');
|
|
173
|
+
rl.prompt();
|
|
174
|
+
});
|
|
175
|
+
rl.on('close', () => {
|
|
176
|
+
if (this.running) {
|
|
177
|
+
this.options.stdout.write('\n');
|
|
178
|
+
void this.stop();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
rl.on('error', (err) => {
|
|
182
|
+
this.options.stdout.write(`Readline error: ${err.message}\n`);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Stop the terminal loop. Closes the readline interface.
|
|
187
|
+
*/
|
|
188
|
+
async stop() {
|
|
189
|
+
if (this.running) {
|
|
190
|
+
for (const hook of this._beforeExitHooks) {
|
|
191
|
+
await hook.exec();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
this.running = false;
|
|
195
|
+
this.rl?.close();
|
|
196
|
+
this.rl = null;
|
|
197
|
+
this.options.stdin.pause();
|
|
198
|
+
}
|
|
199
|
+
async handleLine(input) {
|
|
200
|
+
try {
|
|
201
|
+
const tokens = await this.processInput(input);
|
|
202
|
+
if (tokens.length === 0) {
|
|
203
|
+
this.rl?.prompt();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const resolved = this.resolveCommand(tokens);
|
|
207
|
+
const record = parseFlags(resolved.args, resolved.command.definitions());
|
|
208
|
+
const args = new CommandArguments(record, this.rl, resolved.command.definitions());
|
|
209
|
+
await this.executeWithHooks(resolved.command, args);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
await this.handleError(error);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
if (this.running) {
|
|
216
|
+
this.rl?.prompt();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async processInput(input) {
|
|
221
|
+
let processed = input;
|
|
222
|
+
for (const hook of this._beforeParseHooks) {
|
|
223
|
+
processed = await hook.exec(processed);
|
|
224
|
+
}
|
|
225
|
+
const tokens = tokenize(processed);
|
|
226
|
+
let finalTokens = tokens;
|
|
227
|
+
for (const hook of this._afterParseHooks) {
|
|
228
|
+
finalTokens = await hook.exec(finalTokens);
|
|
229
|
+
}
|
|
230
|
+
return finalTokens;
|
|
231
|
+
}
|
|
232
|
+
resolveCommand(tokens) {
|
|
233
|
+
const resolved = this.tree.find(tokens);
|
|
234
|
+
if (!resolved) {
|
|
235
|
+
const suggestions = this.tree.findSuggestions(tokens[0]);
|
|
236
|
+
const msg = suggestions.length > 0
|
|
237
|
+
? `Unknown command: ${tokens[0]}. Did you mean: ${suggestions.join(', ')}?`
|
|
238
|
+
: `Unknown command: ${tokens[0]}`;
|
|
239
|
+
throw new CommandNotFoundError(msg, suggestions);
|
|
240
|
+
}
|
|
241
|
+
return resolved;
|
|
242
|
+
}
|
|
243
|
+
async executeWithHooks(command, args) {
|
|
244
|
+
for (const hook of this._beforeExecuteHooks) {
|
|
245
|
+
if ((await hook.exec(command, this.ctx, args)) === false)
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const result = await command.execute(this.ctx, args);
|
|
249
|
+
for (const hook of this._afterExecuteHooks) {
|
|
250
|
+
await hook.exec(result);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async handleError(error) {
|
|
254
|
+
for (const hook of this._onErrorHooks) {
|
|
255
|
+
try {
|
|
256
|
+
if ((await hook.exec(error)) === true)
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
catch (hookError) {
|
|
260
|
+
this.options.stdout.write(`Error in onError hook: ${hookError.message}\n`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (error instanceof CommandNotFoundError) {
|
|
264
|
+
this.options.stdout.write(`${error.message}\n`);
|
|
265
|
+
}
|
|
266
|
+
else if (error instanceof Error) {
|
|
267
|
+
this.options.stdout.write(`Error: ${error.message}\n`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
this.options.stdout.write(`Error: ${String(error)}\n`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Terminal } from './terminal.js';
|
|
2
|
+
import type { CommandArguments, CommandArgumentDefinition } from './command-arguments.js';
|
|
3
|
+
/**
|
|
4
|
+
* A leaf node in the command tree. Has no subcommand support.
|
|
5
|
+
* Subclasses must implement {@link execute}.
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class Command {
|
|
8
|
+
private _name;
|
|
9
|
+
private _description;
|
|
10
|
+
private _definitions;
|
|
11
|
+
/**
|
|
12
|
+
* @param name - Command name used for matching input tokens.
|
|
13
|
+
* @param description - Short description shown in help output.
|
|
14
|
+
* @param argDefs - Argument definitions for `--name value` pairs.
|
|
15
|
+
*/
|
|
16
|
+
constructor(name: string, description?: string, argDefs?: CommandArgumentDefinition[]);
|
|
17
|
+
private _validatePositions;
|
|
18
|
+
/** Command name (used for matching input tokens). */
|
|
19
|
+
name(): string;
|
|
20
|
+
/** Shown in `help` output. */
|
|
21
|
+
description(): string | undefined;
|
|
22
|
+
/** Argument definitions for this command. */
|
|
23
|
+
definitions(): CommandArgumentDefinition[];
|
|
24
|
+
/**
|
|
25
|
+
* Execute the command.
|
|
26
|
+
* @param ctx - Execution context with streams, state, and lifecycle access.
|
|
27
|
+
* @param args - Parsed `--name value` pairs wrapped in an Args instance.
|
|
28
|
+
*/
|
|
29
|
+
abstract execute(ctx: CommandContext, args: CommandArguments): void | Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A command that acts as a namespace for subcommands.
|
|
33
|
+
* Subcommands are registered via {@link add} and are used for
|
|
34
|
+
* tree traversal and help output.
|
|
35
|
+
*
|
|
36
|
+
* The default {@link execute} implementation prints detailed help
|
|
37
|
+
* for this container (description, arguments, subcommands).
|
|
38
|
+
* Subclasses may override `execute` to provide custom behavior.
|
|
39
|
+
*/
|
|
40
|
+
export declare class CommandContainer extends Command {
|
|
41
|
+
private _commands;
|
|
42
|
+
/** Register a child command. */
|
|
43
|
+
add(command: Command): void;
|
|
44
|
+
/** Returns all registered child commands. */
|
|
45
|
+
commands(): Command[];
|
|
46
|
+
execute(ctx: CommandContext, _args: CommandArguments): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/** Context passed to every command's `execute()`. */
|
|
49
|
+
export interface CommandContext {
|
|
50
|
+
/** The running Terminal instance. */
|
|
51
|
+
terminal: Terminal;
|
|
52
|
+
/** Output stream — write command results here. */
|
|
53
|
+
stdout: NodeJS.WriteStream;
|
|
54
|
+
/** Input stream. */
|
|
55
|
+
stdin: NodeJS.ReadStream;
|
|
56
|
+
/** Shared mutable state accessible to all commands. */
|
|
57
|
+
state: Record<string, unknown>;
|
|
58
|
+
/** Console-compatible logger. */
|
|
59
|
+
logger: Logger;
|
|
60
|
+
/** Shortcut for `terminal.stop()`. */
|
|
61
|
+
exit: () => void;
|
|
62
|
+
}
|
|
63
|
+
/** Console-compatible logger interface. */
|
|
64
|
+
export interface Logger {
|
|
65
|
+
info(...args: unknown[]): void;
|
|
66
|
+
warn(...args: unknown[]): void;
|
|
67
|
+
error(...args: unknown[]): void;
|
|
68
|
+
}
|
|
69
|
+
/** Options for the Terminal constructor. */
|
|
70
|
+
export interface TerminalOptions {
|
|
71
|
+
/** Prompt string (default `"> "`). */
|
|
72
|
+
prompt?: string;
|
|
73
|
+
/** Input stream (default `process.stdin`). */
|
|
74
|
+
stdin?: NodeJS.ReadStream;
|
|
75
|
+
/** Output stream (default `process.stdout`). */
|
|
76
|
+
stdout?: NodeJS.WriteStream;
|
|
77
|
+
/** Readline history size (default `100`). */
|
|
78
|
+
historySize?: number;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAG1F;;;GAGG;AACH,8BAAsB,OAAO;IACzB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,YAAY,CAA8B;IAElD;;;;OAIG;gBACS,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,yBAAyB,EAAE;IAarF,OAAO,CAAC,kBAAkB;IA2B1B,qDAAqD;IACrD,IAAI,IAAI,MAAM;IAId,8BAA8B;IAC9B,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC,6CAA6C;IAC7C,WAAW,IAAI,yBAAyB,EAAE;IAI1C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CACtF;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,SAAQ,OAAO;IACzC,OAAO,CAAC,SAAS,CAAiB;IAElC,gCAAgC;IAChC,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI3B,6CAA6C;IAC7C,QAAQ,IAAI,OAAO,EAAE;IAIf,OAAO,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAI7E;AAED,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC3B,qCAAqC;IACrC,QAAQ,EAAE,QAAQ,CAAC;IACnB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC;IAC3B,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC;IACzB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,IAAI,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,MAAM;IACnB,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CACnC;AAED,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC5B,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC1B,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC;IAC5B,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { InvalidArgumentsError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* A leaf node in the command tree. Has no subcommand support.
|
|
4
|
+
* Subclasses must implement {@link execute}.
|
|
5
|
+
*/
|
|
6
|
+
export class Command {
|
|
7
|
+
_name;
|
|
8
|
+
_description;
|
|
9
|
+
_definitions;
|
|
10
|
+
/**
|
|
11
|
+
* @param name - Command name used for matching input tokens.
|
|
12
|
+
* @param description - Short description shown in help output.
|
|
13
|
+
* @param argDefs - Argument definitions for `--name value` pairs.
|
|
14
|
+
*/
|
|
15
|
+
constructor(name, description, argDefs) {
|
|
16
|
+
if (name.length === 0) {
|
|
17
|
+
throw new InvalidArgumentsError('Command name cannot be empty');
|
|
18
|
+
}
|
|
19
|
+
if (/\s/.test(name)) {
|
|
20
|
+
throw new InvalidArgumentsError(`Command name "${name}" must not contain whitespace`);
|
|
21
|
+
}
|
|
22
|
+
this._name = name;
|
|
23
|
+
this._description = description;
|
|
24
|
+
this._definitions = argDefs ?? [];
|
|
25
|
+
this._validatePositions(name);
|
|
26
|
+
}
|
|
27
|
+
_validatePositions(name) {
|
|
28
|
+
const positions = this._definitions
|
|
29
|
+
.map((d) => d.position)
|
|
30
|
+
.filter((p) => p !== undefined);
|
|
31
|
+
if (positions.length === 0)
|
|
32
|
+
return;
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
for (const pos of positions) {
|
|
35
|
+
if (seen.has(pos)) {
|
|
36
|
+
throw new InvalidArgumentsError(`Duplicate position index ${pos} in command "${name}"`);
|
|
37
|
+
}
|
|
38
|
+
seen.add(pos);
|
|
39
|
+
}
|
|
40
|
+
const sorted = [...positions].sort((a, b) => a - b);
|
|
41
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
42
|
+
if (sorted[i] !== i) {
|
|
43
|
+
throw new InvalidArgumentsError(`Positional arguments must form a contiguous sequence starting at 0. ` +
|
|
44
|
+
`Expected position ${i}, got ${sorted[i]} in command "${name}"`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Command name (used for matching input tokens). */
|
|
49
|
+
name() {
|
|
50
|
+
return this._name;
|
|
51
|
+
}
|
|
52
|
+
/** Shown in `help` output. */
|
|
53
|
+
description() {
|
|
54
|
+
return this._description;
|
|
55
|
+
}
|
|
56
|
+
/** Argument definitions for this command. */
|
|
57
|
+
definitions() {
|
|
58
|
+
return this._definitions;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A command that acts as a namespace for subcommands.
|
|
63
|
+
* Subcommands are registered via {@link add} and are used for
|
|
64
|
+
* tree traversal and help output.
|
|
65
|
+
*
|
|
66
|
+
* The default {@link execute} implementation prints detailed help
|
|
67
|
+
* for this container (description, arguments, subcommands).
|
|
68
|
+
* Subclasses may override `execute` to provide custom behavior.
|
|
69
|
+
*/
|
|
70
|
+
export class CommandContainer extends Command {
|
|
71
|
+
_commands = [];
|
|
72
|
+
/** Register a child command. */
|
|
73
|
+
add(command) {
|
|
74
|
+
this._commands.push(command);
|
|
75
|
+
}
|
|
76
|
+
/** Returns all registered child commands. */
|
|
77
|
+
commands() {
|
|
78
|
+
return this._commands;
|
|
79
|
+
}
|
|
80
|
+
async execute(ctx, _args) {
|
|
81
|
+
const { commandHelp } = await import('./commands/help.js');
|
|
82
|
+
ctx.stdout.write(commandHelp(this) + '\n');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|