@portel/cli 1.0.0

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.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CLI Output Formatter
3
+ *
4
+ * Shared formatting utilities for beautiful CLI output.
5
+ * Used by photon CLI, lumina, ncp, and other projects.
6
+ *
7
+ * Structural formats:
8
+ * - primitive: Single values (string, number, boolean)
9
+ * - table: Flat objects or arrays of flat objects (bordered tables)
10
+ * - tree: Nested/hierarchical structures (indented)
11
+ * - list: Arrays of primitives (bullet points)
12
+ * - none: No data to display
13
+ *
14
+ * Content formats:
15
+ * - json: Pretty-printed JSON
16
+ * - markdown: Rendered markdown
17
+ * - yaml: YAML content
18
+ * - code / code:<lang>: Syntax highlighted code
19
+ */
20
+ import type { OutputFormat } from './types.js';
21
+ /**
22
+ * Format and output data with optional format hint
23
+ */
24
+ export declare function formatOutput(data: any, hint?: OutputFormat): void;
25
+ /**
26
+ * Detect format type from data structure
27
+ */
28
+ export declare function detectFormat(data: any): OutputFormat;
29
+ export declare function renderPrimitive(value: any): void;
30
+ export declare function renderList(data: any[]): void;
31
+ export declare function renderTable(data: any): void;
32
+ export declare function renderTree(data: any, indent?: string): void;
33
+ /**
34
+ * Render data as a bordered card
35
+ * Great for single objects with mixed content types
36
+ */
37
+ export declare function renderCard(data: any): void;
38
+ /**
39
+ * Render data as tabbed sections
40
+ * Each top-level key becomes a tab
41
+ */
42
+ export declare function renderTabs(data: any): void;
43
+ /**
44
+ * Render data as collapsible accordion sections
45
+ * Similar to tabs but with visual collapse indicators
46
+ */
47
+ export declare function renderAccordion(data: any): void;
48
+ export declare function renderNone(): void;
49
+ export declare function formatKey(key: string): string;
50
+ export declare function formatValue(value: any): string | number | boolean;
51
+ export declare const STATUS: {
52
+ readonly OK: "ok";
53
+ readonly UPDATE: "update";
54
+ readonly WARN: "warn";
55
+ readonly ERROR: "!";
56
+ readonly OFF: "off";
57
+ readonly UNKNOWN: "?";
58
+ };
59
+ export declare function formatToMimeType(format: OutputFormat): string | undefined;
60
+ export declare function printSuccess(message: string): void;
61
+ export declare function printError(message: string): void;
62
+ export declare function printInfo(message: string): void;
63
+ export declare function printWarning(message: string): void;
64
+ export declare function printHeader(title: string): void;
65
+ //# sourceMappingURL=cli-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-formatter.d.ts","sourceRoot":"","sources":["../src/cli-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CASjE;AAyJD;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,GAAG,YAAY,CAuBpD;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI,CAMhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAgB5C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CA2E3C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAwC/D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAU1C;AA8ED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAuC1C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAsD/C;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAIjE;AAED,eAAO,MAAM,MAAM;;;;;;;CAOT,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAazE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG/C"}
@@ -0,0 +1,585 @@
1
+ /**
2
+ * CLI Output Formatter
3
+ *
4
+ * Shared formatting utilities for beautiful CLI output.
5
+ * Used by photon CLI, lumina, ncp, and other projects.
6
+ *
7
+ * Structural formats:
8
+ * - primitive: Single values (string, number, boolean)
9
+ * - table: Flat objects or arrays of flat objects (bordered tables)
10
+ * - tree: Nested/hierarchical structures (indented)
11
+ * - list: Arrays of primitives (bullet points)
12
+ * - none: No data to display
13
+ *
14
+ * Content formats:
15
+ * - json: Pretty-printed JSON
16
+ * - markdown: Rendered markdown
17
+ * - yaml: YAML content
18
+ * - code / code:<lang>: Syntax highlighted code
19
+ */
20
+ import { highlight } from 'cli-highlight';
21
+ import chalk from 'chalk';
22
+ /**
23
+ * Format and output data with optional format hint
24
+ */
25
+ export function formatOutput(data, hint) {
26
+ const format = hint || detectFormat(data);
27
+ if (typeof data === 'string' && isContentFormat(format)) {
28
+ renderContent(data, format);
29
+ return;
30
+ }
31
+ formatDataWithHint(data, format);
32
+ }
33
+ function isContentFormat(format) {
34
+ return ['json', 'markdown', 'yaml', 'xml', 'html'].includes(format) ||
35
+ format === 'code' ||
36
+ format.startsWith('code:');
37
+ }
38
+ function renderContent(content, format) {
39
+ switch (format) {
40
+ case 'json':
41
+ renderJson(content);
42
+ break;
43
+ case 'markdown':
44
+ renderMarkdown(content);
45
+ break;
46
+ case 'yaml':
47
+ renderYaml(content);
48
+ break;
49
+ case 'xml':
50
+ case 'html':
51
+ renderXml(content);
52
+ break;
53
+ default:
54
+ if (format === 'code' || format.startsWith('code:')) {
55
+ const lang = format === 'code' ? undefined : format.split(':')[1];
56
+ renderCode(content, lang);
57
+ }
58
+ else {
59
+ console.log(content);
60
+ }
61
+ }
62
+ }
63
+ function renderJson(content) {
64
+ try {
65
+ const parsed = typeof content === 'string' ? JSON.parse(content) : content;
66
+ const formatted = JSON.stringify(parsed, null, 2);
67
+ console.log(highlight(formatted, { language: 'json', ignoreIllegals: true }));
68
+ }
69
+ catch {
70
+ console.log(content);
71
+ }
72
+ }
73
+ /**
74
+ * Render markdown with colored terminal output
75
+ */
76
+ function renderMarkdown(content) {
77
+ let rendered = content;
78
+ // Code blocks
79
+ rendered = rendered.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
80
+ const trimmedCode = code.trim();
81
+ if (lang && lang !== '') {
82
+ try {
83
+ return '\n' + highlight(trimmedCode, { language: lang, ignoreIllegals: true }) + '\n';
84
+ }
85
+ catch {
86
+ return '\n' + chalk.gray(trimmedCode) + '\n';
87
+ }
88
+ }
89
+ return '\n' + chalk.gray(trimmedCode) + '\n';
90
+ });
91
+ // Links
92
+ rendered = rendered.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, text, url) => chalk.blue.underline(text) + chalk.dim(` (${url})`));
93
+ // Headers
94
+ rendered = rendered
95
+ .replace(/^### (.+)$/gm, (_m, h) => '\n' + chalk.cyan(' ' + h) + '\n ' + chalk.dim('-'.repeat(20)))
96
+ .replace(/^## (.+)$/gm, (_m, h) => '\n' + chalk.yellow.bold(' ' + h) + '\n ' + chalk.dim('='.repeat(30)))
97
+ .replace(/^# (.+)$/gm, (_m, h) => '\n' + chalk.magenta.bold(h) + '\n' + chalk.dim('='.repeat(40)));
98
+ // Blockquotes
99
+ rendered = rendered.replace(/^> (.+)$/gm, (_m, quote) => chalk.dim('│ ') + chalk.italic(quote));
100
+ // Horizontal rules
101
+ rendered = rendered.replace(/^---+$/gm, chalk.dim('─'.repeat(40)));
102
+ // Lists
103
+ rendered = rendered.replace(/^- /gm, chalk.dim(' • '));
104
+ rendered = rendered.replace(/^(\d+)\. /gm, (_m, num) => chalk.dim(` ${num}. `));
105
+ // Bold and italic
106
+ rendered = rendered.replace(/\*\*(.+?)\*\*/g, (_m, text) => chalk.bold(text));
107
+ rendered = rendered.replace(/\*(.+?)\*/g, (_m, text) => chalk.italic(text));
108
+ rendered = rendered.replace(/_(.+?)_/g, (_m, text) => chalk.italic(text));
109
+ // Inline code
110
+ rendered = rendered.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
111
+ console.log(rendered);
112
+ }
113
+ function renderYaml(content) {
114
+ try {
115
+ console.log(highlight(content, { language: 'yaml', ignoreIllegals: true }));
116
+ }
117
+ catch {
118
+ console.log(content);
119
+ }
120
+ }
121
+ function renderXml(content) {
122
+ try {
123
+ console.log(highlight(content, { language: 'xml', ignoreIllegals: true }));
124
+ }
125
+ catch {
126
+ console.log(content);
127
+ }
128
+ }
129
+ function renderCode(content, lang) {
130
+ try {
131
+ if (lang) {
132
+ console.log(highlight(content, { language: lang, ignoreIllegals: true }));
133
+ }
134
+ else {
135
+ console.log(highlight(content, { ignoreIllegals: true }));
136
+ }
137
+ }
138
+ catch {
139
+ console.log(content);
140
+ }
141
+ }
142
+ function formatDataWithHint(data, format) {
143
+ switch (format) {
144
+ case 'primitive':
145
+ renderPrimitive(data);
146
+ break;
147
+ case 'list':
148
+ renderList(Array.isArray(data) ? data : [data]);
149
+ break;
150
+ case 'table':
151
+ renderTable(data);
152
+ break;
153
+ case 'tree':
154
+ renderTree(data);
155
+ break;
156
+ case 'card':
157
+ renderCard(data);
158
+ break;
159
+ case 'tabs':
160
+ renderTabs(data);
161
+ break;
162
+ case 'accordion':
163
+ renderAccordion(data);
164
+ break;
165
+ case 'none':
166
+ renderNone();
167
+ break;
168
+ }
169
+ }
170
+ /**
171
+ * Detect format type from data structure
172
+ */
173
+ export function detectFormat(data) {
174
+ if (data === null || data === undefined) {
175
+ return 'none';
176
+ }
177
+ if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean') {
178
+ return 'primitive';
179
+ }
180
+ if (Array.isArray(data)) {
181
+ if (data.length === 0)
182
+ return 'list';
183
+ const firstItem = data[0];
184
+ if (typeof firstItem !== 'object' || firstItem === null)
185
+ return 'list';
186
+ if (isFlatObject(firstItem))
187
+ return 'table';
188
+ return 'tree';
189
+ }
190
+ if (typeof data === 'object') {
191
+ if (isFlatObject(data))
192
+ return 'table';
193
+ return 'tree';
194
+ }
195
+ return 'none';
196
+ }
197
+ function isFlatObject(obj) {
198
+ if (typeof obj !== 'object' || obj === null)
199
+ return false;
200
+ for (const value of Object.values(obj)) {
201
+ if (typeof value === 'object' && value !== null)
202
+ return false;
203
+ }
204
+ return true;
205
+ }
206
+ export function renderPrimitive(value) {
207
+ if (typeof value === 'boolean') {
208
+ console.log(value ? 'yes' : 'no');
209
+ }
210
+ else {
211
+ console.log(value);
212
+ }
213
+ }
214
+ export function renderList(data) {
215
+ if (data.length === 0) {
216
+ console.log('(empty)');
217
+ return;
218
+ }
219
+ // Check if items are objects - if so, render as table instead
220
+ const firstItem = data[0];
221
+ if (typeof firstItem === 'object' && firstItem !== null && !Array.isArray(firstItem)) {
222
+ // For arrays of objects, delegate to table rendering for better display
223
+ renderTable(data);
224
+ return;
225
+ }
226
+ // Simple list for primitives
227
+ data.forEach(item => console.log(` * ${item}`));
228
+ }
229
+ export function renderTable(data) {
230
+ if (!Array.isArray(data)) {
231
+ const entries = Object.entries(data).filter(([key, value]) => !(key === 'returnValue' && value === true));
232
+ if (entries.length === 0) {
233
+ console.log('(empty)');
234
+ return;
235
+ }
236
+ const maxKeyLength = Math.max(...entries.map(([k]) => formatKey(k).length));
237
+ const maxValueLength = Math.max(...entries.map(([_, v]) => String(formatValue(v)).length));
238
+ console.log(`┌─${'─'.repeat(maxKeyLength)}─┬─${'─'.repeat(maxValueLength)}─┐`);
239
+ for (let i = 0; i < entries.length; i++) {
240
+ const [key, value] = entries[i];
241
+ const formattedKey = formatKey(key);
242
+ const formattedValue = String(formatValue(value));
243
+ const keyPadding = ' '.repeat(maxKeyLength - formattedKey.length);
244
+ const valuePadding = ' '.repeat(maxValueLength - formattedValue.length);
245
+ console.log(`| ${formattedKey}${keyPadding} | ${formattedValue}${valuePadding} |`);
246
+ if (i < entries.length - 1) {
247
+ console.log(`├─${'─'.repeat(maxKeyLength)}─┼─${'─'.repeat(maxValueLength)}─┤`);
248
+ }
249
+ }
250
+ console.log(`└─${'─'.repeat(maxKeyLength)}─┴─${'─'.repeat(maxValueLength)}─┘`);
251
+ return;
252
+ }
253
+ if (data.length === 0) {
254
+ console.log('(empty)');
255
+ return;
256
+ }
257
+ const allKeys = Array.from(new Set(data.flatMap(obj => Object.keys(obj)))).filter(k => k !== 'returnValue');
258
+ if (allKeys.length === 0) {
259
+ console.log('(no data)');
260
+ return;
261
+ }
262
+ const columnWidths = new Map();
263
+ for (const key of allKeys) {
264
+ const headerWidth = formatKey(key).length;
265
+ const maxValueWidth = Math.max(...data.map(obj => String(formatValue(obj[key] ?? '')).length));
266
+ columnWidths.set(key, Math.max(headerWidth, maxValueWidth));
267
+ }
268
+ const topBorderParts = allKeys.map(key => '─'.repeat(columnWidths.get(key) + 2));
269
+ console.log('┌' + topBorderParts.join('┬') + '┐');
270
+ const headerParts = allKeys.map(key => {
271
+ const formattedKey = formatKey(key);
272
+ const width = columnWidths.get(key);
273
+ return ' ' + formattedKey.padEnd(width) + ' ';
274
+ });
275
+ console.log('│' + headerParts.join('│') + '│');
276
+ const separatorParts = allKeys.map(key => '─'.repeat(columnWidths.get(key) + 2));
277
+ console.log('├' + separatorParts.join('┼') + '┤');
278
+ for (const row of data) {
279
+ const rowParts = allKeys.map(key => {
280
+ const value = formatValue(row[key] ?? '');
281
+ const width = columnWidths.get(key);
282
+ return ' ' + String(value).padEnd(width) + ' ';
283
+ });
284
+ console.log('│' + rowParts.join('│') + '│');
285
+ }
286
+ const bottomBorderParts = allKeys.map(key => '─'.repeat(columnWidths.get(key) + 2));
287
+ console.log('└' + bottomBorderParts.join('┴') + '┘');
288
+ }
289
+ export function renderTree(data, indent = '') {
290
+ if (Array.isArray(data)) {
291
+ if (data.length === 0) {
292
+ console.log(`${indent}(empty)`);
293
+ return;
294
+ }
295
+ data.forEach((item, index) => {
296
+ if (typeof item === 'object' && item !== null) {
297
+ console.log(`${indent}[${index}]`);
298
+ renderTree(item, indent + ' ');
299
+ }
300
+ else {
301
+ console.log(`${indent}* ${item}`);
302
+ }
303
+ });
304
+ return;
305
+ }
306
+ if (typeof data === 'object' && data !== null) {
307
+ const entries = Object.entries(data).filter(([key, value]) => !(key === 'returnValue' && value === true));
308
+ for (const [key, value] of entries) {
309
+ const formattedKey = formatKey(key);
310
+ if (value === null || value === undefined) {
311
+ console.log(`${indent}${formattedKey}: (none)`);
312
+ }
313
+ else if (Array.isArray(value)) {
314
+ console.log(`${indent}${formattedKey}:`);
315
+ renderTree(value, indent + ' ');
316
+ }
317
+ else if (typeof value === 'object') {
318
+ console.log(`${indent}${formattedKey}:`);
319
+ renderTree(value, indent + ' ');
320
+ }
321
+ else {
322
+ console.log(`${indent}${formattedKey}: ${formatValue(value)}`);
323
+ }
324
+ }
325
+ return;
326
+ }
327
+ console.log(`${indent}${formatValue(data)}`);
328
+ }
329
+ /**
330
+ * Render data as a bordered card
331
+ * Great for single objects with mixed content types
332
+ */
333
+ export function renderCard(data) {
334
+ if (Array.isArray(data)) {
335
+ // Multiple cards
336
+ data.forEach((item, index) => {
337
+ if (index > 0)
338
+ console.log('');
339
+ renderSingleCard(item);
340
+ });
341
+ }
342
+ else {
343
+ renderSingleCard(data);
344
+ }
345
+ }
346
+ function renderSingleCard(data) {
347
+ if (typeof data !== 'object' || data === null) {
348
+ console.log(data);
349
+ return;
350
+ }
351
+ const entries = Object.entries(data).filter(([key]) => key !== 'returnValue');
352
+ if (entries.length === 0) {
353
+ console.log('(empty)');
354
+ return;
355
+ }
356
+ // Calculate width based on content
357
+ const maxKeyLength = Math.max(...entries.map(([k]) => formatKey(k).length));
358
+ const width = Math.min(Math.max(maxKeyLength + 40, 50), 80);
359
+ // Card top border
360
+ console.log(chalk.dim('┌' + '─'.repeat(width - 2) + '┐'));
361
+ // Card content
362
+ for (const [key, value] of entries) {
363
+ const formattedKey = chalk.bold(formatKey(key));
364
+ if (Array.isArray(value)) {
365
+ // Render arrays inline or as bullets
366
+ console.log(chalk.dim('│') + ` ${formattedKey}:`);
367
+ if (value.length === 0) {
368
+ console.log(chalk.dim('│') + ' (empty)');
369
+ }
370
+ else if (typeof value[0] === 'object') {
371
+ // Array of objects - render as mini-table
372
+ value.forEach((item, idx) => {
373
+ const summary = typeof item === 'object'
374
+ ? Object.entries(item).slice(0, 2).map(([k, v]) => `${formatKey(k)}: ${formatValue(v)}`).join(', ')
375
+ : String(item);
376
+ console.log(chalk.dim('│') + ` ${chalk.cyan(`[${idx}]`)} ${summary}`);
377
+ });
378
+ }
379
+ else {
380
+ // Array of primitives - render as bullets
381
+ value.forEach(item => {
382
+ console.log(chalk.dim('│') + ` • ${formatValue(item)}`);
383
+ });
384
+ }
385
+ }
386
+ else if (typeof value === 'object' && value !== null) {
387
+ // Nested object - render inline
388
+ console.log(chalk.dim('│') + ` ${formattedKey}:`);
389
+ for (const [subKey, subVal] of Object.entries(value)) {
390
+ console.log(chalk.dim('│') + ` ${formatKey(subKey)}: ${formatValue(subVal)}`);
391
+ }
392
+ }
393
+ else {
394
+ // Simple value
395
+ const formattedValue = formatCardValue(value);
396
+ console.log(chalk.dim('│') + ` ${formattedKey}: ${formattedValue}`);
397
+ }
398
+ }
399
+ // Card bottom border
400
+ console.log(chalk.dim('└' + '─'.repeat(width - 2) + '┘'));
401
+ }
402
+ function formatCardValue(value) {
403
+ if (value === null || value === undefined)
404
+ return chalk.dim('-');
405
+ if (typeof value === 'boolean')
406
+ return value ? chalk.green('yes') : chalk.red('no');
407
+ if (typeof value === 'number')
408
+ return chalk.yellow(String(value));
409
+ if (typeof value === 'string') {
410
+ // Check for URLs
411
+ if (value.startsWith('http://') || value.startsWith('https://')) {
412
+ return chalk.blue.underline(value);
413
+ }
414
+ // Check for dates
415
+ if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
416
+ return chalk.cyan(value);
417
+ }
418
+ }
419
+ return String(value);
420
+ }
421
+ /**
422
+ * Render data as tabbed sections
423
+ * Each top-level key becomes a tab
424
+ */
425
+ export function renderTabs(data) {
426
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
427
+ console.log('Tabs format requires an object with sections');
428
+ renderTree(data);
429
+ return;
430
+ }
431
+ const sections = Object.entries(data).filter(([key]) => key !== 'returnValue');
432
+ sections.forEach(([title, content], index) => {
433
+ // Tab separator
434
+ if (index > 0) {
435
+ console.log('\n' + chalk.dim('─'.repeat(60)));
436
+ }
437
+ // Tab header
438
+ console.log(chalk.bold.cyan(`\n▸ ${formatKey(title)}`));
439
+ console.log('');
440
+ // Tab content
441
+ if (content === null || content === undefined) {
442
+ console.log(' (empty)');
443
+ }
444
+ else if (Array.isArray(content)) {
445
+ if (content.length === 0) {
446
+ console.log(' (empty)');
447
+ }
448
+ else if (typeof content[0] === 'object') {
449
+ // Array of objects - render as table
450
+ renderTable(content);
451
+ }
452
+ else {
453
+ // Array of primitives - render as list
454
+ content.forEach(item => console.log(` • ${formatValue(item)}`));
455
+ }
456
+ }
457
+ else if (typeof content === 'object') {
458
+ // Nested object - render as indented tree
459
+ renderTree(content, ' ');
460
+ }
461
+ else {
462
+ console.log(` ${formatValue(content)}`);
463
+ }
464
+ });
465
+ }
466
+ /**
467
+ * Render data as collapsible accordion sections
468
+ * Similar to tabs but with visual collapse indicators
469
+ */
470
+ export function renderAccordion(data) {
471
+ if (Array.isArray(data)) {
472
+ // Array of items - each becomes an accordion section
473
+ data.forEach((item, index) => {
474
+ if (index > 0)
475
+ console.log('');
476
+ if (typeof item === 'object' && item !== null) {
477
+ // Use title/name/label field as header, or index
478
+ const title = item.title || item.name || item.label || `Item ${index + 1}`;
479
+ const content = item.content || item.data || item;
480
+ console.log(chalk.bold(`▼ ${title}`));
481
+ if (typeof content === 'object') {
482
+ // Filter out the title field from display
483
+ const filtered = Object.fromEntries(Object.entries(content).filter(([k]) => !['title', 'name', 'label'].includes(k)));
484
+ renderTree(filtered, ' ');
485
+ }
486
+ else {
487
+ console.log(` ${formatValue(content)}`);
488
+ }
489
+ }
490
+ else {
491
+ console.log(`▼ ${formatValue(item)}`);
492
+ }
493
+ });
494
+ }
495
+ else if (typeof data === 'object' && data !== null) {
496
+ // Object - each key becomes an accordion section
497
+ const entries = Object.entries(data).filter(([key]) => key !== 'returnValue');
498
+ entries.forEach(([key, value], index) => {
499
+ if (index > 0)
500
+ console.log('');
501
+ console.log(chalk.bold(`▼ ${formatKey(key)}`));
502
+ if (value === null || value === undefined) {
503
+ console.log(' (empty)');
504
+ }
505
+ else if (Array.isArray(value)) {
506
+ value.forEach(item => {
507
+ if (typeof item === 'object') {
508
+ renderTree(item, ' ');
509
+ }
510
+ else {
511
+ console.log(` • ${formatValue(item)}`);
512
+ }
513
+ });
514
+ }
515
+ else if (typeof value === 'object') {
516
+ renderTree(value, ' ');
517
+ }
518
+ else {
519
+ console.log(` ${formatValue(value)}`);
520
+ }
521
+ });
522
+ }
523
+ else {
524
+ console.log(formatValue(data));
525
+ }
526
+ }
527
+ export function renderNone() {
528
+ console.log('Done');
529
+ }
530
+ export function formatKey(key) {
531
+ return key
532
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
533
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
534
+ .replace(/_/g, ' ')
535
+ .replace(/^./, str => str.toUpperCase())
536
+ .trim();
537
+ }
538
+ export function formatValue(value) {
539
+ if (value === null || value === undefined)
540
+ return '-';
541
+ if (typeof value === 'boolean')
542
+ return value ? 'yes' : 'no';
543
+ return value;
544
+ }
545
+ export const STATUS = {
546
+ OK: 'ok',
547
+ UPDATE: 'update',
548
+ WARN: 'warn',
549
+ ERROR: '!',
550
+ OFF: 'off',
551
+ UNKNOWN: '?',
552
+ };
553
+ export function formatToMimeType(format) {
554
+ const mimeTypes = {
555
+ json: 'application/json',
556
+ markdown: 'text/markdown',
557
+ yaml: 'text/yaml',
558
+ xml: 'application/xml',
559
+ html: 'text/html',
560
+ };
561
+ if (mimeTypes[format])
562
+ return mimeTypes[format];
563
+ if (format === 'code')
564
+ return 'text/plain';
565
+ if (format.startsWith('code:'))
566
+ return `text/x-${format.split(':')[1]}`;
567
+ return undefined;
568
+ }
569
+ export function printSuccess(message) {
570
+ console.error(`✓ ${message}`);
571
+ }
572
+ export function printError(message) {
573
+ console.error(`✗ ${message}`);
574
+ }
575
+ export function printInfo(message) {
576
+ console.error(`${message}`);
577
+ }
578
+ export function printWarning(message) {
579
+ console.error(`! ${message}`);
580
+ }
581
+ export function printHeader(title) {
582
+ console.error(`\n${title}`);
583
+ console.error('─'.repeat(title.length));
584
+ }
585
+ //# sourceMappingURL=cli-formatter.js.map