@pokit/reporter-clack 0.0.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/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/adapter.d.ts +44 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +666 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/symbols.d.ts +50 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +47 -0
- package/package.json +61 -0
- package/src/index.ts +13 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Daniel Grant
|
|
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,51 @@
|
|
|
1
|
+
# @pokit/reporter-clack
|
|
2
|
+
|
|
3
|
+
Terminal output adapter for pok using [Clack](https://github.com/natemoo-re/clack).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @pokit/reporter-clack
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { run } from '@pokit/core';
|
|
15
|
+
import { createReporterAdapter } from '@pokit/reporter-clack';
|
|
16
|
+
|
|
17
|
+
await run(args, {
|
|
18
|
+
reporterAdapter: createReporterAdapter(),
|
|
19
|
+
// ...
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- Styled log messages (info, success, warn, error)
|
|
26
|
+
- Progress spinners
|
|
27
|
+
- Grouped output with collapsible sections
|
|
28
|
+
- Activity indicators
|
|
29
|
+
- Unicode and ASCII symbol sets
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
createReporterAdapter({
|
|
35
|
+
plain: false, // Disable colors and spinners
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Exports
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Main adapter
|
|
43
|
+
import { createReporterAdapter } from '@pokit/reporter-clack';
|
|
44
|
+
|
|
45
|
+
// Symbol customization
|
|
46
|
+
import { getSymbols, UNICODE_SYMBOLS, ASCII_SYMBOLS } from '@pokit/reporter-clack';
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Documentation
|
|
50
|
+
|
|
51
|
+
See the [full documentation](https://github.com/openpok/pok/blob/main/docs/packages/reporter-clack.md).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clack Reporter Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the ReporterAdapter interface using @clack/prompts.
|
|
5
|
+
* Consumes CLI events from the EventBus and renders them to the terminal.
|
|
6
|
+
*
|
|
7
|
+
* Design principles:
|
|
8
|
+
* - Groups are visual containers with a bold title (intro) and completion indicator (outro)
|
|
9
|
+
* - Activities within sequential groups show as spinner items that complete with checkmarks
|
|
10
|
+
* - Parallel groups use a single spinner that tracks progress, showing completions as they finish
|
|
11
|
+
* - Logs during an active spinner will pause the spinner, show the log, then resume
|
|
12
|
+
* - Process output is never interleaved with spinners
|
|
13
|
+
*
|
|
14
|
+
* Rendering strategy:
|
|
15
|
+
* - group:start -> p.intro() with bold label (or plain text in plain mode)
|
|
16
|
+
* - group:end -> p.outro() with success indicator
|
|
17
|
+
* - activity:start (sequential) -> spinner.start() (or plain text indicator)
|
|
18
|
+
* - activity:start (parallel) -> track activity, update combined spinner
|
|
19
|
+
* - activity:success -> spinner.stop() with checkmark (code 0) or update combined spinner
|
|
20
|
+
* - activity:failure -> spinner.stop() with X (code 1)
|
|
21
|
+
* - activity:update -> spinner.message()
|
|
22
|
+
* - log -> pause spinner if active, p.log.*, resume spinner
|
|
23
|
+
*
|
|
24
|
+
* Plain mode (--plain or CI environment):
|
|
25
|
+
* - When unicode is disabled, uses ASCII symbols and bypasses clack's decorative output
|
|
26
|
+
* - When color is disabled (--no-color or NO_COLOR env), strips ANSI color codes
|
|
27
|
+
*/
|
|
28
|
+
import type { ReporterAdapter, OutputConfig } from '@pokit/core';
|
|
29
|
+
/**
|
|
30
|
+
* Options for the reporter adapter
|
|
31
|
+
*/
|
|
32
|
+
export type ReporterAdapterOptions = {
|
|
33
|
+
/** When true, logs are displayed immediately instead of being buffered during spinners */
|
|
34
|
+
verbose?: boolean;
|
|
35
|
+
/** Output configuration (color, unicode, verbose settings) */
|
|
36
|
+
output?: OutputConfig;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Create a Clack-based ReporterAdapter
|
|
40
|
+
*
|
|
41
|
+
* @param options - Optional configuration for the adapter
|
|
42
|
+
*/
|
|
43
|
+
export declare function createReporterAdapter(options?: ReporterAdapterOptions): ReporterAdapter;
|
|
44
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EACV,eAAe,EAQf,YAAY,EACb,MAAM,aAAa,CAAC;AA+MrB;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,eAAe,CA0iBvF"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clack Reporter Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the ReporterAdapter interface using @clack/prompts.
|
|
5
|
+
* Consumes CLI events from the EventBus and renders them to the terminal.
|
|
6
|
+
*
|
|
7
|
+
* Design principles:
|
|
8
|
+
* - Groups are visual containers with a bold title (intro) and completion indicator (outro)
|
|
9
|
+
* - Activities within sequential groups show as spinner items that complete with checkmarks
|
|
10
|
+
* - Parallel groups use a single spinner that tracks progress, showing completions as they finish
|
|
11
|
+
* - Logs during an active spinner will pause the spinner, show the log, then resume
|
|
12
|
+
* - Process output is never interleaved with spinners
|
|
13
|
+
*
|
|
14
|
+
* Rendering strategy:
|
|
15
|
+
* - group:start -> p.intro() with bold label (or plain text in plain mode)
|
|
16
|
+
* - group:end -> p.outro() with success indicator
|
|
17
|
+
* - activity:start (sequential) -> spinner.start() (or plain text indicator)
|
|
18
|
+
* - activity:start (parallel) -> track activity, update combined spinner
|
|
19
|
+
* - activity:success -> spinner.stop() with checkmark (code 0) or update combined spinner
|
|
20
|
+
* - activity:failure -> spinner.stop() with X (code 1)
|
|
21
|
+
* - activity:update -> spinner.message()
|
|
22
|
+
* - log -> pause spinner if active, p.log.*, resume spinner
|
|
23
|
+
*
|
|
24
|
+
* Plain mode (--plain or CI environment):
|
|
25
|
+
* - When unicode is disabled, uses ASCII symbols and bypasses clack's decorative output
|
|
26
|
+
* - When color is disabled (--no-color or NO_COLOR env), strips ANSI color codes
|
|
27
|
+
*/
|
|
28
|
+
import * as p from '@clack/prompts';
|
|
29
|
+
import pc from 'picocolors';
|
|
30
|
+
import { CommandError } from '@pokit/core';
|
|
31
|
+
/**
|
|
32
|
+
* Extract error message and optional output from an error.
|
|
33
|
+
* If the error is a CommandError with output, includes that in the message.
|
|
34
|
+
*/
|
|
35
|
+
function formatErrorMessage(error) {
|
|
36
|
+
if (typeof error === 'string') {
|
|
37
|
+
return error;
|
|
38
|
+
}
|
|
39
|
+
if (error instanceof CommandError && error.output) {
|
|
40
|
+
return `${error.message}\n\n${error.output}`;
|
|
41
|
+
}
|
|
42
|
+
return error.message;
|
|
43
|
+
}
|
|
44
|
+
import { getSymbols } from './symbols';
|
|
45
|
+
/**
|
|
46
|
+
* Helper to conditionally apply color.
|
|
47
|
+
* In no-color mode, returns text unchanged.
|
|
48
|
+
*/
|
|
49
|
+
function colorize(text, colorFn, useColor) {
|
|
50
|
+
return useColor ? colorFn(text) : text;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Write a line to stdout.
|
|
54
|
+
* Uses process.stdout.write to ensure proper capture in all environments.
|
|
55
|
+
* (Bun's console.log may not be captured by stdout interception)
|
|
56
|
+
*/
|
|
57
|
+
function writeLine(line) {
|
|
58
|
+
process.stdout.write(line + '\n');
|
|
59
|
+
}
|
|
60
|
+
/** Maximum number of logs to buffer per activity to prevent memory issues */
|
|
61
|
+
const MAX_BUFFERED_LOGS_PER_ACTIVITY = 100;
|
|
62
|
+
/**
|
|
63
|
+
* Update the parallel spinner message based on current activity states
|
|
64
|
+
*/
|
|
65
|
+
function updateParallelSpinnerMessage(state) {
|
|
66
|
+
if (!state.parallelSpinner)
|
|
67
|
+
return;
|
|
68
|
+
const activities = Array.from(state.parallelActivities.values()).filter((a) => a.groupId === state.parallelSpinnerGroupId);
|
|
69
|
+
const pending = activities.filter((a) => a.status === 'pending');
|
|
70
|
+
const completed = activities.filter((a) => a.status !== 'pending');
|
|
71
|
+
if (pending.length === 0) {
|
|
72
|
+
// All done - this will be cleaned up by group:end
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const message = pending.length === 1
|
|
76
|
+
? pending[0].label
|
|
77
|
+
: `Running ${pending.length} tasks (${completed.length}/${activities.length} done)`;
|
|
78
|
+
state.parallelSpinner.currentMessage = message;
|
|
79
|
+
state.parallelSpinner.spinner.message(message);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Display a log message.
|
|
83
|
+
* Uses clack's log functions in unicode mode, plain console.log in plain mode.
|
|
84
|
+
*
|
|
85
|
+
* @param level - The log level
|
|
86
|
+
* @param message - The message to display
|
|
87
|
+
* @param state - The adapter state (for output config)
|
|
88
|
+
* @param indented - Whether to indent the log (for buffered logs inside activity context)
|
|
89
|
+
*/
|
|
90
|
+
function displayLog(level, message, state, indented = false) {
|
|
91
|
+
const { outputConfig, symbols } = state;
|
|
92
|
+
// In plain mode (no unicode), use simple console output
|
|
93
|
+
if (!outputConfig.unicode) {
|
|
94
|
+
const prefix = indented ? `${symbols.groupLine} ` : '';
|
|
95
|
+
const levelPrefix = {
|
|
96
|
+
info: symbols.info,
|
|
97
|
+
warn: symbols.warning,
|
|
98
|
+
error: symbols.error,
|
|
99
|
+
success: symbols.success,
|
|
100
|
+
step: symbols.step,
|
|
101
|
+
}[level];
|
|
102
|
+
writeLine(`${prefix}${levelPrefix} ${message}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Unicode mode - use clack's decorative output
|
|
106
|
+
const prefix = indented ? '\u2502 ' : ''; // │ for indented logs
|
|
107
|
+
const formattedMessage = prefix + message;
|
|
108
|
+
switch (level) {
|
|
109
|
+
case 'info':
|
|
110
|
+
p.log.info(formattedMessage);
|
|
111
|
+
break;
|
|
112
|
+
case 'warn':
|
|
113
|
+
p.log.warn(formattedMessage);
|
|
114
|
+
break;
|
|
115
|
+
case 'error':
|
|
116
|
+
p.log.error(formattedMessage);
|
|
117
|
+
break;
|
|
118
|
+
case 'success':
|
|
119
|
+
p.log.success(formattedMessage);
|
|
120
|
+
break;
|
|
121
|
+
case 'step':
|
|
122
|
+
p.log.step(formattedMessage);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Flush buffered logs for a specific activity.
|
|
128
|
+
*
|
|
129
|
+
* @param state - The adapter state
|
|
130
|
+
* @param activityId - The activity ID whose logs should be flushed
|
|
131
|
+
*/
|
|
132
|
+
function flushLogsForActivity(state, activityId) {
|
|
133
|
+
// Filter logs for this activity, sorted by timestamp
|
|
134
|
+
const activityLogs = state.bufferedLogs
|
|
135
|
+
.filter((log) => log.activityId === activityId)
|
|
136
|
+
.sort((a, b) => a.timestamp - b.timestamp);
|
|
137
|
+
// Display each buffered log with indentation
|
|
138
|
+
for (const log of activityLogs) {
|
|
139
|
+
displayLog(log.level, log.message, state, true);
|
|
140
|
+
}
|
|
141
|
+
// Remove flushed logs from buffer
|
|
142
|
+
state.bufferedLogs = state.bufferedLogs.filter((log) => log.activityId !== activityId);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Create a Clack-based ReporterAdapter
|
|
146
|
+
*
|
|
147
|
+
* @param options - Optional configuration for the adapter
|
|
148
|
+
*/
|
|
149
|
+
export function createReporterAdapter(options) {
|
|
150
|
+
// Get output config from options, or use defaults
|
|
151
|
+
const outputConfig = options?.output ?? {
|
|
152
|
+
color: true,
|
|
153
|
+
unicode: true,
|
|
154
|
+
verbose: options?.verbose ?? false,
|
|
155
|
+
};
|
|
156
|
+
// Verbose can be set via options.verbose for backwards compatibility
|
|
157
|
+
if (options?.verbose !== undefined) {
|
|
158
|
+
outputConfig.verbose = options.verbose;
|
|
159
|
+
}
|
|
160
|
+
const symbols = getSymbols(outputConfig);
|
|
161
|
+
return {
|
|
162
|
+
start(bus) {
|
|
163
|
+
const state = {
|
|
164
|
+
spinners: new Map(),
|
|
165
|
+
groups: new Map(),
|
|
166
|
+
parallelActivities: new Map(),
|
|
167
|
+
parallelSpinner: null,
|
|
168
|
+
parallelSpinnerGroupId: null,
|
|
169
|
+
suspended: false,
|
|
170
|
+
suspendedActivities: new Map(),
|
|
171
|
+
bufferedLogs: [],
|
|
172
|
+
verbose: outputConfig.verbose,
|
|
173
|
+
outputConfig,
|
|
174
|
+
symbols,
|
|
175
|
+
};
|
|
176
|
+
const handleEvent = (event) => {
|
|
177
|
+
switch (event.type) {
|
|
178
|
+
// Root lifecycle (app-level intro/outro)
|
|
179
|
+
case 'root:start':
|
|
180
|
+
case 'root:end':
|
|
181
|
+
// Handled by the router
|
|
182
|
+
break;
|
|
183
|
+
// Group lifecycle (command-level intro/outro)
|
|
184
|
+
case 'group:start': {
|
|
185
|
+
if (state.suspended)
|
|
186
|
+
break;
|
|
187
|
+
state.groups.set(event.id, {
|
|
188
|
+
label: event.label,
|
|
189
|
+
layout: event.layout,
|
|
190
|
+
hasFailure: false,
|
|
191
|
+
});
|
|
192
|
+
// In plain mode, use simple bracket notation
|
|
193
|
+
if (!state.outputConfig.unicode) {
|
|
194
|
+
const label = colorize(event.label, pc.bold, state.outputConfig.color);
|
|
195
|
+
writeLine(`${state.symbols.groupStart}${label}${state.symbols.groupEnd}`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
p.intro(colorize(event.label, pc.bold, state.outputConfig.color));
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case 'group:end': {
|
|
203
|
+
if (state.suspended)
|
|
204
|
+
break;
|
|
205
|
+
const group = state.groups.get(event.id);
|
|
206
|
+
state.groups.delete(event.id);
|
|
207
|
+
let hasFailures = group?.hasFailure ?? false;
|
|
208
|
+
const deferredErrors = [];
|
|
209
|
+
// If this was a parallel group, show completion results and clean up
|
|
210
|
+
if (group?.layout === 'parallel') {
|
|
211
|
+
// Collect activities for this group
|
|
212
|
+
const activities = Array.from(state.parallelActivities.entries()).filter(([, a]) => a.groupId === event.id);
|
|
213
|
+
// Sort: successes first, then failures (so failures are more visible at end)
|
|
214
|
+
const sorted = activities.sort(([, a], [, b]) => {
|
|
215
|
+
if (a.status === 'success' && b.status !== 'success')
|
|
216
|
+
return -1;
|
|
217
|
+
if (a.status !== 'success' && b.status === 'success')
|
|
218
|
+
return 1;
|
|
219
|
+
return 0;
|
|
220
|
+
});
|
|
221
|
+
// Stop the parallel spinner - use first success as the message
|
|
222
|
+
if (state.parallelSpinner && state.parallelSpinnerGroupId === event.id) {
|
|
223
|
+
const firstSuccess = sorted.find(([, a]) => a.status === 'success');
|
|
224
|
+
if (firstSuccess) {
|
|
225
|
+
// Show first success via spinner stop
|
|
226
|
+
state.parallelSpinner.spinner.stop(firstSuccess[1].label, 0);
|
|
227
|
+
// Flush buffered logs for this activity
|
|
228
|
+
flushLogsForActivity(state, firstSuccess[0]);
|
|
229
|
+
state.parallelActivities.delete(firstSuccess[0]);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// All failed - show first failure label (not error) via spinner stop
|
|
233
|
+
const firstFailure = sorted[0];
|
|
234
|
+
if (firstFailure) {
|
|
235
|
+
state.parallelSpinner.spinner.stop(firstFailure[1].label, 1);
|
|
236
|
+
hasFailures = true;
|
|
237
|
+
// Defer the error message with remediation to print after outro
|
|
238
|
+
if (firstFailure[1].error) {
|
|
239
|
+
deferredErrors.push({
|
|
240
|
+
error: firstFailure[1].error,
|
|
241
|
+
remediation: firstFailure[1].remediation,
|
|
242
|
+
documentationUrl: firstFailure[1].documentationUrl,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// Flush buffered logs for this activity
|
|
246
|
+
flushLogsForActivity(state, firstFailure[0]);
|
|
247
|
+
state.parallelActivities.delete(firstFailure[0]);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
state.parallelSpinner.spinner.stop('Complete', 0);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
state.parallelSpinner = null;
|
|
254
|
+
state.parallelSpinnerGroupId = null;
|
|
255
|
+
}
|
|
256
|
+
// Show remaining results (only labels inside group, defer errors)
|
|
257
|
+
for (const [activityId, activity] of state.parallelActivities) {
|
|
258
|
+
if (activity.groupId !== event.id)
|
|
259
|
+
continue;
|
|
260
|
+
if (activity.status === 'success') {
|
|
261
|
+
if (!state.outputConfig.unicode) {
|
|
262
|
+
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
263
|
+
writeLine(` ${prefix} ${activity.label}`);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
p.log.success(activity.label);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else if (activity.status === 'failure') {
|
|
270
|
+
hasFailures = true;
|
|
271
|
+
// Show label inside group, defer error message with remediation
|
|
272
|
+
if (!state.outputConfig.unicode) {
|
|
273
|
+
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
274
|
+
writeLine(` ${prefix} ${activity.label}`);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
p.log.error(activity.label);
|
|
278
|
+
}
|
|
279
|
+
if (activity.error) {
|
|
280
|
+
deferredErrors.push({
|
|
281
|
+
error: activity.error,
|
|
282
|
+
remediation: activity.remediation,
|
|
283
|
+
documentationUrl: activity.documentationUrl,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// Flush any buffered logs for this parallel activity
|
|
288
|
+
flushLogsForActivity(state, activityId);
|
|
289
|
+
}
|
|
290
|
+
// Clean up all activities for this group
|
|
291
|
+
for (const [id, activity] of [...state.parallelActivities]) {
|
|
292
|
+
if (activity.groupId === event.id) {
|
|
293
|
+
state.parallelActivities.delete(id);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Show appropriate outro based on whether there were failures
|
|
298
|
+
if (!state.outputConfig.unicode) {
|
|
299
|
+
// Plain mode - simple bracket notation
|
|
300
|
+
if (hasFailures) {
|
|
301
|
+
const failedText = colorize(state.symbols.failed, pc.red, state.outputConfig.color);
|
|
302
|
+
writeLine(`${state.symbols.groupStart}${failedText}${state.symbols.groupEnd}`);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
const doneText = colorize(state.symbols.done, pc.green, state.outputConfig.color);
|
|
306
|
+
writeLine(`${state.symbols.groupStart}${doneText}${state.symbols.groupEnd}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// Unicode mode - use clack's outro
|
|
311
|
+
if (hasFailures) {
|
|
312
|
+
p.outro(colorize(`${state.symbols.failed} Failed`, pc.red, state.outputConfig.color));
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
p.outro(colorize(`${state.symbols.done} Done`, pc.green, state.outputConfig.color));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Print deferred error messages with remediation after the group closes
|
|
319
|
+
for (const deferred of deferredErrors) {
|
|
320
|
+
if (!state.outputConfig.unicode) {
|
|
321
|
+
writeLine(`${state.symbols.error} ${deferred.error}`);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
p.log.error(deferred.error);
|
|
325
|
+
}
|
|
326
|
+
// Display remediation steps if available
|
|
327
|
+
if (deferred.remediation && deferred.remediation.length > 0) {
|
|
328
|
+
writeLine('');
|
|
329
|
+
writeLine(' To fix:');
|
|
330
|
+
for (const step of deferred.remediation) {
|
|
331
|
+
writeLine(` - ${step}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Display documentation link if available
|
|
335
|
+
if (deferred.documentationUrl) {
|
|
336
|
+
writeLine('');
|
|
337
|
+
writeLine(` More info: ${deferred.documentationUrl}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
// Activity lifecycle (task-level spinner)
|
|
343
|
+
case 'activity:start': {
|
|
344
|
+
if (state.suspended)
|
|
345
|
+
break;
|
|
346
|
+
// Find the parent group to determine layout
|
|
347
|
+
const parentGroup = event.parentId ? state.groups.get(event.parentId) : null;
|
|
348
|
+
if (parentGroup?.layout === 'parallel') {
|
|
349
|
+
// Track this activity for the parallel group
|
|
350
|
+
state.parallelActivities.set(event.id, {
|
|
351
|
+
label: event.label,
|
|
352
|
+
groupId: event.parentId,
|
|
353
|
+
status: 'pending',
|
|
354
|
+
});
|
|
355
|
+
// In plain mode, just track activities - results shown at group:end
|
|
356
|
+
if (!state.outputConfig.unicode) {
|
|
357
|
+
if (!state.parallelSpinnerGroupId) {
|
|
358
|
+
state.parallelSpinnerGroupId = event.parentId;
|
|
359
|
+
const activities = state.parallelActivities.size;
|
|
360
|
+
writeLine(` Running ${activities} task${activities > 1 ? 's' : ''}...`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
// Unicode mode: create or update the parallel spinner
|
|
365
|
+
if (!state.parallelSpinner) {
|
|
366
|
+
const spinner = p.spinner();
|
|
367
|
+
state.parallelSpinner = {
|
|
368
|
+
spinner,
|
|
369
|
+
label: event.label,
|
|
370
|
+
currentMessage: event.label,
|
|
371
|
+
};
|
|
372
|
+
state.parallelSpinnerGroupId = event.parentId;
|
|
373
|
+
spinner.start(event.label);
|
|
374
|
+
}
|
|
375
|
+
updateParallelSpinnerMessage(state);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Sequential activity
|
|
380
|
+
if (!state.outputConfig.unicode) {
|
|
381
|
+
// Plain mode: track activity without spinner - result shown on completion
|
|
382
|
+
state.spinners.set(event.id, {
|
|
383
|
+
spinner: null, // Not used in plain mode
|
|
384
|
+
label: event.label,
|
|
385
|
+
currentMessage: event.label,
|
|
386
|
+
parentGroupId: event.parentId,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// Unicode mode: create individual spinner
|
|
391
|
+
const spinner = p.spinner();
|
|
392
|
+
state.spinners.set(event.id, {
|
|
393
|
+
spinner,
|
|
394
|
+
label: event.label,
|
|
395
|
+
currentMessage: event.label,
|
|
396
|
+
parentGroupId: event.parentId,
|
|
397
|
+
});
|
|
398
|
+
spinner.start(event.label);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
case 'activity:update': {
|
|
404
|
+
if (state.suspended)
|
|
405
|
+
break;
|
|
406
|
+
// Check if this is a parallel activity
|
|
407
|
+
const parallelActivity = state.parallelActivities.get(event.id);
|
|
408
|
+
if (parallelActivity) {
|
|
409
|
+
// Update the activity label if message provided
|
|
410
|
+
if (event.payload.message) {
|
|
411
|
+
parallelActivity.label = event.payload.message;
|
|
412
|
+
if (state.outputConfig.unicode) {
|
|
413
|
+
updateParallelSpinnerMessage(state);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
// Sequential activity
|
|
419
|
+
const entry = state.spinners.get(event.id);
|
|
420
|
+
if (entry) {
|
|
421
|
+
const text = event.payload.message ||
|
|
422
|
+
(event.payload.progress !== undefined ? `${event.payload.progress}%` : null);
|
|
423
|
+
if (text) {
|
|
424
|
+
entry.currentMessage = text;
|
|
425
|
+
// Only update spinner in unicode mode
|
|
426
|
+
if (state.outputConfig.unicode && entry.spinner) {
|
|
427
|
+
entry.spinner.message(text);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case 'activity:success': {
|
|
434
|
+
// Check if this is a parallel activity
|
|
435
|
+
const parallelActivity = state.parallelActivities.get(event.id);
|
|
436
|
+
if (parallelActivity) {
|
|
437
|
+
parallelActivity.status = 'success';
|
|
438
|
+
if (state.outputConfig.unicode) {
|
|
439
|
+
updateParallelSpinnerMessage(state);
|
|
440
|
+
}
|
|
441
|
+
// Note: For parallel activities, logs will be flushed at group:end
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
// Sequential activity
|
|
445
|
+
const entry = state.spinners.get(event.id);
|
|
446
|
+
if (entry) {
|
|
447
|
+
if (!state.outputConfig.unicode) {
|
|
448
|
+
// Plain mode - print success line
|
|
449
|
+
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
450
|
+
writeLine(` ${prefix} ${entry.label}`);
|
|
451
|
+
}
|
|
452
|
+
else if (entry.spinner) {
|
|
453
|
+
// Unicode mode - stop spinner
|
|
454
|
+
entry.spinner.stop(entry.label, 0);
|
|
455
|
+
}
|
|
456
|
+
state.spinners.delete(event.id);
|
|
457
|
+
// Flush any buffered logs for this activity
|
|
458
|
+
flushLogsForActivity(state, event.id);
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
// Check if this was a suspended activity
|
|
462
|
+
const suspended = state.suspendedActivities.get(event.id);
|
|
463
|
+
if (suspended && !state.suspended) {
|
|
464
|
+
if (!state.outputConfig.unicode) {
|
|
465
|
+
const prefix = colorize(state.symbols.success, pc.green, state.outputConfig.color);
|
|
466
|
+
writeLine(` ${prefix} ${suspended.label}`);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
p.log.success(suspended.label);
|
|
470
|
+
}
|
|
471
|
+
state.suspendedActivities.delete(event.id);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
case 'activity:failure': {
|
|
477
|
+
const errorMessage = formatErrorMessage(event.error);
|
|
478
|
+
// Check if this is a parallel activity
|
|
479
|
+
const parallelActivity = state.parallelActivities.get(event.id);
|
|
480
|
+
if (parallelActivity) {
|
|
481
|
+
parallelActivity.status = 'failure';
|
|
482
|
+
parallelActivity.error = errorMessage;
|
|
483
|
+
parallelActivity.remediation = event.remediation;
|
|
484
|
+
parallelActivity.documentationUrl = event.documentationUrl;
|
|
485
|
+
// Mark the parent group as having failures
|
|
486
|
+
const parentGroup = state.groups.get(parallelActivity.groupId);
|
|
487
|
+
if (parentGroup) {
|
|
488
|
+
parentGroup.hasFailure = true;
|
|
489
|
+
}
|
|
490
|
+
if (state.outputConfig.unicode) {
|
|
491
|
+
updateParallelSpinnerMessage(state);
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
// Sequential activity
|
|
496
|
+
const entry = state.spinners.get(event.id);
|
|
497
|
+
if (entry) {
|
|
498
|
+
// Mark the parent group as having failures
|
|
499
|
+
if (entry.parentGroupId) {
|
|
500
|
+
const parentGroup = state.groups.get(entry.parentGroupId);
|
|
501
|
+
if (parentGroup) {
|
|
502
|
+
parentGroup.hasFailure = true;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (!state.outputConfig.unicode) {
|
|
506
|
+
// Plain mode - print error line
|
|
507
|
+
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
508
|
+
writeLine(` ${prefix} ${errorMessage}`);
|
|
509
|
+
}
|
|
510
|
+
else if (entry.spinner) {
|
|
511
|
+
// Unicode mode - stop spinner with error
|
|
512
|
+
entry.spinner.stop(errorMessage, 1);
|
|
513
|
+
}
|
|
514
|
+
state.spinners.delete(event.id);
|
|
515
|
+
// Display remediation steps if available
|
|
516
|
+
const linePrefix = state.outputConfig.unicode ? '\u2502' : state.symbols.groupLine;
|
|
517
|
+
if (event.remediation && event.remediation.length > 0) {
|
|
518
|
+
writeLine(linePrefix);
|
|
519
|
+
writeLine(`${linePrefix} To fix:`);
|
|
520
|
+
for (const step of event.remediation) {
|
|
521
|
+
writeLine(`${linePrefix} - ${step}`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// Display documentation link if available
|
|
525
|
+
if (event.documentationUrl) {
|
|
526
|
+
writeLine(linePrefix);
|
|
527
|
+
writeLine(`${linePrefix} More info: ${event.documentationUrl}`);
|
|
528
|
+
}
|
|
529
|
+
// Flush any buffered logs for this activity
|
|
530
|
+
flushLogsForActivity(state, event.id);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
// Check if this was a suspended activity
|
|
534
|
+
const suspended = state.suspendedActivities.get(event.id);
|
|
535
|
+
if (suspended && !state.suspended) {
|
|
536
|
+
if (!state.outputConfig.unicode) {
|
|
537
|
+
const prefix = colorize(state.symbols.error, pc.red, state.outputConfig.color);
|
|
538
|
+
writeLine(` ${prefix} ${suspended.label}: ${errorMessage}`);
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
p.log.error(`${suspended.label}: ${errorMessage}`);
|
|
542
|
+
}
|
|
543
|
+
state.suspendedActivities.delete(event.id);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
// Log events
|
|
549
|
+
case 'log': {
|
|
550
|
+
if (state.suspended)
|
|
551
|
+
break;
|
|
552
|
+
// Verbose mode: always display logs immediately
|
|
553
|
+
if (state.verbose) {
|
|
554
|
+
displayLog(event.level, event.message, state, false);
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
const hasActiveSpinners = state.spinners.size > 0 || state.parallelSpinner !== null;
|
|
558
|
+
// Error logs interrupt spinners immediately (only in unicode mode)
|
|
559
|
+
if (event.level === 'error' &&
|
|
560
|
+
hasActiveSpinners &&
|
|
561
|
+
event.activityId &&
|
|
562
|
+
state.outputConfig.unicode) {
|
|
563
|
+
const spinner = state.spinners.get(event.activityId);
|
|
564
|
+
if (spinner && spinner.spinner) {
|
|
565
|
+
// Temporarily stop spinner, show error, resume
|
|
566
|
+
const currentMessage = spinner.currentMessage;
|
|
567
|
+
spinner.spinner.stop(currentMessage, 0);
|
|
568
|
+
displayLog(event.level, event.message, state, false);
|
|
569
|
+
spinner.spinner.start(currentMessage);
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
// No spinner for this activity, just display
|
|
573
|
+
displayLog(event.level, event.message, state, false);
|
|
574
|
+
}
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
// Buffer logs during active spinners
|
|
578
|
+
if (hasActiveSpinners && event.activityId) {
|
|
579
|
+
// Check if we've hit the buffer limit for this activity
|
|
580
|
+
const activityLogCount = state.bufferedLogs.filter((log) => log.activityId === event.activityId).length;
|
|
581
|
+
if (activityLogCount < MAX_BUFFERED_LOGS_PER_ACTIVITY) {
|
|
582
|
+
state.bufferedLogs.push({
|
|
583
|
+
activityId: event.activityId,
|
|
584
|
+
level: event.level,
|
|
585
|
+
message: event.message,
|
|
586
|
+
timestamp: Date.now(),
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
// If over limit, silently drop (prevent memory issues)
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
// No active spinners - display immediately
|
|
593
|
+
displayLog(event.level, event.message, state, false);
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
// Reporter control events
|
|
597
|
+
case 'reporter:suspend': {
|
|
598
|
+
state.suspended = true;
|
|
599
|
+
// Stop all active spinners and track them for completion messages (only in unicode mode)
|
|
600
|
+
if (state.outputConfig.unicode) {
|
|
601
|
+
for (const [id, entry] of state.spinners) {
|
|
602
|
+
try {
|
|
603
|
+
if (entry.spinner) {
|
|
604
|
+
entry.spinner.stop(entry.label + '...', 0);
|
|
605
|
+
}
|
|
606
|
+
state.suspendedActivities.set(id, { label: entry.label });
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
// Spinner may already be stopped
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
state.spinners.clear();
|
|
613
|
+
// Also stop parallel spinner
|
|
614
|
+
if (state.parallelSpinner) {
|
|
615
|
+
try {
|
|
616
|
+
state.parallelSpinner.spinner.stop('Paused...', 0);
|
|
617
|
+
}
|
|
618
|
+
catch {
|
|
619
|
+
// Spinner may already be stopped
|
|
620
|
+
}
|
|
621
|
+
state.parallelSpinner = null;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
case 'reporter:resume': {
|
|
627
|
+
state.suspended = false;
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
const unsubscribe = bus.on(handleEvent);
|
|
633
|
+
return {
|
|
634
|
+
stop() {
|
|
635
|
+
// Stop all active spinners (only in unicode mode where spinners exist)
|
|
636
|
+
if (state.outputConfig.unicode) {
|
|
637
|
+
for (const entry of state.spinners.values()) {
|
|
638
|
+
try {
|
|
639
|
+
if (entry.spinner) {
|
|
640
|
+
entry.spinner.stop('Stopped', 1);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
// Spinner may already be stopped
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (state.parallelSpinner) {
|
|
648
|
+
try {
|
|
649
|
+
state.parallelSpinner.spinner.stop('Stopped', 1);
|
|
650
|
+
}
|
|
651
|
+
catch {
|
|
652
|
+
// Spinner may already be stopped
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
state.spinners.clear();
|
|
657
|
+
state.groups.clear();
|
|
658
|
+
state.parallelActivities.clear();
|
|
659
|
+
state.parallelSpinner = null;
|
|
660
|
+
state.parallelSpinnerGroupId = null;
|
|
661
|
+
unsubscribe();
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pokit/reporter-clack
|
|
3
|
+
*
|
|
4
|
+
* Clack-based implementation of the ReporterAdapter interface.
|
|
5
|
+
* Consumes CLI events and renders them using @clack/prompts.
|
|
6
|
+
*/
|
|
7
|
+
export { createReporterAdapter } from './adapter';
|
|
8
|
+
export type { ReporterAdapterOptions } from './adapter';
|
|
9
|
+
export { getSymbols, UNICODE_SYMBOLS, ASCII_SYMBOLS } from './symbols';
|
|
10
|
+
export type { SymbolSet } from './symbols';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,YAAY,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAGxD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pokit/reporter-clack
|
|
3
|
+
*
|
|
4
|
+
* Clack-based implementation of the ReporterAdapter interface.
|
|
5
|
+
* Consumes CLI events and renders them using @clack/prompts.
|
|
6
|
+
*/
|
|
7
|
+
export { createReporterAdapter } from './adapter';
|
|
8
|
+
// Symbol exports for custom formatting
|
|
9
|
+
export { getSymbols, UNICODE_SYMBOLS, ASCII_SYMBOLS } from './symbols';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol Sets for CLI Output
|
|
3
|
+
*
|
|
4
|
+
* Provides Unicode and ASCII symbol sets for terminal output.
|
|
5
|
+
* The appropriate set is selected based on OutputConfig.
|
|
6
|
+
*/
|
|
7
|
+
import type { OutputConfig } from '@pokit/core';
|
|
8
|
+
/**
|
|
9
|
+
* Complete set of symbols used in CLI output
|
|
10
|
+
*/
|
|
11
|
+
export type SymbolSet = {
|
|
12
|
+
/** Success indicator (e.g., checkmark) */
|
|
13
|
+
success: string;
|
|
14
|
+
/** Error indicator (e.g., X mark) */
|
|
15
|
+
error: string;
|
|
16
|
+
/** Warning indicator */
|
|
17
|
+
warning: string;
|
|
18
|
+
/** Info indicator */
|
|
19
|
+
info: string;
|
|
20
|
+
/** Step/progress indicator */
|
|
21
|
+
step: string;
|
|
22
|
+
/** Group start (e.g., top-left corner) */
|
|
23
|
+
groupStart: string;
|
|
24
|
+
/** Group end (e.g., bottom-left corner) */
|
|
25
|
+
groupEnd: string;
|
|
26
|
+
/** Group line (e.g., vertical bar) */
|
|
27
|
+
groupLine: string;
|
|
28
|
+
/** Done message */
|
|
29
|
+
done: string;
|
|
30
|
+
/** Failed message */
|
|
31
|
+
failed: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Unicode symbols for rich terminal output
|
|
35
|
+
* Used when unicode support is detected
|
|
36
|
+
*/
|
|
37
|
+
export declare const UNICODE_SYMBOLS: SymbolSet;
|
|
38
|
+
/**
|
|
39
|
+
* ASCII symbols for plain text output
|
|
40
|
+
* Used in CI environments or when unicode is not supported
|
|
41
|
+
*/
|
|
42
|
+
export declare const ASCII_SYMBOLS: SymbolSet;
|
|
43
|
+
/**
|
|
44
|
+
* Get the appropriate symbol set based on output configuration
|
|
45
|
+
*
|
|
46
|
+
* @param config - Output configuration
|
|
47
|
+
* @returns Symbol set to use
|
|
48
|
+
*/
|
|
49
|
+
export declare function getSymbols(config: OutputConfig): SymbolSet;
|
|
50
|
+
//# sourceMappingURL=symbols.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../src/symbols.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,SAW7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAW3B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CAE1D"}
|
package/dist/symbols.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol Sets for CLI Output
|
|
3
|
+
*
|
|
4
|
+
* Provides Unicode and ASCII symbol sets for terminal output.
|
|
5
|
+
* The appropriate set is selected based on OutputConfig.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Unicode symbols for rich terminal output
|
|
9
|
+
* Used when unicode support is detected
|
|
10
|
+
*/
|
|
11
|
+
export const UNICODE_SYMBOLS = {
|
|
12
|
+
success: '\u25C7', // ◇
|
|
13
|
+
error: '\u25A0', // ■
|
|
14
|
+
warning: '\u25B2', // ▲
|
|
15
|
+
info: '\u25CF', // ●
|
|
16
|
+
step: '\u25C7', // ◇
|
|
17
|
+
groupStart: '\u250C', // ┌
|
|
18
|
+
groupEnd: '\u2514', // └
|
|
19
|
+
groupLine: '\u2502', // │
|
|
20
|
+
done: '\u2714', // ✔
|
|
21
|
+
failed: '\u2718', // ✘
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* ASCII symbols for plain text output
|
|
25
|
+
* Used in CI environments or when unicode is not supported
|
|
26
|
+
*/
|
|
27
|
+
export const ASCII_SYMBOLS = {
|
|
28
|
+
success: '[OK]',
|
|
29
|
+
error: '[ERR]',
|
|
30
|
+
warning: '[WARN]',
|
|
31
|
+
info: '[INFO]',
|
|
32
|
+
step: '-',
|
|
33
|
+
groupStart: '[',
|
|
34
|
+
groupEnd: ']',
|
|
35
|
+
groupLine: '|',
|
|
36
|
+
done: 'Done',
|
|
37
|
+
failed: 'Failed',
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Get the appropriate symbol set based on output configuration
|
|
41
|
+
*
|
|
42
|
+
* @param config - Output configuration
|
|
43
|
+
* @returns Symbol set to use
|
|
44
|
+
*/
|
|
45
|
+
export function getSymbols(config) {
|
|
46
|
+
return config.unicode ? UNICODE_SYMBOLS : ASCII_SYMBOLS;
|
|
47
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pokit/reporter-clack",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Clack-based event reporter for pok CLI applications",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"command-line",
|
|
8
|
+
"typescript",
|
|
9
|
+
"bun",
|
|
10
|
+
"terminal",
|
|
11
|
+
"pok",
|
|
12
|
+
"pokjs",
|
|
13
|
+
"clack",
|
|
14
|
+
"reporter",
|
|
15
|
+
"spinner",
|
|
16
|
+
"progress"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/notation-dev/openpok.git",
|
|
23
|
+
"directory": "packages/reporter-clack"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/notation-dev/openpok#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/notation-dev/openpok/issues"
|
|
28
|
+
},
|
|
29
|
+
"main": "./src/index.ts",
|
|
30
|
+
"module": "./src/index.ts",
|
|
31
|
+
"types": "./src/index.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"bun": "./src/index.ts",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@clack/prompts": "^0.11.0",
|
|
49
|
+
"picocolors": "^1.1.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/bun": "latest",
|
|
53
|
+
"@pokit/core": "0.0.1"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@pokit/core": "0.0.1"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"bun": ">=1.0.0"
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pokit/reporter-clack
|
|
3
|
+
*
|
|
4
|
+
* Clack-based implementation of the ReporterAdapter interface.
|
|
5
|
+
* Consumes CLI events and renders them using @clack/prompts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { createReporterAdapter } from './adapter';
|
|
9
|
+
export type { ReporterAdapterOptions } from './adapter';
|
|
10
|
+
|
|
11
|
+
// Symbol exports for custom formatting
|
|
12
|
+
export { getSymbols, UNICODE_SYMBOLS, ASCII_SYMBOLS } from './symbols';
|
|
13
|
+
export type { SymbolSet } from './symbols';
|