@alterior/terminal 3.6.7 → 3.13.3

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/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './style';
2
2
  export * from './read';
3
3
  export * from './terminal-ui';
4
+ export * from './task-list';
package/dist/index.js CHANGED
@@ -17,4 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./style"), exports);
18
18
  __exportStar(require("./read"), exports);
19
19
  __exportStar(require("./terminal-ui"), exports);
20
+ __exportStar(require("./task-list"), exports);
20
21
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,yCAAuB;AACvB,gDAA8B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,yCAAuB;AACvB,gDAA8B;AAC9B,8CAA4B"}
@@ -0,0 +1,104 @@
1
+ /// <reference types="node" />
2
+ export interface CLITaskInit {
3
+ omitInNonInteractive?: boolean;
4
+ parentTask?: CLITask;
5
+ }
6
+ export declare class CLITaskList {
7
+ readonly interactive: boolean;
8
+ readonly silent: boolean;
9
+ constructor(interactive?: boolean, silent?: boolean);
10
+ private timer;
11
+ private renderedLineCount;
12
+ readonly spinner: CLITaskStatusWidget;
13
+ private globalTask;
14
+ private started;
15
+ startCursor: {
16
+ x: number;
17
+ y: number;
18
+ };
19
+ private start;
20
+ stop(): void;
21
+ get remainingLines(): number;
22
+ startTask(title: string, init?: CLITaskInit): CLITask;
23
+ addTask(task: CLITask): void;
24
+ get allTasksFinished(): boolean;
25
+ idle: boolean;
26
+ private idleTimeout;
27
+ beforeCpu: NodeJS.CpuUsage;
28
+ cpu: NodeJS.CpuUsage;
29
+ ram: NodeJS.MemoryUsage;
30
+ lastTimestamp: bigint;
31
+ secondTicks: number;
32
+ startedAt: number;
33
+ private render;
34
+ drawHeader(): void;
35
+ private startFrame;
36
+ private endFrame;
37
+ drawLine(input?: string): void;
38
+ private truncateLineIfNeeded;
39
+ }
40
+ declare class LineTracker {
41
+ private _maxLines;
42
+ constructor(_maxLines: number);
43
+ static unbounded: LineTracker;
44
+ private _consumed;
45
+ get maxLines(): number;
46
+ get consumed(): number;
47
+ consume(count?: number): void;
48
+ }
49
+ export type CLITaskCharm = () => string;
50
+ export type CLITaskStatus = 'running' | 'error' | 'finished';
51
+ export declare class CLITask {
52
+ constructor(title: string, init?: CLITaskInit);
53
+ parent?: CLITask;
54
+ children: CLITask[];
55
+ title: string;
56
+ status: CLITaskStatus;
57
+ finishedAt: number;
58
+ startedAt: number;
59
+ charms: CLITaskCharm[];
60
+ staleAfter: number;
61
+ staleWithErrorsAndLogAfter: number;
62
+ omitInNonInteractive: boolean;
63
+ private logMessages;
64
+ get depth(): any;
65
+ get indent(): string;
66
+ get isFinishedAndStale(): boolean;
67
+ get containsLogsOrError(): boolean;
68
+ get isDeleted(): boolean;
69
+ get parents(): CLITask[];
70
+ get chain(): CLITask[];
71
+ onUpdated: (task: CLITask) => void;
72
+ onDeleted: (task: CLITask) => void;
73
+ onLog: (task: CLITask, message: string) => void;
74
+ log(message: string): void;
75
+ error(message?: string): void;
76
+ finish(notify?: boolean): void;
77
+ delete(): void;
78
+ subtask(title: string, init?: CLITaskInit): CLITask;
79
+ addTask(child: CLITask): CLITask;
80
+ render(renderer: CLITaskList, lineTracker: LineTracker, parentFinished?: boolean): number;
81
+ renderNonInteractive(renderer: CLITaskList): void;
82
+ renderSelfAndParents(renderer: CLITaskList): void;
83
+ renderSelf(renderer: CLITaskList, lineTracker: LineTracker): void;
84
+ renderLogs(renderer: CLITaskList, lineTracker: LineTracker): void;
85
+ renderLogLine(renderer: CLITaskList, line: string): void;
86
+ renderChildren(renderer: CLITaskList, lineTracker: LineTracker, parentFinished?: boolean): number;
87
+ private summarizeCounts;
88
+ private summarizeCount;
89
+ private statusStyle;
90
+ private iconForStatus;
91
+ }
92
+ export declare class CLITaskStatusWidget {
93
+ private task?;
94
+ constructor(task?: CLITask);
95
+ protected readonly spinner: string[];
96
+ private id?;
97
+ private spinnerPosition;
98
+ spin(): void;
99
+ render(): string;
100
+ isRunning(): boolean;
101
+ start(cb?: () => void, interval?: number): void;
102
+ stop(): void;
103
+ }
104
+ export {};
@@ -0,0 +1,452 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLITaskStatusWidget = exports.CLITask = exports.CLITaskList = void 0;
4
+ const functions_1 = require("@alterior/functions");
5
+ const style_1 = require("./style");
6
+ const CLI_TASK_ERROR_ICON = '𐄂';
7
+ const CLI_TASK_FINISHED_ICON = '✓';
8
+ const INDENT = ' ';
9
+ class CLITaskList {
10
+ constructor(interactive = true, silent = false) {
11
+ this.interactive = interactive;
12
+ this.silent = silent;
13
+ this.renderedLineCount = 0;
14
+ this.spinner = new CLITaskStatusWidget();
15
+ this.globalTask = new CLITask('Global Task');
16
+ this.started = false;
17
+ this.startCursor = { x: 0, y: 0 };
18
+ this.idle = false;
19
+ this.lastTimestamp = BigInt(0);
20
+ this.secondTicks = 1000000;
21
+ this.startedAt = Date.now();
22
+ this.globalTask.onUpdated = task => {
23
+ if (!this.interactive) {
24
+ if (task.children.length === 0)
25
+ task.renderNonInteractive(this);
26
+ return;
27
+ }
28
+ // When the last task completes, we need to re-render inline to be sure we render the right state.
29
+ // But we only want to do this once, and only when needed, otherwise its a waste.
30
+ if (this.globalTask.children.every(t => t.status !== 'running'))
31
+ this.render();
32
+ };
33
+ this.globalTask.onLog = (task, message) => {
34
+ if (this.interactive)
35
+ return;
36
+ task.renderNonInteractive(this);
37
+ for (let line of message.split(/\r?\n/))
38
+ task.renderLogLine(this, line);
39
+ };
40
+ }
41
+ start() {
42
+ if (!this.interactive)
43
+ return;
44
+ if (this.started)
45
+ return;
46
+ this.started = true;
47
+ this.timer = setInterval(() => this.render(), 1000 / 30);
48
+ this.spinner.start();
49
+ }
50
+ stop() {
51
+ if (!this.interactive)
52
+ return;
53
+ if (!this.started)
54
+ return;
55
+ // Render once more to be sure we capture the final state.
56
+ this.render();
57
+ this.started = false;
58
+ clearInterval(this.timer);
59
+ this.timer = undefined;
60
+ this.spinner.stop();
61
+ this.renderedLineCount = 0;
62
+ }
63
+ get remainingLines() {
64
+ // The -2 here is:
65
+ // - (x) Its desirable to keep the prompt line visible at the top of the screen if possible (-1)
66
+ // - We will have one empty line visible at the end of the output to avoid flickering the
67
+ // cursor (-1)
68
+ return Math.max(0, process.stdout.rows - this.renderedLineCount - 1);
69
+ }
70
+ startTask(title, init) {
71
+ let task = new CLITask(title, init);
72
+ this.addTask(task);
73
+ return task;
74
+ }
75
+ addTask(task) {
76
+ if (!this.started) {
77
+ this.start();
78
+ }
79
+ this.globalTask.addTask(task);
80
+ }
81
+ get allTasksFinished() {
82
+ return this.globalTask.children.every(x => x.status !== 'running');
83
+ }
84
+ render() {
85
+ let newSecond = process.hrtime.bigint();
86
+ if (this.lastTimestamp + BigInt(1000000000) < newSecond) {
87
+ this.secondTicks = Number((newSecond - this.lastTimestamp) / BigInt(1000));
88
+ this.lastTimestamp = newSecond;
89
+ this.cpu = process.cpuUsage(this.beforeCpu);
90
+ this.beforeCpu = process.cpuUsage();
91
+ this.ram = process.memoryUsage();
92
+ }
93
+ this.startFrame();
94
+ // other cool u characters include: 𝖚
95
+ // see more: https://unicodeplus.com/U+0075
96
+ let elapsedTime = formatDuration(Date.now() - this.startedAt);
97
+ this.drawHeader();
98
+ this.globalTask.renderChildren(this, new LineTracker(this.remainingLines));
99
+ this.endFrame();
100
+ }
101
+ drawHeader() {
102
+ }
103
+ startFrame() {
104
+ process.stdout.cork();
105
+ process.stdout.moveCursor(0, -this.renderedLineCount);
106
+ this.renderedLineCount = 0;
107
+ }
108
+ endFrame() {
109
+ process.stdout.clearScreenDown();
110
+ process.stdout.uncork();
111
+ // Update idle
112
+ if (this.idle !== this.allTasksFinished) {
113
+ this.idle = this.allTasksFinished;
114
+ if (this.idle) {
115
+ this.idleTimeout = setTimeout(() => {
116
+ if (this.idle) {
117
+ this.stop();
118
+ }
119
+ }, 1000);
120
+ }
121
+ else {
122
+ this.idle = false;
123
+ clearTimeout(this.idleTimeout);
124
+ }
125
+ }
126
+ }
127
+ drawLine(input = '') {
128
+ if (this.silent)
129
+ return;
130
+ if (!this.interactive) {
131
+ console.log(input);
132
+ return;
133
+ }
134
+ let lines = input.split(/\r?\n/);
135
+ let count = 0;
136
+ for (let line of lines) {
137
+ process.stdout.clearLine(0);
138
+ process.stdout.write(`${this.truncateLineIfNeeded(line)}\n`);
139
+ process.stdout.clearLine(1);
140
+ this.renderedLineCount += 1;
141
+ count += 1;
142
+ }
143
+ }
144
+ truncateLineIfNeeded(line) {
145
+ let indicatorPattern = /\u001b\[[^m]*m/g;
146
+ let visibleLength = line.replace(indicatorPattern, '').length;
147
+ if (visibleLength < process.stdout.columns)
148
+ return line;
149
+ let newLine = '';
150
+ let newVisible = 0;
151
+ for (let i = 0, max = line.length; i < max && newVisible < process.stdout.columns - 3; ++i) {
152
+ let match = /^\u001b\[[^m]*m/.exec(line.slice(i));
153
+ if (match) {
154
+ newLine += `${match[0]}`;
155
+ i += match[0].length - 1;
156
+ }
157
+ else {
158
+ newLine += line.at(i);
159
+ newVisible += 1;
160
+ }
161
+ }
162
+ newLine += (0, style_1.styled)(style_1.style.$dim('...'));
163
+ // We may have cut off style codes by eliding this line. To prevent weirdness, we must
164
+ // reset the terminal back to default
165
+ newLine += (0, style_1.startStyle)('reset');
166
+ return newLine;
167
+ }
168
+ }
169
+ exports.CLITaskList = CLITaskList;
170
+ class LineTracker {
171
+ constructor(_maxLines) {
172
+ this._maxLines = _maxLines;
173
+ this._consumed = 0;
174
+ }
175
+ get maxLines() {
176
+ if (this._maxLines === undefined)
177
+ return Number.MAX_SAFE_INTEGER;
178
+ return this._maxLines;
179
+ }
180
+ get consumed() {
181
+ if (this._maxLines === undefined)
182
+ return 0;
183
+ return this._consumed;
184
+ }
185
+ consume(count = 1) {
186
+ if (this._maxLines !== undefined)
187
+ this._maxLines -= count;
188
+ this._consumed += count;
189
+ }
190
+ }
191
+ LineTracker.unbounded = new LineTracker(undefined);
192
+ class CLITask {
193
+ constructor(title, init = {}) {
194
+ this.children = [];
195
+ this.status = 'running';
196
+ this.startedAt = Date.now();
197
+ this.charms = [];
198
+ this.staleAfter = 0;
199
+ this.staleWithErrorsAndLogAfter = 10000;
200
+ this.omitInNonInteractive = false;
201
+ this.logMessages = [];
202
+ this.title = title;
203
+ if (init.omitInNonInteractive)
204
+ this.omitInNonInteractive = true;
205
+ }
206
+ // Computed Properties
207
+ get depth() {
208
+ var _a, _b;
209
+ return ((_b = (_a = this.parent) === null || _a === void 0 ? void 0 : _a.depth) !== null && _b !== void 0 ? _b : -1) + 1;
210
+ }
211
+ get indent() {
212
+ if (this.depth === 0)
213
+ return ``;
214
+ return Array.from(Array(this.depth - 1)).map(() => INDENT).join('');
215
+ }
216
+ get isFinishedAndStale() {
217
+ let staleTime = this.containsLogsOrError ? this.staleWithErrorsAndLogAfter : this.staleAfter;
218
+ let freshness = Math.min(staleTime);
219
+ return this.status === 'finished' && this.finishedAt + freshness < Date.now();
220
+ }
221
+ get containsLogsOrError() {
222
+ return this.logMessages.length > 0 || this.status === 'error' || this.children.some(x => x.containsLogsOrError);
223
+ }
224
+ get isDeleted() {
225
+ var _a;
226
+ return this.parent ? !((_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.includes(this)) : false;
227
+ }
228
+ get parents() {
229
+ let parents = [];
230
+ let parent = this.parent;
231
+ while (parent === null || parent === void 0 ? void 0 : parent.parent) {
232
+ parents.unshift(parent);
233
+ parent = parent.parent;
234
+ }
235
+ return parents;
236
+ }
237
+ get chain() {
238
+ return [...this.parents, this];
239
+ }
240
+ // Lifecycle API
241
+ log(message) {
242
+ var _a;
243
+ this.logMessages.push(...message.split(/\r?\n/));
244
+ (_a = this.onLog) === null || _a === void 0 ? void 0 : _a.call(this, this, message);
245
+ }
246
+ error(message) {
247
+ this.status = 'error';
248
+ this.log(`[Error] ${message || 'The task failed.'}`);
249
+ }
250
+ finish(notify = true) {
251
+ var _a;
252
+ this.status = 'finished';
253
+ this.finishedAt = Date.now();
254
+ for (let child of this.children)
255
+ child.finish(false);
256
+ if (notify)
257
+ (_a = this.onUpdated) === null || _a === void 0 ? void 0 : _a.call(this, this);
258
+ }
259
+ delete() {
260
+ var _a;
261
+ (_a = this.onDeleted) === null || _a === void 0 ? void 0 : _a.call(this, this);
262
+ }
263
+ // Subtasks
264
+ subtask(title, init = {}) {
265
+ let child = new CLITask(title, init);
266
+ this.addTask(child);
267
+ return child;
268
+ }
269
+ addTask(child) {
270
+ this.children.push(child);
271
+ child.parent = this;
272
+ child.onUpdated = child => { var _a; return (_a = this.onUpdated) === null || _a === void 0 ? void 0 : _a.call(this, child); };
273
+ child.onLog = (task, message) => { var _a; return (_a = this.onLog) === null || _a === void 0 ? void 0 : _a.call(this, task, message); };
274
+ child.onDeleted = child => {
275
+ var _a;
276
+ let index = this.children.indexOf(child);
277
+ if (index >= 0)
278
+ this.children.splice(index, 1);
279
+ (_a = this.onUpdated) === null || _a === void 0 ? void 0 : _a.call(this, child);
280
+ };
281
+ return child;
282
+ }
283
+ // Rendering
284
+ render(renderer, lineTracker, parentFinished = false) {
285
+ if (parentFinished && !this.containsLogsOrError)
286
+ return 0;
287
+ this.renderSelf(renderer, lineTracker);
288
+ this.renderChildren(renderer, lineTracker, parentFinished || this.isFinishedAndStale);
289
+ this.renderLogs(renderer, lineTracker);
290
+ }
291
+ renderNonInteractive(renderer) {
292
+ if (this.omitInNonInteractive)
293
+ return;
294
+ renderer.drawLine(this.chain.filter(x => !x.omitInNonInteractive).map(p => `${p.statusStyle(p.title)}`).join(' » '));
295
+ }
296
+ renderSelfAndParents(renderer) {
297
+ for (let parent of this.parents)
298
+ parent.renderSelf(renderer, LineTracker.unbounded);
299
+ this.renderSelf(renderer, LineTracker.unbounded);
300
+ }
301
+ renderSelf(renderer, lineTracker) {
302
+ var _a;
303
+ renderer.drawLine(`${(_a = this.indent) !== null && _a !== void 0 ? _a : ''}`
304
+ + `${this.statusStyle(`${this.iconForStatus(renderer)} ${this.title}`, this.status)}`
305
+ + `${this.summarizeCounts(renderer)}`
306
+ //+ ` DEBUG: ${maxLines}`
307
+ );
308
+ lineTracker.consume();
309
+ }
310
+ renderLogs(renderer, lineTracker) {
311
+ let displayedLogs = this.logMessages.slice();
312
+ while (displayedLogs.length > lineTracker.maxLines)
313
+ displayedLogs.shift();
314
+ for (let line of displayedLogs) {
315
+ this.renderLogLine(renderer, line);
316
+ lineTracker.consume();
317
+ }
318
+ }
319
+ renderLogLine(renderer, line) {
320
+ var _a;
321
+ renderer.drawLine(`${(_a = this.indent) !== null && _a !== void 0 ? _a : ''} ${(0, style_1.styled)(style_1.style.$dim(line))}`);
322
+ }
323
+ renderChildren(renderer, lineTracker, parentFinished = false) {
324
+ if (lineTracker.maxLines <= 0)
325
+ return 0;
326
+ let statusOrder = {
327
+ 'error': 0,
328
+ 'finished': 1,
329
+ 'running': 2
330
+ };
331
+ this.children.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
332
+ let maxDisplayedTasks = lineTracker.maxLines;
333
+ let displayedTasks = this.children.slice();
334
+ if (parentFinished) {
335
+ displayedTasks = displayedTasks.filter(x => x.containsLogsOrError);
336
+ }
337
+ else {
338
+ displayedTasks = displayedTasks.filter(x => !x.isFinishedAndStale);
339
+ }
340
+ // If we're tight on space, elide freshly finished tasks, too.
341
+ while (displayedTasks.length > maxDisplayedTasks) {
342
+ let taskIndex = displayedTasks.findIndex(x => x.status === 'finished');
343
+ if (taskIndex < 0)
344
+ break;
345
+ displayedTasks.splice(taskIndex, 1);
346
+ }
347
+ // Elide errors (better to see ongoing progress) ////////////////
348
+ while (displayedTasks.length > maxDisplayedTasks) {
349
+ let taskIndex = displayedTasks.findIndex(x => x.status === 'error');
350
+ if (taskIndex < 0)
351
+ break;
352
+ displayedTasks.splice(taskIndex, 1);
353
+ }
354
+ // Elide all remaining from end of sorted list until we fit within the maxDisplayedTasks
355
+ if (displayedTasks.length > maxDisplayedTasks)
356
+ displayedTasks.splice(maxDisplayedTasks, displayedTasks.length);
357
+ let extraLinesForChildren = lineTracker.maxLines - displayedTasks.length;
358
+ for (let task of displayedTasks) {
359
+ let childLineTracker = new LineTracker(1 + extraLinesForChildren);
360
+ task.render(renderer, childLineTracker, parentFinished);
361
+ lineTracker.consume(childLineTracker.consumed);
362
+ extraLinesForChildren -= (childLineTracker.consumed - 1);
363
+ extraLinesForChildren = Math.max(0, extraLinesForChildren);
364
+ }
365
+ }
366
+ // Helpers
367
+ summarizeCounts(r) {
368
+ let counts = [
369
+ this.logMessages.length > 0 ? `${(0, style_1.styled)(style_1.style.$yellow(`• ${this.logMessages.length}`))}` : undefined,
370
+ this.summarizeCount(r, (0, functions_1.count)(this.children, x => x.status === 'finished'), 'finished'),
371
+ this.summarizeCount(r, (0, functions_1.count)(this.children, x => x.status === 'error'), 'error'),
372
+ this.summarizeCount(r, (0, functions_1.count)(this.children, x => x.status === 'running'), 'running'),
373
+ ].filter(x => x);
374
+ if (counts.length === 0)
375
+ return '';
376
+ return ` ${(0, style_1.styled)(style_1.style.$dim(counts.join(' ')))}`;
377
+ }
378
+ summarizeCount(r, count, status) {
379
+ if (count <= 0)
380
+ return undefined;
381
+ return this.statusStyle(`${this.iconForStatus(r, status)} ${count}`, status);
382
+ }
383
+ statusStyle(line, status = this.status) {
384
+ if (status === 'running') {
385
+ line = (0, style_1.styled)(style_1.style.$bold(style_1.style.$blue(line)));
386
+ }
387
+ else if (status === 'finished') {
388
+ line = (0, style_1.styled)(style_1.style.$green(line));
389
+ }
390
+ else if (status === 'error') {
391
+ line = (0, style_1.styled)(style_1.style.$red(line));
392
+ }
393
+ return line;
394
+ }
395
+ iconForStatus(renderer, status = this.status) {
396
+ if (this.status === 'error')
397
+ return CLI_TASK_ERROR_ICON;
398
+ else if (this.status === 'finished')
399
+ return CLI_TASK_FINISHED_ICON;
400
+ return renderer.spinner.render();
401
+ }
402
+ }
403
+ exports.CLITask = CLITask;
404
+ class CLITaskStatusWidget {
405
+ constructor(task) {
406
+ this.task = task;
407
+ this.spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
408
+ this.spinnerPosition = 0;
409
+ this.start();
410
+ }
411
+ spin() {
412
+ this.spinnerPosition = ++this.spinnerPosition % this.spinner.length;
413
+ if (this.task && this.task.status !== 'running') {
414
+ clearInterval(this.id);
415
+ }
416
+ }
417
+ render() {
418
+ var _a, _b;
419
+ if (((_a = this.task) === null || _a === void 0 ? void 0 : _a.status) === 'error')
420
+ return CLI_TASK_ERROR_ICON;
421
+ else if (((_b = this.task) === null || _b === void 0 ? void 0 : _b.status) === 'finished')
422
+ return CLI_TASK_FINISHED_ICON;
423
+ return this.spinner[this.spinnerPosition];
424
+ }
425
+ isRunning() {
426
+ return !!this.id;
427
+ }
428
+ start(cb, interval = 100) {
429
+ clearInterval(this.id);
430
+ this.id = setInterval(() => {
431
+ this.spin();
432
+ if (cb) {
433
+ cb();
434
+ }
435
+ }, interval);
436
+ }
437
+ stop() {
438
+ clearInterval(this.id);
439
+ }
440
+ }
441
+ exports.CLITaskStatusWidget = CLITaskStatusWidget;
442
+ function formatDuration(duration) {
443
+ const msPerSec = 1000;
444
+ const msPerMinute = (1000 * 60);
445
+ const msPerHour = (1000 * 60 * 60);
446
+ let hours = duration / msPerHour | 0;
447
+ let minutes = (duration - (hours * msPerHour)) / msPerMinute | 0;
448
+ let seconds = (duration - (hours * msPerHour + minutes * msPerMinute)) / msPerSec | 0;
449
+ let ms = duration % 1000;
450
+ return `${(0, functions_1.zeroPad)(hours)}:${(0, functions_1.zeroPad)(minutes)}:${(0, functions_1.zeroPad)(seconds)}:${(0, functions_1.zeroPad)(ms, 3)}`;
451
+ }
452
+ //# sourceMappingURL=task-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-list.js","sourceRoot":"","sources":["../src/task-list.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@alterior/terminal",
3
- "version": "3.6.7",
3
+ "version": "3.13.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
9
  "dependencies": {
10
- "mute-stream": "~1.0.0"
10
+ "mute-stream": "~1.0.0",
11
+ "@alterior/functions": "^3.11.1"
11
12
  },
12
13
  "devDependencies": {
13
14
  "@types/mute-stream": "^0.0.1"
@@ -27,5 +28,5 @@
27
28
  "test": "npm run build && node dist/test",
28
29
  "prepublishOnly": "npm test"
29
30
  },
30
- "gitHead": "5a2dddeed850b8e59217dbbe0614f641206c9072"
31
- }
31
+ "gitHead": "87f31ea4078faaca53b45573c6a0013cdb40dd07"
32
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './style';
2
2
  export * from './read';
3
- export * from './terminal-ui';
3
+ export * from './terminal-ui';
4
+ export * from './task-list';
@@ -0,0 +1,561 @@
1
+ import { count, zeroPad } from '@alterior/functions';
2
+ import { startStyle, style, styled } from './style';
3
+
4
+ const CLI_TASK_ERROR_ICON = '𐄂';
5
+ const CLI_TASK_FINISHED_ICON = '✓';
6
+ const INDENT = ' ';
7
+
8
+ export interface CLITaskInit {
9
+ omitInNonInteractive?: boolean;
10
+ parentTask?: CLITask;
11
+ }
12
+
13
+ export class CLITaskList {
14
+ constructor(readonly interactive = true, readonly silent = false) {
15
+ this.globalTask.onUpdated = task => {
16
+ if (!this.interactive) {
17
+ if (task.children.length === 0)
18
+ task.renderNonInteractive(this);
19
+ return;
20
+ }
21
+ // When the last task completes, we need to re-render inline to be sure we render the right state.
22
+ // But we only want to do this once, and only when needed, otherwise its a waste.
23
+ if (this.globalTask.children.every(t => t.status !== 'running'))
24
+ this.render();
25
+ };
26
+ this.globalTask.onLog = (task: CLITask, message: string) => {
27
+ if (this.interactive)
28
+ return;
29
+
30
+ task.renderNonInteractive(this);
31
+ for (let line of message.split(/\r?\n/))
32
+ task.renderLogLine(this, line);
33
+ }
34
+ }
35
+
36
+ private timer;
37
+ private renderedLineCount = 0;
38
+ readonly spinner = new CLITaskStatusWidget();
39
+ private globalTask = new CLITask('Global Task');
40
+
41
+ private started = false;
42
+ startCursor: { x: number, y: number } = { x: 0, y: 0 };
43
+
44
+ private start() {
45
+ if (!this.interactive)
46
+ return;
47
+
48
+ if (this.started)
49
+ return;
50
+
51
+ this.started = true;
52
+ this.timer = setInterval(() => this.render(), 1000 / 30);
53
+ this.spinner.start();
54
+ }
55
+
56
+ stop() {
57
+ if (!this.interactive)
58
+ return;
59
+ if (!this.started)
60
+ return;
61
+
62
+ // Render once more to be sure we capture the final state.
63
+ this.render();
64
+
65
+ this.started = false;
66
+ clearInterval(this.timer);
67
+ this.timer = undefined;
68
+ this.spinner.stop();
69
+
70
+
71
+ this.renderedLineCount = 0;
72
+ }
73
+
74
+ get remainingLines() {
75
+ // The -2 here is:
76
+ // - (x) Its desirable to keep the prompt line visible at the top of the screen if possible (-1)
77
+ // - We will have one empty line visible at the end of the output to avoid flickering the
78
+ // cursor (-1)
79
+ return Math.max(0, process.stdout.rows - this.renderedLineCount - 1);
80
+ }
81
+
82
+ startTask(title: string, init?: CLITaskInit) {
83
+ let task = new CLITask(title, init);
84
+ this.addTask(task);
85
+ return task;
86
+ }
87
+
88
+ addTask(task: CLITask) {
89
+ if (!this.started) {
90
+ this.start();
91
+ }
92
+
93
+ this.globalTask.addTask(task);
94
+ }
95
+
96
+ get allTasksFinished() {
97
+ return this.globalTask.children.every(x => x.status !== 'running');
98
+ }
99
+
100
+ idle = false;
101
+ private idleTimeout: any;
102
+ beforeCpu: NodeJS.CpuUsage;
103
+ cpu: NodeJS.CpuUsage;
104
+ ram: NodeJS.MemoryUsage;
105
+
106
+ lastTimestamp: bigint = BigInt(0);
107
+ secondTicks = 1_000_000;
108
+ startedAt = Date.now();
109
+
110
+ private render() {
111
+ let newSecond = process.hrtime.bigint();
112
+ if (this.lastTimestamp + BigInt(1_000_000_000) < newSecond) {
113
+ this.secondTicks = Number((newSecond - this.lastTimestamp) / BigInt(1_000));
114
+ this.lastTimestamp = newSecond;
115
+ this.cpu = process.cpuUsage(this.beforeCpu);
116
+ this.beforeCpu = process.cpuUsage();
117
+ this.ram = process.memoryUsage();
118
+ }
119
+
120
+ this.startFrame();
121
+
122
+ // other cool u characters include: 𝖚
123
+ // see more: https://unicodeplus.com/U+0075
124
+
125
+ let elapsedTime = formatDuration(Date.now() - this.startedAt)
126
+
127
+ this.drawHeader();
128
+
129
+ this.globalTask.renderChildren(this, new LineTracker(this.remainingLines));
130
+ this.endFrame();
131
+ }
132
+
133
+ drawHeader() {
134
+
135
+ }
136
+
137
+ private startFrame() {
138
+ process.stdout.cork();
139
+ process.stdout.moveCursor(0, -this.renderedLineCount);
140
+ this.renderedLineCount = 0;
141
+ }
142
+
143
+ private endFrame() {
144
+ process.stdout.clearScreenDown();
145
+ process.stdout.uncork();
146
+
147
+ // Update idle
148
+ if (this.idle !== this.allTasksFinished) {
149
+ this.idle = this.allTasksFinished;
150
+ if (this.idle) {
151
+ this.idleTimeout = setTimeout(() => {
152
+ if (this.idle) {
153
+ this.stop();
154
+ }
155
+ }, 1_000);
156
+ } else {
157
+ this.idle = false;
158
+ clearTimeout(this.idleTimeout);
159
+ }
160
+ }
161
+ }
162
+
163
+ drawLine(input: string = ''): void {
164
+ if (this.silent)
165
+ return;
166
+
167
+ if (!this.interactive) {
168
+ console.log(input);
169
+ return;
170
+ }
171
+
172
+ let lines = input.split(/\r?\n/);
173
+ let count = 0;
174
+
175
+ for (let line of lines) {
176
+ process.stdout.clearLine(0);
177
+ process.stdout.write(`${this.truncateLineIfNeeded(line)}\n`);
178
+ process.stdout.clearLine(1);
179
+
180
+ this.renderedLineCount += 1;
181
+ count += 1;
182
+ }
183
+ }
184
+
185
+ private truncateLineIfNeeded(line: string) {
186
+ let indicatorPattern = /\u001b\[[^m]*m/g;
187
+ let visibleLength = line.replace(indicatorPattern, '').length;
188
+ if (visibleLength < process.stdout.columns)
189
+ return line;
190
+
191
+ let newLine = '';
192
+ let newVisible = 0;
193
+
194
+ for (let i = 0, max = line.length; i < max && newVisible < process.stdout.columns - 3; ++i) {
195
+ let match = /^\u001b\[[^m]*m/.exec(line.slice(i));
196
+ if (match) {
197
+ newLine += `${match[0]}`;
198
+ i += match[0].length - 1;
199
+ } else {
200
+ newLine += line.at(i);
201
+ newVisible += 1;
202
+ }
203
+ }
204
+
205
+ newLine += styled(style.$dim('...'));
206
+
207
+ // We may have cut off style codes by eliding this line. To prevent weirdness, we must
208
+ // reset the terminal back to default
209
+ newLine += startStyle('reset');
210
+ return newLine;
211
+ }
212
+ }
213
+
214
+ class LineTracker {
215
+ constructor(private _maxLines: number) {
216
+ }
217
+
218
+ static unbounded = new LineTracker(undefined);
219
+ private _consumed = 0;
220
+
221
+ get maxLines() {
222
+ if (this._maxLines === undefined)
223
+ return Number.MAX_SAFE_INTEGER;
224
+ return this._maxLines;
225
+ }
226
+
227
+ get consumed() {
228
+ if (this._maxLines === undefined)
229
+ return 0;
230
+ return this._consumed;
231
+ }
232
+
233
+ consume(count: number = 1) {
234
+ if (this._maxLines !== undefined)
235
+ this._maxLines -= count;
236
+ this._consumed += count;
237
+ }
238
+ }
239
+
240
+ export type CLITaskCharm = () => string;
241
+ export type CLITaskStatus = 'running' | 'error' | 'finished';
242
+ export class CLITask {
243
+ constructor(title: string, init: CLITaskInit = {}) {
244
+ this.title = title;
245
+ if (init.omitInNonInteractive)
246
+ this.omitInNonInteractive = true;
247
+ }
248
+ parent?: CLITask;
249
+ children: CLITask[] = [];
250
+ title: string;
251
+ status: CLITaskStatus = 'running';
252
+ finishedAt: number;
253
+ startedAt = Date.now();
254
+
255
+ charms: CLITaskCharm[] = [];
256
+ staleAfter = 0;
257
+ staleWithErrorsAndLogAfter = 10_000;
258
+ omitInNonInteractive = false;
259
+
260
+ private logMessages: string[] = [];
261
+
262
+ // Computed Properties
263
+
264
+ get depth() {
265
+ return (this.parent?.depth ?? -1) + 1;
266
+ }
267
+
268
+ get indent() {
269
+ if (this.depth === 0)
270
+ return ``;
271
+ return Array.from(Array(this.depth - 1)).map(() => INDENT).join('');
272
+ }
273
+
274
+ get isFinishedAndStale(): boolean {
275
+ let staleTime = this.containsLogsOrError ? this.staleWithErrorsAndLogAfter : this.staleAfter;
276
+ let freshness = Math.min(staleTime);
277
+ return this.status === 'finished' && this.finishedAt + freshness < Date.now();
278
+ }
279
+
280
+ get containsLogsOrError(): boolean {
281
+ return this.logMessages.length > 0 || this.status === 'error' || this.children.some(x => x.containsLogsOrError);
282
+ }
283
+
284
+ get isDeleted() {
285
+ return this.parent ? !this.parent?.children.includes(this) : false;
286
+ }
287
+
288
+ get parents() {
289
+ let parents: CLITask[] = [];
290
+ let parent = this.parent;
291
+ while (parent?.parent) {
292
+ parents.unshift(parent);
293
+ parent = parent.parent;
294
+ }
295
+ return parents;
296
+ }
297
+
298
+ get chain() {
299
+ return [...this.parents, this];
300
+ }
301
+
302
+ // Events
303
+
304
+ onUpdated: (task: CLITask) => void;
305
+ onDeleted: (task: CLITask) => void;
306
+ onLog: (task: CLITask, message: string) => void;
307
+
308
+ // Lifecycle API
309
+
310
+ log(message: string) {
311
+ this.logMessages.push(...message.split(/\r?\n/));
312
+ this.onLog?.(this, message);
313
+ }
314
+
315
+ error(message?: string) {
316
+ this.status = 'error';
317
+ this.log(`[Error] ${message || 'The task failed.'}`);
318
+ }
319
+
320
+ finish(notify = true) {
321
+ this.status = 'finished';
322
+ this.finishedAt = Date.now();
323
+
324
+ for (let child of this.children)
325
+ child.finish(false);
326
+
327
+ if (notify)
328
+ this.onUpdated?.(this);
329
+ }
330
+
331
+ delete() {
332
+ this.onDeleted?.(this);
333
+ }
334
+
335
+ // Subtasks
336
+
337
+ subtask(title: string, init: CLITaskInit = {}): CLITask {
338
+ let child = new CLITask(title, init);
339
+ this.addTask(child);
340
+ return child;
341
+ }
342
+
343
+ addTask(child: CLITask) {
344
+ this.children.push(child);
345
+ child.parent = this;
346
+ child.onUpdated = child => this.onUpdated?.(child);
347
+ child.onLog = (task, message) => this.onLog?.(task, message);
348
+ child.onDeleted = child => {
349
+ let index = this.children.indexOf(child);
350
+ if (index >= 0)
351
+ this.children.splice(index, 1);
352
+ this.onUpdated?.(child);
353
+ };
354
+
355
+ return child;
356
+ }
357
+
358
+ // Rendering
359
+
360
+ render(renderer: CLITaskList, lineTracker: LineTracker, parentFinished = false) {
361
+ if (parentFinished && !this.containsLogsOrError)
362
+ return 0;
363
+
364
+ this.renderSelf(renderer, lineTracker);
365
+ this.renderChildren(renderer, lineTracker, parentFinished || this.isFinishedAndStale);
366
+ this.renderLogs(renderer, lineTracker);
367
+ }
368
+
369
+ renderNonInteractive(renderer: CLITaskList) {
370
+ if (this.omitInNonInteractive)
371
+ return;
372
+ renderer.drawLine(this.chain.filter(x => !x.omitInNonInteractive).map(p => `${p.statusStyle(p.title)}`).join(' » '));
373
+ }
374
+
375
+ renderSelfAndParents(renderer: CLITaskList) {
376
+ for (let parent of this.parents)
377
+ parent.renderSelf(renderer, LineTracker.unbounded);
378
+ this.renderSelf(renderer, LineTracker.unbounded);
379
+ }
380
+
381
+ renderSelf(renderer: CLITaskList, lineTracker: LineTracker) {
382
+ renderer.drawLine(
383
+ `${this.indent ?? ''}`
384
+ + `${this.statusStyle(`${this.iconForStatus(renderer)} ${this.title}`, this.status)}`
385
+ + `${this.summarizeCounts(renderer)}`
386
+ //+ ` DEBUG: ${maxLines}`
387
+ );
388
+ lineTracker.consume();
389
+ }
390
+
391
+ renderLogs(renderer: CLITaskList, lineTracker: LineTracker) {
392
+ let displayedLogs = this.logMessages.slice();
393
+ while (displayedLogs.length > lineTracker.maxLines)
394
+ displayedLogs.shift();
395
+ for (let line of displayedLogs) {
396
+ this.renderLogLine(renderer, line);
397
+ lineTracker.consume();
398
+ }
399
+ }
400
+
401
+ renderLogLine(renderer: CLITaskList, line: string) {
402
+ renderer.drawLine(`${this.indent ?? ''} ${styled(style.$dim(line))}`);
403
+ }
404
+
405
+ renderChildren(renderer: CLITaskList, lineTracker: LineTracker, parentFinished = false) {
406
+ if (lineTracker.maxLines <= 0)
407
+ return 0;
408
+
409
+ let statusOrder = {
410
+ 'error': 0,
411
+ 'finished': 1,
412
+ 'running': 2
413
+ };
414
+ this.children.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
415
+
416
+ let maxDisplayedTasks = lineTracker.maxLines;
417
+ let displayedTasks = this.children.slice();
418
+
419
+ if (parentFinished) {
420
+ displayedTasks = displayedTasks.filter(x => x.containsLogsOrError);
421
+ } else {
422
+ displayedTasks = displayedTasks.filter(x => !x.isFinishedAndStale);
423
+ }
424
+
425
+ // If we're tight on space, elide freshly finished tasks, too.
426
+ while (displayedTasks.length > maxDisplayedTasks) {
427
+ let taskIndex = displayedTasks.findIndex(x => x.status === 'finished');
428
+ if (taskIndex < 0)
429
+ break;
430
+
431
+ displayedTasks.splice(taskIndex, 1);
432
+ }
433
+
434
+ // Elide errors (better to see ongoing progress) ////////////////
435
+
436
+ while (displayedTasks.length > maxDisplayedTasks) {
437
+ let taskIndex = displayedTasks.findIndex(x => x.status === 'error');
438
+ if (taskIndex < 0)
439
+ break;
440
+
441
+ displayedTasks.splice(taskIndex, 1);
442
+ }
443
+
444
+ // Elide all remaining from end of sorted list until we fit within the maxDisplayedTasks
445
+
446
+ if (displayedTasks.length > maxDisplayedTasks)
447
+ displayedTasks.splice(maxDisplayedTasks, displayedTasks.length);
448
+
449
+ let extraLinesForChildren = lineTracker.maxLines - displayedTasks.length;
450
+ for (let task of displayedTasks) {
451
+ let childLineTracker = new LineTracker(1 + extraLinesForChildren);
452
+ task.render(renderer, childLineTracker, parentFinished);
453
+ lineTracker.consume(childLineTracker.consumed);
454
+ extraLinesForChildren -= (childLineTracker.consumed - 1);
455
+ extraLinesForChildren = Math.max(0, extraLinesForChildren);
456
+ }
457
+ }
458
+
459
+ // Helpers
460
+
461
+ private summarizeCounts(r: CLITaskList) {
462
+ let counts = [
463
+ this.logMessages.length > 0 ? `${styled(style.$yellow(`• ${this.logMessages.length}`))}` : undefined,
464
+ this.summarizeCount(r, count(this.children, x => x.status === 'finished'), 'finished'),
465
+ this.summarizeCount(r, count(this.children, x => x.status === 'error'), 'error'),
466
+ this.summarizeCount(r, count(this.children, x => x.status === 'running'), 'running'),
467
+ ].filter(x => x);
468
+
469
+ if (counts.length === 0)
470
+ return '';
471
+
472
+ return ` ${styled(style.$dim(counts.join(' ')))}`;
473
+ }
474
+
475
+ private summarizeCount(r: CLITaskList, count: number, status: CLITaskStatus) {
476
+ if (count <= 0)
477
+ return undefined;
478
+ return this.statusStyle(`${this.iconForStatus(r, status)} ${count}`, status);
479
+ }
480
+
481
+ private statusStyle(line: string, status: CLITaskStatus = this.status) {
482
+ if (status === 'running') {
483
+ line = styled(style.$bold(style.$blue(line)));
484
+ } else if (status === 'finished') {
485
+ line = styled(style.$green(line));
486
+ } else if (status === 'error') {
487
+ line = styled(style.$red(line));
488
+ }
489
+
490
+ return line;
491
+ }
492
+
493
+ private iconForStatus(renderer: CLITaskList, status: CLITaskStatus = this.status) {
494
+ if (this.status === 'error')
495
+ return CLI_TASK_ERROR_ICON;
496
+ else if (this.status === 'finished')
497
+ return CLI_TASK_FINISHED_ICON;
498
+
499
+ return renderer.spinner.render();
500
+ }
501
+ }
502
+
503
+ export class CLITaskStatusWidget {
504
+ constructor(private task?: CLITask) {
505
+ this.start();
506
+ }
507
+
508
+ protected readonly spinner: string[] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
509
+ private id?: NodeJS.Timeout
510
+
511
+ private spinnerPosition = 0
512
+
513
+ public spin(): void {
514
+ this.spinnerPosition = ++this.spinnerPosition % this.spinner.length
515
+ if (this.task && this.task.status !== 'running') {
516
+ clearInterval(this.id);
517
+ }
518
+ }
519
+
520
+ public render(): string {
521
+ if (this.task?.status === 'error')
522
+ return CLI_TASK_ERROR_ICON;
523
+ else if (this.task?.status === 'finished')
524
+ return CLI_TASK_FINISHED_ICON;
525
+
526
+ return this.spinner[this.spinnerPosition]
527
+ }
528
+
529
+ public isRunning(): boolean {
530
+ return !!this.id
531
+ }
532
+
533
+ public start(cb?: () => void, interval = 100): void {
534
+ clearInterval(this.id);
535
+ this.id = setInterval(() => {
536
+ this.spin()
537
+
538
+ if (cb) {
539
+ cb()
540
+ }
541
+ }, interval)
542
+ }
543
+
544
+ public stop(): void {
545
+ clearInterval(this.id)
546
+ }
547
+ }
548
+
549
+ function formatDuration(duration: number) {
550
+
551
+ const msPerSec = 1000;
552
+ const msPerMinute = (1000 * 60);
553
+ const msPerHour = (1000 * 60 * 60);
554
+
555
+ let hours = duration / msPerHour | 0;
556
+ let minutes = (duration - (hours * msPerHour)) / msPerMinute | 0;
557
+ let seconds = (duration - (hours * msPerHour + minutes * msPerMinute)) / msPerSec | 0;
558
+ let ms = duration % 1000;
559
+
560
+ return `${zeroPad(hours)}:${zeroPad(minutes)}:${zeroPad(seconds)}:${zeroPad(ms, 3)}`;
561
+ }