@chainlink/external-adapter-framework 2.12.0 → 2.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/config/index.d.ts +9 -0
- package/config/index.js +12 -1
- package/config/index.js.map +1 -1
- package/generator-adapter/generators/app/templates/src/config/index.ts.ejs +3 -0
- package/generator-adapter/node_modules/.yarn-integrity +31 -35
- package/generator-adapter/node_modules/@inquirer/ansi/package.json +28 -26
- package/generator-adapter/node_modules/@inquirer/checkbox/dist/index.d.ts +6 -1
- package/generator-adapter/node_modules/@inquirer/checkbox/dist/index.js +30 -10
- package/generator-adapter/node_modules/@inquirer/checkbox/package.json +23 -21
- package/generator-adapter/node_modules/@inquirer/confirm/package.json +21 -19
- package/generator-adapter/node_modules/@inquirer/core/dist/index.d.ts +1 -1
- package/generator-adapter/node_modules/@inquirer/core/dist/index.js +1 -1
- package/generator-adapter/node_modules/@inquirer/core/dist/lib/create-prompt.js +51 -25
- package/generator-adapter/node_modules/@inquirer/core/dist/lib/key.d.ts +2 -0
- package/generator-adapter/node_modules/@inquirer/core/dist/lib/key.js +1 -0
- package/generator-adapter/node_modules/@inquirer/core/dist/lib/utils.js +1 -1
- package/generator-adapter/node_modules/@inquirer/core/package.json +22 -20
- package/generator-adapter/node_modules/@inquirer/editor/dist/index.d.ts +3 -0
- package/generator-adapter/node_modules/@inquirer/editor/dist/index.js +5 -5
- package/generator-adapter/node_modules/@inquirer/editor/package.json +22 -20
- package/generator-adapter/node_modules/@inquirer/expand/package.json +21 -19
- package/generator-adapter/node_modules/@inquirer/external-editor/package.json +33 -31
- package/generator-adapter/node_modules/@inquirer/figures/package.json +18 -16
- package/generator-adapter/node_modules/@inquirer/input/dist/index.js +5 -3
- package/generator-adapter/node_modules/@inquirer/input/package.json +21 -19
- package/generator-adapter/node_modules/@inquirer/number/package.json +21 -19
- package/generator-adapter/node_modules/@inquirer/password/dist/index.d.ts +6 -1
- package/generator-adapter/node_modules/@inquirer/password/dist/index.js +7 -2
- package/generator-adapter/node_modules/@inquirer/password/package.json +22 -20
- package/generator-adapter/node_modules/@inquirer/prompts/README.md +15 -0
- package/generator-adapter/node_modules/@inquirer/prompts/package.json +31 -29
- package/generator-adapter/node_modules/@inquirer/rawlist/README.md +4 -0
- package/generator-adapter/node_modules/@inquirer/rawlist/dist/index.d.ts +9 -2
- package/generator-adapter/node_modules/@inquirer/rawlist/dist/index.js +24 -7
- package/generator-adapter/node_modules/@inquirer/rawlist/package.json +21 -19
- package/generator-adapter/node_modules/@inquirer/search/README.md +2 -1
- package/generator-adapter/node_modules/@inquirer/search/dist/index.d.ts +1 -0
- package/generator-adapter/node_modules/@inquirer/search/dist/index.js +11 -4
- package/generator-adapter/node_modules/@inquirer/search/package.json +22 -20
- package/generator-adapter/node_modules/@inquirer/select/dist/index.d.ts +5 -2
- package/generator-adapter/node_modules/@inquirer/select/dist/index.js +28 -11
- package/generator-adapter/node_modules/@inquirer/select/package.json +23 -21
- package/generator-adapter/node_modules/@inquirer/type/package.json +18 -16
- package/generator-adapter/node_modules/@types/node/README.md +1 -1
- package/generator-adapter/node_modules/@types/node/http.d.ts +1 -1
- package/generator-adapter/node_modules/@types/node/package.json +2 -2
- package/generator-adapter/node_modules/@types/node/stream/web.d.ts +1 -1
- package/generator-adapter/node_modules/@types/node/test.d.ts +2 -1
- package/generator-adapter/node_modules/@types/node/web-globals/fetch.d.ts +12 -0
- package/generator-adapter/node_modules/@yeoman/adapter/dist/adapter.d.ts +0 -3
- package/generator-adapter/node_modules/@yeoman/adapter/dist/adapter.js +1 -0
- package/generator-adapter/node_modules/@yeoman/adapter/dist/adapter.js.map +1 -1
- package/generator-adapter/node_modules/@yeoman/adapter/dist/log.d.ts +0 -2
- package/generator-adapter/node_modules/@yeoman/adapter/dist/log.js.map +1 -1
- package/generator-adapter/node_modules/@yeoman/adapter/dist/queued-adapter.d.ts +0 -1
- package/generator-adapter/node_modules/@yeoman/adapter/dist/queued-adapter.js.map +1 -1
- package/generator-adapter/node_modules/@yeoman/adapter/dist/testing/test-adapter.d.ts +0 -1
- package/generator-adapter/node_modules/@yeoman/adapter/dist/testing/test-adapter.js.map +1 -1
- package/generator-adapter/node_modules/@yeoman/adapter/package.json +11 -11
- package/generator-adapter/node_modules/ansi-styles/index.d.ts +341 -232
- package/generator-adapter/node_modules/ansi-styles/index.js +130 -190
- package/generator-adapter/node_modules/ansi-styles/license +1 -1
- package/generator-adapter/node_modules/ansi-styles/package.json +10 -8
- package/generator-adapter/node_modules/ansi-styles/readme.md +32 -53
- package/generator-adapter/node_modules/chalk/package.json +3 -3
- package/generator-adapter/node_modules/chalk/readme.md +25 -53
- package/generator-adapter/node_modules/chalk/source/index.d.ts +6 -1
- package/generator-adapter/node_modules/chalk/source/vendor/supports-color/browser.js +6 -2
- package/generator-adapter/node_modules/chalk/source/vendor/supports-color/index.js +10 -2
- package/generator-adapter/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/dist/index.js +111 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
- package/generator-adapter/node_modules/{p-queue/dist/options copy.js → fast-string-truncated-width/dist/types.js} +1 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/license +21 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/package.json +35 -0
- package/generator-adapter/node_modules/fast-string-truncated-width/readme.md +59 -0
- package/generator-adapter/node_modules/fast-string-width/dist/index.d.ts +4 -0
- package/generator-adapter/node_modules/fast-string-width/dist/index.js +14 -0
- package/generator-adapter/node_modules/fast-string-width/license +21 -0
- package/generator-adapter/node_modules/fast-string-width/package.json +34 -0
- package/generator-adapter/node_modules/fast-string-width/readme.md +42 -0
- package/generator-adapter/node_modules/fast-wrap-ansi/LICENSE +23 -0
- package/generator-adapter/node_modules/fast-wrap-ansi/README.md +26 -0
- package/generator-adapter/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
- package/generator-adapter/node_modules/fast-wrap-ansi/lib/main.js +219 -0
- package/generator-adapter/node_modules/fast-wrap-ansi/lib/main.js.map +1 -0
- package/generator-adapter/node_modules/fast-wrap-ansi/package.json +51 -0
- package/generator-adapter/node_modules/iconv-lite/lib/index.d.ts +114 -26
- package/generator-adapter/node_modules/iconv-lite/lib/index.js +39 -40
- package/generator-adapter/node_modules/iconv-lite/package.json +13 -2
- package/generator-adapter/node_modules/iconv-lite/types/encodings.d.ts +423 -0
- package/generator-adapter/node_modules/inquirer/dist/types.d.ts +12 -11
- package/generator-adapter/node_modules/inquirer/dist/ui/prompt.js +9 -1
- package/generator-adapter/node_modules/inquirer/dist/ui/skipped-renderer.d.ts +13 -0
- package/generator-adapter/node_modules/inquirer/dist/ui/skipped-renderer.js +57 -0
- package/generator-adapter/node_modules/inquirer/package.json +21 -19
- package/generator-adapter/node_modules/log-symbols/package.json +1 -1
- package/generator-adapter/node_modules/log-symbols/symbols.js +1 -1
- package/generator-adapter/node_modules/mem-fs/dist/index.d.ts +0 -2
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/LICENSE +21 -0
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/README.md +447 -0
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/index.js +346 -0
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/lib/inspect-stream.js +13 -0
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/lib/is-stream.js +15 -0
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/lib/normalize.js +9 -0
- package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/package.json +59 -0
- package/generator-adapter/node_modules/mem-fs/package.json +9 -9
- package/generator-adapter/node_modules/ora/index.d.ts +3 -1
- package/generator-adapter/node_modules/ora/index.js +299 -109
- package/generator-adapter/node_modules/ora/package.json +3 -4
- package/generator-adapter/node_modules/ora/readme.md +24 -0
- package/generator-adapter/node_modules/p-queue/dist/index.js +145 -22
- package/generator-adapter/node_modules/p-queue/dist/options.d.ts +17 -1
- package/generator-adapter/node_modules/p-queue/package.json +1 -1
- package/generator-adapter/node_modules/p-queue/readme.md +32 -1
- package/generator-adapter/node_modules/stdin-discarder/index.js +50 -22
- package/generator-adapter/node_modules/stdin-discarder/package.json +1 -1
- package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/package.json +3 -3
- package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/readme.md +53 -25
- package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/index.d.ts +1 -6
- package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/browser.js +2 -6
- package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/index.js +2 -10
- package/generator-adapter/package.json +3 -3
- package/package.json +12 -12
- package/transports/websocket.d.ts +5 -0
- package/transports/websocket.js +25 -13
- package/transports/websocket.js.map +1 -1
- package/generator-adapter/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
- package/generator-adapter/node_modules/emoji-regex/README.md +0 -107
- package/generator-adapter/node_modules/emoji-regex/index.d.ts +0 -3
- package/generator-adapter/node_modules/emoji-regex/index.js +0 -4
- package/generator-adapter/node_modules/emoji-regex/index.mjs +0 -4
- package/generator-adapter/node_modules/emoji-regex/package.json +0 -45
- package/generator-adapter/node_modules/iconv-lite/Changelog.md +0 -236
- package/generator-adapter/node_modules/jake/node_modules/ansi-styles/index.d.ts +0 -345
- package/generator-adapter/node_modules/jake/node_modules/ansi-styles/index.js +0 -163
- package/generator-adapter/node_modules/jake/node_modules/ansi-styles/license +0 -9
- package/generator-adapter/node_modules/jake/node_modules/ansi-styles/package.json +0 -56
- package/generator-adapter/node_modules/jake/node_modules/ansi-styles/readme.md +0 -152
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/browser-symbols.js +0 -4
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/browser.js +0 -1
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/index.d.ts +0 -22
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/index.js +0 -1
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/license +0 -9
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/package.json +0 -60
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/readme.md +0 -39
- package/generator-adapter/node_modules/ora/node_modules/log-symbols/symbols.js +0 -14
- package/generator-adapter/node_modules/ora/node_modules/strip-ansi/index.d.ts +0 -15
- package/generator-adapter/node_modules/ora/node_modules/strip-ansi/index.js +0 -14
- package/generator-adapter/node_modules/ora/node_modules/strip-ansi/license +0 -9
- package/generator-adapter/node_modules/ora/node_modules/strip-ansi/package.json +0 -59
- package/generator-adapter/node_modules/ora/node_modules/strip-ansi/readme.md +0 -37
- package/generator-adapter/node_modules/p-queue/dist/options copy.d.ts +0 -121
- package/generator-adapter/node_modules/wrap-ansi/index.d.ts +0 -41
- package/generator-adapter/node_modules/wrap-ansi/index.js +0 -222
- package/generator-adapter/node_modules/wrap-ansi/license +0 -9
- package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/index.d.ts +0 -39
- package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/index.js +0 -82
- package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/license +0 -9
- package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/package.json +0 -64
- package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/readme.md +0 -66
- package/generator-adapter/node_modules/wrap-ansi/package.json +0 -69
- package/generator-adapter/node_modules/wrap-ansi/readme.md +0 -75
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/license +0 -0
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/index.js +0 -0
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/utilities.js +0 -0
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +0 -0
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/ansi-styles/index.js +0 -0
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/browser.d.ts +0 -0
- /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/index.d.ts +0 -0
|
@@ -1,34 +1,117 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
|
+
import {stripVTControlCharacters} from 'node:util';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import cliCursor from 'cli-cursor';
|
|
4
5
|
import cliSpinners from 'cli-spinners';
|
|
5
6
|
import logSymbols from 'log-symbols';
|
|
6
|
-
import stripAnsi from 'strip-ansi';
|
|
7
7
|
import stringWidth from 'string-width';
|
|
8
8
|
import isInteractive from 'is-interactive';
|
|
9
9
|
import isUnicodeSupported from 'is-unicode-supported';
|
|
10
10
|
import stdinDiscarder from 'stdin-discarder';
|
|
11
11
|
|
|
12
|
+
// Constants
|
|
13
|
+
const RENDER_DEFERRAL_TIMEOUT = 200; // Milliseconds to wait before re-rendering after partial chunk write
|
|
14
|
+
const SYNCHRONIZED_OUTPUT_ENABLE = '\u001B[?2026h';
|
|
15
|
+
const SYNCHRONIZED_OUTPUT_DISABLE = '\u001B[?2026l';
|
|
16
|
+
|
|
17
|
+
// Global state for concurrent spinner detection
|
|
18
|
+
const activeHooksPerStream = new Map(); // Stream → ora instance
|
|
19
|
+
|
|
12
20
|
class Ora {
|
|
13
21
|
#linesToClear = 0;
|
|
14
|
-
#isDiscardingStdin = false;
|
|
15
|
-
#lineCount = 0;
|
|
16
22
|
#frameIndex = -1;
|
|
17
|
-
#
|
|
18
|
-
#lastIndent = 0;
|
|
23
|
+
#lastFrameTime = 0;
|
|
19
24
|
#options;
|
|
20
25
|
#spinner;
|
|
21
26
|
#stream;
|
|
22
27
|
#id;
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#prefixText;
|
|
29
|
-
#suffixText;
|
|
28
|
+
#hookedStreams = new Map();
|
|
29
|
+
#isInternalWrite = false;
|
|
30
|
+
#drainHandler;
|
|
31
|
+
#deferRenderTimer;
|
|
32
|
+
#isDiscardingStdin = false;
|
|
30
33
|
color;
|
|
31
34
|
|
|
35
|
+
// Helper to execute writes while preventing hook recursion
|
|
36
|
+
#internalWrite(fn) {
|
|
37
|
+
this.#isInternalWrite = true;
|
|
38
|
+
try {
|
|
39
|
+
return fn();
|
|
40
|
+
} finally {
|
|
41
|
+
this.#isInternalWrite = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Helper to render if still spinning
|
|
46
|
+
#tryRender() {
|
|
47
|
+
if (this.isSpinning) {
|
|
48
|
+
this.render();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#stringifyChunk(chunk, encoding) {
|
|
53
|
+
if (chunk === undefined || chunk === null) {
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof chunk === 'string') {
|
|
58
|
+
return chunk;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* eslint-disable n/prefer-global/buffer */
|
|
62
|
+
if (Buffer.isBuffer(chunk) || ArrayBuffer.isView(chunk)) {
|
|
63
|
+
const normalizedEncoding = (typeof encoding === 'string' && encoding && encoding !== 'buffer') ? encoding : 'utf8';
|
|
64
|
+
return Buffer.from(chunk).toString(normalizedEncoding);
|
|
65
|
+
}
|
|
66
|
+
/* eslint-enable n/prefer-global/buffer */
|
|
67
|
+
|
|
68
|
+
return String(chunk);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#chunkTerminatesLine(chunkString) {
|
|
72
|
+
if (!chunkString) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const lastCharacter = chunkString.at(-1);
|
|
77
|
+
return lastCharacter === '\n' || lastCharacter === '\r';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#scheduleRenderDeferral() {
|
|
81
|
+
// If already deferred, don't reset timer - let it complete
|
|
82
|
+
if (this.#deferRenderTimer) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.#deferRenderTimer = setTimeout(() => {
|
|
87
|
+
this.#deferRenderTimer = undefined;
|
|
88
|
+
|
|
89
|
+
if (this.isSpinning) {
|
|
90
|
+
this.#tryRender();
|
|
91
|
+
}
|
|
92
|
+
}, RENDER_DEFERRAL_TIMEOUT);
|
|
93
|
+
|
|
94
|
+
if (typeof this.#deferRenderTimer?.unref === 'function') {
|
|
95
|
+
this.#deferRenderTimer.unref();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#clearRenderDeferral() {
|
|
100
|
+
if (this.#deferRenderTimer) {
|
|
101
|
+
clearTimeout(this.#deferRenderTimer);
|
|
102
|
+
this.#deferRenderTimer = undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Helper to build complete line with symbol, text, prefix, and suffix
|
|
107
|
+
#buildOutputLine(symbol, text, prefixText, suffixText) {
|
|
108
|
+
const fullPrefixText = this.#getFullPrefixText(prefixText, ' ');
|
|
109
|
+
const separatorText = symbol ? ' ' : '';
|
|
110
|
+
const fullText = (typeof text === 'string') ? separatorText + text : '';
|
|
111
|
+
const fullSuffixText = this.#getFullSuffixText(suffixText, ' ');
|
|
112
|
+
return fullPrefixText + symbol + fullText + fullSuffixText;
|
|
113
|
+
}
|
|
114
|
+
|
|
32
115
|
constructor(options) {
|
|
33
116
|
if (typeof options === 'string') {
|
|
34
117
|
options = {
|
|
@@ -47,16 +130,23 @@ class Ora {
|
|
|
47
130
|
// Public
|
|
48
131
|
this.color = this.#options.color;
|
|
49
132
|
|
|
50
|
-
// It's important that these use the public setters.
|
|
51
|
-
this.spinner = this.#options.spinner;
|
|
52
|
-
|
|
53
|
-
this.#initialInterval = this.#options.interval;
|
|
54
133
|
this.#stream = this.#options.stream;
|
|
55
|
-
|
|
56
|
-
|
|
134
|
+
|
|
135
|
+
// Normalize isEnabled and isSilent into options
|
|
136
|
+
if (typeof this.#options.isEnabled !== 'boolean') {
|
|
137
|
+
this.#options.isEnabled = isInteractive({stream: this.#stream});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof this.#options.isSilent !== 'boolean') {
|
|
141
|
+
this.#options.isSilent = false;
|
|
142
|
+
}
|
|
57
143
|
|
|
58
144
|
// Set *after* `this.#stream`.
|
|
145
|
+
// Store original interval before spinner setter clears it
|
|
146
|
+
const userInterval = this.#options.interval;
|
|
59
147
|
// It's important that these use the public setters.
|
|
148
|
+
this.spinner = this.#options.spinner;
|
|
149
|
+
this.#options.interval = userInterval;
|
|
60
150
|
this.text = this.#options.text;
|
|
61
151
|
this.prefixText = this.#options.prefixText;
|
|
62
152
|
this.suffixText = this.#options.suffixText;
|
|
@@ -64,7 +154,7 @@ class Ora {
|
|
|
64
154
|
|
|
65
155
|
if (process.env.NODE_ENV === 'test') {
|
|
66
156
|
this._stream = this.#stream;
|
|
67
|
-
this._isEnabled = this.#isEnabled;
|
|
157
|
+
this._isEnabled = this.#options.isEnabled;
|
|
68
158
|
|
|
69
159
|
Object.defineProperty(this, '_linesToClear', {
|
|
70
160
|
get() {
|
|
@@ -83,14 +173,21 @@ class Ora {
|
|
|
83
173
|
|
|
84
174
|
Object.defineProperty(this, '_lineCount', {
|
|
85
175
|
get() {
|
|
86
|
-
|
|
176
|
+
const columns = this.#stream.columns ?? 80;
|
|
177
|
+
const prefixText = typeof this.#options.prefixText === 'function' ? '' : this.#options.prefixText;
|
|
178
|
+
const suffixText = typeof this.#options.suffixText === 'function' ? '' : this.#options.suffixText;
|
|
179
|
+
const fullPrefixText = (typeof prefixText === 'string' && prefixText !== '') ? prefixText + ' ' : '';
|
|
180
|
+
const fullSuffixText = (typeof suffixText === 'string' && suffixText !== '') ? ' ' + suffixText : '';
|
|
181
|
+
const spinnerChar = '-';
|
|
182
|
+
const fullText = ' '.repeat(this.#options.indent) + fullPrefixText + spinnerChar + (typeof this.#options.text === 'string' ? ' ' + this.#options.text : '') + fullSuffixText;
|
|
183
|
+
return this.#computeLineCountFrom(fullText, columns);
|
|
87
184
|
},
|
|
88
185
|
});
|
|
89
186
|
}
|
|
90
187
|
}
|
|
91
188
|
|
|
92
189
|
get indent() {
|
|
93
|
-
return this.#indent;
|
|
190
|
+
return this.#options.indent;
|
|
94
191
|
}
|
|
95
192
|
|
|
96
193
|
set indent(indent = 0) {
|
|
@@ -98,12 +195,11 @@ class Ora {
|
|
|
98
195
|
throw new Error('The `indent` option must be an integer from 0 and up');
|
|
99
196
|
}
|
|
100
197
|
|
|
101
|
-
this.#indent = indent;
|
|
102
|
-
this.#updateLineCount();
|
|
198
|
+
this.#options.indent = indent;
|
|
103
199
|
}
|
|
104
200
|
|
|
105
201
|
get interval() {
|
|
106
|
-
return this.#
|
|
202
|
+
return this.#options.interval ?? this.#spinner.interval ?? 100;
|
|
107
203
|
}
|
|
108
204
|
|
|
109
205
|
get spinner() {
|
|
@@ -112,7 +208,7 @@ class Ora {
|
|
|
112
208
|
|
|
113
209
|
set spinner(spinner) {
|
|
114
210
|
this.#frameIndex = -1;
|
|
115
|
-
this.#
|
|
211
|
+
this.#options.interval = undefined;
|
|
116
212
|
|
|
117
213
|
if (typeof spinner === 'object') {
|
|
118
214
|
if (!Array.isArray(spinner.frames) || spinner.frames.length === 0 || spinner.frames.some(frame => typeof frame !== 'string')) {
|
|
@@ -137,30 +233,27 @@ class Ora {
|
|
|
137
233
|
}
|
|
138
234
|
|
|
139
235
|
get text() {
|
|
140
|
-
return this.#text;
|
|
236
|
+
return this.#options.text;
|
|
141
237
|
}
|
|
142
238
|
|
|
143
239
|
set text(value = '') {
|
|
144
|
-
this.#text = value;
|
|
145
|
-
this.#updateLineCount();
|
|
240
|
+
this.#options.text = value;
|
|
146
241
|
}
|
|
147
242
|
|
|
148
243
|
get prefixText() {
|
|
149
|
-
return this.#prefixText;
|
|
244
|
+
return this.#options.prefixText;
|
|
150
245
|
}
|
|
151
246
|
|
|
152
247
|
set prefixText(value = '') {
|
|
153
|
-
this.#prefixText = value;
|
|
154
|
-
this.#updateLineCount();
|
|
248
|
+
this.#options.prefixText = value;
|
|
155
249
|
}
|
|
156
250
|
|
|
157
251
|
get suffixText() {
|
|
158
|
-
return this.#suffixText;
|
|
252
|
+
return this.#options.suffixText;
|
|
159
253
|
}
|
|
160
254
|
|
|
161
255
|
set suffixText(value = '') {
|
|
162
|
-
this.#suffixText = value;
|
|
163
|
-
this.#updateLineCount();
|
|
256
|
+
this.#options.suffixText = value;
|
|
164
257
|
}
|
|
165
258
|
|
|
166
259
|
get isSpinning() {
|
|
@@ -176,39 +269,25 @@ class Ora {
|
|
|
176
269
|
return '';
|
|
177
270
|
}
|
|
178
271
|
|
|
179
|
-
#getFullPrefixText(prefixText = this.#prefixText, postfix = ' ') {
|
|
272
|
+
#getFullPrefixText(prefixText = this.#options.prefixText, postfix = ' ') {
|
|
180
273
|
return this.#formatAffix(prefixText, postfix, false);
|
|
181
274
|
}
|
|
182
275
|
|
|
183
|
-
#getFullSuffixText(suffixText = this.#suffixText, prefix = ' ') {
|
|
276
|
+
#getFullSuffixText(suffixText = this.#options.suffixText, prefix = ' ') {
|
|
184
277
|
return this.#formatAffix(suffixText, prefix, true);
|
|
185
278
|
}
|
|
186
279
|
|
|
187
280
|
#computeLineCountFrom(text, columns) {
|
|
188
281
|
let count = 0;
|
|
189
|
-
for (const line of
|
|
282
|
+
for (const line of stripVTControlCharacters(text).split('\n')) {
|
|
190
283
|
count += Math.max(1, Math.ceil(stringWidth(line) / columns));
|
|
191
284
|
}
|
|
192
285
|
|
|
193
286
|
return count;
|
|
194
287
|
}
|
|
195
288
|
|
|
196
|
-
#updateLineCount() {
|
|
197
|
-
const columns = this.#stream.columns ?? 80;
|
|
198
|
-
|
|
199
|
-
// Simple side-effect free approximation (do not call functions)
|
|
200
|
-
const prefixText = typeof this.#prefixText === 'function' ? '' : this.#prefixText;
|
|
201
|
-
const suffixText = typeof this.#suffixText === 'function' ? '' : this.#suffixText;
|
|
202
|
-
const fullPrefixText = (typeof prefixText === 'string' && prefixText !== '') ? prefixText + ' ' : '';
|
|
203
|
-
const fullSuffixText = (typeof suffixText === 'string' && suffixText !== '') ? ' ' + suffixText : '';
|
|
204
|
-
const spinnerChar = '-';
|
|
205
|
-
const fullText = ' '.repeat(this.#indent) + fullPrefixText + spinnerChar + (typeof this.#text === 'string' ? ' ' + this.#text : '') + fullSuffixText;
|
|
206
|
-
|
|
207
|
-
this.#lineCount = this.#computeLineCountFrom(fullText, columns);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
289
|
get isEnabled() {
|
|
211
|
-
return this.#isEnabled && !this.#isSilent;
|
|
290
|
+
return this.#options.isEnabled && !this.#options.isSilent;
|
|
212
291
|
}
|
|
213
292
|
|
|
214
293
|
set isEnabled(value) {
|
|
@@ -216,11 +295,11 @@ class Ora {
|
|
|
216
295
|
throw new TypeError('The `isEnabled` option must be a boolean');
|
|
217
296
|
}
|
|
218
297
|
|
|
219
|
-
this.#isEnabled = value;
|
|
298
|
+
this.#options.isEnabled = value;
|
|
220
299
|
}
|
|
221
300
|
|
|
222
301
|
get isSilent() {
|
|
223
|
-
return this.#isSilent;
|
|
302
|
+
return this.#options.isSilent;
|
|
224
303
|
}
|
|
225
304
|
|
|
226
305
|
set isSilent(value) {
|
|
@@ -228,16 +307,15 @@ class Ora {
|
|
|
228
307
|
throw new TypeError('The `isSilent` option must be a boolean');
|
|
229
308
|
}
|
|
230
309
|
|
|
231
|
-
this.#isSilent = value;
|
|
310
|
+
this.#options.isSilent = value;
|
|
232
311
|
}
|
|
233
312
|
|
|
234
313
|
frame() {
|
|
235
|
-
//
|
|
236
|
-
// even if the render method is called more often.
|
|
314
|
+
// Only advance frame if enough time has passed (throttle to interval)
|
|
237
315
|
const now = Date.now();
|
|
238
|
-
if (this.#frameIndex === -1 || now - this.#
|
|
239
|
-
this.#frameIndex =
|
|
240
|
-
this.#
|
|
316
|
+
if (this.#frameIndex === -1 || now - this.#lastFrameTime >= this.interval) {
|
|
317
|
+
this.#frameIndex = (this.#frameIndex + 1) % this.#spinner.frames.length;
|
|
318
|
+
this.#lastFrameTime = now;
|
|
241
319
|
}
|
|
242
320
|
|
|
243
321
|
const {frames} = this.#spinner;
|
|
@@ -247,59 +325,166 @@ class Ora {
|
|
|
247
325
|
frame = chalk[this.color](frame);
|
|
248
326
|
}
|
|
249
327
|
|
|
250
|
-
const fullPrefixText = this.#getFullPrefixText(this.#prefixText, ' ');
|
|
328
|
+
const fullPrefixText = this.#getFullPrefixText(this.#options.prefixText, ' ');
|
|
251
329
|
const fullText = typeof this.text === 'string' ? ' ' + this.text : '';
|
|
252
|
-
const fullSuffixText = this.#getFullSuffixText(this.#suffixText, ' ');
|
|
330
|
+
const fullSuffixText = this.#getFullSuffixText(this.#options.suffixText, ' ');
|
|
253
331
|
|
|
254
332
|
return fullPrefixText + frame + fullText + fullSuffixText;
|
|
255
333
|
}
|
|
256
334
|
|
|
257
335
|
clear() {
|
|
258
|
-
if (!this
|
|
336
|
+
if (!this.isEnabled || !this.#stream.isTTY) {
|
|
259
337
|
return this;
|
|
260
338
|
}
|
|
261
339
|
|
|
262
|
-
|
|
340
|
+
// Protect cursor control methods (cursorTo, moveCursor, clearLine) which internally call stream.write
|
|
341
|
+
this.#internalWrite(() => {
|
|
342
|
+
this.#stream.cursorTo(0);
|
|
343
|
+
|
|
344
|
+
for (let index = 0; index < this.#linesToClear; index++) {
|
|
345
|
+
if (index > 0) {
|
|
346
|
+
this.#stream.moveCursor(0, -1);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
this.#stream.clearLine(1);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (this.#options.indent) {
|
|
353
|
+
this.#stream.cursorTo(this.#options.indent);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
this.#linesToClear = 0;
|
|
358
|
+
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Helper to hook a single stream
|
|
363
|
+
#hookStream(stream) {
|
|
364
|
+
if (!stream || this.#hookedStreams.has(stream) || !stream.isTTY || typeof stream.write !== 'function') {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Detect concurrent spinners
|
|
369
|
+
if (activeHooksPerStream.has(stream)) {
|
|
370
|
+
console.warn('[ora] Multiple concurrent spinners detected. This may cause visual corruption. Use one spinner at a time.');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const originalWrite = stream.write;
|
|
374
|
+
this.#hookedStreams.set(stream, originalWrite);
|
|
375
|
+
activeHooksPerStream.set(stream, this);
|
|
376
|
+
stream.write = (chunk, encoding, callback) => this.#hookedWrite(stream, originalWrite, chunk, encoding, callback);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
Intercept stream writes while spinner is active to handle external writes cleanly without visual corruption.
|
|
381
|
+
Hooks process stdio streams and the active spinner stream so console.log(), console.error(), and direct writes stay tidy.
|
|
382
|
+
*/
|
|
383
|
+
#installHook() {
|
|
384
|
+
if (!this.isEnabled || this.#hookedStreams.size > 0) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const streamsToHook = new Set([this.#stream, process.stdout, process.stderr]);
|
|
263
389
|
|
|
264
|
-
for (
|
|
265
|
-
|
|
266
|
-
|
|
390
|
+
for (const stream of streamsToHook) {
|
|
391
|
+
this.#hookStream(stream);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
#uninstallHook() {
|
|
396
|
+
for (const [stream, originalWrite] of this.#hookedStreams) {
|
|
397
|
+
stream.write = originalWrite;
|
|
398
|
+
if (activeHooksPerStream.get(stream) === this) {
|
|
399
|
+
activeHooksPerStream.delete(stream);
|
|
267
400
|
}
|
|
401
|
+
}
|
|
268
402
|
|
|
269
|
-
|
|
403
|
+
this.#hookedStreams.clear();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// eslint-disable-next-line max-params -- Need stream and originalWrite for multi-stream support
|
|
407
|
+
#hookedWrite(stream, originalWrite, chunk, encoding, callback) {
|
|
408
|
+
// Handle both write(chunk, encoding, callback) and write(chunk, callback) signatures
|
|
409
|
+
if (typeof encoding === 'function') {
|
|
410
|
+
callback = encoding;
|
|
411
|
+
encoding = undefined;
|
|
270
412
|
}
|
|
271
413
|
|
|
272
|
-
|
|
273
|
-
|
|
414
|
+
// Pass through our own internal writes (spinner rendering, cursor control)
|
|
415
|
+
if (this.#isInternalWrite) {
|
|
416
|
+
return originalWrite.call(stream, chunk, encoding, callback);
|
|
274
417
|
}
|
|
275
418
|
|
|
276
|
-
|
|
277
|
-
this
|
|
419
|
+
// External write detected - clear spinner, write content, re-render if appropriate
|
|
420
|
+
this.clear();
|
|
278
421
|
|
|
279
|
-
|
|
422
|
+
const chunkString = this.#stringifyChunk(chunk, encoding);
|
|
423
|
+
const chunkTerminatesLine = this.#chunkTerminatesLine(chunkString);
|
|
424
|
+
|
|
425
|
+
const writeResult = originalWrite.call(stream, chunk, encoding, callback);
|
|
426
|
+
|
|
427
|
+
// Schedule or clear render deferral based on chunk content
|
|
428
|
+
if (chunkTerminatesLine) {
|
|
429
|
+
this.#clearRenderDeferral();
|
|
430
|
+
} else if (chunkString.length > 0) {
|
|
431
|
+
this.#scheduleRenderDeferral();
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Re-render spinner below the new output if still spinning and not deferred
|
|
435
|
+
if (this.isSpinning && !this.#deferRenderTimer) {
|
|
436
|
+
this.render();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return writeResult;
|
|
280
440
|
}
|
|
281
441
|
|
|
282
442
|
render() {
|
|
283
|
-
if (!this
|
|
443
|
+
if (!this.isEnabled || this.#drainHandler || this.#deferRenderTimer) {
|
|
284
444
|
return this;
|
|
285
445
|
}
|
|
286
446
|
|
|
287
|
-
this.
|
|
447
|
+
const useSynchronizedOutput = this.#stream.isTTY;
|
|
448
|
+
let shouldDisableSynchronizedOutput = false;
|
|
288
449
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
450
|
+
try {
|
|
451
|
+
if (useSynchronizedOutput) {
|
|
452
|
+
this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_ENABLE));
|
|
453
|
+
shouldDisableSynchronizedOutput = true;
|
|
454
|
+
}
|
|
292
455
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
456
|
+
this.clear();
|
|
457
|
+
|
|
458
|
+
let frameContent = this.frame();
|
|
459
|
+
const columns = this.#stream.columns ?? 80;
|
|
460
|
+
const actualLineCount = this.#computeLineCountFrom(frameContent, columns);
|
|
461
|
+
|
|
462
|
+
// If content would exceed viewport height, truncate it to prevent garbage
|
|
463
|
+
const consoleHeight = this.#stream.rows;
|
|
464
|
+
if (consoleHeight && consoleHeight > 1 && actualLineCount > consoleHeight) {
|
|
465
|
+
const lines = frameContent.split('\n');
|
|
466
|
+
const maxLines = consoleHeight - 1; // Reserve one line for truncation message
|
|
467
|
+
frameContent = [...lines.slice(0, maxLines), '... (content truncated to fit terminal)'].join('\n');
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const canContinue = this.#internalWrite(() => this.#stream.write(frameContent));
|
|
300
471
|
|
|
301
|
-
|
|
302
|
-
|
|
472
|
+
// Handle backpressure - pause rendering if stream buffer is full
|
|
473
|
+
if (canContinue === false && this.#stream.isTTY) {
|
|
474
|
+
this.#drainHandler = () => {
|
|
475
|
+
this.#drainHandler = undefined;
|
|
476
|
+
this.#tryRender();
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
this.#stream.once('drain', this.#drainHandler);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
this.#linesToClear = this.#computeLineCountFrom(frameContent, columns);
|
|
483
|
+
} finally {
|
|
484
|
+
if (shouldDisableSynchronizedOutput) {
|
|
485
|
+
this.#internalWrite(() => this.#stream.write(SYNCHRONIZED_OUTPUT_DISABLE));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
303
488
|
|
|
304
489
|
return this;
|
|
305
490
|
}
|
|
@@ -309,15 +494,16 @@ class Ora {
|
|
|
309
494
|
this.text = text;
|
|
310
495
|
}
|
|
311
496
|
|
|
312
|
-
if (this
|
|
497
|
+
if (this.isSilent) {
|
|
313
498
|
return this;
|
|
314
499
|
}
|
|
315
500
|
|
|
316
|
-
if (!this
|
|
317
|
-
const
|
|
501
|
+
if (!this.isEnabled) {
|
|
502
|
+
const symbol = this.text ? '-' : '';
|
|
503
|
+
const line = ' '.repeat(this.#options.indent) + this.#buildOutputLine(symbol, this.text, this.#options.prefixText, this.#options.suffixText);
|
|
318
504
|
|
|
319
505
|
if (line.trim() !== '') {
|
|
320
|
-
this.#stream.write(line + '\n');
|
|
506
|
+
this.#internalWrite(() => this.#stream.write(line + '\n'));
|
|
321
507
|
}
|
|
322
508
|
|
|
323
509
|
return this;
|
|
@@ -332,10 +518,11 @@ class Ora {
|
|
|
332
518
|
}
|
|
333
519
|
|
|
334
520
|
if (this.#options.discardStdin && process.stdin.isTTY) {
|
|
335
|
-
this.#isDiscardingStdin = true;
|
|
336
521
|
stdinDiscarder.start();
|
|
522
|
+
this.#isDiscardingStdin = true;
|
|
337
523
|
}
|
|
338
524
|
|
|
525
|
+
this.#installHook();
|
|
339
526
|
this.render();
|
|
340
527
|
this.#id = setInterval(this.render.bind(this), this.interval);
|
|
341
528
|
|
|
@@ -345,18 +532,28 @@ class Ora {
|
|
|
345
532
|
stop() {
|
|
346
533
|
clearInterval(this.#id);
|
|
347
534
|
this.#id = undefined;
|
|
348
|
-
this.#frameIndex =
|
|
535
|
+
this.#frameIndex = -1;
|
|
536
|
+
this.#lastFrameTime = 0;
|
|
537
|
+
|
|
538
|
+
this.#clearRenderDeferral();
|
|
539
|
+
this.#uninstallHook();
|
|
349
540
|
|
|
350
|
-
if
|
|
541
|
+
// Clean up drain handler if it exists
|
|
542
|
+
if (this.#drainHandler) {
|
|
543
|
+
this.#stream.removeListener('drain', this.#drainHandler);
|
|
544
|
+
this.#drainHandler = undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (this.isEnabled) {
|
|
351
548
|
this.clear();
|
|
352
549
|
if (this.#options.hideCursor) {
|
|
353
550
|
cliCursor.show(this.#stream);
|
|
354
551
|
}
|
|
355
552
|
}
|
|
356
553
|
|
|
357
|
-
if (this.#
|
|
358
|
-
stdinDiscarder.stop();
|
|
554
|
+
if (this.#isDiscardingStdin) {
|
|
359
555
|
this.#isDiscardingStdin = false;
|
|
556
|
+
stdinDiscarder.stop();
|
|
360
557
|
}
|
|
361
558
|
|
|
362
559
|
return this;
|
|
@@ -379,26 +576,19 @@ class Ora {
|
|
|
379
576
|
}
|
|
380
577
|
|
|
381
578
|
stopAndPersist(options = {}) {
|
|
382
|
-
if (this
|
|
579
|
+
if (this.isSilent) {
|
|
383
580
|
return this;
|
|
384
581
|
}
|
|
385
582
|
|
|
386
|
-
const
|
|
387
|
-
const fullPrefixText = this.#getFullPrefixText(prefixText, ' ');
|
|
388
|
-
|
|
389
|
-
const symbolText = options.symbol ?? ' ';
|
|
390
|
-
|
|
583
|
+
const symbol = options.symbol ?? ' ';
|
|
391
584
|
const text = options.text ?? this.text;
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
const suffixText = options.suffixText ?? this.#suffixText;
|
|
396
|
-
const fullSuffixText = this.#getFullSuffixText(suffixText, ' ');
|
|
585
|
+
const prefixText = options.prefixText ?? this.#options.prefixText;
|
|
586
|
+
const suffixText = options.suffixText ?? this.#options.suffixText;
|
|
397
587
|
|
|
398
|
-
const textToWrite =
|
|
588
|
+
const textToWrite = this.#buildOutputLine(symbol, text, prefixText, suffixText) + '\n';
|
|
399
589
|
|
|
400
590
|
this.stop();
|
|
401
|
-
this.#stream.write(textToWrite);
|
|
591
|
+
this.#internalWrite(() => this.#stream.write(textToWrite));
|
|
402
592
|
|
|
403
593
|
return this;
|
|
404
594
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ora",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"description": "Elegant terminal spinner",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/ora",
|
|
@@ -49,9 +49,8 @@
|
|
|
49
49
|
"is-interactive": "^2.0.0",
|
|
50
50
|
"is-unicode-supported": "^2.1.0",
|
|
51
51
|
"log-symbols": "^7.0.1",
|
|
52
|
-
"stdin-discarder": "^0.
|
|
53
|
-
"string-width": "^8.1.0"
|
|
54
|
-
"strip-ansi": "^7.1.2"
|
|
52
|
+
"stdin-discarder": "^0.3.1",
|
|
53
|
+
"string-width": "^8.1.0"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
|
57
56
|
"@types/node": "^24.5.0",
|
|
@@ -138,6 +138,8 @@ Discard stdin input (except Ctrl+C) while running if it's TTY. This prevents the
|
|
|
138
138
|
|
|
139
139
|
This has no effect on Windows as there is no good way to implement discarding stdin properly there.
|
|
140
140
|
|
|
141
|
+
Note: `discardStdin` puts stdin into raw mode. In raw mode, `Ctrl+C` no longer generates `SIGINT` from the terminal. Ora re-emits `Ctrl+C` from stdin input, but if your code blocks the event loop with synchronous work, `Ctrl+C` handling is delayed until the blocking work ends. Use async APIs, a worker thread, or a child process to keep `Ctrl+C` responsive, or set `discardStdin` to `false`.
|
|
142
|
+
|
|
141
143
|
### Instance
|
|
142
144
|
|
|
143
145
|
#### .text <sup>get/set</sup>
|
|
@@ -312,6 +314,28 @@ const spinner = ora(`Loading ${chalk.red('unicorns')}`).start();
|
|
|
312
314
|
|
|
313
315
|
JavaScript is single-threaded, so any synchronous operations will block the spinner's animation. To avoid this, prefer using asynchronous operations.
|
|
314
316
|
|
|
317
|
+
### Why do I get the line spinner on Windows Terminal or WSL?
|
|
318
|
+
|
|
319
|
+
Windows Terminal does [not expose](https://github.com/microsoft/terminal/issues/1040) a reliable, stable way to detect itself or Unicode support from Node, and `WT_SESSION` is explicitly informative, not a detection API and can be inherited by other terminals. That makes environment-based detection best-effort. If you are in a Unicode-capable terminal, set `spinner` explicitly, for example `ora({spinner: 'dots'})`.
|
|
320
|
+
|
|
321
|
+
### Can I log messages while the spinner is running?
|
|
322
|
+
|
|
323
|
+
Yes! Ora automatically handles writes to the same stream. The spinner will temporarily clear itself, output your message, and re-render below:
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
const spinner = ora('Processing...').start();
|
|
327
|
+
|
|
328
|
+
console.log('Step 1 complete');
|
|
329
|
+
console.log('Step 2 complete');
|
|
330
|
+
|
|
331
|
+
spinner.succeed('Done!');
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
The output will be clean with each log appearing above the spinner. This works seamlessly without requiring any special logging methods. Both `console.log()` (stdout) and `console.error()`/`console.warn()` (stderr) are supported.
|
|
335
|
+
|
|
336
|
+
> [!NOTE]
|
|
337
|
+
> Don't run multiple spinners concurrently. Use one spinner at a time.
|
|
338
|
+
|
|
315
339
|
### Can I display multiple spinners simultaneously?
|
|
316
340
|
|
|
317
341
|
No. Ora is designed to display a single spinner at a time. For multiple concurrent progress indicators, consider alternatives like [listr2](https://github.com/listr2/listr2) or [spinnies](https://github.com/jcarpanelli/spinnies).
|