@alterior/terminal 3.1.7

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-2018 William Lahti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # @alterior/terminal
2
+
3
+ Provides utilities useful for terminal applications, including reading input and styling output.
4
+
5
+ # Reading input
6
+
7
+ ```typescript
8
+ import { read } from '@alterior/terminal';
9
+
10
+ let name = await read({ prompt: `What's your name? ` });
11
+
12
+ console.log(`Hello ${name}!`);
13
+ ```
14
+
15
+ # Styling output
16
+
17
+ ```typescript
18
+ import { styled, style } from '@alterior/terminal';
19
+
20
+ console.log(
21
+ styled(`Hello `, style.$blue(`world`), `, how are `, style.$green(`you`), `?`)
22
+ );
23
+
24
+ ```
@@ -0,0 +1,2 @@
1
+ export * from './style';
2
+ export * from './read';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./style"), exports);
18
+ __exportStar(require("./read"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,yCAAuB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const chai_1 = require("chai");
7
+ const razmin_1 = require("razmin");
8
+ const fixtures_1 = require("./fixtures");
9
+ const read_1 = __importDefault(require("./read"));
10
+ if (process.argv[2] === 'child') {
11
+ child();
12
+ }
13
+ else {
14
+ (0, razmin_1.describe)('read', it => {
15
+ it('basic', async () => {
16
+ const { stdout, stderr } = await (0, fixtures_1.spawnRead)(__filename, {
17
+ 'Username: (test-user)': 'a user',
18
+ 'Password: (<default hidden>)': 'a password',
19
+ 'Password again: (<default hidden>)': 'a password',
20
+ });
21
+ (0, chai_1.expect)(JSON.parse(stderr)).to.eql({ user: 'a user', pass: 'a password', verify: 'a password', passMatch: true });
22
+ (0, chai_1.expect)(stdout).to.equal('Username: (test-user) Password: (<default hidden>) Password again: (<default hidden>) ');
23
+ });
24
+ it('defaults', async () => {
25
+ const { stdout, stderr } = await (0, fixtures_1.spawnRead)(__filename, {
26
+ 'Username: (test-user)': '',
27
+ 'Password: (<default hidden>)': '',
28
+ 'Password again: (<default hidden>)': '',
29
+ });
30
+ (0, chai_1.expect)(JSON.parse(stderr)).to.eql({ user: 'test-user', pass: 'test-pass', verify: 'test-pass', passMatch: true });
31
+ (0, chai_1.expect)(stdout).to.equal('Username: (test-user) Password: (<default hidden>) Password again: (<default hidden>) ');
32
+ });
33
+ it('errors', async () => {
34
+ try {
35
+ await (0, read_1.default)({ default: {} });
36
+ }
37
+ catch (e) {
38
+ return;
39
+ }
40
+ throw new Error(`Expected rejection`);
41
+ });
42
+ });
43
+ }
44
+ async function child() {
45
+ const user = await (0, read_1.default)({ prompt: 'Username: ', default: 'test-user' });
46
+ const pass = await (0, read_1.default)({ prompt: 'Password: ', default: 'test-pass', silent: true });
47
+ const verify = await (0, read_1.default)({ prompt: 'Password again: ', default: 'test-pass', silent: true });
48
+ console.error(JSON.stringify({
49
+ user,
50
+ pass,
51
+ verify,
52
+ passMatch: pass === verify,
53
+ }));
54
+ if (process.stdin.unref) {
55
+ process.stdin.unref();
56
+ }
57
+ }
58
+ //# sourceMappingURL=basic.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basic.test.js","sourceRoot":"","sources":["../../src/read/basic.test.ts"],"names":[],"mappings":";;;;;AAAA,+BAA6B;AAC7B,mCAAiC;AACjC,yCAAuC;AAEvC,kDAA0B;AAE1B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE;IAC/B,KAAK,EAAE,CAAA;CACR;KAAM;IACL,IAAA,iBAAQ,EAAC,MAAM,EAAE,EAAE,CAAC,EAAE;QACpB,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACrB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,oBAAS,EAAC,UAAU,EAAE;gBACrD,uBAAuB,EAAE,QAAQ;gBACjC,8BAA8B,EAAE,YAAY;gBAC5C,oCAAoC,EAAE,YAAY;aACnD,CAAC,CAAA;YAEF,IAAA,aAAM,EAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACjH,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,wFAAwF,CAAC,CAAC;QACpH,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,oBAAS,EAAC,UAAU,EAAE;gBACrD,uBAAuB,EAAE,EAAE;gBAC3B,8BAA8B,EAAE,EAAE;gBAClC,oCAAoC,EAAE,EAAE;aACzC,CAAC,CAAA;YAEF,IAAA,aAAM,EAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClH,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,wFAAwF,CAAC,CAAC;QACpH,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YACtB,IAAI;gBACF,MAAM,IAAA,cAAI,EAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAA;aAC5B;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO;aACR;YAED,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACxC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;CACH;AAED,KAAK,UAAU,KAAK;IAClB,MAAM,IAAI,GAAG,MAAM,IAAA,cAAI,EAAC,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAA;IACvE,MAAM,IAAI,GAAG,MAAM,IAAA,cAAI,EAAC,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACrF,MAAM,MAAM,GAAG,MAAM,IAAA,cAAI,EAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IAE7F,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;QAC3B,IAAI;QACJ,IAAI;QACJ,MAAM;QACN,SAAS,EAAE,IAAI,KAAK,MAAM;KAC3B,CAAC,CAAC,CAAA;IAEH,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE;QACvB,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;KACtB;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function spawnRead(filename: any, writes: any): Promise<{
2
+ stdout: string;
3
+ stderr: string;
4
+ }>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.spawnRead = void 0;
4
+ const spawn = require('child_process').spawn;
5
+ const esc = (str) => str
6
+ .replace(/\(/g, '\\(')
7
+ .replace(/\)/g, '\\)');
8
+ async function spawnRead(filename, writes) {
9
+ const proc = spawn(process.execPath, [filename, 'child']);
10
+ return new Promise((resolve, reject) => {
11
+ let stdout = '';
12
+ let stderr = '';
13
+ proc.stdout.on('data', (c) => {
14
+ stdout += c;
15
+ for (const [prompt, write] of Object.entries(writes)) {
16
+ if (stdout.match(new RegExp(`${esc(prompt)} $`))) {
17
+ return process.nextTick(() => proc.stdin.write(`${write}\n`));
18
+ }
19
+ }
20
+ reject(new Error(`unknown prompt:\n${stdout}\n${JSON.stringify(writes)}`));
21
+ });
22
+ proc.stderr.on('data', (c) => {
23
+ stderr += c;
24
+ });
25
+ proc.on('close', () => resolve({
26
+ stdout,
27
+ stderr,
28
+ }));
29
+ });
30
+ }
31
+ exports.spawnRead = spawnRead;
32
+ //# sourceMappingURL=fixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../../src/read/fixtures.ts"],"names":[],"mappings":";;;AAAA,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,CAAA;AAC5C,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;KACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;KACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAEjB,KAAK,UAAU,SAAS,CAAC,QAAQ,EAAE,MAAM;IAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;IAEzD,OAAO,IAAI,OAAO,CAAqC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACzE,IAAI,MAAM,GAAG,EAAE,CAAA;QACf,IAAI,MAAM,GAAG,EAAE,CAAA;QAEf,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YAC3B,MAAM,IAAI,CAAC,CAAA;YACX,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;gBACpD,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE;oBAChD,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAA;iBAC9D;aACF;YACD,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5E,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YAC3B,MAAM,IAAI,CAAC,CAAA;QACb,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC;YAC7B,MAAM;YACN,MAAM;SACP,CAAC,CAAC,CAAA;IACL,CAAC,CAAC,CAAA;AACJ,CAAC;AA1BD,8BA0BC"}
@@ -0,0 +1 @@
1
+ export * from './read';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./read"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/read/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAuB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const chai_1 = require("chai");
7
+ const razmin_1 = require("razmin");
8
+ const fixtures_1 = require("./fixtures");
9
+ const read_1 = __importDefault(require("./read"));
10
+ const times = new Array(18).fill(null).map((_, i) => (i + 1).toString());
11
+ if (process.argv[2] === 'child') {
12
+ child();
13
+ }
14
+ else {
15
+ (0, razmin_1.describe)('read', it => {
16
+ it('many reads', async () => {
17
+ const writes = times.reduce((acc, k) => {
18
+ acc[`num ${k}`] = k;
19
+ return acc;
20
+ }, {});
21
+ const { stdout, stderr } = await (0, fixtures_1.spawnRead)(__filename, writes);
22
+ (0, chai_1.expect)(stdout).to.equal(Object.keys(writes).join(' ') + ' ');
23
+ (0, chai_1.expect)(stderr).to.equal(times.join(' ') + '\n');
24
+ });
25
+ });
26
+ }
27
+ async function child() {
28
+ const res = [];
29
+ for (const t of times) {
30
+ const r = await (0, read_1.default)({ prompt: `num ${t}` });
31
+ res.push(r);
32
+ }
33
+ console.error(...res);
34
+ if (process.stdin.unref) {
35
+ process.stdin.unref();
36
+ }
37
+ }
38
+ //# sourceMappingURL=many.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"many.test.js","sourceRoot":"","sources":["../../src/read/many.test.ts"],"names":[],"mappings":";;;;;AAAA,+BAA8B;AAC9B,mCAAkC;AAClC,yCAAuC;AACvC,kDAA0B;AAE1B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;AAExE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE;IAC/B,KAAK,EAAE,CAAC;CACT;KAAM;IACL,IAAA,iBAAQ,EAAC,MAAM,EAAE,EAAE,CAAC,EAAE;QACpB,EAAE,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;YAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBACrC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;gBACnB,OAAO,GAAG,CAAA;YACZ,CAAC,EAAE,EAAE,CAAC,CAAA;YACN,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,oBAAS,EAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YAE9D,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;YAC7D,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAA;CAEH;AAED,KAAK,UAAU,KAAK;IAClB,MAAM,GAAG,GAAG,EAAE,CAAA;IACd,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE;QACrB,MAAM,CAAC,GAAG,MAAM,IAAA,cAAI,EAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;QAC5C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KACZ;IAED,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAA;IAErB,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE;QACvB,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;KACtB;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ /// <reference types="node" />
2
+ import { ReadStream, WriteStream } from 'tty';
3
+ export interface ReadOptions {
4
+ /**
5
+ * The default value if the user enters nothing.
6
+ */
7
+ default?: any;
8
+ /**
9
+ * Readable stream to get input data from. (default `process.stdin`)
10
+ */
11
+ input?: ReadStream;
12
+ /**
13
+ * Writable stream to write prompts to. (default: `process.stdout`)
14
+ */
15
+ output?: WriteStream;
16
+ /**
17
+ * What to write to stdout before reading input.
18
+ */
19
+ prompt?: string;
20
+ /**
21
+ * Don't echo the output as the user types it.
22
+ */
23
+ silent?: boolean;
24
+ /**
25
+ * Number of ms to wait for user input before giving up.
26
+ */
27
+ timeout?: number;
28
+ /**
29
+ * Allow the user to edit the default value.
30
+ */
31
+ edit?: boolean;
32
+ /**
33
+ * Treat the output as a TTY, whether it is or not.
34
+ */
35
+ terminal?: boolean;
36
+ /**
37
+ * Replace silenced characters with the supplied character value.
38
+ */
39
+ replace?: string;
40
+ }
41
+ export default function read(options?: ReadOptions): Promise<string>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const readline_1 = __importDefault(require("readline"));
7
+ const mute_stream_1 = __importDefault(require("mute-stream"));
8
+ async function read(options) {
9
+ var _a, _b;
10
+ let opts = { prompt: '', ...options };
11
+ (_a = opts.input) !== null && _a !== void 0 ? _a : (opts.input = process.stdin);
12
+ (_b = opts.output) !== null && _b !== void 0 ? _b : (opts.output = process.stdout);
13
+ if (typeof opts.default !== 'undefined' && typeof opts.default !== 'string' && typeof opts.default !== 'number') {
14
+ throw new Error('default value must be string or number');
15
+ }
16
+ let editDef = false;
17
+ opts.prompt = opts.prompt.trim() + ' ';
18
+ opts.terminal = !!(opts.terminal || opts.output.isTTY);
19
+ if (opts.default) {
20
+ if (opts.silent) {
21
+ opts.prompt += '(<default hidden>) ';
22
+ }
23
+ else if (opts.edit) {
24
+ editDef = true;
25
+ }
26
+ else {
27
+ opts.prompt += '(' + opts.default + ') ';
28
+ }
29
+ }
30
+ const input = opts.input;
31
+ const output = new mute_stream_1.default({ replace: opts.replace, prompt: opts.prompt });
32
+ output.pipe(opts.output, { end: false });
33
+ return new Promise((resolve, reject) => {
34
+ const rl = readline_1.default.createInterface({ input, output, terminal: opts.terminal });
35
+ const timer = opts.timeout && setTimeout(() => onError(new Error('timed out')), opts.timeout);
36
+ output.unmute();
37
+ rl.setPrompt(opts.prompt);
38
+ rl.prompt();
39
+ if (opts.silent) {
40
+ output.mute();
41
+ }
42
+ else if (editDef) {
43
+ rl.line = opts.default;
44
+ rl.cursor = opts.default.length;
45
+ rl._refreshLine();
46
+ }
47
+ const done = () => {
48
+ rl.close();
49
+ clearTimeout(timer);
50
+ output.mute();
51
+ output.end();
52
+ };
53
+ const onError = (er) => {
54
+ done();
55
+ reject(er);
56
+ };
57
+ rl.on('error', onError);
58
+ rl.on('line', (line) => {
59
+ if (opts.silent && opts.terminal) {
60
+ output.unmute();
61
+ output.write('\r\n');
62
+ }
63
+ done();
64
+ // truncate the \n at the end.
65
+ const res = line.replace(/\r?\n$/, '') || opts.default || '';
66
+ return resolve(res);
67
+ });
68
+ rl.on('SIGINT', () => {
69
+ rl.close();
70
+ onError(new Error('canceled'));
71
+ });
72
+ });
73
+ }
74
+ exports.default = read;
75
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/read/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ declare const STYLE_CODES: {
2
+ reset: number[];
3
+ bold: number[];
4
+ dim: number[];
5
+ italic: number[];
6
+ underline: number[];
7
+ inverse: number[];
8
+ hidden: number[];
9
+ strikethrough: number[];
10
+ black: number[];
11
+ red: number[];
12
+ green: number[];
13
+ yellow: number[];
14
+ blue: number[];
15
+ magenta: number[];
16
+ cyan: number[];
17
+ white: number[];
18
+ gray: number[];
19
+ grey: number[];
20
+ bgBlack: number[];
21
+ bgRed: number[];
22
+ bgGreen: number[];
23
+ bgYellow: number[];
24
+ bgBlue: number[];
25
+ bgMagenta: number[];
26
+ bgCyan: number[];
27
+ bgWhite: number[];
28
+ };
29
+ export type BoundStyler = (...contents: (StyledString | string)[]) => StyledString;
30
+ export type StyleShortcuts = {
31
+ [P in keyof typeof STYLE_CODES as `$${P}`]: (...contents: (StyledString | string)[]) => StyledString;
32
+ };
33
+ export type Styler = BoundStyler & StyleShortcuts;
34
+ export type Style = keyof typeof STYLE_CODES;
35
+ export declare const startStyle: (key: keyof typeof STYLE_CODES) => string;
36
+ export declare const endStyle: (key: keyof typeof STYLE_CODES) => string;
37
+ export declare function styleIndicator(key: keyof typeof STYLE_CODES, index: number): string;
38
+ export declare class StyledString {
39
+ readonly style?: keyof typeof STYLE_CODES;
40
+ readonly parts: (StyledString | string)[];
41
+ constructor(style?: keyof typeof STYLE_CODES, parts?: (StyledString | string)[]);
42
+ start(): string;
43
+ end(): string;
44
+ toString(): string;
45
+ }
46
+ export declare const style: Styler;
47
+ export declare function styled(...contents: (string | StyledString)[]): string;
48
+ export {};
package/dist/style.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /*
3
+ * Portions are Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
4
+ * https://github.com/Marak/colors.js
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.styled = exports.style = exports.StyledString = exports.styleIndicator = exports.endStyle = exports.startStyle = void 0;
8
+ const STYLE_CODES = {
9
+ reset: [0, 0],
10
+ bold: [1, 22],
11
+ dim: [2, 22],
12
+ italic: [3, 23],
13
+ underline: [4, 24],
14
+ inverse: [7, 27],
15
+ hidden: [8, 28],
16
+ strikethrough: [9, 29],
17
+ black: [30, 39],
18
+ red: [31, 39],
19
+ green: [32, 39],
20
+ yellow: [33, 39],
21
+ blue: [34, 39],
22
+ magenta: [35, 39],
23
+ cyan: [36, 39],
24
+ white: [37, 39],
25
+ gray: [90, 39],
26
+ grey: [90, 39],
27
+ bgBlack: [40, 49],
28
+ bgRed: [41, 49],
29
+ bgGreen: [42, 49],
30
+ bgYellow: [43, 49],
31
+ bgBlue: [44, 49],
32
+ bgMagenta: [45, 49],
33
+ bgCyan: [46, 49],
34
+ bgWhite: [47, 49]
35
+ };
36
+ const startStyle = (key) => styleIndicator(key, 0);
37
+ exports.startStyle = startStyle;
38
+ const endStyle = (key) => styleIndicator(key, 1);
39
+ exports.endStyle = endStyle;
40
+ function styleIndicator(key, index) {
41
+ var _a;
42
+ if (key === undefined)
43
+ return '';
44
+ if (!(key in STYLE_CODES))
45
+ throw new Error(`No such style '${key}'`);
46
+ return `\u001b[${(_a = STYLE_CODES[key]) === null || _a === void 0 ? void 0 : _a[index]}m`;
47
+ }
48
+ exports.styleIndicator = styleIndicator;
49
+ class StyledString {
50
+ constructor(style, parts = []) {
51
+ this.style = style;
52
+ this.parts = parts;
53
+ }
54
+ start() {
55
+ return (0, exports.startStyle)(this.style);
56
+ }
57
+ end() {
58
+ return (0, exports.endStyle)(this.style);
59
+ }
60
+ toString() {
61
+ return `${this.start()}${this.parts.join('')}${this.end()}`;
62
+ }
63
+ }
64
+ exports.StyledString = StyledString;
65
+ let _style = (style, ...contents) => {
66
+ return new StyledString(style, contents);
67
+ };
68
+ for (let key of Object.keys(STYLE_CODES))
69
+ _style[`$${key}`] = (...contents) => new StyledString(key, contents);
70
+ exports.style = _style;
71
+ function styled(...contents) {
72
+ return (0, exports.style)(undefined, ...contents).toString();
73
+ }
74
+ exports.styled = styled;
75
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"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"}
package/dist/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const razmin_1 = require("razmin");
4
+ (0, razmin_1.suite)().include(['**/*.test.js']).run();
5
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":";;AAAA,mCAA+B;AAC/B,IAAA,cAAK,GAAE,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@alterior/terminal",
3
+ "version": "3.1.7",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "dependencies": {
10
+ "mute-stream": "~1.0.0"
11
+ },
12
+ "devDependencies": {
13
+ "@types/mute-stream": "^0.0.1"
14
+ },
15
+ "engines": {
16
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
17
+ },
18
+ "author": "The Alterior Project (https://github.com/alterior-mvc)",
19
+ "description": "Utilities for terminal applications",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/alterior-mvc/alterior.git"
23
+ },
24
+ "license": "MIT",
25
+ "scripts": {
26
+ "build": "tsc -b .",
27
+ "test": "npm run build && node dist/test",
28
+ "prepublishOnly": "npm test"
29
+ },
30
+ "gitHead": "ec71d565d6fceb5054bbefe1f1c6648e713dcca9"
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './style';
2
+ export * from './read';
@@ -0,0 +1,60 @@
1
+ import { expect } from "chai"
2
+ import { describe } from "razmin"
3
+ import { spawnRead } from './fixtures';
4
+
5
+ import read from './read';
6
+
7
+ if (process.argv[2] === 'child') {
8
+ child()
9
+ } else {
10
+ describe('read', it => {
11
+ it('basic', async () => {
12
+ const { stdout, stderr } = await spawnRead(__filename, {
13
+ 'Username: (test-user)': 'a user',
14
+ 'Password: (<default hidden>)': 'a password',
15
+ 'Password again: (<default hidden>)': 'a password',
16
+ })
17
+
18
+ expect(JSON.parse(stderr)).to.eql({ user: 'a user', pass: 'a password', verify: 'a password', passMatch: true });
19
+ expect(stdout).to.equal('Username: (test-user) Password: (<default hidden>) Password again: (<default hidden>) ');
20
+ })
21
+
22
+ it('defaults', async () => {
23
+ const { stdout, stderr } = await spawnRead(__filename, {
24
+ 'Username: (test-user)': '',
25
+ 'Password: (<default hidden>)': '',
26
+ 'Password again: (<default hidden>)': '',
27
+ })
28
+
29
+ expect(JSON.parse(stderr)).to.eql({ user: 'test-user', pass: 'test-pass', verify: 'test-pass', passMatch: true });
30
+ expect(stdout).to.equal('Username: (test-user) Password: (<default hidden>) Password again: (<default hidden>) ');
31
+ })
32
+
33
+ it('errors', async () => {
34
+ try {
35
+ await read({ default: {} })
36
+ } catch (e) {
37
+ return;
38
+ }
39
+
40
+ throw new Error(`Expected rejection`);
41
+ })
42
+ })
43
+ }
44
+
45
+ async function child () {
46
+ const user = await read({ prompt: 'Username: ', default: 'test-user' })
47
+ const pass = await read({ prompt: 'Password: ', default: 'test-pass', silent: true })
48
+ const verify = await read({ prompt: 'Password again: ', default: 'test-pass', silent: true })
49
+
50
+ console.error(JSON.stringify({
51
+ user,
52
+ pass,
53
+ verify,
54
+ passMatch: pass === verify,
55
+ }))
56
+
57
+ if (process.stdin.unref) {
58
+ process.stdin.unref()
59
+ }
60
+ }
@@ -0,0 +1,32 @@
1
+ const spawn = require('child_process').spawn
2
+ const esc = (str) => str
3
+ .replace(/\(/g, '\\(')
4
+ .replace(/\)/g, '\\)')
5
+
6
+ export async function spawnRead(filename, writes) {
7
+ const proc = spawn(process.execPath, [filename, 'child'])
8
+
9
+ return new Promise<{ stdout: string, stderr: string }>((resolve, reject) => {
10
+ let stdout = ''
11
+ let stderr = ''
12
+
13
+ proc.stdout.on('data', (c) => {
14
+ stdout += c
15
+ for (const [prompt, write] of Object.entries(writes)) {
16
+ if (stdout.match(new RegExp(`${esc(prompt)} $`))) {
17
+ return process.nextTick(() => proc.stdin.write(`${write}\n`))
18
+ }
19
+ }
20
+ reject(new Error(`unknown prompt:\n${stdout}\n${JSON.stringify(writes)}`))
21
+ })
22
+
23
+ proc.stderr.on('data', (c) => {
24
+ stderr += c
25
+ })
26
+
27
+ proc.on('close', () => resolve({
28
+ stdout,
29
+ stderr,
30
+ }))
31
+ })
32
+ }
@@ -0,0 +1 @@
1
+ export * from './read';
@@ -0,0 +1,38 @@
1
+ import { expect } from 'chai';
2
+ import { describe } from 'razmin';
3
+ import { spawnRead } from './fixtures';
4
+ import read from './read';
5
+
6
+ const times = new Array(18).fill(null).map((_, i) => (i + 1).toString())
7
+
8
+ if (process.argv[2] === 'child') {
9
+ child();
10
+ } else {
11
+ describe('read', it => {
12
+ it('many reads', async () => {
13
+ const writes = times.reduce((acc, k) => {
14
+ acc[`num ${k}`] = k
15
+ return acc
16
+ }, {})
17
+ const { stdout, stderr } = await spawnRead(__filename, writes)
18
+
19
+ expect(stdout).to.equal(Object.keys(writes).join(' ') + ' ');
20
+ expect(stderr).to.equal(times.join(' ') + '\n');
21
+ });
22
+ })
23
+
24
+ }
25
+
26
+ async function child () {
27
+ const res = []
28
+ for (const t of times) {
29
+ const r = await read({ prompt: `num ${t}` })
30
+ res.push(r)
31
+ }
32
+
33
+ console.error(...res)
34
+
35
+ if (process.stdin.unref) {
36
+ process.stdin.unref()
37
+ }
38
+ }
@@ -0,0 +1,124 @@
1
+ import readline from 'readline';
2
+ import Mute from 'mute-stream';
3
+ import { ReadStream, WriteStream } from 'tty';
4
+
5
+ export interface ReadOptions {
6
+ /**
7
+ * The default value if the user enters nothing.
8
+ */
9
+ default?: any;
10
+
11
+ /**
12
+ * Readable stream to get input data from. (default `process.stdin`)
13
+ */
14
+ input?: ReadStream;
15
+
16
+ /**
17
+ * Writable stream to write prompts to. (default: `process.stdout`)
18
+ */
19
+ output?: WriteStream;
20
+
21
+ /**
22
+ * What to write to stdout before reading input.
23
+ */
24
+ prompt?: string;
25
+
26
+ /**
27
+ * Don't echo the output as the user types it.
28
+ */
29
+ silent?: boolean;
30
+
31
+ /**
32
+ * Number of ms to wait for user input before giving up.
33
+ */
34
+ timeout?: number;
35
+
36
+ /**
37
+ * Allow the user to edit the default value.
38
+ */
39
+ edit?: boolean;
40
+
41
+ /**
42
+ * Treat the output as a TTY, whether it is or not.
43
+ */
44
+ terminal?: boolean;
45
+
46
+ /**
47
+ * Replace silenced characters with the supplied character value.
48
+ */
49
+ replace?: string;
50
+ }
51
+
52
+ export default async function read (options?: ReadOptions): Promise<string> {
53
+ let opts = { prompt: '', ...options };
54
+ opts.input ??= process.stdin;
55
+ opts.output ??= process.stdout;
56
+
57
+ if (typeof opts.default !== 'undefined' && typeof opts.default !== 'string' && typeof opts.default !== 'number') {
58
+ throw new Error('default value must be string or number')
59
+ }
60
+
61
+ let editDef = false;
62
+ opts.prompt = opts.prompt.trim() + ' '
63
+ opts.terminal = !!(opts.terminal || opts.output.isTTY)
64
+
65
+ if (opts.default) {
66
+ if (opts.silent) {
67
+ opts.prompt += '(<default hidden>) '
68
+ } else if (opts.edit) {
69
+ editDef = true
70
+ } else {
71
+ opts.prompt += '(' + opts.default + ') '
72
+ }
73
+ }
74
+
75
+ const input = opts.input;
76
+ const output = new Mute({ replace: opts.replace, prompt: opts.prompt })
77
+ output.pipe(opts.output, { end: false });
78
+
79
+ return new Promise<string>((resolve, reject) => {
80
+ const rl = readline.createInterface({ input, output, terminal: opts.terminal })
81
+ const timer = opts.timeout && setTimeout(() => onError(new Error('timed out')), opts.timeout)
82
+
83
+ output.unmute();
84
+ rl.setPrompt(opts.prompt);
85
+ rl.prompt();
86
+
87
+ if (opts.silent) {
88
+ output.mute();
89
+ } else if (editDef) {
90
+ (rl as any).line = opts.default;
91
+ (rl as any).cursor = opts.default!.length;
92
+ (rl as any)._refreshLine();
93
+ }
94
+
95
+ const done = () => {
96
+ rl.close()
97
+ clearTimeout(timer)
98
+ output.mute()
99
+ output.end()
100
+ }
101
+
102
+ const onError = (er) => {
103
+ done()
104
+ reject(er)
105
+ }
106
+
107
+ rl.on('error', onError)
108
+ rl.on('line', (line) => {
109
+ if (opts.silent && opts.terminal) {
110
+ output.unmute()
111
+ output.write('\r\n')
112
+ }
113
+ done()
114
+ // truncate the \n at the end.
115
+ const res = line.replace(/\r?\n$/, '') || opts.default || ''
116
+ return resolve(res)
117
+ })
118
+
119
+ rl.on('SIGINT', () => {
120
+ rl.close()
121
+ onError(new Error('canceled'))
122
+ })
123
+ })
124
+ }
package/src/style.ts ADDED
@@ -0,0 +1,88 @@
1
+ /*
2
+ * Portions are Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
3
+ * https://github.com/Marak/colors.js
4
+ */
5
+
6
+ const STYLE_CODES = {
7
+ reset: [0, 0],
8
+
9
+ bold: [1, 22],
10
+ dim: [2, 22],
11
+ italic: [3, 23],
12
+ underline: [4, 24],
13
+ inverse: [7, 27],
14
+ hidden: [8, 28],
15
+ strikethrough: [9, 29],
16
+
17
+ black: [30, 39],
18
+ red: [31, 39],
19
+ green: [32, 39],
20
+ yellow: [33, 39],
21
+ blue: [34, 39],
22
+ magenta: [35, 39],
23
+ cyan: [36, 39],
24
+ white: [37, 39],
25
+ gray: [90, 39],
26
+ grey: [90, 39],
27
+
28
+ bgBlack: [40, 49],
29
+ bgRed: [41, 49],
30
+ bgGreen: [42, 49],
31
+ bgYellow: [43, 49],
32
+ bgBlue: [44, 49],
33
+ bgMagenta: [45, 49],
34
+ bgCyan: [46, 49],
35
+ bgWhite: [47, 49]
36
+ };
37
+
38
+ export type BoundStyler = (...contents: (StyledString | string)[]) => StyledString;
39
+ export type StyleShortcuts = {
40
+ [P in keyof typeof STYLE_CODES as `$${P}`]: (...contents: (StyledString | string)[]) => StyledString;
41
+ }
42
+
43
+ export type Styler = BoundStyler & StyleShortcuts;
44
+
45
+ export type Style = keyof typeof STYLE_CODES;
46
+ export const startStyle = (key: keyof typeof STYLE_CODES) => styleIndicator(key, 0);
47
+ export const endStyle = (key: keyof typeof STYLE_CODES) => styleIndicator(key, 1);
48
+
49
+ export function styleIndicator(key: keyof typeof STYLE_CODES, index: number) {
50
+ if (key === undefined)
51
+ return '';
52
+ if (!(key in STYLE_CODES))
53
+ throw new Error(`No such style '${key}'`);
54
+ return `\u001b[${STYLE_CODES[key]?.[index]}m`;
55
+ }
56
+
57
+ export class StyledString {
58
+ constructor(
59
+ readonly style?: keyof typeof STYLE_CODES,
60
+ readonly parts: (StyledString | string)[] = []
61
+ ) {
62
+ }
63
+
64
+ start() {
65
+ return startStyle(this.style);
66
+ }
67
+
68
+ end() {
69
+ return endStyle(this.style);
70
+ }
71
+
72
+ toString() {
73
+ return `${this.start()}${this.parts.join('')}${this.end()}`;
74
+ }
75
+ }
76
+
77
+ let _style = (style: Style, ...contents: (string | StyledString)[]): Readonly<StyledString> => {
78
+ return new StyledString(style, contents);
79
+ }
80
+
81
+ for (let key of <Style[]>Object.keys(STYLE_CODES))
82
+ _style[`$${key}`] = (...contents: (string | StyledString)[]) => new StyledString(key, contents);
83
+
84
+ export const style = <Styler>_style;
85
+
86
+ export function styled(...contents: (string | StyledString)[]): string {
87
+ return style(undefined, ...contents).toString();
88
+ }
package/src/test.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { suite } from "razmin";
2
+ suite().include(['**/*.test.js']).run();
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2019",
4
+ "module": "CommonJS",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "sourceMap": true,
8
+ "declaration": true,
9
+ "outDir": "dist"
10
+ },
11
+ "include": [
12
+ "./src/**/*.ts"
13
+ ]
14
+ }