freelii-passkey-kit
Version:
A helper library for creating and using smart wallet accounts on the Stellar blockchain.
130 lines (108 loc) • 4.26 kB
text/typescript
import type { LoggingConfig, LoggerTransport } from './types';
// Check if we're in a browser environment
const isBrowser = typeof window !== 'undefined';
// Browser-compatible logger interface
interface BrowserLogger {
info: (msg: string, ...args: any[]) => void;
error: (msg: string, ...args: any[]) => void;
warn: (msg: string, ...args: any[]) => void;
debug: (msg: string, ...args: any[]) => void;
child: (obj: any) => BrowserLogger;
}
let logger: any;
// Create a browser-compatible console logger
function createBrowserLogger(name: string = 'passkey-kit'): BrowserLogger {
const prefix = `[${name}]`;
return {
info: (msg: string, ...args: any[]) => console.info(prefix, msg, ...args),
error: (msg: string, ...args: any[]) => console.error(prefix, msg, ...args),
warn: (msg: string, ...args: any[]) => console.warn(prefix, msg, ...args),
debug: (msg: string, ...args: any[]) => console.debug(prefix, msg, ...args),
child: (obj: any) => createBrowserLogger(`${name}:${Object.keys(obj).join(':')}`)
};
}
function isPinoLogger(obj: any): boolean {
return obj &&
typeof obj === 'object' &&
typeof obj.info === 'function' &&
typeof obj.error === 'function' &&
typeof obj.warn === 'function' &&
typeof obj.debug === 'function' &&
typeof obj.child === 'function';
}
export class LoggingService {
static async init(config: LoggingConfig | any) {
if (isPinoLogger(config)) {
logger = config;
return;
}
// Always use browser logger first as fallback
logger = createBrowserLogger(config.name || 'passkey-kit');
// Only try pino in Node.js environment and if it's available
if (!isBrowser) {
try {
// Check if pino is available without importing it
const pinoModule = await import('pino').catch(() => null);
if (!pinoModule) return;
const prettyModule = await import('pino-pretty').catch(() => null);
const { Transform } = await import('stream').catch(() => ({ Transform: null }));
if (pinoModule && prettyModule && Transform) {
const pinoInstance = pinoModule.default || pinoModule;
const prettyInstance = prettyModule.default || prettyModule;
const streams: any[] = [];
const level = config.level || 'info';
const prettyStream: any = prettyInstance({ colorize: true });
streams.push({ stream: prettyStream, level });
Object.entries(config.transports || {}).forEach(([, stream]) => {
streams.push({ stream, level });
});
logger = pinoInstance({ name: config.name || 'passkey-kit', level }, pinoInstance.multistream(streams));
}
} catch (error) {
// Keep browser logger fallback
}
}
}
static get(): any {
if (!logger) {
logger = createBrowserLogger('passkey-kit');
// Only try pino in Node.js environment
if (!isBrowser) {
import('pino').then(async (pinoModule) => {
try {
const pinoInstance = pinoModule.default || pinoModule;
const prettyModule = await import('pino-pretty');
const prettyInstance = prettyModule.default || prettyModule;
logger = pinoInstance({ name: 'passkey-kit' }, pinoInstance.multistream([
{ stream: prettyInstance({ colorize: true }) }
]));
} catch (error) {
// Keep existing browser logger
}
}).catch(() => {
// Keep existing browser logger
});
}
}
return logger;
}
}
export function createCustomTransport(transform: any): LoggerTransport {
if (isBrowser) {
// In browser, return a simple writable stream-like object
return {
write: (chunk: any) => console.log(chunk),
retrieveLogs: () => {
throw new Error('retrieveLogs not implemented for browser transport');
}
} as LoggerTransport;
}
const transport = transform as LoggerTransport;
// Add optional retrieveLogs method if not present
if (!transport.retrieveLogs) {
transport.retrieveLogs = () => {
throw new Error('retrieveLogs not implemented for this transport');
};
}
return transport;
}