const LOGGER_ICON_DICTIONARY = { // Lifecycle "๐Ÿ”จ": `Create state`, "๐Ÿ› ๏ธ": `Install state into store`, // Families "๐Ÿ‘ช": `Family member added`, "๐Ÿ”ฅ": `Family member deleted`, // Cache "๐Ÿ“ƒ": `Copy mutable`, "๐Ÿ“–": `Read from cache`, "๐Ÿ“": `Write to cache`, "โ”": `Cache miss`, "โœจ": `Value derived`, "โญ": `Value set`, // Operations "โญ•": `Operation start`, "๐Ÿ”ด": `Operation complete`, "๐Ÿšซ": `Operation blocked`, "๐ŸŸข": `Operation unblocked`, // Selectors "๐Ÿ—‘": `Evict cached value`, "๐Ÿงน": `Prepare to evict`, "๐Ÿงฎ": `Computing selector`, "๐Ÿ”Œ": `Register direct dependency`, "๐Ÿ”": `Discover root`, // Transactions "๐Ÿ“": `Stow update`, "๐Ÿ›ซ": `Begin transaction`, "๐Ÿ›„": `Apply transaction`, "๐Ÿ›ฌ": `Complete transaction`, "๐Ÿ’ฅ": `Transaction caught error`, "๐Ÿช‚": `Abort transaction`, // Timelines "โŒ›": `Timeline event fully captured`, "โฉ": `Timeline redo`, "โช": `Timeline undo`, "โญ๏ธ": `Transaction redo`, "โฎ๏ธ": `Transaction undo`, "โณ": `Timeline event partially captured`, "โธ๏ธ": `Time-travel complete`, "๐Ÿงผ": `Timeline cleared`, // Problems "๐Ÿ’ฃ": `Dangerous action likely to cause bad errors down the line`, "โ—": `Dangerous action unless in development mode`, "โŒ": `Conflict prevents attempted action`, "๐Ÿž": `Possible bug in AtomIO`, "๐Ÿ’": `Advice and guidance to the developer`, // Subscriptions "๐Ÿ‘€": `Subscription added`, "๐Ÿ™ˆ": `Subscription canceled`, "๐Ÿ“ข": `Notify subscribers`, // Realtime "๐Ÿ›ฐ๏ธ": `Server socket`, "๐Ÿ“ก": `Client socket`, "๐Ÿ‘": `Successful operation`, } as const export type LoggerIcon = keyof typeof LOGGER_ICON_DICTIONARY export type EntityDenomination = | `atom_family` | `atom` | `key` | `mutable_atom_family` | `mutable_atom` | `readonly_held_selector_family` | `readonly_held_selector` | `readonly_pure_selector_family` | `readonly_pure_selector` | `socket` | `state` | `timeline` | `timeline_family` | `transaction` | `unknown` | `user` | `writable_held_selector_family` | `writable_held_selector` | `writable_pure_selector_family` | `writable_pure_selector` export const PRETTY_ENTITY_NAMES: Record = { atom_family: `atom family`, atom: `atom`, key: `key`, mutable_atom_family: `atom family [m]`, mutable_atom: `atom [m]`, readonly_held_selector_family: `selector family [h]`, readonly_held_selector: `selector [h]`, readonly_pure_selector_family: `selector family`, readonly_pure_selector: `selector`, socket: `socket`, state: `state`, timeline: `timeline`, timeline_family: `timeline family`, transaction: `transaction`, unknown: `unknown`, user: `๐Ÿ‘ค`, writable_held_selector_family: `selector family [wh]`, writable_held_selector: `selector [wh]`, writable_pure_selector_family: `selector family [w]`, writable_pure_selector: `selector [w]`, } export const LOG_LEVELS = [`info`, `warn`, `error`] as const export type LogLevel = (typeof LOG_LEVELS)[number] export type LogFn = ( icon: LoggerIcon, denomination: EntityDenomination, tokenKey: string, message: string, ...rest: unknown[] ) => void export type LogFilter = ( ...params: Parameters ) => Parameters | boolean export type Logger = Record export const simpleLog = (logLevel: keyof Logger, prefix?: string): LogFn => (icon, denomination, tokenKey, message, ...rest) => { /* eslint-disable-next-line no-console */ console[logLevel]( `${prefix ? `${prefix} ` : ``}${icon} ${PRETTY_ENTITY_NAMES[denomination]} \`${tokenKey}\` ${message}`, ...rest, ) } export const simpleLogger: Logger = { error: simpleLog(`error`), info: simpleLog(`info`), warn: simpleLog(`warn`), } export class AtomIOLogger implements Logger { public logLevel: `error` | `info` | `warn` | null public filter: LogFilter | undefined private readonly logger: Logger public constructor( logLevel: `error` | `info` | `warn` | null, filter?: LogFilter, logger: Logger = simpleLogger, ) { this.logLevel = logLevel this.filter = filter this.logger = logger } public error: LogFn = (...args) => { if (this.logLevel !== null) { const filterResult = this.filter?.(...args) ?? true if (filterResult === true) { this.logger.error(...args) } else if (filterResult) { this.logger.error(...filterResult) } } } public info: LogFn = (...args) => { if (this.logLevel === `info`) { const filterResult = this.filter?.(...args) ?? true if (filterResult === true) { this.logger.info(...args) } else if (filterResult) { this.logger.info(...filterResult) } } } public warn: LogFn = (...args) => { if (this.logLevel !== `error` && this.logLevel !== null) { const filterResult = this.filter?.(...args) ?? true if (filterResult === true) { this.logger.warn(...args) } else if (filterResult) { this.logger.warn(...filterResult) } } } }