|
1 | | -import { Base, Package } from '@open-audio-stack/core'; |
| 1 | +import { Base, isTests, Package } from '@open-audio-stack/core'; |
2 | 2 | import CliTable3 from 'cli-table3'; |
3 | | -import ora from 'ora'; |
| 3 | +import ora, { Ora } from 'ora'; |
4 | 4 | import type { CliOptions } from './types/options.js'; |
5 | 5 |
|
6 | 6 | export function formatOutput(result: Package[] | Package | undefined, versions?: string[], json?: boolean): string { |
@@ -62,96 +62,46 @@ export function truncateString(str: string, num: number) { |
62 | 62 | } |
63 | 63 | } |
64 | 64 |
|
65 | | -// Simple spinner registry for the `output` API so callers can start/stop spinners |
66 | | -const _spinners: Map<string, ReturnType<typeof ora>> = new Map(); |
67 | | - |
68 | 65 | export enum OutputType { |
69 | 66 | START = 'start', |
70 | 67 | SUCCESS = 'success', |
71 | 68 | ERROR = 'error', |
72 | 69 | } |
73 | 70 |
|
74 | | -/** |
75 | | - * Low level output API that callers can use to mark start, success, and error |
76 | | - * states for long-running operations. This provides clear control over when |
77 | | - * spinners start/stop and when textual/json output is emitted. |
78 | | - */ |
79 | | -export function output(type: OutputType, payload: any, options?: CliOptions, base?: Base) { |
80 | | - const useJson = Boolean(options && options.json); |
81 | | - const isTest = Boolean(process.env.VITEST || process.env.NODE_ENV === 'test'); |
82 | | - const key = String(typeof payload === 'string' ? payload : (payload && payload.message) || payload || ''); |
83 | | - |
84 | | - if (type === OutputType.START) { |
85 | | - // configure base logging at start |
86 | | - if (base) { |
87 | | - if (options && options.log) base.logEnable(); |
88 | | - else base.logDisable(); |
89 | | - } |
90 | | - |
91 | | - if (useJson) { |
92 | | - console.log(JSON.stringify({ status: 'inprogress', message: key }, null, 2)); |
93 | | - return; |
94 | | - } |
| 71 | +let spinner: Ora | undefined; |
95 | 72 |
|
96 | | - if (isTest) { |
97 | | - console.log(key); |
98 | | - return; |
99 | | - } |
100 | | - |
101 | | - // interactive run: create and start a spinner for this key |
102 | | - const s = ora(key).start(); |
103 | | - _spinners.set(key, s); |
104 | | - return; |
105 | | - } |
106 | | - |
107 | | - if (type === OutputType.SUCCESS) { |
108 | | - // If JSON requested and payload is an object, print it |
109 | | - if (useJson && typeof payload === 'object') { |
110 | | - console.log(JSON.stringify(payload, null, 2)); |
111 | | - return; |
112 | | - } |
113 | | - |
114 | | - // For non-json modes we expect payload to be a string (commands should pass a string) |
115 | | - const messageOut = String(payload); |
116 | | - |
117 | | - if (isTest) { |
118 | | - // In test mode only print the final payload (no start/checkmark). |
119 | | - console.log(messageOut); |
120 | | - return; |
121 | | - } |
122 | | - |
123 | | - const s = _spinners.get(key); |
124 | | - if (s) { |
125 | | - s.succeed(key); |
126 | | - if (messageOut && messageOut !== key) console.log(messageOut); |
127 | | - _spinners.delete(key); |
128 | | - return; |
129 | | - } |
| 73 | +export function output(type: OutputType, message: any, options?: CliOptions, base?: Base) { |
| 74 | + // console.log('\n output', type, message, options); |
| 75 | + const useJson = Boolean(options && options.json); |
| 76 | + if (message.message) message = message.message; |
130 | 77 |
|
131 | | - // fallback |
132 | | - console.log(key); |
133 | | - if (messageOut && messageOut !== key) console.log(messageOut); |
134 | | - return; |
| 78 | + // If logging, ensure core package logging is enabled. |
| 79 | + if (base) { |
| 80 | + if (options && options.log) base.logEnable(); |
| 81 | + else base.logDisable(); |
135 | 82 | } |
136 | 83 |
|
137 | | - // ERROR |
138 | | - const errMsg = payload instanceof Error ? payload.message : String(payload); |
| 84 | + // If json, output json only. |
139 | 85 | if (useJson) { |
140 | | - console.log(JSON.stringify({ error: errMsg }, null, 2)); |
| 86 | + console.log(JSON.stringify({ type, message }, null, 2)); |
141 | 87 | return; |
142 | 88 | } |
143 | 89 |
|
144 | | - if (isTest) { |
145 | | - console.error(errMsg); |
| 90 | + // If test, output text only. |
| 91 | + if (isTests()) { |
| 92 | + console.log(message); |
146 | 93 | return; |
147 | 94 | } |
148 | 95 |
|
149 | | - const s2 = _spinners.get(key); |
150 | | - if (s2) { |
151 | | - s2.fail(errMsg); |
152 | | - _spinners.delete(key); |
| 96 | + // If interactive run, use spinners. |
| 97 | + if (type === OutputType.START) { |
| 98 | + spinner = ora(message).start(); |
| 99 | + return; |
| 100 | + } else if (type === OutputType.SUCCESS) { |
| 101 | + spinner?.succeed(message); |
| 102 | + return; |
| 103 | + } else { |
| 104 | + spinner?.fail(message); |
153 | 105 | return; |
154 | 106 | } |
155 | | - |
156 | | - console.error(errMsg); |
157 | 107 | } |
0 commit comments