import { PiniaPluginContext, StoreGeneric, _ActionsTree, _StoreOnActionListenerContext } from 'pinia'; const cloneDeep = (obj: T): T => { try { return JSON.parse(JSON.stringify(obj)); } catch { return { ...obj }; } }; const formatTime = (date = new Date()) => { const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); const seconds = date.getSeconds().toString().padStart(2, '0'); return `${hours}:${minutes}:${seconds}`; }; export interface PiniaLoggerOptions { disabled?: boolean; expanded?: boolean; showDuration?: boolean showStoreName?: boolean; logErrors?: boolean; } export type PiniaActionListenerContext = _StoreOnActionListenerContext; const defaultOptions: PiniaLoggerOptions = { logErrors: true, disabled: false, expanded: true, showStoreName: true, showDuration: false, }; export const PiniaLogger = (config = defaultOptions) => (ctx: PiniaPluginContext) => { const options = { ...defaultOptions, ...config, }; if (options.disabled) return; ctx.store.$onAction((action: PiniaActionListenerContext) => { const startTime = Date.now(); const prevState = cloneDeep(ctx.store.$state); const log = (isError?: boolean, error?: any) => { const endTime = Date.now(); const duration = endTime - startTime + 'ms'; const nextState = cloneDeep(ctx.store.$state); const storeName = action.store.$id; const title = `${formatTime()} action 🍍 ${options.showStoreName ? `[${storeName}] ` : ''}${action.name} ${isError ? `failed after ` : ''}in ${duration}`; console[options.expanded ? 'group' : 'groupCollapsed'](`%c${title}`, `font-weight: bold; ${isError ? 'color: #ed4981;' : ''}`); console.log('%cprev state', 'font-weight: bold; color: grey;', prevState); console.log('%caction', 'font-weight: bold; color: #69B7FF;', { type: action.name, args: action.args.length > 0 ? { ...action.args } : undefined, ...(options.showStoreName && { store: action.store.$id }), ...(options.showDuration && { duration }), ...(isError && { error }), }); console.log('%cnext state', 'font-weight: bold; color: #4caf50;', nextState); console.groupEnd(); }; action.after(() => { log(); }); if (options.logErrors) { action.onError((error) => { log(true, error); }); } }); }; export default PiniaLogger;