devlog-ui 0.1.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.
package/dist/noop.d.ts ADDED
@@ -0,0 +1,682 @@
1
+ /**
2
+ * DevLogger - No-Op Export for Production
3
+ *
4
+ * Import this instead of the main export in production builds
5
+ * to completely eliminate logging code via tree-shaking.
6
+ *
7
+ * Usage in vite.config.ts:
8
+ * ```ts
9
+ * resolve: {
10
+ * alias: {
11
+ * 'devlogger': process.env.NODE_ENV === 'production'
12
+ * ? 'devlogger/noop'
13
+ * : 'devlogger'
14
+ * }
15
+ * }
16
+ * ```
17
+ *
18
+ * @packageDocumentation
19
+ */
20
+
21
+ export declare const computeDiff: () => readonly [];
22
+
23
+ /**
24
+ * Context-bound logger - logs with specific context attached
25
+ */
26
+ export declare class ContextLogger {
27
+ private logger;
28
+ private context;
29
+ constructor(logger: LoggerCore, context: LogContext);
30
+ debug(message: string, ...data: unknown[]): void;
31
+ info(message: string, ...data: unknown[]): void;
32
+ warn(message: string, ...data: unknown[]): void;
33
+ error(message: string, ...data: unknown[]): void;
34
+ /** Create a span with this context */
35
+ span(name: string, extraContext?: LogContext): LogSpan;
36
+ /** Create a new context logger with merged context */
37
+ withContext(extraContext: LogContext): ContextLogger;
38
+ }
39
+
40
+ export declare const createDiffResult: () => {
41
+ changes: readonly [];
42
+ summary: {
43
+ added: number;
44
+ removed: number;
45
+ changed: number;
46
+ unchanged: number;
47
+ };
48
+ };
49
+
50
+ export declare const createTimeline: () => NoopTimeline;
51
+
52
+ export declare const DevLoggerUI: {
53
+ init: () => void;
54
+ open: () => void;
55
+ close: () => void;
56
+ toggle: () => void;
57
+ popout: () => void;
58
+ closePopout: () => void;
59
+ isPopoutOpen: () => boolean;
60
+ setFilter: () => void;
61
+ getFilter: () => {
62
+ levels: Set<"debug" | "info" | "warn" | "error">;
63
+ search: string;
64
+ file: string;
65
+ };
66
+ clearFilter: () => void;
67
+ destroy: () => void;
68
+ isVisible: () => boolean;
69
+ isInitialized: () => boolean;
70
+ };
71
+
72
+ /**
73
+ * Diff change type
74
+ */
75
+ export declare type DiffChangeType = 'added' | 'removed' | 'changed' | 'unchanged';
76
+
77
+ /**
78
+ * A single diff entry for object comparison
79
+ */
80
+ export declare interface DiffEntry {
81
+ /** Path to the property (e.g., "user.profile.name") */
82
+ path: string;
83
+ /** Type of change */
84
+ type: DiffChangeType;
85
+ /** Old value (for removed/changed) */
86
+ oldValue?: unknown;
87
+ /** New value (for added/changed) */
88
+ newValue?: unknown;
89
+ }
90
+
91
+ /**
92
+ * Diff result for object comparison
93
+ */
94
+ export declare interface DiffResult {
95
+ /** All changes found */
96
+ changes: DiffEntry[];
97
+ /** Summary counts */
98
+ summary: {
99
+ added: number;
100
+ removed: number;
101
+ changed: number;
102
+ unchanged: number;
103
+ };
104
+ }
105
+
106
+ export declare const ErrorCapture: {
107
+ install: () => void;
108
+ uninstall: () => void;
109
+ isActive: () => boolean;
110
+ getConfig: () => {
111
+ captureErrors: boolean;
112
+ captureRejections: boolean;
113
+ errorPrefix: string;
114
+ rejectionPrefix: string;
115
+ };
116
+ };
117
+
118
+ /**
119
+ * Global Error Capture Module
120
+ *
121
+ * Automatically captures uncaught errors and unhandled promise rejections
122
+ * and logs them through the DevLogger system.
123
+ *
124
+ * Features:
125
+ * - window.onerror for synchronous errors
126
+ * - window.onunhandledrejection for promise rejections
127
+ * - Preserves existing handlers (chaining)
128
+ * - Can be enabled/disabled at runtime
129
+ * - Zero-throw policy maintained
130
+ */
131
+ /** Configuration for error capture */
132
+ export declare interface ErrorCaptureConfig {
133
+ /** Capture synchronous errors via window.onerror */
134
+ captureErrors?: boolean;
135
+ /** Capture unhandled promise rejections */
136
+ captureRejections?: boolean;
137
+ /** Prefix for error messages */
138
+ errorPrefix?: string;
139
+ /** Prefix for rejection messages */
140
+ rejectionPrefix?: string;
141
+ }
142
+
143
+ /**
144
+ * Export format options
145
+ */
146
+ export declare interface ExportOptions {
147
+ /** Export format: 'json' or 'text' */
148
+ format?: 'json' | 'text';
149
+ /** Include only logs from the last N milliseconds */
150
+ lastMs?: number;
151
+ /** Include only logs matching these levels */
152
+ levels?: LogLevel[];
153
+ /** Include only logs matching this search */
154
+ search?: string;
155
+ /** Pretty print JSON (default: true) */
156
+ pretty?: boolean;
157
+ }
158
+
159
+ /**
160
+ * Filter state interface
161
+ */
162
+ export declare interface FilterState {
163
+ /** Active log levels (empty = all) */
164
+ levels: Set<LogLevel>;
165
+ /** Text search query */
166
+ search: string;
167
+ /** File filter (partial match) */
168
+ file: string;
169
+ }
170
+
171
+ export declare const formatValue: (v: unknown) => string;
172
+
173
+ export declare const hasChanges: () => boolean;
174
+
175
+ /**
176
+ * Context/tags for log correlation
177
+ */
178
+ export declare type LogContext = Record<string, string | number | boolean>;
179
+
180
+ /**
181
+ * A single log event with all metadata
182
+ */
183
+ export declare interface LogEvent {
184
+ /** Unique identifier for this log entry */
185
+ id: string;
186
+ /** Unix timestamp in milliseconds */
187
+ timestamp: number;
188
+ /** Log severity level */
189
+ level: LogLevel;
190
+ /** Primary log message */
191
+ message: string;
192
+ /** Additional data passed to the log call */
193
+ data: unknown[];
194
+ /** Source code location */
195
+ source: Source;
196
+ /** Browser session identifier */
197
+ sessionId: string;
198
+ /** Optional context/tags for filtering */
199
+ context?: LogContext;
200
+ /** Span ID if this log belongs to a span */
201
+ spanId?: string;
202
+ }
203
+
204
+ /**
205
+ * DevLogger - No-Op Export for Production
206
+ *
207
+ * Import this instead of the main export in production builds
208
+ * to completely eliminate logging code via tree-shaking.
209
+ *
210
+ * Usage in vite.config.ts:
211
+ * ```ts
212
+ * resolve: {
213
+ * alias: {
214
+ * 'devlogger': process.env.NODE_ENV === 'production'
215
+ * ? 'devlogger/noop'
216
+ * : 'devlogger'
217
+ * }
218
+ * }
219
+ * ```
220
+ *
221
+ * @packageDocumentation
222
+ */
223
+ export declare const logger: {
224
+ info: () => void;
225
+ warn: () => void;
226
+ error: () => void;
227
+ debug: () => void;
228
+ configure: () => void;
229
+ clear: () => void;
230
+ importLogs: () => void;
231
+ getLogs: () => readonly [];
232
+ subscribe: () => () => void;
233
+ getSessionId: () => string;
234
+ getConfig: () => {
235
+ maxLogs: number;
236
+ persist: boolean;
237
+ minLevel: "debug";
238
+ enabled: boolean;
239
+ };
240
+ isEnabled: () => boolean;
241
+ span: () => {
242
+ id: string;
243
+ event: never;
244
+ ended: boolean;
245
+ debug: () => void;
246
+ info: () => void;
247
+ warn: () => void;
248
+ error: () => void;
249
+ span: () => /*elided*/ any;
250
+ end: () => void;
251
+ fail: () => void;
252
+ };
253
+ getSpans: () => readonly [];
254
+ getSpan: () => undefined;
255
+ getSpanLogs: () => readonly [];
256
+ subscribeSpans: () => () => void;
257
+ setGlobalContext: () => void;
258
+ updateGlobalContext: () => void;
259
+ getGlobalContext: () => {};
260
+ clearGlobalContext: () => void;
261
+ withContext: () => {
262
+ debug: () => void;
263
+ info: () => void;
264
+ warn: () => void;
265
+ error: () => void;
266
+ span: () => {
267
+ id: string;
268
+ event: never;
269
+ ended: boolean;
270
+ debug: () => void;
271
+ info: () => void;
272
+ warn: () => void;
273
+ error: () => void;
274
+ span: () => /*elided*/ any;
275
+ end: () => void;
276
+ fail: () => void;
277
+ };
278
+ withContext: () => /*elided*/ any;
279
+ };
280
+ exportLogs: () => string;
281
+ copyLogs: () => Promise<boolean>;
282
+ diff: () => {
283
+ changes: readonly [];
284
+ summary: {
285
+ added: number;
286
+ removed: number;
287
+ changed: number;
288
+ unchanged: number;
289
+ };
290
+ };
291
+ computeDiff: () => {
292
+ changes: readonly [];
293
+ summary: {
294
+ added: number;
295
+ removed: number;
296
+ changed: number;
297
+ unchanged: number;
298
+ };
299
+ };
300
+ };
301
+
302
+ /**
303
+ * Logger configuration options
304
+ */
305
+ export declare interface LoggerConfig {
306
+ /** Maximum number of logs to keep in memory (default: 1000) */
307
+ maxLogs?: number;
308
+ /** Persist logs to sessionStorage (default: false) */
309
+ persist?: boolean;
310
+ /** Minimum log level to capture (default: 'debug') */
311
+ minLevel?: LogLevel;
312
+ /** Enable/disable logging entirely (default: true) */
313
+ enabled?: boolean;
314
+ }
315
+
316
+ /**
317
+ * Core Logger Class
318
+ *
319
+ * Lifecycle of a log:
320
+ * 1. Log is created (info/warn/error/debug called)
321
+ * 2. Log is enriched (timestamp, source, sessionId added)
322
+ * 3. Log is stored (in-memory, with rotation)
323
+ * 4. Log is distributed (subscribers notified)
324
+ * 5. Log is displayed (by UI subscribers)
325
+ */
326
+ declare class LoggerCore {
327
+ private logs;
328
+ private spans;
329
+ private subscribers;
330
+ private spanSubscribers;
331
+ private config;
332
+ private sessionId;
333
+ private globalContext;
334
+ constructor();
335
+ /**
336
+ * Core logging method - all public methods delegate to this
337
+ */
338
+ private log;
339
+ /**
340
+ * Log with context (used by ContextLogger)
341
+ */
342
+ private logWithContext;
343
+ /**
344
+ * Log with span (used by LogSpan)
345
+ */
346
+ private logWithSpan;
347
+ /**
348
+ * Notify span subscribers
349
+ */
350
+ private notifySpan;
351
+ /**
352
+ * Safely clone data to prevent mutations and handle special cases
353
+ */
354
+ private safeCloneData;
355
+ /**
356
+ * Deep clone with circular reference handling
357
+ */
358
+ private safeClone;
359
+ /**
360
+ * Store log with FIFO rotation
361
+ */
362
+ private store;
363
+ /**
364
+ * Notify all subscribers of new log
365
+ */
366
+ private notify;
367
+ /**
368
+ * Log an info message
369
+ */
370
+ info(message: string, ...data: unknown[]): void;
371
+ /**
372
+ * Log a warning message
373
+ */
374
+ warn(message: string, ...data: unknown[]): void;
375
+ /**
376
+ * Log an error message
377
+ */
378
+ error(message: string, ...data: unknown[]): void;
379
+ /**
380
+ * Log a debug message
381
+ */
382
+ debug(message: string, ...data: unknown[]): void;
383
+ /**
384
+ * Update logger configuration
385
+ */
386
+ configure(config: Partial<LoggerConfig>): void;
387
+ /**
388
+ * Clear all stored logs and spans
389
+ */
390
+ clear(): void;
391
+ /**
392
+ * Import logs (used for rehydration from persistence)
393
+ * Imported logs are added at the beginning, preserving order
394
+ */
395
+ importLogs(logs: LogEvent[]): void;
396
+ /**
397
+ * Get all stored logs (readonly)
398
+ */
399
+ getLogs(): readonly LogEvent[];
400
+ /**
401
+ * Subscribe to new log events
402
+ */
403
+ subscribe(fn: LogSubscriber): Unsubscribe;
404
+ /**
405
+ * Get current session ID
406
+ */
407
+ getSessionId(): string;
408
+ /**
409
+ * Get current configuration
410
+ */
411
+ getConfig(): Readonly<Required<LoggerConfig>>;
412
+ /**
413
+ * Check if logging is currently enabled
414
+ */
415
+ isEnabled(): boolean;
416
+ /**
417
+ * Create a new span for grouping related logs
418
+ */
419
+ span(name: string, context?: LogContext): LogSpan;
420
+ /**
421
+ * Get all spans
422
+ */
423
+ getSpans(): readonly SpanEvent[];
424
+ /**
425
+ * Get a specific span by ID
426
+ */
427
+ getSpan(spanId: string): SpanEvent | undefined;
428
+ /**
429
+ * Get logs belonging to a specific span
430
+ */
431
+ getSpanLogs(spanId: string): readonly LogEvent[];
432
+ /**
433
+ * Subscribe to span events
434
+ */
435
+ subscribeSpans(fn: SpanSubscriber): Unsubscribe;
436
+ /**
437
+ * Set global context that will be attached to all logs
438
+ */
439
+ setGlobalContext(context: LogContext): void;
440
+ /**
441
+ * Update global context (merge with existing)
442
+ */
443
+ updateGlobalContext(context: LogContext): void;
444
+ /**
445
+ * Get current global context
446
+ */
447
+ getGlobalContext(): Readonly<LogContext>;
448
+ /**
449
+ * Clear global context
450
+ */
451
+ clearGlobalContext(): void;
452
+ /**
453
+ * Create a context-bound logger
454
+ */
455
+ withContext(context: LogContext): ContextLogger;
456
+ /**
457
+ * Export logs in specified format
458
+ */
459
+ exportLogs(options?: ExportOptions): string;
460
+ /**
461
+ * Format logs as human-readable text
462
+ */
463
+ private formatLogsAsText;
464
+ /**
465
+ * Copy logs to clipboard
466
+ */
467
+ copyLogs(options?: ExportOptions): Promise<boolean>;
468
+ /**
469
+ * Log a visual diff between two objects
470
+ */
471
+ diff(message: string, oldObj: unknown, newObj: unknown, level?: LogLevel): DiffResult;
472
+ /**
473
+ * Compute diff without logging (utility method)
474
+ */
475
+ computeDiff(oldObj: unknown, newObj: unknown): DiffResult;
476
+ }
477
+
478
+ /**
479
+ * Core type definitions for DevLogger
480
+ *
481
+ * These types define the contract for all logging operations.
482
+ * They are stable and should not change without major version bump.
483
+ */
484
+ /**
485
+ * Available log levels in order of severity
486
+ */
487
+ export declare type LogLevel = 'debug' | 'info' | 'warn' | 'error';
488
+
489
+ export declare const LogPersistence: {
490
+ enable: () => void;
491
+ disable: () => void;
492
+ isActive: () => boolean;
493
+ hadCrash: () => boolean;
494
+ getPersistedLogs: () => readonly [];
495
+ rehydrate: () => number;
496
+ clear: () => void;
497
+ getConfig: () => {
498
+ storage: "session";
499
+ maxPersisted: number;
500
+ debounceMs: number;
501
+ };
502
+ };
503
+
504
+ /**
505
+ * Log Span class - represents a grouped set of related logs
506
+ */
507
+ export declare class LogSpan {
508
+ private logger;
509
+ private _event;
510
+ private _ended;
511
+ constructor(logger: LoggerCore, name: string, context?: LogContext, parentId?: string);
512
+ /** Get span ID */
513
+ get id(): string;
514
+ /** Get span event data */
515
+ get event(): Readonly<SpanEvent>;
516
+ /** Check if span has ended */
517
+ get ended(): boolean;
518
+ /** Log debug within this span */
519
+ debug(message: string, ...data: unknown[]): void;
520
+ /** Log info within this span */
521
+ info(message: string, ...data: unknown[]): void;
522
+ /** Log warn within this span */
523
+ warn(message: string, ...data: unknown[]): void;
524
+ /** Log error within this span */
525
+ error(message: string, ...data: unknown[]): void;
526
+ /** Create a child span */
527
+ span(name: string, context?: LogContext): LogSpan;
528
+ /** End the span successfully */
529
+ end(): void;
530
+ /** End the span with error status */
531
+ fail(error?: Error | string): void;
532
+ /** Internal finish method */
533
+ private finish;
534
+ }
535
+
536
+ /**
537
+ * Subscriber callback for new log events
538
+ */
539
+ export declare type LogSubscriber = (event: LogEvent) => void;
540
+
541
+ export declare const NetworkCapture: {
542
+ install: () => void;
543
+ uninstall: () => void;
544
+ isActive: () => boolean;
545
+ getConfig: () => {
546
+ captureFetch: boolean;
547
+ captureXHR: boolean;
548
+ includeHeaders: boolean;
549
+ includeBody: boolean;
550
+ includeResponse: boolean;
551
+ maxResponseLength: number;
552
+ ignorePatterns: readonly [];
553
+ context: {};
554
+ };
555
+ addIgnorePattern: () => void;
556
+ };
557
+
558
+ /**
559
+ * Network capture configuration
560
+ */
561
+ export declare interface NetworkCaptureConfig {
562
+ /** Capture fetch requests (default: true) */
563
+ captureFetch?: boolean;
564
+ /** Capture XHR requests (default: true) */
565
+ captureXHR?: boolean;
566
+ /** Include request headers (default: false, may contain sensitive data) */
567
+ includeHeaders?: boolean;
568
+ /** Include request body (default: false, may be large) */
569
+ includeBody?: boolean;
570
+ /** Include response body (default: false, may be large) */
571
+ includeResponse?: boolean;
572
+ /** Max response body length to capture (default: 1000) */
573
+ maxResponseLength?: number;
574
+ /** URL patterns to ignore (e.g., analytics, hot reload) */
575
+ ignorePatterns?: (string | RegExp)[];
576
+ /** Custom context to add to all network logs */
577
+ context?: LogContext;
578
+ }
579
+
580
+ declare class NoopTimeline {
581
+ setTimeWindow: () => void;
582
+ destroy: () => void;
583
+ }
584
+
585
+ /** Configuration for persistence */
586
+ export declare interface PersistenceConfig {
587
+ /** Storage type: 'session' (sessionStorage) or 'local' (localStorage) */
588
+ storage?: 'session' | 'local';
589
+ /** Maximum logs to persist (default: 500) */
590
+ maxPersisted?: number;
591
+ /** Debounce delay in ms for batching writes (default: 100) */
592
+ debounceMs?: number;
593
+ }
594
+
595
+ /**
596
+ * Source location of a log entry
597
+ */
598
+ export declare interface Source {
599
+ /** File path or name */
600
+ file: string;
601
+ /** Line number (1-based) */
602
+ line: number;
603
+ /** Column number (1-based, optional) */
604
+ column?: number;
605
+ /** Function or method name (optional) */
606
+ function?: string;
607
+ }
608
+
609
+ /**
610
+ * A span (grouped logs) event
611
+ */
612
+ export declare interface SpanEvent {
613
+ /** Unique span identifier */
614
+ id: string;
615
+ /** Span name/label */
616
+ name: string;
617
+ /** Start timestamp */
618
+ startTime: number;
619
+ /** End timestamp (set when span ends) */
620
+ endTime?: number;
621
+ /** Duration in milliseconds */
622
+ duration?: number;
623
+ /** Current status */
624
+ status: SpanStatus;
625
+ /** Parent span ID for nesting */
626
+ parentId?: string;
627
+ /** Context inherited by all logs in this span */
628
+ context?: LogContext;
629
+ /** Source where span was created */
630
+ source: Source;
631
+ /** Session ID */
632
+ sessionId: string;
633
+ }
634
+
635
+ /**
636
+ * Span status for grouped logs
637
+ */
638
+ export declare type SpanStatus = 'running' | 'success' | 'error';
639
+
640
+ /**
641
+ * Subscriber callback for span events
642
+ */
643
+ export declare type SpanSubscriber = (event: SpanEvent) => void;
644
+
645
+ export declare const Timeline: typeof NoopTimeline;
646
+
647
+ /**
648
+ * Timeline / Flame-View Component
649
+ *
650
+ * Lightweight timeline visualization for logs and spans.
651
+ * Features:
652
+ * - Logs displayed on time axis
653
+ * - Spans visualized as bars
654
+ * - Zoom to last X seconds
655
+ * - Hover for details
656
+ */
657
+ /**
658
+ * Timeline configuration
659
+ */
660
+ export declare interface TimelineConfig {
661
+ /** Container element or selector */
662
+ container: HTMLElement | string;
663
+ /** Time window in milliseconds (default: 60000 = 1 minute) */
664
+ timeWindow?: number;
665
+ /** Auto-refresh interval in ms (default: 1000) */
666
+ refreshInterval?: number;
667
+ /** Show span bars (default: true) */
668
+ showSpans?: boolean;
669
+ /** Show log markers (default: true) */
670
+ showLogs?: boolean;
671
+ /** Height of timeline in pixels (default: 200) */
672
+ height?: number;
673
+ }
674
+
675
+ /**
676
+ * Function to unsubscribe from log events
677
+ */
678
+ export declare type Unsubscribe = () => void;
679
+
680
+ export declare const VERSION = "0.1.0";
681
+
682
+ export { }