@blac/logging-plugin 2.0.16 → 2.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"LoggingPlugin.d.ts","sourceRoot":"","sources":["../src/LoggingPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,OAAO,EACP,aAAa,EAEd,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EACV,mBAAmB,EAGpB,MAAM,SAAS,CAAC;AAsBjB,qBAAa,aAAc,YAAW,UAAU;IAC9C,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,WAAW;IAE3B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,gBAAgB,CAAmB;IAG3C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,sBAAsB,CAAc;IAC5C,OAAO,CAAC,0BAA0B,CAAS;gBAE/B,MAAM,GAAE,mBAAwB;IAmB5C,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAWnC,WAAW,IAAI,IAAI;IAenB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAwBnC,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5B,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,CAAC,EAChB,YAAY,EAAE,CAAC,EACf,KAAK,EAAE,OAAO,GACb,IAAI;IA8DP,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IA6BrC,QAAQ,IAAI,IAAI;IAWhB,OAAO,CAAC,SAAS;IAyBjB,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,UAAU;CAOnB"}
1
+ {"version":3,"file":"LoggingPlugin.d.ts","sourceRoot":"","sources":["../src/LoggingPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,OAAO,EACP,aAAa,EAEd,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EACV,mBAAmB,EAGpB,MAAM,SAAS,CAAC;AA0BjB,qBAAa,aAAc,YAAW,UAAU;IAC9C,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,WAAW;IAE3B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,gBAAgB,CAAmB;IAG3C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,sBAAsB,CAAc;IAC5C,OAAO,CAAC,0BAA0B,CAAS;gBAE/B,MAAM,GAAE,mBAAwB;IAmB5C,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAWnC,WAAW,IAAI,IAAI;IAenB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAwBnC,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5B,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,CAAC,EAChB,YAAY,EAAE,CAAC,EACf,KAAK,EAAE,OAAO,GACb,IAAI;IA8DP,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IA6BrC,QAAQ,IAAI,IAAI;IAWhB,OAAO,CAAC,SAAS;IAyBjB,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,UAAU;CAOnB"}
package/dist/index.cjs CHANGED
@@ -263,7 +263,7 @@ const STATE_CHANGE_RATE_LIMIT = 1e3;
263
263
  */
264
264
  function decodePaths(paths, interner) {
265
265
  if (paths === _blac_core.ALL_PATHS) return ["<all>"];
266
- return Array.from(paths).map((id) => interner.lookup(id));
266
+ return Array.from(new Set(Array.from(paths).map((id) => interner.lookup(id))));
267
267
  }
268
268
  var LoggingPlugin = class {
269
269
  constructor(config = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["ALL_PATHS"],"sources":["../src/formatters/SimpleFormatter.ts","../src/formatters/GroupedFormatter.ts","../src/monitors/InstanceCountMonitor.ts","../src/monitors/LifecycleMonitor.ts","../src/types.ts","../src/LoggingPlugin.ts"],"sourcesContent":["import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class SimpleFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.logger.log(\n `${this.prefix} Plugin installed (${stats.registeredTypes} types, ${stats.totalInstances} instances)`,\n );\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.logger.log(`${this.prefix} Created ${instanceId}`);\n }\n\n logInstanceDisposed(metadata: InstanceMetadata, lifespanMs: number): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const lifespan = this.formatDuration(lifespanMs);\n this.logger.log(\n `${this.prefix} Disposed ${instanceId} (lived ${lifespan})`,\n );\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n _callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const prev = this.formatState(previousState);\n const curr = this.formatState(currentState);\n this.logger.log(`${this.prefix} ${instanceId} state: ${prev} → ${curr}`);\n\n if (paths !== undefined) {\n this.logger.log(`${this.prefix} Paths: ${paths.join(', ')}`);\n }\n\n if (includeCallstack && _callstack) {\n this.logger.log(`${this.prefix} Callstack: ${_callstack}`);\n }\n }\n\n logWarning(message: string): void {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.logger.log(\n `${this.prefix} Stats: ${stats.totalInstances} instances across ${stats.registeredTypes} types`,\n );\n }\n\n private formatState(state: unknown): string {\n if (state === null) return 'null';\n if (state === undefined) return 'undefined';\n if (typeof state === 'object') {\n try {\n const str = JSON.stringify(state);\n return str.length > 50 ? str.slice(0, 50) + '...' : str;\n } catch {\n return '[Object]';\n }\n }\n return JSON.stringify(state);\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class GroupedFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n private group(label: string, collapsed = true): void {\n if (collapsed && this.logger.groupCollapsed) {\n this.logger.groupCollapsed(label);\n } else if (this.logger.group) {\n this.logger.group(label);\n } else {\n this.logger.log(label);\n }\n }\n\n private groupEnd(): void {\n if (this.logger.groupEnd) {\n this.logger.groupEnd();\n }\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.group(`${this.prefix} Plugin installed`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.groupEnd();\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata, initialState: unknown): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Created ${instanceId}`);\n this.logger.log('Class:', metadata.className);\n this.logger.log('Instance ID:', metadata.id);\n this.logger.log('Initial state:', initialState);\n this.groupEnd();\n }\n\n logInstanceDisposed(\n metadata: InstanceMetadata,\n lifespanMs: number,\n stateChangeCount: number,\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Disposed ${instanceId}`);\n this.logger.log('Lifespan:', this.formatDuration(lifespanMs));\n this.logger.log('State changes:', stateChangeCount);\n this.logger.log('Final state:', metadata.state);\n this.groupEnd();\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} ${instanceId} state changed`);\n this.logger.log('Previous:', previousState);\n this.logger.log('Current:', currentState);\n if (paths !== undefined) {\n this.logger.log('Paths:', paths);\n }\n if (includeCallstack && callstack) {\n this.logger.log('Callstack:', callstack);\n }\n this.groupEnd();\n }\n\n logWarning(message: string, details?: Record<string, unknown>): void {\n if (details) {\n this.group(`${this.prefix} ⚠️ ${message}`, false);\n for (const [key, value] of Object.entries(details)) {\n this.logger.log(`${key}:`, value);\n }\n this.groupEnd();\n } else {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.group(`${this.prefix} Stats`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.logger.log('Breakdown:', stats.typeBreakdown);\n this.groupEnd();\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { ResolvedConfig } from '../types';\n\nexport class InstanceCountMonitor {\n private instanceCounts = new Map<string, number>();\n private warnedClasses = new Set<string>();\n private threshold: number;\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.threshold = config.instanceCountWarningThreshold;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string): void {\n const count = (this.instanceCounts.get(className) ?? 0) + 1;\n this.instanceCounts.set(className, count);\n\n if (count >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `High instance count: ${className} has ${count} instances`,\n {\n className,\n count,\n threshold: this.threshold,\n },\n );\n }\n }\n\n onInstanceDisposed(className: string): void {\n const count = this.instanceCounts.get(className) ?? 0;\n if (count > 0) {\n this.instanceCounts.set(className, count - 1);\n }\n\n if (count - 1 < this.threshold) {\n this.warnedClasses.delete(className);\n }\n }\n\n getStats(): Map<string, number> {\n return new Map(this.instanceCounts);\n }\n\n reset(): void {\n this.instanceCounts.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { ResolvedConfig } from '../types';\n\ninterface LifecycleEvent {\n timestamp: number;\n type: 'created' | 'disposed';\n}\n\ninterface InstanceInfo {\n createdAt: number;\n stateChangeCount: number;\n}\n\nexport class LifecycleMonitor {\n private lifecycleHistory = new Map<string, LifecycleEvent[]>();\n private instanceInfo = new Map<string, InstanceInfo>();\n private windowMs: number;\n private threshold: number;\n private enabled: boolean;\n private warnedClasses = new Set<string>();\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.windowMs = config.rapidLifecycleWindowMs;\n this.threshold = config.rapidLifecycleThreshold;\n this.enabled = config.detectRapidLifecycles;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.set(instanceId, {\n createdAt: Date.now(),\n stateChangeCount: 0,\n });\n\n this.recordEvent(className, 'created');\n this.checkRapidLifecycle(className);\n }\n\n onInstanceDisposed(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.delete(instanceId);\n this.recordEvent(className, 'disposed');\n this.checkRapidLifecycle(className);\n }\n\n onStateChanged(instanceId: string): void {\n const info = this.instanceInfo.get(instanceId);\n if (info) {\n info.stateChangeCount++;\n }\n }\n\n getInstanceInfo(instanceId: string): InstanceInfo | undefined {\n return this.instanceInfo.get(instanceId);\n }\n\n private recordEvent(className: string, type: 'created' | 'disposed'): void {\n const now = Date.now();\n const events = this.lifecycleHistory.get(className) ?? [];\n\n events.push({ timestamp: now, type });\n\n const cutoff = now - this.windowMs;\n const filtered = events.filter((e) => e.timestamp > cutoff);\n this.lifecycleHistory.set(className, filtered);\n }\n\n private checkRapidLifecycle(className: string): void {\n const events = this.lifecycleHistory.get(className) ?? [];\n const cycleCount = Math.min(\n events.filter((e) => e.type === 'created').length,\n events.filter((e) => e.type === 'disposed').length,\n );\n\n if (cycleCount >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `Rapid lifecycle: ${className} created/disposed ${cycleCount} times in ${this.windowMs}ms`,\n {\n className,\n cycleCount,\n windowMs: this.windowMs,\n threshold: this.threshold,\n },\n );\n\n setTimeout(() => {\n this.warnedClasses.delete(className);\n }, this.windowMs);\n }\n }\n\n reset(): void {\n this.lifecycleHistory.clear();\n this.instanceInfo.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { StateContainer } from '@blac/core';\n\nexport type LogLevel = 'minimal' | 'info' | 'debug' | 'verbose';\n\nexport type LogFormat = 'simple' | 'grouped';\n\nexport interface Logger {\n log(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n group?(label: string): void;\n groupEnd?(): void;\n groupCollapsed?(label: string): void;\n}\n\nexport interface FilterContext {\n instance: StateContainer<any>;\n className: string;\n instanceId: string;\n}\n\nexport type FilterFn = (ctx: FilterContext) => boolean;\n\nexport interface LoggingPluginConfig {\n /** Log verbosity level (default: 'info') */\n level?: LogLevel;\n\n /** Output format (default: 'grouped') */\n format?: LogFormat;\n\n /** Custom logger implementation (default: console) */\n logger?: Logger;\n\n /** Filter which instances to log */\n filter?: FilterFn;\n\n /** Include class names to log (whitelist) */\n include?: string[];\n\n /** Exclude class names from logging (blacklist) */\n exclude?: string[];\n\n /** Log instance creation/disposal (default: true) */\n logLifecycle?: boolean;\n\n /** Log state changes (default: true) */\n logStateChanges?: boolean;\n\n /** Include callstack in state change logs (default: false) */\n includeCallstack?: boolean;\n\n /** Log changed path names in state change output (default: false) */\n logPaths?: boolean;\n\n /** Warn when instance count exceeds threshold (default: 50) */\n instanceCountWarningThreshold?: number;\n\n /** Warn on rapid init/dispose cycles (default: true) */\n detectRapidLifecycles?: boolean;\n\n /** Time window for rapid lifecycle detection in ms (default: 1000) */\n rapidLifecycleWindowMs?: number;\n\n /** Number of cycles in window to trigger warning (default: 5) */\n rapidLifecycleThreshold?: number;\n\n /** Log prefix (default: '[BlaC]') */\n prefix?: string;\n}\n\nexport interface ResolvedConfig {\n level: LogLevel;\n format: LogFormat;\n logger: Logger;\n filter: FilterFn | undefined;\n include: string[] | undefined;\n exclude: string[] | undefined;\n logLifecycle: boolean;\n logStateChanges: boolean;\n includeCallstack: boolean;\n logPaths: boolean;\n instanceCountWarningThreshold: number;\n detectRapidLifecycles: boolean;\n rapidLifecycleWindowMs: number;\n rapidLifecycleThreshold: number;\n prefix: string;\n}\n\nexport function resolveConfig(\n config: LoggingPluginConfig = {},\n): ResolvedConfig {\n return {\n level: config.level ?? 'info',\n format: config.format ?? 'grouped',\n logger: config.logger ?? console,\n filter: config.filter,\n include: config.include,\n exclude: config.exclude,\n logLifecycle: config.logLifecycle ?? true,\n logStateChanges: config.logStateChanges ?? true,\n includeCallstack: config.includeCallstack ?? false,\n logPaths: config.logPaths ?? false,\n instanceCountWarningThreshold: config.instanceCountWarningThreshold ?? 50,\n detectRapidLifecycles: config.detectRapidLifecycles ?? true,\n rapidLifecycleWindowMs: config.rapidLifecycleWindowMs ?? 1000,\n rapidLifecycleThreshold: config.rapidLifecycleThreshold ?? 5,\n prefix: config.prefix ?? '[BlaC]',\n };\n}\n","import type {\n BlacPlugin,\n PathSet,\n PluginContext,\n StateContainer,\n} from '@blac/core';\nimport { ALL_PATHS } from '@blac/core';\nimport { SimpleFormatter } from './formatters/SimpleFormatter';\nimport { GroupedFormatter } from './formatters/GroupedFormatter';\nimport { InstanceCountMonitor } from './monitors/InstanceCountMonitor';\nimport { LifecycleMonitor } from './monitors/LifecycleMonitor';\nimport type {\n LoggingPluginConfig,\n ResolvedConfig,\n FilterContext,\n} from './types';\nimport { resolveConfig } from './types';\n\nconst STATE_CHANGE_RATE_LIMIT = 1000; // max state changes per second\n\n/**\n * Decode a `PathSet` to an array of path name strings.\n *\n * Returns `['<all>']` when `paths === ALL_PATHS`.\n * Uses `interner.lookup(id)` for all other IDs.\n *\n * The `interner` is typed structurally to avoid a direct compile-time\n * dependency on `@dirtytalk/structural` from this package.\n */\nfunction decodePaths(\n paths: PathSet,\n interner: { lookup(id: number): string },\n): string[] {\n if (paths === ALL_PATHS) return ['<all>'];\n return Array.from(paths as Set<number>).map((id) => interner.lookup(id));\n}\n\nexport class LoggingPlugin implements BlacPlugin {\n readonly name = 'LoggingPlugin';\n readonly version = '1.0.0';\n\n private config: ResolvedConfig;\n private context?: PluginContext;\n private simpleFormatter: SimpleFormatter;\n private groupedFormatter: GroupedFormatter;\n private instanceCountMonitor: InstanceCountMonitor;\n private lifecycleMonitor: LifecycleMonitor;\n\n // Rate limiting for state changes\n private stateChangeCount = 0;\n private stateChangeWindowStart = Date.now();\n private stateChangeLoggingDisabled = false;\n\n constructor(config: LoggingPluginConfig = {}) {\n this.config = resolveConfig(config);\n\n const warningHandler = (\n message: string,\n details?: Record<string, unknown>,\n ) => {\n this.logWarning(message, details);\n };\n\n this.simpleFormatter = new SimpleFormatter(this.config);\n this.groupedFormatter = new GroupedFormatter(this.config);\n this.instanceCountMonitor = new InstanceCountMonitor(\n this.config,\n warningHandler,\n );\n this.lifecycleMonitor = new LifecycleMonitor(this.config, warningHandler);\n }\n\n onInstall(ctx: PluginContext): void {\n this.context = ctx;\n const stats = ctx.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstall(stats);\n } else {\n this.groupedFormatter.logInstall(stats);\n }\n }\n\n onUninstall(): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logUninstall();\n } else {\n this.groupedFormatter.logUninstall();\n }\n\n this.instanceCountMonitor.reset();\n this.lifecycleMonitor.reset();\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = Date.now();\n this.stateChangeLoggingDisabled = false;\n this.context = undefined;\n }\n\n onCreated(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.instanceCountMonitor.onInstanceCreated(metadata.className);\n this.lifecycleMonitor.onInstanceCreated(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceCreated(metadata);\n } else {\n this.groupedFormatter.logInstanceCreated(\n metadata,\n ctx.getState(instance),\n );\n }\n }\n\n onStateChange<S extends object>(\n ctx: PluginContext,\n previousState: S,\n currentState: S,\n paths: PathSet,\n ): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.lifecycleMonitor.onStateChanged(metadata.id);\n\n if (!this.shouldLogStateChanges()) return;\n\n // Check rate limit\n if (this.stateChangeLoggingDisabled) return;\n\n const now = Date.now();\n if (now - this.stateChangeWindowStart >= 1000) {\n // Reset window\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = now;\n }\n\n this.stateChangeCount++;\n\n if (this.stateChangeCount > STATE_CHANGE_RATE_LIMIT) {\n this.stateChangeLoggingDisabled = true;\n this.logWarning(\n `State change logging disabled: exceeded ${STATE_CHANGE_RATE_LIMIT} changes/second`,\n );\n return;\n }\n\n const callstack = this.config.includeCallstack\n ? new Error().stack\n : undefined;\n\n const decodedPaths = this.config.logPaths\n ? decodePaths(paths, instance.interner)\n : undefined;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n } else {\n this.groupedFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n }\n }\n\n onDestroyed(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n const info = this.lifecycleMonitor.getInstanceInfo(metadata.id);\n const lifespanMs = info ? Date.now() - info.createdAt : 0;\n const stateChangeCount = info?.stateChangeCount ?? 0;\n\n this.instanceCountMonitor.onInstanceDisposed(metadata.className);\n this.lifecycleMonitor.onInstanceDisposed(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceDisposed(metadata, lifespanMs);\n } else {\n this.groupedFormatter.logInstanceDisposed(\n metadata,\n lifespanMs,\n stateChangeCount,\n );\n }\n }\n\n logStats(): void {\n if (!this.context) return;\n const stats = this.context.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStats(stats);\n } else {\n this.groupedFormatter.logStats(stats);\n }\n }\n\n private shouldLog(\n instance: StateContainer<any>,\n className: string,\n instanceId: string,\n ): boolean {\n if (this.config.include && !this.config.include.includes(className)) {\n return false;\n }\n\n if (this.config.exclude?.includes(className)) {\n return false;\n }\n\n if (this.config.filter) {\n const filterCtx: FilterContext = {\n instance,\n className,\n instanceId,\n };\n return this.config.filter(filterCtx);\n }\n\n return true;\n }\n\n private shouldLogLifecycle(): boolean {\n if (!this.config.logLifecycle) return false;\n return this.config.level !== 'minimal';\n }\n\n private shouldLogStateChanges(): boolean {\n if (!this.config.logStateChanges) return false;\n return (\n this.config.level === 'info' ||\n this.config.level === 'debug' ||\n this.config.level === 'verbose'\n );\n }\n\n private logWarning(message: string, details?: Record<string, unknown>): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logWarning(message);\n } else {\n this.groupedFormatter.logWarning(message, details);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["ALL_PATHS"],"sources":["../src/formatters/SimpleFormatter.ts","../src/formatters/GroupedFormatter.ts","../src/monitors/InstanceCountMonitor.ts","../src/monitors/LifecycleMonitor.ts","../src/types.ts","../src/LoggingPlugin.ts"],"sourcesContent":["import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class SimpleFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.logger.log(\n `${this.prefix} Plugin installed (${stats.registeredTypes} types, ${stats.totalInstances} instances)`,\n );\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.logger.log(`${this.prefix} Created ${instanceId}`);\n }\n\n logInstanceDisposed(metadata: InstanceMetadata, lifespanMs: number): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const lifespan = this.formatDuration(lifespanMs);\n this.logger.log(\n `${this.prefix} Disposed ${instanceId} (lived ${lifespan})`,\n );\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n _callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const prev = this.formatState(previousState);\n const curr = this.formatState(currentState);\n this.logger.log(`${this.prefix} ${instanceId} state: ${prev} → ${curr}`);\n\n if (paths !== undefined) {\n this.logger.log(`${this.prefix} Paths: ${paths.join(', ')}`);\n }\n\n if (includeCallstack && _callstack) {\n this.logger.log(`${this.prefix} Callstack: ${_callstack}`);\n }\n }\n\n logWarning(message: string): void {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.logger.log(\n `${this.prefix} Stats: ${stats.totalInstances} instances across ${stats.registeredTypes} types`,\n );\n }\n\n private formatState(state: unknown): string {\n if (state === null) return 'null';\n if (state === undefined) return 'undefined';\n if (typeof state === 'object') {\n try {\n const str = JSON.stringify(state);\n return str.length > 50 ? str.slice(0, 50) + '...' : str;\n } catch {\n return '[Object]';\n }\n }\n return JSON.stringify(state);\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class GroupedFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n private group(label: string, collapsed = true): void {\n if (collapsed && this.logger.groupCollapsed) {\n this.logger.groupCollapsed(label);\n } else if (this.logger.group) {\n this.logger.group(label);\n } else {\n this.logger.log(label);\n }\n }\n\n private groupEnd(): void {\n if (this.logger.groupEnd) {\n this.logger.groupEnd();\n }\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.group(`${this.prefix} Plugin installed`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.groupEnd();\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata, initialState: unknown): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Created ${instanceId}`);\n this.logger.log('Class:', metadata.className);\n this.logger.log('Instance ID:', metadata.id);\n this.logger.log('Initial state:', initialState);\n this.groupEnd();\n }\n\n logInstanceDisposed(\n metadata: InstanceMetadata,\n lifespanMs: number,\n stateChangeCount: number,\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Disposed ${instanceId}`);\n this.logger.log('Lifespan:', this.formatDuration(lifespanMs));\n this.logger.log('State changes:', stateChangeCount);\n this.logger.log('Final state:', metadata.state);\n this.groupEnd();\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} ${instanceId} state changed`);\n this.logger.log('Previous:', previousState);\n this.logger.log('Current:', currentState);\n if (paths !== undefined) {\n this.logger.log('Paths:', paths);\n }\n if (includeCallstack && callstack) {\n this.logger.log('Callstack:', callstack);\n }\n this.groupEnd();\n }\n\n logWarning(message: string, details?: Record<string, unknown>): void {\n if (details) {\n this.group(`${this.prefix} ⚠️ ${message}`, false);\n for (const [key, value] of Object.entries(details)) {\n this.logger.log(`${key}:`, value);\n }\n this.groupEnd();\n } else {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.group(`${this.prefix} Stats`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.logger.log('Breakdown:', stats.typeBreakdown);\n this.groupEnd();\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { ResolvedConfig } from '../types';\n\nexport class InstanceCountMonitor {\n private instanceCounts = new Map<string, number>();\n private warnedClasses = new Set<string>();\n private threshold: number;\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.threshold = config.instanceCountWarningThreshold;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string): void {\n const count = (this.instanceCounts.get(className) ?? 0) + 1;\n this.instanceCounts.set(className, count);\n\n if (count >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `High instance count: ${className} has ${count} instances`,\n {\n className,\n count,\n threshold: this.threshold,\n },\n );\n }\n }\n\n onInstanceDisposed(className: string): void {\n const count = this.instanceCounts.get(className) ?? 0;\n if (count > 0) {\n this.instanceCounts.set(className, count - 1);\n }\n\n if (count - 1 < this.threshold) {\n this.warnedClasses.delete(className);\n }\n }\n\n getStats(): Map<string, number> {\n return new Map(this.instanceCounts);\n }\n\n reset(): void {\n this.instanceCounts.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { ResolvedConfig } from '../types';\n\ninterface LifecycleEvent {\n timestamp: number;\n type: 'created' | 'disposed';\n}\n\ninterface InstanceInfo {\n createdAt: number;\n stateChangeCount: number;\n}\n\nexport class LifecycleMonitor {\n private lifecycleHistory = new Map<string, LifecycleEvent[]>();\n private instanceInfo = new Map<string, InstanceInfo>();\n private windowMs: number;\n private threshold: number;\n private enabled: boolean;\n private warnedClasses = new Set<string>();\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.windowMs = config.rapidLifecycleWindowMs;\n this.threshold = config.rapidLifecycleThreshold;\n this.enabled = config.detectRapidLifecycles;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.set(instanceId, {\n createdAt: Date.now(),\n stateChangeCount: 0,\n });\n\n this.recordEvent(className, 'created');\n this.checkRapidLifecycle(className);\n }\n\n onInstanceDisposed(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.delete(instanceId);\n this.recordEvent(className, 'disposed');\n this.checkRapidLifecycle(className);\n }\n\n onStateChanged(instanceId: string): void {\n const info = this.instanceInfo.get(instanceId);\n if (info) {\n info.stateChangeCount++;\n }\n }\n\n getInstanceInfo(instanceId: string): InstanceInfo | undefined {\n return this.instanceInfo.get(instanceId);\n }\n\n private recordEvent(className: string, type: 'created' | 'disposed'): void {\n const now = Date.now();\n const events = this.lifecycleHistory.get(className) ?? [];\n\n events.push({ timestamp: now, type });\n\n const cutoff = now - this.windowMs;\n const filtered = events.filter((e) => e.timestamp > cutoff);\n this.lifecycleHistory.set(className, filtered);\n }\n\n private checkRapidLifecycle(className: string): void {\n const events = this.lifecycleHistory.get(className) ?? [];\n const cycleCount = Math.min(\n events.filter((e) => e.type === 'created').length,\n events.filter((e) => e.type === 'disposed').length,\n );\n\n if (cycleCount >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `Rapid lifecycle: ${className} created/disposed ${cycleCount} times in ${this.windowMs}ms`,\n {\n className,\n cycleCount,\n windowMs: this.windowMs,\n threshold: this.threshold,\n },\n );\n\n setTimeout(() => {\n this.warnedClasses.delete(className);\n }, this.windowMs);\n }\n }\n\n reset(): void {\n this.lifecycleHistory.clear();\n this.instanceInfo.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { StateContainer } from '@blac/core';\n\nexport type LogLevel = 'minimal' | 'info' | 'debug' | 'verbose';\n\nexport type LogFormat = 'simple' | 'grouped';\n\nexport interface Logger {\n log(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n group?(label: string): void;\n groupEnd?(): void;\n groupCollapsed?(label: string): void;\n}\n\nexport interface FilterContext {\n instance: StateContainer<any>;\n className: string;\n instanceId: string;\n}\n\nexport type FilterFn = (ctx: FilterContext) => boolean;\n\nexport interface LoggingPluginConfig {\n /** Log verbosity level (default: 'info') */\n level?: LogLevel;\n\n /** Output format (default: 'grouped') */\n format?: LogFormat;\n\n /** Custom logger implementation (default: console) */\n logger?: Logger;\n\n /** Filter which instances to log */\n filter?: FilterFn;\n\n /** Include class names to log (whitelist) */\n include?: string[];\n\n /** Exclude class names from logging (blacklist) */\n exclude?: string[];\n\n /** Log instance creation/disposal (default: true) */\n logLifecycle?: boolean;\n\n /** Log state changes (default: true) */\n logStateChanges?: boolean;\n\n /** Include callstack in state change logs (default: false) */\n includeCallstack?: boolean;\n\n /** Log changed path names in state change output (default: false) */\n logPaths?: boolean;\n\n /** Warn when instance count exceeds threshold (default: 50) */\n instanceCountWarningThreshold?: number;\n\n /** Warn on rapid init/dispose cycles (default: true) */\n detectRapidLifecycles?: boolean;\n\n /** Time window for rapid lifecycle detection in ms (default: 1000) */\n rapidLifecycleWindowMs?: number;\n\n /** Number of cycles in window to trigger warning (default: 5) */\n rapidLifecycleThreshold?: number;\n\n /** Log prefix (default: '[BlaC]') */\n prefix?: string;\n}\n\nexport interface ResolvedConfig {\n level: LogLevel;\n format: LogFormat;\n logger: Logger;\n filter: FilterFn | undefined;\n include: string[] | undefined;\n exclude: string[] | undefined;\n logLifecycle: boolean;\n logStateChanges: boolean;\n includeCallstack: boolean;\n logPaths: boolean;\n instanceCountWarningThreshold: number;\n detectRapidLifecycles: boolean;\n rapidLifecycleWindowMs: number;\n rapidLifecycleThreshold: number;\n prefix: string;\n}\n\nexport function resolveConfig(\n config: LoggingPluginConfig = {},\n): ResolvedConfig {\n return {\n level: config.level ?? 'info',\n format: config.format ?? 'grouped',\n logger: config.logger ?? console,\n filter: config.filter,\n include: config.include,\n exclude: config.exclude,\n logLifecycle: config.logLifecycle ?? true,\n logStateChanges: config.logStateChanges ?? true,\n includeCallstack: config.includeCallstack ?? false,\n logPaths: config.logPaths ?? false,\n instanceCountWarningThreshold: config.instanceCountWarningThreshold ?? 50,\n detectRapidLifecycles: config.detectRapidLifecycles ?? true,\n rapidLifecycleWindowMs: config.rapidLifecycleWindowMs ?? 1000,\n rapidLifecycleThreshold: config.rapidLifecycleThreshold ?? 5,\n prefix: config.prefix ?? '[BlaC]',\n };\n}\n","import type {\n BlacPlugin,\n PathSet,\n PluginContext,\n StateContainer,\n} from '@blac/core';\nimport { ALL_PATHS } from '@blac/core';\nimport { SimpleFormatter } from './formatters/SimpleFormatter';\nimport { GroupedFormatter } from './formatters/GroupedFormatter';\nimport { InstanceCountMonitor } from './monitors/InstanceCountMonitor';\nimport { LifecycleMonitor } from './monitors/LifecycleMonitor';\nimport type {\n LoggingPluginConfig,\n ResolvedConfig,\n FilterContext,\n} from './types';\nimport { resolveConfig } from './types';\n\nconst STATE_CHANGE_RATE_LIMIT = 1000; // max state changes per second\n\n/**\n * Decode a `PathSet` to an array of path name strings.\n *\n * Returns `['<all>']` when `paths === ALL_PATHS`.\n * Uses `interner.lookup(id)` for all other IDs.\n *\n * The `interner` is typed structurally to avoid a direct compile-time\n * dependency on `@dirtytalk/structural` from this package.\n */\nfunction decodePaths(\n paths: PathSet,\n interner: { lookup(id: number): string },\n): string[] {\n if (paths === ALL_PATHS) return ['<all>'];\n // `lookup` decodes ancestor-watch ids back to their real path, so a normal\n // mark and its ancestor-watch sibling can decode to the same string — dedup.\n return Array.from(\n new Set(Array.from(paths as Set<number>).map((id) => interner.lookup(id))),\n );\n}\n\nexport class LoggingPlugin implements BlacPlugin {\n readonly name = 'LoggingPlugin';\n readonly version = '1.0.0';\n\n private config: ResolvedConfig;\n private context?: PluginContext;\n private simpleFormatter: SimpleFormatter;\n private groupedFormatter: GroupedFormatter;\n private instanceCountMonitor: InstanceCountMonitor;\n private lifecycleMonitor: LifecycleMonitor;\n\n // Rate limiting for state changes\n private stateChangeCount = 0;\n private stateChangeWindowStart = Date.now();\n private stateChangeLoggingDisabled = false;\n\n constructor(config: LoggingPluginConfig = {}) {\n this.config = resolveConfig(config);\n\n const warningHandler = (\n message: string,\n details?: Record<string, unknown>,\n ) => {\n this.logWarning(message, details);\n };\n\n this.simpleFormatter = new SimpleFormatter(this.config);\n this.groupedFormatter = new GroupedFormatter(this.config);\n this.instanceCountMonitor = new InstanceCountMonitor(\n this.config,\n warningHandler,\n );\n this.lifecycleMonitor = new LifecycleMonitor(this.config, warningHandler);\n }\n\n onInstall(ctx: PluginContext): void {\n this.context = ctx;\n const stats = ctx.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstall(stats);\n } else {\n this.groupedFormatter.logInstall(stats);\n }\n }\n\n onUninstall(): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logUninstall();\n } else {\n this.groupedFormatter.logUninstall();\n }\n\n this.instanceCountMonitor.reset();\n this.lifecycleMonitor.reset();\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = Date.now();\n this.stateChangeLoggingDisabled = false;\n this.context = undefined;\n }\n\n onCreated(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.instanceCountMonitor.onInstanceCreated(metadata.className);\n this.lifecycleMonitor.onInstanceCreated(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceCreated(metadata);\n } else {\n this.groupedFormatter.logInstanceCreated(\n metadata,\n ctx.getState(instance),\n );\n }\n }\n\n onStateChange<S extends object>(\n ctx: PluginContext,\n previousState: S,\n currentState: S,\n paths: PathSet,\n ): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.lifecycleMonitor.onStateChanged(metadata.id);\n\n if (!this.shouldLogStateChanges()) return;\n\n // Check rate limit\n if (this.stateChangeLoggingDisabled) return;\n\n const now = Date.now();\n if (now - this.stateChangeWindowStart >= 1000) {\n // Reset window\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = now;\n }\n\n this.stateChangeCount++;\n\n if (this.stateChangeCount > STATE_CHANGE_RATE_LIMIT) {\n this.stateChangeLoggingDisabled = true;\n this.logWarning(\n `State change logging disabled: exceeded ${STATE_CHANGE_RATE_LIMIT} changes/second`,\n );\n return;\n }\n\n const callstack = this.config.includeCallstack\n ? new Error().stack\n : undefined;\n\n const decodedPaths = this.config.logPaths\n ? decodePaths(paths, instance.interner)\n : undefined;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n } else {\n this.groupedFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n }\n }\n\n onDestroyed(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n const info = this.lifecycleMonitor.getInstanceInfo(metadata.id);\n const lifespanMs = info ? Date.now() - info.createdAt : 0;\n const stateChangeCount = info?.stateChangeCount ?? 0;\n\n this.instanceCountMonitor.onInstanceDisposed(metadata.className);\n this.lifecycleMonitor.onInstanceDisposed(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceDisposed(metadata, lifespanMs);\n } else {\n this.groupedFormatter.logInstanceDisposed(\n metadata,\n lifespanMs,\n stateChangeCount,\n );\n }\n }\n\n logStats(): void {\n if (!this.context) return;\n const stats = this.context.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStats(stats);\n } else {\n this.groupedFormatter.logStats(stats);\n }\n }\n\n private shouldLog(\n instance: StateContainer<any>,\n className: string,\n instanceId: string,\n ): boolean {\n if (this.config.include && !this.config.include.includes(className)) {\n return false;\n }\n\n if (this.config.exclude?.includes(className)) {\n return false;\n }\n\n if (this.config.filter) {\n const filterCtx: FilterContext = {\n instance,\n className,\n instanceId,\n };\n return this.config.filter(filterCtx);\n }\n\n return true;\n }\n\n private shouldLogLifecycle(): boolean {\n if (!this.config.logLifecycle) return false;\n return this.config.level !== 'minimal';\n }\n\n private shouldLogStateChanges(): boolean {\n if (!this.config.logStateChanges) return false;\n return (\n this.config.level === 'info' ||\n this.config.level === 'debug' ||\n this.config.level === 'verbose'\n );\n }\n\n private logWarning(message: string, details?: Record<string, unknown>): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logWarning(message);\n } else {\n this.groupedFormatter.logWarning(message, details);\n }\n }\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -262,7 +262,7 @@ const STATE_CHANGE_RATE_LIMIT = 1e3;
262
262
  */
263
263
  function decodePaths(paths, interner) {
264
264
  if (paths === ALL_PATHS) return ["<all>"];
265
- return Array.from(paths).map((id) => interner.lookup(id));
265
+ return Array.from(new Set(Array.from(paths).map((id) => interner.lookup(id))));
266
266
  }
267
267
  var LoggingPlugin = class {
268
268
  constructor(config = {}) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/formatters/SimpleFormatter.ts","../src/formatters/GroupedFormatter.ts","../src/monitors/InstanceCountMonitor.ts","../src/monitors/LifecycleMonitor.ts","../src/types.ts","../src/LoggingPlugin.ts"],"sourcesContent":["import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class SimpleFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.logger.log(\n `${this.prefix} Plugin installed (${stats.registeredTypes} types, ${stats.totalInstances} instances)`,\n );\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.logger.log(`${this.prefix} Created ${instanceId}`);\n }\n\n logInstanceDisposed(metadata: InstanceMetadata, lifespanMs: number): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const lifespan = this.formatDuration(lifespanMs);\n this.logger.log(\n `${this.prefix} Disposed ${instanceId} (lived ${lifespan})`,\n );\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n _callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const prev = this.formatState(previousState);\n const curr = this.formatState(currentState);\n this.logger.log(`${this.prefix} ${instanceId} state: ${prev} → ${curr}`);\n\n if (paths !== undefined) {\n this.logger.log(`${this.prefix} Paths: ${paths.join(', ')}`);\n }\n\n if (includeCallstack && _callstack) {\n this.logger.log(`${this.prefix} Callstack: ${_callstack}`);\n }\n }\n\n logWarning(message: string): void {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.logger.log(\n `${this.prefix} Stats: ${stats.totalInstances} instances across ${stats.registeredTypes} types`,\n );\n }\n\n private formatState(state: unknown): string {\n if (state === null) return 'null';\n if (state === undefined) return 'undefined';\n if (typeof state === 'object') {\n try {\n const str = JSON.stringify(state);\n return str.length > 50 ? str.slice(0, 50) + '...' : str;\n } catch {\n return '[Object]';\n }\n }\n return JSON.stringify(state);\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class GroupedFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n private group(label: string, collapsed = true): void {\n if (collapsed && this.logger.groupCollapsed) {\n this.logger.groupCollapsed(label);\n } else if (this.logger.group) {\n this.logger.group(label);\n } else {\n this.logger.log(label);\n }\n }\n\n private groupEnd(): void {\n if (this.logger.groupEnd) {\n this.logger.groupEnd();\n }\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.group(`${this.prefix} Plugin installed`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.groupEnd();\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata, initialState: unknown): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Created ${instanceId}`);\n this.logger.log('Class:', metadata.className);\n this.logger.log('Instance ID:', metadata.id);\n this.logger.log('Initial state:', initialState);\n this.groupEnd();\n }\n\n logInstanceDisposed(\n metadata: InstanceMetadata,\n lifespanMs: number,\n stateChangeCount: number,\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Disposed ${instanceId}`);\n this.logger.log('Lifespan:', this.formatDuration(lifespanMs));\n this.logger.log('State changes:', stateChangeCount);\n this.logger.log('Final state:', metadata.state);\n this.groupEnd();\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} ${instanceId} state changed`);\n this.logger.log('Previous:', previousState);\n this.logger.log('Current:', currentState);\n if (paths !== undefined) {\n this.logger.log('Paths:', paths);\n }\n if (includeCallstack && callstack) {\n this.logger.log('Callstack:', callstack);\n }\n this.groupEnd();\n }\n\n logWarning(message: string, details?: Record<string, unknown>): void {\n if (details) {\n this.group(`${this.prefix} ⚠️ ${message}`, false);\n for (const [key, value] of Object.entries(details)) {\n this.logger.log(`${key}:`, value);\n }\n this.groupEnd();\n } else {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.group(`${this.prefix} Stats`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.logger.log('Breakdown:', stats.typeBreakdown);\n this.groupEnd();\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { ResolvedConfig } from '../types';\n\nexport class InstanceCountMonitor {\n private instanceCounts = new Map<string, number>();\n private warnedClasses = new Set<string>();\n private threshold: number;\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.threshold = config.instanceCountWarningThreshold;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string): void {\n const count = (this.instanceCounts.get(className) ?? 0) + 1;\n this.instanceCounts.set(className, count);\n\n if (count >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `High instance count: ${className} has ${count} instances`,\n {\n className,\n count,\n threshold: this.threshold,\n },\n );\n }\n }\n\n onInstanceDisposed(className: string): void {\n const count = this.instanceCounts.get(className) ?? 0;\n if (count > 0) {\n this.instanceCounts.set(className, count - 1);\n }\n\n if (count - 1 < this.threshold) {\n this.warnedClasses.delete(className);\n }\n }\n\n getStats(): Map<string, number> {\n return new Map(this.instanceCounts);\n }\n\n reset(): void {\n this.instanceCounts.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { ResolvedConfig } from '../types';\n\ninterface LifecycleEvent {\n timestamp: number;\n type: 'created' | 'disposed';\n}\n\ninterface InstanceInfo {\n createdAt: number;\n stateChangeCount: number;\n}\n\nexport class LifecycleMonitor {\n private lifecycleHistory = new Map<string, LifecycleEvent[]>();\n private instanceInfo = new Map<string, InstanceInfo>();\n private windowMs: number;\n private threshold: number;\n private enabled: boolean;\n private warnedClasses = new Set<string>();\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.windowMs = config.rapidLifecycleWindowMs;\n this.threshold = config.rapidLifecycleThreshold;\n this.enabled = config.detectRapidLifecycles;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.set(instanceId, {\n createdAt: Date.now(),\n stateChangeCount: 0,\n });\n\n this.recordEvent(className, 'created');\n this.checkRapidLifecycle(className);\n }\n\n onInstanceDisposed(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.delete(instanceId);\n this.recordEvent(className, 'disposed');\n this.checkRapidLifecycle(className);\n }\n\n onStateChanged(instanceId: string): void {\n const info = this.instanceInfo.get(instanceId);\n if (info) {\n info.stateChangeCount++;\n }\n }\n\n getInstanceInfo(instanceId: string): InstanceInfo | undefined {\n return this.instanceInfo.get(instanceId);\n }\n\n private recordEvent(className: string, type: 'created' | 'disposed'): void {\n const now = Date.now();\n const events = this.lifecycleHistory.get(className) ?? [];\n\n events.push({ timestamp: now, type });\n\n const cutoff = now - this.windowMs;\n const filtered = events.filter((e) => e.timestamp > cutoff);\n this.lifecycleHistory.set(className, filtered);\n }\n\n private checkRapidLifecycle(className: string): void {\n const events = this.lifecycleHistory.get(className) ?? [];\n const cycleCount = Math.min(\n events.filter((e) => e.type === 'created').length,\n events.filter((e) => e.type === 'disposed').length,\n );\n\n if (cycleCount >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `Rapid lifecycle: ${className} created/disposed ${cycleCount} times in ${this.windowMs}ms`,\n {\n className,\n cycleCount,\n windowMs: this.windowMs,\n threshold: this.threshold,\n },\n );\n\n setTimeout(() => {\n this.warnedClasses.delete(className);\n }, this.windowMs);\n }\n }\n\n reset(): void {\n this.lifecycleHistory.clear();\n this.instanceInfo.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { StateContainer } from '@blac/core';\n\nexport type LogLevel = 'minimal' | 'info' | 'debug' | 'verbose';\n\nexport type LogFormat = 'simple' | 'grouped';\n\nexport interface Logger {\n log(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n group?(label: string): void;\n groupEnd?(): void;\n groupCollapsed?(label: string): void;\n}\n\nexport interface FilterContext {\n instance: StateContainer<any>;\n className: string;\n instanceId: string;\n}\n\nexport type FilterFn = (ctx: FilterContext) => boolean;\n\nexport interface LoggingPluginConfig {\n /** Log verbosity level (default: 'info') */\n level?: LogLevel;\n\n /** Output format (default: 'grouped') */\n format?: LogFormat;\n\n /** Custom logger implementation (default: console) */\n logger?: Logger;\n\n /** Filter which instances to log */\n filter?: FilterFn;\n\n /** Include class names to log (whitelist) */\n include?: string[];\n\n /** Exclude class names from logging (blacklist) */\n exclude?: string[];\n\n /** Log instance creation/disposal (default: true) */\n logLifecycle?: boolean;\n\n /** Log state changes (default: true) */\n logStateChanges?: boolean;\n\n /** Include callstack in state change logs (default: false) */\n includeCallstack?: boolean;\n\n /** Log changed path names in state change output (default: false) */\n logPaths?: boolean;\n\n /** Warn when instance count exceeds threshold (default: 50) */\n instanceCountWarningThreshold?: number;\n\n /** Warn on rapid init/dispose cycles (default: true) */\n detectRapidLifecycles?: boolean;\n\n /** Time window for rapid lifecycle detection in ms (default: 1000) */\n rapidLifecycleWindowMs?: number;\n\n /** Number of cycles in window to trigger warning (default: 5) */\n rapidLifecycleThreshold?: number;\n\n /** Log prefix (default: '[BlaC]') */\n prefix?: string;\n}\n\nexport interface ResolvedConfig {\n level: LogLevel;\n format: LogFormat;\n logger: Logger;\n filter: FilterFn | undefined;\n include: string[] | undefined;\n exclude: string[] | undefined;\n logLifecycle: boolean;\n logStateChanges: boolean;\n includeCallstack: boolean;\n logPaths: boolean;\n instanceCountWarningThreshold: number;\n detectRapidLifecycles: boolean;\n rapidLifecycleWindowMs: number;\n rapidLifecycleThreshold: number;\n prefix: string;\n}\n\nexport function resolveConfig(\n config: LoggingPluginConfig = {},\n): ResolvedConfig {\n return {\n level: config.level ?? 'info',\n format: config.format ?? 'grouped',\n logger: config.logger ?? console,\n filter: config.filter,\n include: config.include,\n exclude: config.exclude,\n logLifecycle: config.logLifecycle ?? true,\n logStateChanges: config.logStateChanges ?? true,\n includeCallstack: config.includeCallstack ?? false,\n logPaths: config.logPaths ?? false,\n instanceCountWarningThreshold: config.instanceCountWarningThreshold ?? 50,\n detectRapidLifecycles: config.detectRapidLifecycles ?? true,\n rapidLifecycleWindowMs: config.rapidLifecycleWindowMs ?? 1000,\n rapidLifecycleThreshold: config.rapidLifecycleThreshold ?? 5,\n prefix: config.prefix ?? '[BlaC]',\n };\n}\n","import type {\n BlacPlugin,\n PathSet,\n PluginContext,\n StateContainer,\n} from '@blac/core';\nimport { ALL_PATHS } from '@blac/core';\nimport { SimpleFormatter } from './formatters/SimpleFormatter';\nimport { GroupedFormatter } from './formatters/GroupedFormatter';\nimport { InstanceCountMonitor } from './monitors/InstanceCountMonitor';\nimport { LifecycleMonitor } from './monitors/LifecycleMonitor';\nimport type {\n LoggingPluginConfig,\n ResolvedConfig,\n FilterContext,\n} from './types';\nimport { resolveConfig } from './types';\n\nconst STATE_CHANGE_RATE_LIMIT = 1000; // max state changes per second\n\n/**\n * Decode a `PathSet` to an array of path name strings.\n *\n * Returns `['<all>']` when `paths === ALL_PATHS`.\n * Uses `interner.lookup(id)` for all other IDs.\n *\n * The `interner` is typed structurally to avoid a direct compile-time\n * dependency on `@dirtytalk/structural` from this package.\n */\nfunction decodePaths(\n paths: PathSet,\n interner: { lookup(id: number): string },\n): string[] {\n if (paths === ALL_PATHS) return ['<all>'];\n return Array.from(paths as Set<number>).map((id) => interner.lookup(id));\n}\n\nexport class LoggingPlugin implements BlacPlugin {\n readonly name = 'LoggingPlugin';\n readonly version = '1.0.0';\n\n private config: ResolvedConfig;\n private context?: PluginContext;\n private simpleFormatter: SimpleFormatter;\n private groupedFormatter: GroupedFormatter;\n private instanceCountMonitor: InstanceCountMonitor;\n private lifecycleMonitor: LifecycleMonitor;\n\n // Rate limiting for state changes\n private stateChangeCount = 0;\n private stateChangeWindowStart = Date.now();\n private stateChangeLoggingDisabled = false;\n\n constructor(config: LoggingPluginConfig = {}) {\n this.config = resolveConfig(config);\n\n const warningHandler = (\n message: string,\n details?: Record<string, unknown>,\n ) => {\n this.logWarning(message, details);\n };\n\n this.simpleFormatter = new SimpleFormatter(this.config);\n this.groupedFormatter = new GroupedFormatter(this.config);\n this.instanceCountMonitor = new InstanceCountMonitor(\n this.config,\n warningHandler,\n );\n this.lifecycleMonitor = new LifecycleMonitor(this.config, warningHandler);\n }\n\n onInstall(ctx: PluginContext): void {\n this.context = ctx;\n const stats = ctx.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstall(stats);\n } else {\n this.groupedFormatter.logInstall(stats);\n }\n }\n\n onUninstall(): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logUninstall();\n } else {\n this.groupedFormatter.logUninstall();\n }\n\n this.instanceCountMonitor.reset();\n this.lifecycleMonitor.reset();\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = Date.now();\n this.stateChangeLoggingDisabled = false;\n this.context = undefined;\n }\n\n onCreated(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.instanceCountMonitor.onInstanceCreated(metadata.className);\n this.lifecycleMonitor.onInstanceCreated(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceCreated(metadata);\n } else {\n this.groupedFormatter.logInstanceCreated(\n metadata,\n ctx.getState(instance),\n );\n }\n }\n\n onStateChange<S extends object>(\n ctx: PluginContext,\n previousState: S,\n currentState: S,\n paths: PathSet,\n ): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.lifecycleMonitor.onStateChanged(metadata.id);\n\n if (!this.shouldLogStateChanges()) return;\n\n // Check rate limit\n if (this.stateChangeLoggingDisabled) return;\n\n const now = Date.now();\n if (now - this.stateChangeWindowStart >= 1000) {\n // Reset window\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = now;\n }\n\n this.stateChangeCount++;\n\n if (this.stateChangeCount > STATE_CHANGE_RATE_LIMIT) {\n this.stateChangeLoggingDisabled = true;\n this.logWarning(\n `State change logging disabled: exceeded ${STATE_CHANGE_RATE_LIMIT} changes/second`,\n );\n return;\n }\n\n const callstack = this.config.includeCallstack\n ? new Error().stack\n : undefined;\n\n const decodedPaths = this.config.logPaths\n ? decodePaths(paths, instance.interner)\n : undefined;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n } else {\n this.groupedFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n }\n }\n\n onDestroyed(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n const info = this.lifecycleMonitor.getInstanceInfo(metadata.id);\n const lifespanMs = info ? Date.now() - info.createdAt : 0;\n const stateChangeCount = info?.stateChangeCount ?? 0;\n\n this.instanceCountMonitor.onInstanceDisposed(metadata.className);\n this.lifecycleMonitor.onInstanceDisposed(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceDisposed(metadata, lifespanMs);\n } else {\n this.groupedFormatter.logInstanceDisposed(\n metadata,\n lifespanMs,\n stateChangeCount,\n );\n }\n }\n\n logStats(): void {\n if (!this.context) return;\n const stats = this.context.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStats(stats);\n } else {\n this.groupedFormatter.logStats(stats);\n }\n }\n\n private shouldLog(\n instance: StateContainer<any>,\n className: string,\n instanceId: string,\n ): boolean {\n if (this.config.include && !this.config.include.includes(className)) {\n return false;\n }\n\n if (this.config.exclude?.includes(className)) {\n return false;\n }\n\n if (this.config.filter) {\n const filterCtx: FilterContext = {\n instance,\n className,\n instanceId,\n };\n return this.config.filter(filterCtx);\n }\n\n return true;\n }\n\n private shouldLogLifecycle(): boolean {\n if (!this.config.logLifecycle) return false;\n return this.config.level !== 'minimal';\n }\n\n private shouldLogStateChanges(): boolean {\n if (!this.config.logStateChanges) return false;\n return (\n this.config.level === 'info' ||\n this.config.level === 'debug' ||\n this.config.level === 'verbose'\n );\n }\n\n private logWarning(message: string, details?: Record<string, unknown>): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logWarning(message);\n } else {\n this.groupedFormatter.logWarning(message, details);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/formatters/SimpleFormatter.ts","../src/formatters/GroupedFormatter.ts","../src/monitors/InstanceCountMonitor.ts","../src/monitors/LifecycleMonitor.ts","../src/types.ts","../src/LoggingPlugin.ts"],"sourcesContent":["import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class SimpleFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.logger.log(\n `${this.prefix} Plugin installed (${stats.registeredTypes} types, ${stats.totalInstances} instances)`,\n );\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.logger.log(`${this.prefix} Created ${instanceId}`);\n }\n\n logInstanceDisposed(metadata: InstanceMetadata, lifespanMs: number): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const lifespan = this.formatDuration(lifespanMs);\n this.logger.log(\n `${this.prefix} Disposed ${instanceId} (lived ${lifespan})`,\n );\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n _callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n const prev = this.formatState(previousState);\n const curr = this.formatState(currentState);\n this.logger.log(`${this.prefix} ${instanceId} state: ${prev} → ${curr}`);\n\n if (paths !== undefined) {\n this.logger.log(`${this.prefix} Paths: ${paths.join(', ')}`);\n }\n\n if (includeCallstack && _callstack) {\n this.logger.log(`${this.prefix} Callstack: ${_callstack}`);\n }\n }\n\n logWarning(message: string): void {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.logger.log(\n `${this.prefix} Stats: ${stats.totalInstances} instances across ${stats.registeredTypes} types`,\n );\n }\n\n private formatState(state: unknown): string {\n if (state === null) return 'null';\n if (state === undefined) return 'undefined';\n if (typeof state === 'object') {\n try {\n const str = JSON.stringify(state);\n return str.length > 50 ? str.slice(0, 50) + '...' : str;\n } catch {\n return '[Object]';\n }\n }\n return JSON.stringify(state);\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { InstanceMetadata } from '@blac/core';\nimport type { Logger, ResolvedConfig } from '../types';\n\nexport class GroupedFormatter {\n private logger: Logger;\n private prefix: string;\n\n constructor(config: ResolvedConfig) {\n this.logger = config.logger;\n this.prefix = config.prefix;\n }\n\n private formatInstanceId(className: string, id: string): string {\n const shortId = id.length > 8 ? id.slice(0, 8) : id;\n return `${className}#${shortId}`;\n }\n\n private group(label: string, collapsed = true): void {\n if (collapsed && this.logger.groupCollapsed) {\n this.logger.groupCollapsed(label);\n } else if (this.logger.group) {\n this.logger.group(label);\n } else {\n this.logger.log(label);\n }\n }\n\n private groupEnd(): void {\n if (this.logger.groupEnd) {\n this.logger.groupEnd();\n }\n }\n\n logInstall(stats: { registeredTypes: number; totalInstances: number }): void {\n this.group(`${this.prefix} Plugin installed`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.groupEnd();\n }\n\n logUninstall(): void {\n this.logger.log(`${this.prefix} Plugin uninstalled`);\n }\n\n logInstanceCreated(metadata: InstanceMetadata, initialState: unknown): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Created ${instanceId}`);\n this.logger.log('Class:', metadata.className);\n this.logger.log('Instance ID:', metadata.id);\n this.logger.log('Initial state:', initialState);\n this.groupEnd();\n }\n\n logInstanceDisposed(\n metadata: InstanceMetadata,\n lifespanMs: number,\n stateChangeCount: number,\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} Disposed ${instanceId}`);\n this.logger.log('Lifespan:', this.formatDuration(lifespanMs));\n this.logger.log('State changes:', stateChangeCount);\n this.logger.log('Final state:', metadata.state);\n this.groupEnd();\n }\n\n logStateChanged(\n metadata: InstanceMetadata,\n previousState: unknown,\n currentState: unknown,\n callstack: string | undefined,\n includeCallstack: boolean,\n paths?: string[],\n ): void {\n const instanceId = this.formatInstanceId(metadata.className, metadata.id);\n this.group(`${this.prefix} ${instanceId} state changed`);\n this.logger.log('Previous:', previousState);\n this.logger.log('Current:', currentState);\n if (paths !== undefined) {\n this.logger.log('Paths:', paths);\n }\n if (includeCallstack && callstack) {\n this.logger.log('Callstack:', callstack);\n }\n this.groupEnd();\n }\n\n logWarning(message: string, details?: Record<string, unknown>): void {\n if (details) {\n this.group(`${this.prefix} ⚠️ ${message}`, false);\n for (const [key, value] of Object.entries(details)) {\n this.logger.log(`${key}:`, value);\n }\n this.groupEnd();\n } else {\n this.logger.warn(`${this.prefix} ⚠️ ${message}`);\n }\n }\n\n logStats(stats: {\n registeredTypes: number;\n totalInstances: number;\n typeBreakdown: Record<string, number>;\n }): void {\n this.group(`${this.prefix} Stats`);\n this.logger.log('Registered types:', stats.registeredTypes);\n this.logger.log('Total instances:', stats.totalInstances);\n this.logger.log('Breakdown:', stats.typeBreakdown);\n this.groupEnd();\n }\n\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n }\n}\n","import type { ResolvedConfig } from '../types';\n\nexport class InstanceCountMonitor {\n private instanceCounts = new Map<string, number>();\n private warnedClasses = new Set<string>();\n private threshold: number;\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.threshold = config.instanceCountWarningThreshold;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string): void {\n const count = (this.instanceCounts.get(className) ?? 0) + 1;\n this.instanceCounts.set(className, count);\n\n if (count >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `High instance count: ${className} has ${count} instances`,\n {\n className,\n count,\n threshold: this.threshold,\n },\n );\n }\n }\n\n onInstanceDisposed(className: string): void {\n const count = this.instanceCounts.get(className) ?? 0;\n if (count > 0) {\n this.instanceCounts.set(className, count - 1);\n }\n\n if (count - 1 < this.threshold) {\n this.warnedClasses.delete(className);\n }\n }\n\n getStats(): Map<string, number> {\n return new Map(this.instanceCounts);\n }\n\n reset(): void {\n this.instanceCounts.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { ResolvedConfig } from '../types';\n\ninterface LifecycleEvent {\n timestamp: number;\n type: 'created' | 'disposed';\n}\n\ninterface InstanceInfo {\n createdAt: number;\n stateChangeCount: number;\n}\n\nexport class LifecycleMonitor {\n private lifecycleHistory = new Map<string, LifecycleEvent[]>();\n private instanceInfo = new Map<string, InstanceInfo>();\n private windowMs: number;\n private threshold: number;\n private enabled: boolean;\n private warnedClasses = new Set<string>();\n private onWarning: (\n message: string,\n details?: Record<string, unknown>,\n ) => void;\n\n constructor(\n config: ResolvedConfig,\n onWarning: (message: string, details?: Record<string, unknown>) => void,\n ) {\n this.windowMs = config.rapidLifecycleWindowMs;\n this.threshold = config.rapidLifecycleThreshold;\n this.enabled = config.detectRapidLifecycles;\n this.onWarning = onWarning;\n }\n\n onInstanceCreated(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.set(instanceId, {\n createdAt: Date.now(),\n stateChangeCount: 0,\n });\n\n this.recordEvent(className, 'created');\n this.checkRapidLifecycle(className);\n }\n\n onInstanceDisposed(className: string, instanceId: string): void {\n if (!this.enabled) return;\n\n this.instanceInfo.delete(instanceId);\n this.recordEvent(className, 'disposed');\n this.checkRapidLifecycle(className);\n }\n\n onStateChanged(instanceId: string): void {\n const info = this.instanceInfo.get(instanceId);\n if (info) {\n info.stateChangeCount++;\n }\n }\n\n getInstanceInfo(instanceId: string): InstanceInfo | undefined {\n return this.instanceInfo.get(instanceId);\n }\n\n private recordEvent(className: string, type: 'created' | 'disposed'): void {\n const now = Date.now();\n const events = this.lifecycleHistory.get(className) ?? [];\n\n events.push({ timestamp: now, type });\n\n const cutoff = now - this.windowMs;\n const filtered = events.filter((e) => e.timestamp > cutoff);\n this.lifecycleHistory.set(className, filtered);\n }\n\n private checkRapidLifecycle(className: string): void {\n const events = this.lifecycleHistory.get(className) ?? [];\n const cycleCount = Math.min(\n events.filter((e) => e.type === 'created').length,\n events.filter((e) => e.type === 'disposed').length,\n );\n\n if (cycleCount >= this.threshold && !this.warnedClasses.has(className)) {\n this.warnedClasses.add(className);\n this.onWarning(\n `Rapid lifecycle: ${className} created/disposed ${cycleCount} times in ${this.windowMs}ms`,\n {\n className,\n cycleCount,\n windowMs: this.windowMs,\n threshold: this.threshold,\n },\n );\n\n setTimeout(() => {\n this.warnedClasses.delete(className);\n }, this.windowMs);\n }\n }\n\n reset(): void {\n this.lifecycleHistory.clear();\n this.instanceInfo.clear();\n this.warnedClasses.clear();\n }\n}\n","import type { StateContainer } from '@blac/core';\n\nexport type LogLevel = 'minimal' | 'info' | 'debug' | 'verbose';\n\nexport type LogFormat = 'simple' | 'grouped';\n\nexport interface Logger {\n log(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n error(...args: unknown[]): void;\n group?(label: string): void;\n groupEnd?(): void;\n groupCollapsed?(label: string): void;\n}\n\nexport interface FilterContext {\n instance: StateContainer<any>;\n className: string;\n instanceId: string;\n}\n\nexport type FilterFn = (ctx: FilterContext) => boolean;\n\nexport interface LoggingPluginConfig {\n /** Log verbosity level (default: 'info') */\n level?: LogLevel;\n\n /** Output format (default: 'grouped') */\n format?: LogFormat;\n\n /** Custom logger implementation (default: console) */\n logger?: Logger;\n\n /** Filter which instances to log */\n filter?: FilterFn;\n\n /** Include class names to log (whitelist) */\n include?: string[];\n\n /** Exclude class names from logging (blacklist) */\n exclude?: string[];\n\n /** Log instance creation/disposal (default: true) */\n logLifecycle?: boolean;\n\n /** Log state changes (default: true) */\n logStateChanges?: boolean;\n\n /** Include callstack in state change logs (default: false) */\n includeCallstack?: boolean;\n\n /** Log changed path names in state change output (default: false) */\n logPaths?: boolean;\n\n /** Warn when instance count exceeds threshold (default: 50) */\n instanceCountWarningThreshold?: number;\n\n /** Warn on rapid init/dispose cycles (default: true) */\n detectRapidLifecycles?: boolean;\n\n /** Time window for rapid lifecycle detection in ms (default: 1000) */\n rapidLifecycleWindowMs?: number;\n\n /** Number of cycles in window to trigger warning (default: 5) */\n rapidLifecycleThreshold?: number;\n\n /** Log prefix (default: '[BlaC]') */\n prefix?: string;\n}\n\nexport interface ResolvedConfig {\n level: LogLevel;\n format: LogFormat;\n logger: Logger;\n filter: FilterFn | undefined;\n include: string[] | undefined;\n exclude: string[] | undefined;\n logLifecycle: boolean;\n logStateChanges: boolean;\n includeCallstack: boolean;\n logPaths: boolean;\n instanceCountWarningThreshold: number;\n detectRapidLifecycles: boolean;\n rapidLifecycleWindowMs: number;\n rapidLifecycleThreshold: number;\n prefix: string;\n}\n\nexport function resolveConfig(\n config: LoggingPluginConfig = {},\n): ResolvedConfig {\n return {\n level: config.level ?? 'info',\n format: config.format ?? 'grouped',\n logger: config.logger ?? console,\n filter: config.filter,\n include: config.include,\n exclude: config.exclude,\n logLifecycle: config.logLifecycle ?? true,\n logStateChanges: config.logStateChanges ?? true,\n includeCallstack: config.includeCallstack ?? false,\n logPaths: config.logPaths ?? false,\n instanceCountWarningThreshold: config.instanceCountWarningThreshold ?? 50,\n detectRapidLifecycles: config.detectRapidLifecycles ?? true,\n rapidLifecycleWindowMs: config.rapidLifecycleWindowMs ?? 1000,\n rapidLifecycleThreshold: config.rapidLifecycleThreshold ?? 5,\n prefix: config.prefix ?? '[BlaC]',\n };\n}\n","import type {\n BlacPlugin,\n PathSet,\n PluginContext,\n StateContainer,\n} from '@blac/core';\nimport { ALL_PATHS } from '@blac/core';\nimport { SimpleFormatter } from './formatters/SimpleFormatter';\nimport { GroupedFormatter } from './formatters/GroupedFormatter';\nimport { InstanceCountMonitor } from './monitors/InstanceCountMonitor';\nimport { LifecycleMonitor } from './monitors/LifecycleMonitor';\nimport type {\n LoggingPluginConfig,\n ResolvedConfig,\n FilterContext,\n} from './types';\nimport { resolveConfig } from './types';\n\nconst STATE_CHANGE_RATE_LIMIT = 1000; // max state changes per second\n\n/**\n * Decode a `PathSet` to an array of path name strings.\n *\n * Returns `['<all>']` when `paths === ALL_PATHS`.\n * Uses `interner.lookup(id)` for all other IDs.\n *\n * The `interner` is typed structurally to avoid a direct compile-time\n * dependency on `@dirtytalk/structural` from this package.\n */\nfunction decodePaths(\n paths: PathSet,\n interner: { lookup(id: number): string },\n): string[] {\n if (paths === ALL_PATHS) return ['<all>'];\n // `lookup` decodes ancestor-watch ids back to their real path, so a normal\n // mark and its ancestor-watch sibling can decode to the same string — dedup.\n return Array.from(\n new Set(Array.from(paths as Set<number>).map((id) => interner.lookup(id))),\n );\n}\n\nexport class LoggingPlugin implements BlacPlugin {\n readonly name = 'LoggingPlugin';\n readonly version = '1.0.0';\n\n private config: ResolvedConfig;\n private context?: PluginContext;\n private simpleFormatter: SimpleFormatter;\n private groupedFormatter: GroupedFormatter;\n private instanceCountMonitor: InstanceCountMonitor;\n private lifecycleMonitor: LifecycleMonitor;\n\n // Rate limiting for state changes\n private stateChangeCount = 0;\n private stateChangeWindowStart = Date.now();\n private stateChangeLoggingDisabled = false;\n\n constructor(config: LoggingPluginConfig = {}) {\n this.config = resolveConfig(config);\n\n const warningHandler = (\n message: string,\n details?: Record<string, unknown>,\n ) => {\n this.logWarning(message, details);\n };\n\n this.simpleFormatter = new SimpleFormatter(this.config);\n this.groupedFormatter = new GroupedFormatter(this.config);\n this.instanceCountMonitor = new InstanceCountMonitor(\n this.config,\n warningHandler,\n );\n this.lifecycleMonitor = new LifecycleMonitor(this.config, warningHandler);\n }\n\n onInstall(ctx: PluginContext): void {\n this.context = ctx;\n const stats = ctx.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstall(stats);\n } else {\n this.groupedFormatter.logInstall(stats);\n }\n }\n\n onUninstall(): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logUninstall();\n } else {\n this.groupedFormatter.logUninstall();\n }\n\n this.instanceCountMonitor.reset();\n this.lifecycleMonitor.reset();\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = Date.now();\n this.stateChangeLoggingDisabled = false;\n this.context = undefined;\n }\n\n onCreated(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.instanceCountMonitor.onInstanceCreated(metadata.className);\n this.lifecycleMonitor.onInstanceCreated(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceCreated(metadata);\n } else {\n this.groupedFormatter.logInstanceCreated(\n metadata,\n ctx.getState(instance),\n );\n }\n }\n\n onStateChange<S extends object>(\n ctx: PluginContext,\n previousState: S,\n currentState: S,\n paths: PathSet,\n ): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n this.lifecycleMonitor.onStateChanged(metadata.id);\n\n if (!this.shouldLogStateChanges()) return;\n\n // Check rate limit\n if (this.stateChangeLoggingDisabled) return;\n\n const now = Date.now();\n if (now - this.stateChangeWindowStart >= 1000) {\n // Reset window\n this.stateChangeCount = 0;\n this.stateChangeWindowStart = now;\n }\n\n this.stateChangeCount++;\n\n if (this.stateChangeCount > STATE_CHANGE_RATE_LIMIT) {\n this.stateChangeLoggingDisabled = true;\n this.logWarning(\n `State change logging disabled: exceeded ${STATE_CHANGE_RATE_LIMIT} changes/second`,\n );\n return;\n }\n\n const callstack = this.config.includeCallstack\n ? new Error().stack\n : undefined;\n\n const decodedPaths = this.config.logPaths\n ? decodePaths(paths, instance.interner)\n : undefined;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n } else {\n this.groupedFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n decodedPaths,\n );\n }\n }\n\n onDestroyed(ctx: PluginContext): void {\n const instance = ctx.container;\n if (!instance) return;\n const metadata = ctx.getInstanceMetadata(instance);\n\n if (!this.shouldLog(instance, metadata.className, metadata.id)) {\n return;\n }\n\n const info = this.lifecycleMonitor.getInstanceInfo(metadata.id);\n const lifespanMs = info ? Date.now() - info.createdAt : 0;\n const stateChangeCount = info?.stateChangeCount ?? 0;\n\n this.instanceCountMonitor.onInstanceDisposed(metadata.className);\n this.lifecycleMonitor.onInstanceDisposed(metadata.className, metadata.id);\n\n if (!this.shouldLogLifecycle()) return;\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logInstanceDisposed(metadata, lifespanMs);\n } else {\n this.groupedFormatter.logInstanceDisposed(\n metadata,\n lifespanMs,\n stateChangeCount,\n );\n }\n }\n\n logStats(): void {\n if (!this.context) return;\n const stats = this.context.getStats();\n\n if (this.config.format === 'simple') {\n this.simpleFormatter.logStats(stats);\n } else {\n this.groupedFormatter.logStats(stats);\n }\n }\n\n private shouldLog(\n instance: StateContainer<any>,\n className: string,\n instanceId: string,\n ): boolean {\n if (this.config.include && !this.config.include.includes(className)) {\n return false;\n }\n\n if (this.config.exclude?.includes(className)) {\n return false;\n }\n\n if (this.config.filter) {\n const filterCtx: FilterContext = {\n instance,\n className,\n instanceId,\n };\n return this.config.filter(filterCtx);\n }\n\n return true;\n }\n\n private shouldLogLifecycle(): boolean {\n if (!this.config.logLifecycle) return false;\n return this.config.level !== 'minimal';\n }\n\n private shouldLogStateChanges(): boolean {\n if (!this.config.logStateChanges) return false;\n return (\n this.config.level === 'info' ||\n this.config.level === 'debug' ||\n this.config.level === 'verbose'\n );\n }\n\n private logWarning(message: string, details?: Record<string, unknown>): void {\n if (this.config.format === 'simple') {\n this.simpleFormatter.logWarning(message);\n } else {\n this.groupedFormatter.logWarning(message, details);\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blac/logging-plugin",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "license": "MIT",
5
5
  "author": "Brendan Mullins <jsnanigans@gmail.com>",
6
6
  "description": "Logging and debugging plugin for BlaC state management",
@@ -46,14 +46,14 @@
46
46
  "devtools"
47
47
  ],
48
48
  "peerDependencies": {
49
- "@blac/core": "^2.0.15"
49
+ "@blac/core": "^2.0.17"
50
50
  },
51
51
  "devDependencies": {
52
- "typescript": "^5.9.2",
52
+ "publint": "^0.3.21",
53
+ "typescript": "^6.0.3",
54
+ "vite-plus": "^0.1.23",
53
55
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest",
54
- "publint": "^0.3.12",
55
- "vite-plus": "latest",
56
- "@blac/core": "2.0.15"
56
+ "@blac/core": "2.0.17"
57
57
  },
58
58
  "scripts": {
59
59
  "dev": "vp pack --watch",