@mks2508/better-logger 5.0.0 → 5.0.2

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.
Files changed (162) hide show
  1. package/dist/chunks/{Logger-Cbi1f10o.js → Logger-CKqzXDh3.js} +2 -2
  2. package/dist/chunks/{Logger-Cbi1f10o.js.map → Logger-CKqzXDh3.js.map} +1 -1
  3. package/dist/chunks/{Logger-BZ7B7OXE.js → Logger-D_vuKP4l.js} +2 -2
  4. package/dist/chunks/{Logger-BZ7B7OXE.js.map → Logger-D_vuKP4l.js.map} +1 -1
  5. package/dist/chunks/cli-module-B-njrTWb.js +2 -0
  6. package/dist/chunks/cli-module-B-njrTWb.js.map +1 -0
  7. package/dist/chunks/{cli-module-ClIJmaT8.js → cli-module-Cx7qotPY.js} +16 -9
  8. package/dist/chunks/cli-module-Cx7qotPY.js.map +1 -0
  9. package/dist/cli.cjs +1 -1
  10. package/dist/cli.js +1 -1
  11. package/dist/exports.cjs +1 -1
  12. package/dist/exports.js +1 -1
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.js +3 -3
  15. package/dist/styling.cjs +1 -1
  16. package/dist/styling.js +2 -2
  17. package/dist/terminal/formatter.d.ts.map +1 -1
  18. package/package.json +5 -1
  19. package/.claude/settings.local.json +0 -48
  20. package/.github/workflows/ci-quality.yml +0 -357
  21. package/.github/workflows/docs-demo.yml +0 -119
  22. package/.github/workflows/releases-core.yml +0 -512
  23. package/.github/workflows/releases-full.yml +0 -582
  24. package/.github/workflows-backup/ci.yml +0 -221
  25. package/.github/workflows-backup/nightly.yml +0 -196
  26. package/.github/workflows-backup/release-optimized.yml +0 -373
  27. package/.github/workflows-backup/release.yml +0 -269
  28. package/.release-notes-0.2.0.md +0 -129
  29. package/.yamllint +0 -28
  30. package/CHANGELOG.json +0 -1076
  31. package/CHANGELOG.md +0 -209
  32. package/CLAUDE.md +0 -217
  33. package/bun.lock +0 -333
  34. package/demo.html +0 -847
  35. package/dist/chunks/cli-module-CadqgZ1Z.js +0 -2
  36. package/dist/chunks/cli-module-CadqgZ1Z.js.map +0 -1
  37. package/dist/chunks/cli-module-ClIJmaT8.js.map +0 -1
  38. package/docs/API.md +0 -918
  39. package/docs/CORE.md +0 -264
  40. package/docs/DEVELOPMENT.md +0 -731
  41. package/docs/EXPORTS.md +0 -467
  42. package/docs/PACKAGES.md +0 -244
  43. package/docs/STYLING.md +0 -405
  44. package/docs/_config.yml +0 -36
  45. package/docs/index.md +0 -179
  46. package/examples/README.md +0 -208
  47. package/examples/basic-logging.js +0 -75
  48. package/examples/data-export.js +0 -234
  49. package/examples/package.json +0 -16
  50. package/examples/performance-timing.js +0 -170
  51. package/examples/simplified-api.js +0 -124
  52. package/examples/styling-themes.js +0 -207
  53. package/index.html +0 -358
  54. package/packages/core/package.json +0 -57
  55. package/packages/exports/package.json +0 -41
  56. package/packages/nodejs-opentui/README.md +0 -232
  57. package/packages/nodejs-opentui/package.json +0 -72
  58. package/packages/nodejs-opentui/src/LogRenderer.ts +0 -171
  59. package/packages/nodejs-opentui/src/OpenTUILogHandler.ts +0 -178
  60. package/packages/nodejs-opentui/src/components/LogBadge.tsx +0 -131
  61. package/packages/nodejs-opentui/src/index.ts +0 -133
  62. package/packages/nodejs-opentui/src/types.ts +0 -158
  63. package/packages/nodejs-opentui/tsconfig.json +0 -20
  64. package/packages/styling/package.json +0 -41
  65. package/playground/demo-all.ts +0 -95
  66. package/playground/demo-box.ts +0 -85
  67. package/playground/demo-levels.ts +0 -56
  68. package/playground/demo-real-world.ts +0 -98
  69. package/playground/demo-spinner.ts +0 -77
  70. package/playground/demo-steps.ts +0 -72
  71. package/playground/demo-table.ts +0 -83
  72. package/project-utils/README.md +0 -172
  73. package/project-utils/auto-release-gemini.ts +0 -1193
  74. package/project-utils/auto-release-ui.ts +0 -1329
  75. package/project-utils/commit-generator.ts +0 -1385
  76. package/project-utils/commit-ui.ts +0 -264
  77. package/project-utils/git-utils.ts +0 -199
  78. package/project-utils/github-release-manager.ts +0 -466
  79. package/project-utils/project-config.ts +0 -260
  80. package/project-utils/prompt-templates.js +0 -345
  81. package/project-utils/prompt-templates.ts +0 -422
  82. package/project-utils/version-manager.ts +0 -1078
  83. package/public/vite.svg +0 -1
  84. package/src/Logger.ts +0 -1858
  85. package/src/ScopedLogger.ts +0 -256
  86. package/src/cli/CommandProcessor.ts +0 -248
  87. package/src/cli/commands/ConfigCommand.ts +0 -93
  88. package/src/cli/commands/ExportCommand.ts +0 -276
  89. package/src/cli/commands/HistoryCommand.ts +0 -117
  90. package/src/cli/commands/StatusCommand.ts +0 -112
  91. package/src/cli/commands/ThemeCommand.ts +0 -88
  92. package/src/cli/help.ts +0 -127
  93. package/src/cli/index.ts +0 -71
  94. package/src/cli-module.ts +0 -21
  95. package/src/cli-primitives/box.ts +0 -86
  96. package/src/cli-primitives/cli-table.ts +0 -62
  97. package/src/cli-primitives/divider.ts +0 -17
  98. package/src/cli-primitives/header.ts +0 -18
  99. package/src/cli-primitives/index.ts +0 -13
  100. package/src/cli-primitives/server-fallback.ts +0 -54
  101. package/src/cli-primitives/spinner.ts +0 -133
  102. package/src/cli-primitives/step.ts +0 -22
  103. package/src/constants.ts +0 -313
  104. package/src/core.ts +0 -397
  105. package/src/example.ts +0 -210
  106. package/src/exports-module.ts +0 -311
  107. package/src/handlers/AnalyticsLogHandler.ts +0 -22
  108. package/src/handlers/ExportLogHandler.ts +0 -610
  109. package/src/handlers/FileLogHandler.ts +0 -169
  110. package/src/handlers/RemoteLogHandler.ts +0 -42
  111. package/src/handlers/index.ts +0 -8
  112. package/src/hooks/HookManager.ts +0 -177
  113. package/src/hooks/index.ts +0 -1
  114. package/src/index.ts +0 -395
  115. package/src/main.ts +0 -196
  116. package/src/serializers/SerializerRegistry.ts +0 -173
  117. package/src/serializers/index.ts +0 -1
  118. package/src/style.css +0 -96
  119. package/src/styling/LogStyleBuilder.ts +0 -355
  120. package/src/styling/SemanticStyles.ts +0 -380
  121. package/src/styling/SmartPresets.ts +0 -288
  122. package/src/styling/StyleBuilder.ts +0 -319
  123. package/src/styling/StyleCache.ts +0 -131
  124. package/src/styling/banners.ts +0 -168
  125. package/src/styling/index.ts +0 -27
  126. package/src/styling/themes.ts +0 -235
  127. package/src/styling-module.ts +0 -244
  128. package/src/terminal/color-converter.ts +0 -315
  129. package/src/terminal/formatter.ts +0 -236
  130. package/src/terminal/terminal-renderer.ts +0 -342
  131. package/src/transports/ConsoleTransport.ts +0 -28
  132. package/src/transports/FileTransport.ts +0 -53
  133. package/src/transports/HttpTransport.ts +0 -56
  134. package/src/transports/TransportManager.ts +0 -130
  135. package/src/transports/index.ts +0 -4
  136. package/src/types/core.ts +0 -417
  137. package/src/types/handlers.ts +0 -95
  138. package/src/types/hooks.ts +0 -45
  139. package/src/types/index.ts +0 -83
  140. package/src/types/serializers.ts +0 -30
  141. package/src/types/transports.ts +0 -52
  142. package/src/typescript.svg +0 -1
  143. package/src/utils/adapter.ts +0 -291
  144. package/src/utils/ansi-colors.ts +0 -333
  145. package/src/utils/environment-detector.ts +0 -170
  146. package/src/utils/environment.ts +0 -94
  147. package/src/utils/formatting.ts +0 -332
  148. package/src/utils/index.ts +0 -33
  149. package/src/utils/opentui-detection.ts +0 -138
  150. package/src/utils/output.ts +0 -227
  151. package/src/utils/stackTrace.ts +0 -144
  152. package/src/utils/timestamps.ts +0 -80
  153. package/src/vite-env.d.ts +0 -1
  154. package/src/writers/BufferWriter.ts +0 -157
  155. package/src/writers/index.ts +0 -6
  156. package/test-core-browser.html +0 -237
  157. package/test-core-node.js +0 -63
  158. package/tests/test-conflict-resolution.ts +0 -391
  159. package/tests/validate-workflows.sh +0 -131
  160. package/tests/yaml-autofix.sh +0 -146
  161. package/tsconfig.json +0 -50
  162. package/vite.config.ts +0 -256
@@ -1,610 +0,0 @@
1
- /**
2
- * @fileoverview Export and clipboard log handler for Advanced Logger
3
- */
4
-
5
- import type {
6
- ILogHandler,
7
- LogLevel,
8
- LogMetadata,
9
- LogEntry,
10
- ExportFilters,
11
- ExportOptions,
12
- ExportResult,
13
- BufferStats,
14
- ExportFormat
15
- } from '../types/index.js';
16
- import { parseTimeInput, formatDisplayTime } from '../utils/timestamps.js';
17
- import { safeStringify, escapeHtml, generateLogId } from '../utils/output.js';
18
- import { BUFFER_LIMITS } from '../constants.js';
19
-
20
- /**
21
- * Export log handler with circular buffer and multiple export formats
22
- */
23
- export class ExportLogHandler implements ILogHandler {
24
- private buffer: LogEntry[] = [];
25
- private maxSize: number;
26
- private groupDepth: number = 0;
27
- private currentGroup?: string;
28
-
29
- constructor(maxSize: number = BUFFER_LIMITS.DEFAULT_SIZE) {
30
- this.maxSize = Math.min(Math.max(maxSize, BUFFER_LIMITS.MIN_SIZE), BUFFER_LIMITS.MAX_SIZE);
31
- }
32
-
33
- /**
34
- * Handle incoming log and store in buffer
35
- */
36
- handle(level: LogLevel, message: string, args: any[], metadata: LogMetadata): void {
37
- const entry: LogEntry = {
38
- id: generateLogId(),
39
- timestamp: metadata.timestamp,
40
- level,
41
- prefix: metadata.prefix,
42
- message,
43
- args,
44
- location: metadata.stackInfo,
45
- groupInfo: this.groupDepth > 0 ? {
46
- depth: this.groupDepth,
47
- groupName: this.currentGroup
48
- } : undefined
49
- };
50
-
51
- // Add to circular buffer
52
- this.buffer.push(entry);
53
- if (this.buffer.length > this.maxSize) {
54
- this.buffer.shift(); // Remove oldest entry
55
- }
56
- }
57
-
58
- /**
59
- * Set group tracking for nested logs
60
- */
61
- setGroupInfo(depth: number, groupName?: string): void {
62
- this.groupDepth = depth;
63
- this.currentGroup = groupName;
64
- }
65
-
66
- /**
67
- * Get buffer statistics
68
- */
69
- getBufferStats(): BufferStats {
70
- const levelCounts = this.buffer.reduce((acc, entry) => {
71
- const currentCount = acc[entry.level] ? acc[entry.level] : 0;
72
- acc[entry.level] = currentCount + 1;
73
- return acc;
74
- }, {} as Record<LogLevel, number>);
75
-
76
- let oldestLog: Date | undefined;
77
- let newestLog: Date | undefined;
78
-
79
- if (this.buffer.length > 0) {
80
- const firstEntry = this.buffer[0];
81
- const lastEntry = this.buffer[this.buffer.length - 1];
82
-
83
- if (firstEntry && firstEntry.timestamp) {
84
- oldestLog = new Date(firstEntry.timestamp);
85
- }
86
-
87
- if (lastEntry && lastEntry.timestamp) {
88
- newestLog = new Date(lastEntry.timestamp);
89
- }
90
- }
91
-
92
- return {
93
- size: this.buffer.length,
94
- maxSize: this.maxSize,
95
- usage: (this.buffer.length / this.maxSize) * 100,
96
- oldestLog,
97
- newestLog,
98
- levelCounts: {
99
- debug: levelCounts.debug ? levelCounts.debug : 0,
100
- info: levelCounts.info ? levelCounts.info : 0,
101
- warn: levelCounts.warn ? levelCounts.warn : 0,
102
- error: levelCounts.error ? levelCounts.error : 0,
103
- critical: levelCounts.critical ? levelCounts.critical : 0
104
- }
105
- };
106
- }
107
-
108
- /**
109
- * Clear the buffer
110
- */
111
- clearBuffer(): void {
112
- this.buffer = [];
113
- }
114
-
115
- /**
116
- * Set buffer size (with limits)
117
- */
118
- setBufferSize(size: number): void {
119
- this.maxSize = Math.min(Math.max(size, BUFFER_LIMITS.MIN_SIZE), BUFFER_LIMITS.MAX_SIZE);
120
-
121
- // Trim buffer if new size is smaller
122
- if (this.buffer.length > this.maxSize) {
123
- this.buffer = this.buffer.slice(-this.maxSize);
124
- }
125
- }
126
-
127
- /**
128
- * Filter logs based on criteria
129
- */
130
- private filterLogs(filters: ExportFilters = {}): LogEntry[] {
131
- let filtered = [...this.buffer];
132
-
133
- // Filter by levels
134
- if (filters.levels?.length) {
135
- filtered = filtered.filter(entry => filters.levels!.includes(entry.level));
136
- }
137
-
138
- // Filter by prefixes
139
- if (filters.prefixes?.length) {
140
- filtered = filtered.filter(entry =>
141
- entry.prefix && filters.prefixes!.includes(entry.prefix)
142
- );
143
- }
144
-
145
- // Exclude prefixes
146
- if (filters.excludePrefixes?.length) {
147
- filtered = filtered.filter(entry =>
148
- !entry.prefix || !filters.excludePrefixes!.includes(entry.prefix)
149
- );
150
- }
151
-
152
- // Filter by time range
153
- if (filters.since) {
154
- const sinceDate = parseTimeInput(filters.since);
155
- filtered = filtered.filter(entry => new Date(entry.timestamp) >= sinceDate);
156
- }
157
-
158
- if (filters.until) {
159
- const untilDate = parseTimeInput(filters.until);
160
- filtered = filtered.filter(entry => new Date(entry.timestamp) <= untilDate);
161
- }
162
-
163
- // Filter by stack trace presence
164
- if (filters.withStackTrace) {
165
- filtered = filtered.filter(entry => entry.location);
166
- }
167
-
168
- // Filter errors only
169
- if (filters.errorsOnly) {
170
- filtered = filtered.filter(entry => entry.level === 'error' || entry.level === 'critical');
171
- }
172
-
173
- // Search in messages
174
- if (filters.search) {
175
- const searchTerm = filters.search.toLowerCase();
176
- filtered = filtered.filter(entry =>
177
- entry.message.toLowerCase().includes(searchTerm) ||
178
- entry.args.some(arg =>
179
- String(arg).toLowerCase().includes(searchTerm)
180
- )
181
- );
182
- }
183
-
184
- // Limit results
185
- if (filters.last) {
186
- filtered = filtered.slice(-filters.last);
187
- }
188
-
189
- if (filters.first) {
190
- filtered = filtered.slice(0, filters.first);
191
- }
192
-
193
- return filtered;
194
- }
195
-
196
- /**
197
- * Export logs in JSON format
198
- */
199
- private exportJSON(logs: LogEntry[], options: ExportOptions): string {
200
- const data = logs.map(entry => ({
201
- timestamp: entry.timestamp,
202
- level: entry.level,
203
- prefix: entry.prefix,
204
- message: entry.message,
205
- ...(options.minimal ? {} : {
206
- args: entry.args.slice(1),
207
- location: entry.location,
208
- groupInfo: entry.groupInfo
209
- })
210
- }));
211
-
212
- return options.compact
213
- ? JSON.stringify(data)
214
- : JSON.stringify(data, null, 2);
215
- }
216
-
217
- /**
218
- * Export logs in CSV format
219
- */
220
- private exportCSV(logs: LogEntry[], options: ExportOptions): string {
221
- const headers = options.minimal
222
- ? ['Timestamp', 'Level', 'Prefix', 'Message']
223
- : ['Timestamp', 'Level', 'Prefix', 'Message', 'File', 'Line', 'Args'];
224
-
225
- const rows = logs.map(entry => {
226
- const baseRow = [
227
- formatDisplayTime(new Date(entry.timestamp), 'full'),
228
- entry.level.toUpperCase(),
229
- entry.prefix || '',
230
- `"${entry.message.replace(/"/g, '""')}"`
231
- ];
232
-
233
- if (!options.minimal) {
234
- baseRow.push(
235
- entry.location?.file || '',
236
- entry.location?.line?.toString() || '',
237
- `"${safeStringify(entry.args.slice(1)).replace(/"/g, '""')}"`
238
- );
239
- }
240
-
241
- return baseRow;
242
- });
243
-
244
- return [headers.join(','), ...rows.map(row => row.join(','))].join('\n');
245
- }
246
-
247
- /**
248
- * Export logs in Markdown format
249
- */
250
- private exportMarkdown(logs: LogEntry[], options: ExportOptions): string {
251
- const now = new Date();
252
- let content = `# Log Export - ${formatDisplayTime(now, 'full')}\n\n`;
253
-
254
- // Add summary if not minimal
255
- if (!options.minimal) {
256
- const summary = this.getBufferStats();
257
- content += `## Summary\n`;
258
- content += `- **Total logs**: ${logs.length}\n`;
259
- content += `- **Errors**: ${summary.levelCounts.error + summary.levelCounts.critical}\n`;
260
- content += `- **Warnings**: ${summary.levelCounts.warn}\n\n`;
261
- }
262
-
263
- // Group by level if specified
264
- if (options.groupBy === 'level') {
265
- const grouped = logs.reduce((acc, entry) => {
266
- const level = entry.level;
267
- if (!acc[level]) {
268
- acc[level] = [];
269
- }
270
- // We just ensured the array exists above
271
- const levelArray = acc[level];
272
- if (levelArray) {
273
- levelArray.push(entry);
274
- }
275
- return acc;
276
- }, {} as Record<string, LogEntry[]>);
277
-
278
- Object.entries(grouped).forEach(([level, entries]) => {
279
- const emoji = this.getLevelEmoji(level as LogLevel);
280
- content += `## ${emoji} ${level.toUpperCase()} (${entries.length})\n\n`;
281
-
282
- entries.forEach(entry => {
283
- const time = formatDisplayTime(new Date(entry.timestamp), 'time-only');
284
- const location = entry.location ? ` (${entry.location.file}:${entry.location.line})` : '';
285
- const prefix = entry.prefix ? ` **${entry.prefix}**:` : '';
286
- content += `- \`${time}\`${prefix} ${entry.message}${location}\n`;
287
- });
288
- content += '\n';
289
- });
290
- } else {
291
- content += '## Logs\n\n';
292
- logs.forEach(entry => {
293
- const time = formatDisplayTime(new Date(entry.timestamp), 'time-only');
294
- const emoji = this.getLevelEmoji(entry.level);
295
- const location = entry.location ? ` (${entry.location.file}:${entry.location.line})` : '';
296
- const prefix = entry.prefix ? ` **${entry.prefix}**:` : '';
297
- content += `- \`${time}\` ${emoji}${prefix} ${entry.message}${location}\n`;
298
- });
299
- }
300
-
301
- return content;
302
- }
303
-
304
- /**
305
- * Export logs in plain text format
306
- */
307
- private exportPlain(logs: LogEntry[], options: ExportOptions): string {
308
- return logs.map(entry => {
309
- const time = formatDisplayTime(new Date(entry.timestamp), options.minimal ? 'time-only' : 'short');
310
- const level = entry.level.toUpperCase().padEnd(8);
311
- const prefix = entry.prefix ? `[${entry.prefix}] ` : '';
312
- const location = (!options.minimal && entry.location) ? ` (${entry.location.file}:${entry.location.line})` : '';
313
-
314
- return `${time} ${level} ${prefix}${entry.message}${location}`;
315
- }).join('\n');
316
- }
317
-
318
- /**
319
- * Export logs in HTML format
320
- */
321
- private exportHTML(logs: LogEntry[], _options: ExportOptions): string {
322
- const title = `Log Export - ${formatDisplayTime(new Date(), 'full')}`;
323
-
324
- let html = `<!DOCTYPE html>
325
- <html lang="en">
326
- <head>
327
- <meta charset="UTF-8">
328
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
329
- <title>${title}</title>
330
- <style>
331
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; line-height: 1.6; }
332
- .header { border-bottom: 2px solid #ddd; padding-bottom: 20px; margin-bottom: 20px; }
333
- .log-entry { margin: 5px 0; padding: 8px; border-radius: 4px; font-family: 'Monaco', 'Consolas', monospace; }
334
- .timestamp { color: #666; font-size: 0.9em; }
335
- .level { font-weight: bold; padding: 2px 6px; border-radius: 3px; margin: 0 8px; }
336
- .prefix { background: #2d3748; color: #e2e8f0; padding: 2px 6px; border-radius: 3px; margin: 0 8px; }
337
- .location { color: #718096; font-size: 0.9em; }
338
- .debug { background: #f0f4ff; }
339
- .info { background: #f0f9ff; }
340
- .warn { background: #fffbeb; }
341
- .error { background: #fef2f2; }
342
- .critical { background: #fef2f2; border-left: 4px solid #dc2626; }
343
- .level.debug { background: #667eea; color: white; }
344
- .level.info { background: #74b9ff; color: white; }
345
- .level.warn { background: #fdcb6e; color: #2d3436; }
346
- .level.error { background: #e84393; color: white; }
347
- .level.critical { background: #ff3838; color: white; }
348
- </style>
349
- </head>
350
- <body>
351
- <div class="header">
352
- <h1>${title}</h1>
353
- <p>Generated by Advanced Logger v2.0.0</p>
354
- </div>
355
- <div class="logs">`;
356
-
357
- logs.forEach(entry => {
358
- const time = formatDisplayTime(new Date(entry.timestamp), 'full');
359
- const location = entry.location ? ` <span class="location">(${escapeHtml(entry.location.file)}:${entry.location.line})</span>` : '';
360
- const prefix = entry.prefix ? ` <span class="prefix">${escapeHtml(entry.prefix)}</span>` : '';
361
-
362
- html += `
363
- <div class="log-entry ${entry.level}">
364
- <span class="timestamp">${time}</span>
365
- <span class="level ${entry.level}">${entry.level.toUpperCase()}</span>
366
- ${prefix}
367
- <span class="message">${escapeHtml(entry.message)}</span>
368
- ${location}
369
- </div>`;
370
- });
371
-
372
- html += `
373
- </div>
374
- </body>
375
- </html>`;
376
-
377
- return html;
378
- }
379
-
380
- /**
381
- * Get emoji for log level
382
- */
383
- private getLevelEmoji(level: LogLevel): string {
384
- const emojis = {
385
- debug: '🐞',
386
- info: 'ℹ️',
387
- warn: '⚠️',
388
- error: '❌',
389
- critical: '🔥'
390
- };
391
- return emojis[level] || '';
392
- }
393
-
394
- /**
395
- * Main export function
396
- */
397
- export(format: ExportFormat, filters: ExportFilters = {}, options: ExportOptions = {}): ExportResult {
398
- const filteredLogs = this.filterLogs(filters);
399
-
400
- let data: string;
401
- switch (format) {
402
- case 'json':
403
- data = this.exportJSON(filteredLogs, options);
404
- break;
405
- case 'csv':
406
- data = this.exportCSV(filteredLogs, options);
407
- break;
408
- case 'markdown':
409
- data = this.exportMarkdown(filteredLogs, options);
410
- break;
411
- case 'plain':
412
- data = this.exportPlain(filteredLogs, options);
413
- break;
414
- case 'html':
415
- data = this.exportHTML(filteredLogs, options);
416
- break;
417
- default:
418
- throw new Error(`Unsupported export format: ${format}`);
419
- }
420
-
421
- return {
422
- format,
423
- data,
424
- metadata: {
425
- totalLogs: this.buffer.length,
426
- filteredLogs: filteredLogs.length,
427
- exportedAt: new Date().toISOString(),
428
- filters,
429
- options
430
- }
431
- };
432
- }
433
-
434
- /**
435
- * Copy to clipboard with fallback
436
- */
437
- async copyToClipboard(format: ExportFormat, filters: ExportFilters = {}, options: ExportOptions = {}): Promise<boolean> {
438
- const result = this.export(format, filters, options);
439
-
440
- try {
441
- if (navigator.clipboard && window.isSecureContext) {
442
- await navigator.clipboard.writeText(result.data);
443
- return true;
444
- } else {
445
- // Fallback for non-secure contexts
446
- const textArea = document.createElement('textarea');
447
- textArea.value = result.data;
448
- textArea.style.position = 'fixed';
449
- textArea.style.opacity = '0';
450
- document.body.appendChild(textArea);
451
- textArea.focus();
452
- textArea.select();
453
- const success = document.execCommand('copy');
454
- document.body.removeChild(textArea);
455
- return success;
456
- }
457
- } catch (error) {
458
- console.error('Failed to copy to clipboard:', error);
459
- return false;
460
- }
461
- }
462
-
463
- /**
464
- * Get all logs (for debugging)
465
- */
466
- getAllLogs(): LogEntry[] {
467
- return [...this.buffer];
468
- }
469
-
470
- /**
471
- * Save export to file with download prompt
472
- */
473
- async saveToFile(format: ExportFormat, filters: ExportFilters = {}, options: ExportOptions = {}): Promise<boolean> {
474
- try {
475
- const result = this.export(format, filters, options);
476
- const blob = new Blob([result.data], {
477
- type: this.getMimeType(format)
478
- });
479
-
480
- const url = URL.createObjectURL(blob);
481
- const a = document.createElement('a');
482
- const filename = this.generateFilename(format, filters);
483
-
484
- a.href = url;
485
- a.download = filename;
486
- a.style.display = 'none';
487
- document.body.appendChild(a);
488
- a.click();
489
- document.body.removeChild(a);
490
- URL.revokeObjectURL(url);
491
-
492
- return true;
493
- } catch (error) {
494
- console.error('Failed to save file:', error);
495
- return false;
496
- }
497
- }
498
-
499
- /**
500
- * Get MIME type for format
501
- */
502
- private getMimeType(format: ExportFormat): string {
503
- const mimeTypes = {
504
- json: 'application/json',
505
- csv: 'text/csv',
506
- markdown: 'text/markdown',
507
- plain: 'text/plain',
508
- html: 'text/html'
509
- };
510
- return mimeTypes[format] || 'text/plain';
511
- }
512
-
513
- /**
514
- * Generate filename with timestamp
515
- */
516
- private generateFilename(format: ExportFormat, filters: ExportFilters): string {
517
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
518
- const prefix = filters.prefixes?.length === 1 ? `_${filters.prefixes[0]}` : '';
519
- const extension = format === 'markdown' ? 'md' : format;
520
- return `logs${prefix}_${timestamp}.${extension}`;
521
- }
522
-
523
- /**
524
- * Stream logs to external endpoint
525
- */
526
- async streamToEndpoint(endpoint: string, filters: ExportFilters = {}, options: {
527
- apiKey?: string;
528
- batchSize?: number;
529
- format?: ExportFormat
530
- } = {}): Promise<boolean> {
531
- try {
532
- const logs = this.filterLogs(filters);
533
- const batchSize = options.batchSize || 100;
534
- const format = options.format || 'json';
535
-
536
- // Send in batches
537
- for (let i = 0; i < logs.length; i += batchSize) {
538
- const batch = logs.slice(i, i + batchSize);
539
- const result = this.export(format, {}, { compact: true });
540
-
541
- const headers: Record<string, string> = {
542
- 'Content-Type': 'application/json'
543
- };
544
-
545
- if (options.apiKey) {
546
- headers['Authorization'] = `Bearer ${options.apiKey}`;
547
- }
548
-
549
- const response = await fetch(endpoint, {
550
- method: 'POST',
551
- headers,
552
- body: JSON.stringify({
553
- logs: batch,
554
- metadata: {
555
- batchIndex: Math.floor(i / batchSize),
556
- totalBatches: Math.ceil(logs.length / batchSize),
557
- timestamp: new Date().toISOString()
558
- }
559
- })
560
- });
561
-
562
- if (!response.ok) {
563
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
564
- }
565
- }
566
-
567
- return true;
568
- } catch (error) {
569
- console.error('Failed to stream logs:', error);
570
- return false;
571
- }
572
- }
573
-
574
- /**
575
- * Create export with metadata enrichment
576
- */
577
- exportWithMetadata(format: ExportFormat, filters: ExportFilters = {}, options: ExportOptions = {}): ExportResult {
578
- const baseResult = this.export(format, filters, options);
579
- const bufferStats = this.getBufferStats();
580
-
581
- return {
582
- ...baseResult,
583
- metadata: {
584
- ...baseResult.metadata,
585
- bufferStats: bufferStats,
586
- performance: {
587
- memoryUsage: this.estimateMemoryUsage(),
588
- processingTime: Date.now()
589
- },
590
- system: {
591
- userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'Node.js',
592
- timestamp: new Date().toISOString(),
593
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
594
- }
595
- } as any
596
- };
597
- }
598
-
599
- /**
600
- * Estimate memory usage of buffer
601
- */
602
- private estimateMemoryUsage(): number {
603
- const sampleEntry = this.buffer[0];
604
- if (!sampleEntry) return 0;
605
-
606
- // Rough estimation: JSON.stringify size * buffer length
607
- const sampleSize = JSON.stringify(sampleEntry).length;
608
- return sampleSize * this.buffer.length;
609
- }
610
- }