An in-browser wisp.place site explorer
1/**
2 * Universal logging utility with levels and context support
3 */
4
5export enum LogLevel {
6 DEBUG = 0,
7 INFO = 1,
8 WARN = 2,
9 ERROR = 3,
10}
11
12export interface LoggerOptions {
13 level?: LogLevel;
14 prefix?: string;
15 json?: boolean;
16}
17
18export class Logger {
19 private level: LogLevel;
20 private prefix: string;
21 private json: boolean;
22
23 constructor(options: LoggerOptions = {}) {
24 this.level = options.level ?? LogLevel.INFO;
25 this.prefix = options.prefix ?? '';
26 this.json = options.json ?? false;
27
28 // Browser: check VITE_DEBUG flag
29 if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_DEBUG === 'true' && this.level > LogLevel.DEBUG) {
30 this.level = LogLevel.DEBUG;
31 }
32 }
33
34 private shouldLog(level: LogLevel): boolean {
35 return level >= this.level;
36 }
37
38 private formatMessage(level: string, message: string, meta?: unknown): string {
39 const timestamp = new Date().toISOString();
40 const prefix = this.prefix ? `[${this.prefix}] ` : '';
41
42 if (this.json) {
43 return JSON.stringify({
44 timestamp,
45 level,
46 prefix: this.prefix || undefined,
47 message,
48 ...(meta && typeof meta === 'object' ? meta : { value: meta }),
49 });
50 }
51
52 const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';
53 return `${timestamp} ${prefix}${level}: ${message}${metaStr}`;
54 }
55
56 debug(message: string, meta?: unknown): void {
57 if (this.shouldLog(LogLevel.DEBUG)) {
58 const formatted = this.formatMessage('DEBUG', message, meta);
59 console.debug(formatted);
60 }
61 }
62
63 info(message: string, meta?: unknown): void {
64 if (this.shouldLog(LogLevel.INFO)) {
65 const formatted = this.formatMessage('INFO', message, meta);
66 console.log(formatted);
67 }
68 }
69
70 warn(message: string, meta?: unknown): void {
71 if (this.shouldLog(LogLevel.WARN)) {
72 const formatted = this.formatMessage('WARN', message, meta);
73 console.warn(formatted);
74 }
75 }
76
77 error(message: string, meta?: unknown): void {
78 if (this.shouldLog(LogLevel.ERROR)) {
79 const formatted = this.formatMessage('ERROR', message, meta);
80 console.error(formatted);
81 }
82 }
83
84 child(prefix: string): Logger {
85 return new Logger({
86 level: this.level,
87 prefix: this.prefix ? `${this.prefix}:${prefix}` : prefix,
88 json: this.json,
89 });
90 }
91
92 setLevel(level: LogLevel): void {
93 this.level = level;
94 }
95}
96
97/**
98 * Create a logger instance with default settings
99 */
100export function createLogger(options?: LoggerOptions): Logger {
101 // Universal environment detection
102 const isBrowser = typeof import.meta !== 'undefined';
103 const isDev = isBrowser
104 ? (import.meta.env.DEV ?? true)
105 : (process.env.NODE_ENV !== 'production');
106
107 const defaultLevel = isDev ? LogLevel.DEBUG : LogLevel.INFO;
108
109 return new Logger({
110 level: options?.level ?? defaultLevel,
111 prefix: options?.prefix,
112 json: options?.json ?? !isDev,
113 });
114}
115
116/**
117 * Default logger instance
118 */
119export const logger = createLogger({ prefix: 'wisp' });