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< StoreGeneric, string, _ActionsTree >; 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;