@blac/logging-plugin 2.0.13 → 2.0.15

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,4 +1,4 @@
1
- import type { BlacPlugin, PluginContext, StateContainer } from '@blac/core';
1
+ import type { BlacPlugin, PathSet, PluginContext } from '@blac/core';
2
2
  import type { LoggingPluginConfig } from './types';
3
3
  export declare class LoggingPlugin implements BlacPlugin {
4
4
  readonly name = "LoggingPlugin";
@@ -13,11 +13,11 @@ export declare class LoggingPlugin implements BlacPlugin {
13
13
  private stateChangeWindowStart;
14
14
  private stateChangeLoggingDisabled;
15
15
  constructor(config?: LoggingPluginConfig);
16
- onInstall(context: PluginContext): void;
16
+ onInstall(ctx: PluginContext): void;
17
17
  onUninstall(): void;
18
- onInstanceCreated(instance: StateContainer<any>, context: PluginContext): void;
19
- onStateChanged<S extends object>(instance: StateContainer<S>, previousState: S, currentState: S, callstack: string | undefined, context: PluginContext): void;
20
- onInstanceDisposed(instance: StateContainer<any>, context: PluginContext): void;
18
+ onCreated(ctx: PluginContext): void;
19
+ onStateChange<S extends object>(ctx: PluginContext, previousState: S, currentState: S, paths: PathSet): void;
20
+ onDestroyed(ctx: PluginContext): void;
21
21
  logStats(): void;
22
22
  private shouldLog;
23
23
  private shouldLogLifecycle;
@@ -1 +1 @@
1
- {"version":3,"file":"LoggingPlugin.d.ts","sourceRoot":"","sources":["../src/LoggingPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAK5E,OAAO,KAAK,EACV,mBAAmB,EAGpB,MAAM,SAAS,CAAC;AAKjB,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,OAAO,EAAE,aAAa,GAAG,IAAI;IAWvC,WAAW,IAAI,IAAI;IAenB,iBAAiB,CACf,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,EAC7B,OAAO,EAAE,aAAa,GACrB,IAAI;IAsBP,cAAc,CAAC,CAAC,SAAS,MAAM,EAC7B,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,EAC3B,aAAa,EAAE,CAAC,EAChB,YAAY,EAAE,CAAC,EACf,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,aAAa,GACrB,IAAI;IAkDP,kBAAkB,CAChB,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,EAC7B,OAAO,EAAE,aAAa,GACrB,IAAI;IA2BP,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;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"}
@@ -14,7 +14,7 @@ export declare class GroupedFormatter {
14
14
  logUninstall(): void;
15
15
  logInstanceCreated(metadata: InstanceMetadata, initialState: unknown): void;
16
16
  logInstanceDisposed(metadata: InstanceMetadata, lifespanMs: number, stateChangeCount: number): void;
17
- logStateChanged(metadata: InstanceMetadata, previousState: unknown, currentState: unknown, callstack: string | undefined, includeCallstack: boolean): void;
17
+ logStateChanged(metadata: InstanceMetadata, previousState: unknown, currentState: unknown, callstack: string | undefined, includeCallstack: boolean, paths?: string[]): void;
18
18
  logWarning(message: string, details?: Record<string, unknown>): void;
19
19
  logStats(stats: {
20
20
  registeredTypes: number;
@@ -1 +1 @@
1
- {"version":3,"file":"GroupedFormatter.d.ts","sourceRoot":"","sources":["../../src/formatters/GroupedFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAU,cAAc,EAAE,MAAM,UAAU,CAAC;AAEvD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,cAAc;IAKlC,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,QAAQ;IAMhB,UAAU,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAO5E,YAAY,IAAI,IAAI;IAIpB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI;IAY3E,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,GACvB,IAAI;IASP,eAAe,CACb,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,OAAO,EACrB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,EAAE,OAAO,GACxB,IAAI;IAWP,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAYpE,QAAQ,CAAC,KAAK,EAAE;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,GAAG,IAAI;IAQR,OAAO,CAAC,cAAc;CAKvB"}
1
+ {"version":3,"file":"GroupedFormatter.d.ts","sourceRoot":"","sources":["../../src/formatters/GroupedFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAU,cAAc,EAAE,MAAM,UAAU,CAAC;AAEvD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,cAAc;IAKlC,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,QAAQ;IAMhB,UAAU,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAO5E,YAAY,IAAI,IAAI;IAIpB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI;IAS3E,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,GACvB,IAAI;IASP,eAAe,CACb,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,OAAO,EACrB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,EAAE,OAAO,EACzB,KAAK,CAAC,EAAE,MAAM,EAAE,GACf,IAAI;IAcP,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAYpE,QAAQ,CAAC,KAAK,EAAE;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,GAAG,IAAI;IAQR,OAAO,CAAC,cAAc;CAKvB"}
@@ -12,7 +12,7 @@ export declare class SimpleFormatter {
12
12
  logUninstall(): void;
13
13
  logInstanceCreated(metadata: InstanceMetadata): void;
14
14
  logInstanceDisposed(metadata: InstanceMetadata, lifespanMs: number): void;
15
- logStateChanged(metadata: InstanceMetadata, previousState: unknown, currentState: unknown, _callstack: string | undefined, includeCallstack: boolean): void;
15
+ logStateChanged(metadata: InstanceMetadata, previousState: unknown, currentState: unknown, _callstack: string | undefined, includeCallstack: boolean, paths?: string[]): void;
16
16
  logWarning(message: string): void;
17
17
  logStats(stats: {
18
18
  registeredTypes: number;
@@ -1 +1 @@
1
- {"version":3,"file":"SimpleFormatter.d.ts","sourceRoot":"","sources":["../../src/formatters/SimpleFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAU,cAAc,EAAE,MAAM,UAAU,CAAC;AAEvD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,cAAc;IAKlC,OAAO,CAAC,gBAAgB;IAKxB,UAAU,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAM5E,YAAY,IAAI,IAAI;IAIpB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAKpD,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQzE,eAAe,CACb,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,OAAO,EACrB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,gBAAgB,EAAE,OAAO,GACxB,IAAI;IAWP,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC,QAAQ,CAAC,KAAK,EAAE;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,GAAG,IAAI;IAMR,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,cAAc;CAKvB"}
1
+ {"version":3,"file":"SimpleFormatter.d.ts","sourceRoot":"","sources":["../../src/formatters/SimpleFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAU,cAAc,EAAE,MAAM,UAAU,CAAC;AAEvD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,cAAc;IAKlC,OAAO,CAAC,gBAAgB;IAKxB,UAAU,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAM5E,YAAY,IAAI,IAAI;IAIpB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAKpD,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQzE,eAAe,CACb,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,OAAO,EACtB,YAAY,EAAE,OAAO,EACrB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,gBAAgB,EAAE,OAAO,EACzB,KAAK,CAAC,EAAE,MAAM,EAAE,GACf,IAAI;IAeP,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC,QAAQ,CAAC,KAAK,EAAE;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACvC,GAAG,IAAI;IAMR,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,cAAc;CAKvB"}
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _blac_core = require("@blac/core");
2
3
  //#region src/formatters/SimpleFormatter.ts
3
4
  var SimpleFormatter = class {
4
5
  constructor(config) {
@@ -23,11 +24,12 @@ var SimpleFormatter = class {
23
24
  const lifespan = this.formatDuration(lifespanMs);
24
25
  this.logger.log(`${this.prefix} Disposed ${instanceId} (lived ${lifespan})`);
25
26
  }
26
- logStateChanged(metadata, previousState, currentState, _callstack, includeCallstack) {
27
+ logStateChanged(metadata, previousState, currentState, _callstack, includeCallstack, paths) {
27
28
  const instanceId = this.formatInstanceId(metadata.className, metadata.id);
28
29
  const prev = this.formatState(previousState);
29
30
  const curr = this.formatState(currentState);
30
31
  this.logger.log(`${this.prefix} ${instanceId} state: ${prev} → ${curr}`);
32
+ if (paths !== void 0) this.logger.log(`${this.prefix} Paths: ${paths.join(", ")}`);
31
33
  if (includeCallstack && _callstack) this.logger.log(`${this.prefix} Callstack: ${_callstack}`);
32
34
  }
33
35
  logWarning(message) {
@@ -86,7 +88,6 @@ var GroupedFormatter = class {
86
88
  this.logger.log("Class:", metadata.className);
87
89
  this.logger.log("Instance ID:", metadata.id);
88
90
  this.logger.log("Initial state:", initialState);
89
- if (metadata.callstack) this.logger.log("Callstack:", metadata.callstack);
90
91
  this.groupEnd();
91
92
  }
92
93
  logInstanceDisposed(metadata, lifespanMs, stateChangeCount) {
@@ -97,11 +98,12 @@ var GroupedFormatter = class {
97
98
  this.logger.log("Final state:", metadata.state);
98
99
  this.groupEnd();
99
100
  }
100
- logStateChanged(metadata, previousState, currentState, callstack, includeCallstack) {
101
+ logStateChanged(metadata, previousState, currentState, callstack, includeCallstack, paths) {
101
102
  const instanceId = this.formatInstanceId(metadata.className, metadata.id);
102
103
  this.group(`${this.prefix} ${instanceId} state changed`);
103
104
  this.logger.log("Previous:", previousState);
104
105
  this.logger.log("Current:", currentState);
106
+ if (paths !== void 0) this.logger.log("Paths:", paths);
105
107
  if (includeCallstack && callstack) this.logger.log("Callstack:", callstack);
106
108
  this.groupEnd();
107
109
  }
@@ -239,6 +241,7 @@ function resolveConfig(config = {}) {
239
241
  logLifecycle: config.logLifecycle ?? true,
240
242
  logStateChanges: config.logStateChanges ?? true,
241
243
  includeCallstack: config.includeCallstack ?? false,
244
+ logPaths: config.logPaths ?? false,
242
245
  instanceCountWarningThreshold: config.instanceCountWarningThreshold ?? 50,
243
246
  detectRapidLifecycles: config.detectRapidLifecycles ?? true,
244
247
  rapidLifecycleWindowMs: config.rapidLifecycleWindowMs ?? 1e3,
@@ -249,6 +252,19 @@ function resolveConfig(config = {}) {
249
252
  //#endregion
250
253
  //#region src/LoggingPlugin.ts
251
254
  const STATE_CHANGE_RATE_LIMIT = 1e3;
255
+ /**
256
+ * Decode a `PathSet` to an array of path name strings.
257
+ *
258
+ * Returns `['<all>']` when `paths === ALL_PATHS`.
259
+ * Uses `interner.lookup(id)` for all other IDs.
260
+ *
261
+ * The `interner` is typed structurally to avoid a direct compile-time
262
+ * dependency on `@dirtytalk/structural` from this package.
263
+ */
264
+ function decodePaths(paths, interner) {
265
+ if (paths === _blac_core.ALL_PATHS) return ["<all>"];
266
+ return Array.from(paths).map((id) => interner.lookup(id));
267
+ }
252
268
  var LoggingPlugin = class {
253
269
  constructor(config = {}) {
254
270
  this.name = "LoggingPlugin";
@@ -265,9 +281,9 @@ var LoggingPlugin = class {
265
281
  this.instanceCountMonitor = new InstanceCountMonitor(this.config, warningHandler);
266
282
  this.lifecycleMonitor = new LifecycleMonitor(this.config, warningHandler);
267
283
  }
268
- onInstall(context) {
269
- this.context = context;
270
- const stats = context.getStats();
284
+ onInstall(ctx) {
285
+ this.context = ctx;
286
+ const stats = ctx.getStats();
271
287
  if (this.config.format === "simple") this.simpleFormatter.logInstall(stats);
272
288
  else this.groupedFormatter.logInstall(stats);
273
289
  }
@@ -281,17 +297,21 @@ var LoggingPlugin = class {
281
297
  this.stateChangeLoggingDisabled = false;
282
298
  this.context = void 0;
283
299
  }
284
- onInstanceCreated(instance, context) {
285
- const metadata = context.getInstanceMetadata(instance);
300
+ onCreated(ctx) {
301
+ const instance = ctx.container;
302
+ if (!instance) return;
303
+ const metadata = ctx.getInstanceMetadata(instance);
286
304
  if (!this.shouldLog(instance, metadata.className, metadata.id)) return;
287
305
  this.instanceCountMonitor.onInstanceCreated(metadata.className);
288
306
  this.lifecycleMonitor.onInstanceCreated(metadata.className, metadata.id);
289
307
  if (!this.shouldLogLifecycle()) return;
290
308
  if (this.config.format === "simple") this.simpleFormatter.logInstanceCreated(metadata);
291
- else this.groupedFormatter.logInstanceCreated(metadata, context.getState(instance));
309
+ else this.groupedFormatter.logInstanceCreated(metadata, ctx.getState(instance));
292
310
  }
293
- onStateChanged(instance, previousState, currentState, callstack, context) {
294
- const metadata = context.getInstanceMetadata(instance);
311
+ onStateChange(ctx, previousState, currentState, paths) {
312
+ const instance = ctx.container;
313
+ if (!instance) return;
314
+ const metadata = ctx.getInstanceMetadata(instance);
295
315
  if (!this.shouldLog(instance, metadata.className, metadata.id)) return;
296
316
  this.lifecycleMonitor.onStateChanged(metadata.id);
297
317
  if (!this.shouldLogStateChanges()) return;
@@ -307,11 +327,15 @@ var LoggingPlugin = class {
307
327
  this.logWarning(`State change logging disabled: exceeded ${STATE_CHANGE_RATE_LIMIT} changes/second`);
308
328
  return;
309
329
  }
310
- if (this.config.format === "simple") this.simpleFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack);
311
- else this.groupedFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack);
312
- }
313
- onInstanceDisposed(instance, context) {
314
- const metadata = context.getInstanceMetadata(instance);
330
+ const callstack = this.config.includeCallstack ? (/* @__PURE__ */ new Error()).stack : void 0;
331
+ const decodedPaths = this.config.logPaths ? decodePaths(paths, instance.interner) : void 0;
332
+ if (this.config.format === "simple") this.simpleFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack, decodedPaths);
333
+ else this.groupedFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack, decodedPaths);
334
+ }
335
+ onDestroyed(ctx) {
336
+ const instance = ctx.container;
337
+ if (!instance) return;
338
+ const metadata = ctx.getInstanceMetadata(instance);
315
339
  if (!this.shouldLog(instance, metadata.className, metadata.id)) return;
316
340
  const info = this.lifecycleMonitor.getInstanceInfo(metadata.id);
317
341
  const lifespanMs = info ? Date.now() - info.createdAt : 0;
@@ -332,12 +356,12 @@ var LoggingPlugin = class {
332
356
  if (this.config.include && !this.config.include.includes(className)) return false;
333
357
  if (this.config.exclude?.includes(className)) return false;
334
358
  if (this.config.filter) {
335
- const ctx = {
359
+ const filterCtx = {
336
360
  instance,
337
361
  className,
338
362
  instanceId
339
363
  };
340
- return this.config.filter(ctx);
364
+ return this.config.filter(filterCtx);
341
365
  }
342
366
  return true;
343
367
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","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 ): 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 (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 if (metadata.callstack) {\n this.logger.log('Callstack:', metadata.callstack);\n }\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 ): 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 (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 /** 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 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 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 { BlacPlugin, PluginContext, StateContainer } 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\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(context: PluginContext): void {\n this.context = context;\n const stats = context.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 onInstanceCreated(\n instance: StateContainer<any>,\n context: PluginContext,\n ): void {\n const metadata = context.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 context.getState(instance),\n );\n }\n }\n\n onStateChanged<S extends object>(\n instance: StateContainer<S>,\n previousState: S,\n currentState: S,\n callstack: string | undefined,\n context: PluginContext,\n ): void {\n const metadata = context.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 if (this.config.format === 'simple') {\n this.simpleFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n );\n } else {\n this.groupedFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n );\n }\n }\n\n onInstanceDisposed(\n instance: StateContainer<any>,\n context: PluginContext,\n ): void {\n const metadata = context.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 ctx: FilterContext = {\n instance,\n className,\n instanceId,\n };\n return this.config.filter(ctx);\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 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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"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ALL_PATHS } from "@blac/core";
1
2
  //#region src/formatters/SimpleFormatter.ts
2
3
  var SimpleFormatter = class {
3
4
  constructor(config) {
@@ -22,11 +23,12 @@ var SimpleFormatter = class {
22
23
  const lifespan = this.formatDuration(lifespanMs);
23
24
  this.logger.log(`${this.prefix} Disposed ${instanceId} (lived ${lifespan})`);
24
25
  }
25
- logStateChanged(metadata, previousState, currentState, _callstack, includeCallstack) {
26
+ logStateChanged(metadata, previousState, currentState, _callstack, includeCallstack, paths) {
26
27
  const instanceId = this.formatInstanceId(metadata.className, metadata.id);
27
28
  const prev = this.formatState(previousState);
28
29
  const curr = this.formatState(currentState);
29
30
  this.logger.log(`${this.prefix} ${instanceId} state: ${prev} → ${curr}`);
31
+ if (paths !== void 0) this.logger.log(`${this.prefix} Paths: ${paths.join(", ")}`);
30
32
  if (includeCallstack && _callstack) this.logger.log(`${this.prefix} Callstack: ${_callstack}`);
31
33
  }
32
34
  logWarning(message) {
@@ -85,7 +87,6 @@ var GroupedFormatter = class {
85
87
  this.logger.log("Class:", metadata.className);
86
88
  this.logger.log("Instance ID:", metadata.id);
87
89
  this.logger.log("Initial state:", initialState);
88
- if (metadata.callstack) this.logger.log("Callstack:", metadata.callstack);
89
90
  this.groupEnd();
90
91
  }
91
92
  logInstanceDisposed(metadata, lifespanMs, stateChangeCount) {
@@ -96,11 +97,12 @@ var GroupedFormatter = class {
96
97
  this.logger.log("Final state:", metadata.state);
97
98
  this.groupEnd();
98
99
  }
99
- logStateChanged(metadata, previousState, currentState, callstack, includeCallstack) {
100
+ logStateChanged(metadata, previousState, currentState, callstack, includeCallstack, paths) {
100
101
  const instanceId = this.formatInstanceId(metadata.className, metadata.id);
101
102
  this.group(`${this.prefix} ${instanceId} state changed`);
102
103
  this.logger.log("Previous:", previousState);
103
104
  this.logger.log("Current:", currentState);
105
+ if (paths !== void 0) this.logger.log("Paths:", paths);
104
106
  if (includeCallstack && callstack) this.logger.log("Callstack:", callstack);
105
107
  this.groupEnd();
106
108
  }
@@ -238,6 +240,7 @@ function resolveConfig(config = {}) {
238
240
  logLifecycle: config.logLifecycle ?? true,
239
241
  logStateChanges: config.logStateChanges ?? true,
240
242
  includeCallstack: config.includeCallstack ?? false,
243
+ logPaths: config.logPaths ?? false,
241
244
  instanceCountWarningThreshold: config.instanceCountWarningThreshold ?? 50,
242
245
  detectRapidLifecycles: config.detectRapidLifecycles ?? true,
243
246
  rapidLifecycleWindowMs: config.rapidLifecycleWindowMs ?? 1e3,
@@ -248,6 +251,19 @@ function resolveConfig(config = {}) {
248
251
  //#endregion
249
252
  //#region src/LoggingPlugin.ts
250
253
  const STATE_CHANGE_RATE_LIMIT = 1e3;
254
+ /**
255
+ * Decode a `PathSet` to an array of path name strings.
256
+ *
257
+ * Returns `['<all>']` when `paths === ALL_PATHS`.
258
+ * Uses `interner.lookup(id)` for all other IDs.
259
+ *
260
+ * The `interner` is typed structurally to avoid a direct compile-time
261
+ * dependency on `@dirtytalk/structural` from this package.
262
+ */
263
+ function decodePaths(paths, interner) {
264
+ if (paths === ALL_PATHS) return ["<all>"];
265
+ return Array.from(paths).map((id) => interner.lookup(id));
266
+ }
251
267
  var LoggingPlugin = class {
252
268
  constructor(config = {}) {
253
269
  this.name = "LoggingPlugin";
@@ -264,9 +280,9 @@ var LoggingPlugin = class {
264
280
  this.instanceCountMonitor = new InstanceCountMonitor(this.config, warningHandler);
265
281
  this.lifecycleMonitor = new LifecycleMonitor(this.config, warningHandler);
266
282
  }
267
- onInstall(context) {
268
- this.context = context;
269
- const stats = context.getStats();
283
+ onInstall(ctx) {
284
+ this.context = ctx;
285
+ const stats = ctx.getStats();
270
286
  if (this.config.format === "simple") this.simpleFormatter.logInstall(stats);
271
287
  else this.groupedFormatter.logInstall(stats);
272
288
  }
@@ -280,17 +296,21 @@ var LoggingPlugin = class {
280
296
  this.stateChangeLoggingDisabled = false;
281
297
  this.context = void 0;
282
298
  }
283
- onInstanceCreated(instance, context) {
284
- const metadata = context.getInstanceMetadata(instance);
299
+ onCreated(ctx) {
300
+ const instance = ctx.container;
301
+ if (!instance) return;
302
+ const metadata = ctx.getInstanceMetadata(instance);
285
303
  if (!this.shouldLog(instance, metadata.className, metadata.id)) return;
286
304
  this.instanceCountMonitor.onInstanceCreated(metadata.className);
287
305
  this.lifecycleMonitor.onInstanceCreated(metadata.className, metadata.id);
288
306
  if (!this.shouldLogLifecycle()) return;
289
307
  if (this.config.format === "simple") this.simpleFormatter.logInstanceCreated(metadata);
290
- else this.groupedFormatter.logInstanceCreated(metadata, context.getState(instance));
308
+ else this.groupedFormatter.logInstanceCreated(metadata, ctx.getState(instance));
291
309
  }
292
- onStateChanged(instance, previousState, currentState, callstack, context) {
293
- const metadata = context.getInstanceMetadata(instance);
310
+ onStateChange(ctx, previousState, currentState, paths) {
311
+ const instance = ctx.container;
312
+ if (!instance) return;
313
+ const metadata = ctx.getInstanceMetadata(instance);
294
314
  if (!this.shouldLog(instance, metadata.className, metadata.id)) return;
295
315
  this.lifecycleMonitor.onStateChanged(metadata.id);
296
316
  if (!this.shouldLogStateChanges()) return;
@@ -306,11 +326,15 @@ var LoggingPlugin = class {
306
326
  this.logWarning(`State change logging disabled: exceeded ${STATE_CHANGE_RATE_LIMIT} changes/second`);
307
327
  return;
308
328
  }
309
- if (this.config.format === "simple") this.simpleFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack);
310
- else this.groupedFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack);
311
- }
312
- onInstanceDisposed(instance, context) {
313
- const metadata = context.getInstanceMetadata(instance);
329
+ const callstack = this.config.includeCallstack ? (/* @__PURE__ */ new Error()).stack : void 0;
330
+ const decodedPaths = this.config.logPaths ? decodePaths(paths, instance.interner) : void 0;
331
+ if (this.config.format === "simple") this.simpleFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack, decodedPaths);
332
+ else this.groupedFormatter.logStateChanged(metadata, previousState, currentState, callstack, this.config.includeCallstack, decodedPaths);
333
+ }
334
+ onDestroyed(ctx) {
335
+ const instance = ctx.container;
336
+ if (!instance) return;
337
+ const metadata = ctx.getInstanceMetadata(instance);
314
338
  if (!this.shouldLog(instance, metadata.className, metadata.id)) return;
315
339
  const info = this.lifecycleMonitor.getInstanceInfo(metadata.id);
316
340
  const lifespanMs = info ? Date.now() - info.createdAt : 0;
@@ -331,12 +355,12 @@ var LoggingPlugin = class {
331
355
  if (this.config.include && !this.config.include.includes(className)) return false;
332
356
  if (this.config.exclude?.includes(className)) return false;
333
357
  if (this.config.filter) {
334
- const ctx = {
358
+ const filterCtx = {
335
359
  instance,
336
360
  className,
337
361
  instanceId
338
362
  };
339
- return this.config.filter(ctx);
363
+ return this.config.filter(filterCtx);
340
364
  }
341
365
  return true;
342
366
  }
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 ): 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 (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 if (metadata.callstack) {\n this.logger.log('Callstack:', metadata.callstack);\n }\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 ): 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 (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 /** 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 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 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 { BlacPlugin, PluginContext, StateContainer } 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\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(context: PluginContext): void {\n this.context = context;\n const stats = context.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 onInstanceCreated(\n instance: StateContainer<any>,\n context: PluginContext,\n ): void {\n const metadata = context.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 context.getState(instance),\n );\n }\n }\n\n onStateChanged<S extends object>(\n instance: StateContainer<S>,\n previousState: S,\n currentState: S,\n callstack: string | undefined,\n context: PluginContext,\n ): void {\n const metadata = context.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 if (this.config.format === 'simple') {\n this.simpleFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n );\n } else {\n this.groupedFormatter.logStateChanged(\n metadata,\n previousState,\n currentState,\n callstack,\n this.config.includeCallstack,\n );\n }\n }\n\n onInstanceDisposed(\n instance: StateContainer<any>,\n context: PluginContext,\n ): void {\n const metadata = context.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 ctx: FilterContext = {\n instance,\n className,\n instanceId,\n };\n return this.config.filter(ctx);\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":";AAGA,IAAa,kBAAb,MAA6B;CAI3B,YAAY,QAAwB;AAClC,OAAK,SAAS,OAAO;AACrB,OAAK,SAAS,OAAO;;CAGvB,iBAAyB,WAAmB,IAAoB;AAE9D,SAAO,GAAG,UAAU,GADJ,GAAG,SAAS,IAAI,GAAG,MAAM,GAAG,EAAE,GAAG;;CAInD,WAAW,OAAkE;AAC3E,OAAK,OAAO,IACV,GAAG,KAAK,OAAO,qBAAqB,MAAM,gBAAgB,UAAU,MAAM,eAAe,aAC1F;;CAGH,eAAqB;AACnB,OAAK,OAAO,IAAI,GAAG,KAAK,OAAO,qBAAqB;;CAGtD,mBAAmB,UAAkC;EACnD,MAAM,aAAa,KAAK,iBAAiB,SAAS,WAAW,SAAS,GAAG;AACzE,OAAK,OAAO,IAAI,GAAG,KAAK,OAAO,WAAW,aAAa;;CAGzD,oBAAoB,UAA4B,YAA0B;EACxE,MAAM,aAAa,KAAK,iBAAiB,SAAS,WAAW,SAAS,GAAG;EACzE,MAAM,WAAW,KAAK,eAAe,WAAW;AAChD,OAAK,OAAO,IACV,GAAG,KAAK,OAAO,YAAY,WAAW,UAAU,SAAS,GAC1D;;CAGH,gBACE,UACA,eACA,cACA,YACA,kBACM;EACN,MAAM,aAAa,KAAK,iBAAiB,SAAS,WAAW,SAAS,GAAG;EACzE,MAAM,OAAO,KAAK,YAAY,cAAc;EAC5C,MAAM,OAAO,KAAK,YAAY,aAAa;AAC3C,OAAK,OAAO,IAAI,GAAG,KAAK,OAAO,GAAG,WAAW,UAAU,KAAK,KAAK,OAAO;AAExE,MAAI,oBAAoB,WACtB,MAAK,OAAO,IAAI,GAAG,KAAK,OAAO,gBAAgB,aAAa;;CAIhE,WAAW,SAAuB;AAChC,OAAK,OAAO,KAAK,GAAG,KAAK,OAAO,MAAM,UAAU;;CAGlD,SAAS,OAIA;AACP,OAAK,OAAO,IACV,GAAG,KAAK,OAAO,UAAU,MAAM,eAAe,oBAAoB,MAAM,gBAAgB,QACzF;;CAGH,YAAoB,OAAwB;AAC1C,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,KAAA,EAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SACnB,KAAI;GACF,MAAM,MAAM,KAAK,UAAU,MAAM;AACjC,UAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,GAAG,GAAG,QAAQ;UAC9C;AACN,UAAO;;AAGX,SAAO,KAAK,UAAU,MAAM;;CAG9B,eAAuB,IAAoB;AACzC,MAAI,KAAK,IAAM,QAAO,GAAG,GAAG;AAC5B,MAAI,KAAK,IAAO,QAAO,IAAI,KAAK,KAAM,QAAQ,EAAE,CAAC;AACjD,SAAO,IAAI,KAAK,KAAO,QAAQ,EAAE,CAAC;;;;;ACrFtC,IAAa,mBAAb,MAA8B;CAI5B,YAAY,QAAwB;AAClC,OAAK,SAAS,OAAO;AACrB,OAAK,SAAS,OAAO;;CAGvB,iBAAyB,WAAmB,IAAoB;AAE9D,SAAO,GAAG,UAAU,GADJ,GAAG,SAAS,IAAI,GAAG,MAAM,GAAG,EAAE,GAAG;;CAInD,MAAc,OAAe,YAAY,MAAY;AACnD,MAAI,aAAa,KAAK,OAAO,eAC3B,MAAK,OAAO,eAAe,MAAM;WACxB,KAAK,OAAO,MACrB,MAAK,OAAO,MAAM,MAAM;MAExB,MAAK,OAAO,IAAI,MAAM;;CAI1B,WAAyB;AACvB,MAAI,KAAK,OAAO,SACd,MAAK,OAAO,UAAU;;CAI1B,WAAW,OAAkE;AAC3E,OAAK,MAAM,GAAG,KAAK,OAAO,mBAAmB;AAC7C,OAAK,OAAO,IAAI,qBAAqB,MAAM,gBAAgB;AAC3D,OAAK,OAAO,IAAI,oBAAoB,MAAM,eAAe;AACzD,OAAK,UAAU;;CAGjB,eAAqB;AACnB,OAAK,OAAO,IAAI,GAAG,KAAK,OAAO,qBAAqB;;CAGtD,mBAAmB,UAA4B,cAA6B;EAC1E,MAAM,aAAa,KAAK,iBAAiB,SAAS,WAAW,SAAS,GAAG;AACzE,OAAK,MAAM,GAAG,KAAK,OAAO,WAAW,aAAa;AAClD,OAAK,OAAO,IAAI,UAAU,SAAS,UAAU;AAC7C,OAAK,OAAO,IAAI,gBAAgB,SAAS,GAAG;AAC5C,OAAK,OAAO,IAAI,kBAAkB,aAAa;AAC/C,MAAI,SAAS,UACX,MAAK,OAAO,IAAI,cAAc,SAAS,UAAU;AAEnD,OAAK,UAAU;;CAGjB,oBACE,UACA,YACA,kBACM;EACN,MAAM,aAAa,KAAK,iBAAiB,SAAS,WAAW,SAAS,GAAG;AACzE,OAAK,MAAM,GAAG,KAAK,OAAO,YAAY,aAAa;AACnD,OAAK,OAAO,IAAI,aAAa,KAAK,eAAe,WAAW,CAAC;AAC7D,OAAK,OAAO,IAAI,kBAAkB,iBAAiB;AACnD,OAAK,OAAO,IAAI,gBAAgB,SAAS,MAAM;AAC/C,OAAK,UAAU;;CAGjB,gBACE,UACA,eACA,cACA,WACA,kBACM;EACN,MAAM,aAAa,KAAK,iBAAiB,SAAS,WAAW,SAAS,GAAG;AACzE,OAAK,MAAM,GAAG,KAAK,OAAO,GAAG,WAAW,gBAAgB;AACxD,OAAK,OAAO,IAAI,aAAa,cAAc;AAC3C,OAAK,OAAO,IAAI,YAAY,aAAa;AACzC,MAAI,oBAAoB,UACtB,MAAK,OAAO,IAAI,cAAc,UAAU;AAE1C,OAAK,UAAU;;CAGjB,WAAW,SAAiB,SAAyC;AACnE,MAAI,SAAS;AACX,QAAK,MAAM,GAAG,KAAK,OAAO,MAAM,WAAW,MAAM;AACjD,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAChD,MAAK,OAAO,IAAI,GAAG,IAAI,IAAI,MAAM;AAEnC,QAAK,UAAU;QAEf,MAAK,OAAO,KAAK,GAAG,KAAK,OAAO,MAAM,UAAU;;CAIpD,SAAS,OAIA;AACP,OAAK,MAAM,GAAG,KAAK,OAAO,QAAQ;AAClC,OAAK,OAAO,IAAI,qBAAqB,MAAM,gBAAgB;AAC3D,OAAK,OAAO,IAAI,oBAAoB,MAAM,eAAe;AACzD,OAAK,OAAO,IAAI,cAAc,MAAM,cAAc;AAClD,OAAK,UAAU;;CAGjB,eAAuB,IAAoB;AACzC,MAAI,KAAK,IAAM,QAAO,GAAG,GAAG;AAC5B,MAAI,KAAK,IAAO,QAAO,IAAI,KAAK,KAAM,QAAQ,EAAE,CAAC;AACjD,SAAO,IAAI,KAAK,KAAO,QAAQ,EAAE,CAAC;;;;;AC/GtC,IAAa,uBAAb,MAAkC;CAShC,YACE,QACA,WACA;wCAXuB,IAAI,KAAqB;uCAC1B,IAAI,KAAa;AAWvC,OAAK,YAAY,OAAO;AACxB,OAAK,YAAY;;CAGnB,kBAAkB,WAAyB;EACzC,MAAM,SAAS,KAAK,eAAe,IAAI,UAAU,IAAI,KAAK;AAC1D,OAAK,eAAe,IAAI,WAAW,MAAM;AAEzC,MAAI,SAAS,KAAK,aAAa,CAAC,KAAK,cAAc,IAAI,UAAU,EAAE;AACjE,QAAK,cAAc,IAAI,UAAU;AACjC,QAAK,UACH,wBAAwB,UAAU,OAAO,MAAM,aAC/C;IACE;IACA;IACA,WAAW,KAAK;IACjB,CACF;;;CAIL,mBAAmB,WAAyB;EAC1C,MAAM,QAAQ,KAAK,eAAe,IAAI,UAAU,IAAI;AACpD,MAAI,QAAQ,EACV,MAAK,eAAe,IAAI,WAAW,QAAQ,EAAE;AAG/C,MAAI,QAAQ,IAAI,KAAK,UACnB,MAAK,cAAc,OAAO,UAAU;;CAIxC,WAAgC;AAC9B,SAAO,IAAI,IAAI,KAAK,eAAe;;CAGrC,QAAc;AACZ,OAAK,eAAe,OAAO;AAC3B,OAAK,cAAc,OAAO;;;;;ACzC9B,IAAa,mBAAb,MAA8B;CAY5B,YACE,QACA,WACA;0CAdyB,IAAI,KAA+B;sCACvC,IAAI,KAA2B;uCAI9B,IAAI,KAAa;AAUvC,OAAK,WAAW,OAAO;AACvB,OAAK,YAAY,OAAO;AACxB,OAAK,UAAU,OAAO;AACtB,OAAK,YAAY;;CAGnB,kBAAkB,WAAmB,YAA0B;AAC7D,MAAI,CAAC,KAAK,QAAS;AAEnB,OAAK,aAAa,IAAI,YAAY;GAChC,WAAW,KAAK,KAAK;GACrB,kBAAkB;GACnB,CAAC;AAEF,OAAK,YAAY,WAAW,UAAU;AACtC,OAAK,oBAAoB,UAAU;;CAGrC,mBAAmB,WAAmB,YAA0B;AAC9D,MAAI,CAAC,KAAK,QAAS;AAEnB,OAAK,aAAa,OAAO,WAAW;AACpC,OAAK,YAAY,WAAW,WAAW;AACvC,OAAK,oBAAoB,UAAU;;CAGrC,eAAe,YAA0B;EACvC,MAAM,OAAO,KAAK,aAAa,IAAI,WAAW;AAC9C,MAAI,KACF,MAAK;;CAIT,gBAAgB,YAA8C;AAC5D,SAAO,KAAK,aAAa,IAAI,WAAW;;CAG1C,YAAoB,WAAmB,MAAoC;EACzE,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,SAAS,KAAK,iBAAiB,IAAI,UAAU,IAAI,EAAE;AAEzD,SAAO,KAAK;GAAE,WAAW;GAAK;GAAM,CAAC;EAErC,MAAM,SAAS,MAAM,KAAK;EAC1B,MAAM,WAAW,OAAO,QAAQ,MAAM,EAAE,YAAY,OAAO;AAC3D,OAAK,iBAAiB,IAAI,WAAW,SAAS;;CAGhD,oBAA4B,WAAyB;EACnD,MAAM,SAAS,KAAK,iBAAiB,IAAI,UAAU,IAAI,EAAE;EACzD,MAAM,aAAa,KAAK,IACtB,OAAO,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC,QAC3C,OAAO,QAAQ,MAAM,EAAE,SAAS,WAAW,CAAC,OAC7C;AAED,MAAI,cAAc,KAAK,aAAa,CAAC,KAAK,cAAc,IAAI,UAAU,EAAE;AACtE,QAAK,cAAc,IAAI,UAAU;AACjC,QAAK,UACH,oBAAoB,UAAU,oBAAoB,WAAW,YAAY,KAAK,SAAS,KACvF;IACE;IACA;IACA,UAAU,KAAK;IACf,WAAW,KAAK;IACjB,CACF;AAED,oBAAiB;AACf,SAAK,cAAc,OAAO,UAAU;MACnC,KAAK,SAAS;;;CAIrB,QAAc;AACZ,OAAK,iBAAiB,OAAO;AAC7B,OAAK,aAAa,OAAO;AACzB,OAAK,cAAc,OAAO;;;;;ACpB9B,SAAgB,cACd,SAA8B,EAAE,EAChB;AAChB,QAAO;EACL,OAAO,OAAO,SAAS;EACvB,QAAQ,OAAO,UAAU;EACzB,QAAQ,OAAO,UAAU;EACzB,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,SAAS,OAAO;EAChB,cAAc,OAAO,gBAAgB;EACrC,iBAAiB,OAAO,mBAAmB;EAC3C,kBAAkB,OAAO,oBAAoB;EAC7C,+BAA+B,OAAO,iCAAiC;EACvE,uBAAuB,OAAO,yBAAyB;EACvD,wBAAwB,OAAO,0BAA0B;EACzD,yBAAyB,OAAO,2BAA2B;EAC3D,QAAQ,OAAO,UAAU;EAC1B;;;;AC1FH,MAAM,0BAA0B;AAEhC,IAAa,gBAAb,MAAiD;CAgB/C,YAAY,SAA8B,EAAE,EAAE;cAf9B;iBACG;0BAUQ;gCACM,KAAK,KAAK;oCACN;AAGnC,OAAK,SAAS,cAAc,OAAO;EAEnC,MAAM,kBACJ,SACA,YACG;AACH,QAAK,WAAW,SAAS,QAAQ;;AAGnC,OAAK,kBAAkB,IAAI,gBAAgB,KAAK,OAAO;AACvD,OAAK,mBAAmB,IAAI,iBAAiB,KAAK,OAAO;AACzD,OAAK,uBAAuB,IAAI,qBAC9B,KAAK,QACL,eACD;AACD,OAAK,mBAAmB,IAAI,iBAAiB,KAAK,QAAQ,eAAe;;CAG3E,UAAU,SAA8B;AACtC,OAAK,UAAU;EACf,MAAM,QAAQ,QAAQ,UAAU;AAEhC,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,WAAW,MAAM;MAEtC,MAAK,iBAAiB,WAAW,MAAM;;CAI3C,cAAoB;AAClB,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,cAAc;MAEnC,MAAK,iBAAiB,cAAc;AAGtC,OAAK,qBAAqB,OAAO;AACjC,OAAK,iBAAiB,OAAO;AAC7B,OAAK,mBAAmB;AACxB,OAAK,yBAAyB,KAAK,KAAK;AACxC,OAAK,6BAA6B;AAClC,OAAK,UAAU,KAAA;;CAGjB,kBACE,UACA,SACM;EACN,MAAM,WAAW,QAAQ,oBAAoB,SAAS;AAEtD,MAAI,CAAC,KAAK,UAAU,UAAU,SAAS,WAAW,SAAS,GAAG,CAC5D;AAGF,OAAK,qBAAqB,kBAAkB,SAAS,UAAU;AAC/D,OAAK,iBAAiB,kBAAkB,SAAS,WAAW,SAAS,GAAG;AAExE,MAAI,CAAC,KAAK,oBAAoB,CAAE;AAEhC,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,mBAAmB,SAAS;MAEjD,MAAK,iBAAiB,mBACpB,UACA,QAAQ,SAAS,SAAS,CAC3B;;CAIL,eACE,UACA,eACA,cACA,WACA,SACM;EACN,MAAM,WAAW,QAAQ,oBAAoB,SAAS;AAEtD,MAAI,CAAC,KAAK,UAAU,UAAU,SAAS,WAAW,SAAS,GAAG,CAC5D;AAGF,OAAK,iBAAiB,eAAe,SAAS,GAAG;AAEjD,MAAI,CAAC,KAAK,uBAAuB,CAAE;AAGnC,MAAI,KAAK,2BAA4B;EAErC,MAAM,MAAM,KAAK,KAAK;AACtB,MAAI,MAAM,KAAK,0BAA0B,KAAM;AAE7C,QAAK,mBAAmB;AACxB,QAAK,yBAAyB;;AAGhC,OAAK;AAEL,MAAI,KAAK,mBAAmB,yBAAyB;AACnD,QAAK,6BAA6B;AAClC,QAAK,WACH,2CAA2C,wBAAwB,iBACpE;AACD;;AAGF,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,gBACnB,UACA,eACA,cACA,WACA,KAAK,OAAO,iBACb;MAED,MAAK,iBAAiB,gBACpB,UACA,eACA,cACA,WACA,KAAK,OAAO,iBACb;;CAIL,mBACE,UACA,SACM;EACN,MAAM,WAAW,QAAQ,oBAAoB,SAAS;AAEtD,MAAI,CAAC,KAAK,UAAU,UAAU,SAAS,WAAW,SAAS,GAAG,CAC5D;EAGF,MAAM,OAAO,KAAK,iBAAiB,gBAAgB,SAAS,GAAG;EAC/D,MAAM,aAAa,OAAO,KAAK,KAAK,GAAG,KAAK,YAAY;EACxD,MAAM,mBAAmB,MAAM,oBAAoB;AAEnD,OAAK,qBAAqB,mBAAmB,SAAS,UAAU;AAChE,OAAK,iBAAiB,mBAAmB,SAAS,WAAW,SAAS,GAAG;AAEzE,MAAI,CAAC,KAAK,oBAAoB,CAAE;AAEhC,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,oBAAoB,UAAU,WAAW;MAE9D,MAAK,iBAAiB,oBACpB,UACA,YACA,iBACD;;CAIL,WAAiB;AACf,MAAI,CAAC,KAAK,QAAS;EACnB,MAAM,QAAQ,KAAK,QAAQ,UAAU;AAErC,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,SAAS,MAAM;MAEpC,MAAK,iBAAiB,SAAS,MAAM;;CAIzC,UACE,UACA,WACA,YACS;AACT,MAAI,KAAK,OAAO,WAAW,CAAC,KAAK,OAAO,QAAQ,SAAS,UAAU,CACjE,QAAO;AAGT,MAAI,KAAK,OAAO,SAAS,SAAS,UAAU,CAC1C,QAAO;AAGT,MAAI,KAAK,OAAO,QAAQ;GACtB,MAAM,MAAqB;IACzB;IACA;IACA;IACD;AACD,UAAO,KAAK,OAAO,OAAO,IAAI;;AAGhC,SAAO;;CAGT,qBAAsC;AACpC,MAAI,CAAC,KAAK,OAAO,aAAc,QAAO;AACtC,SAAO,KAAK,OAAO,UAAU;;CAG/B,wBAAyC;AACvC,MAAI,CAAC,KAAK,OAAO,gBAAiB,QAAO;AACzC,SACE,KAAK,OAAO,UAAU,UACtB,KAAK,OAAO,UAAU,WACtB,KAAK,OAAO,UAAU;;CAI1B,WAAmB,SAAiB,SAAyC;AAC3E,MAAI,KAAK,OAAO,WAAW,SACzB,MAAK,gBAAgB,WAAW,QAAQ;MAExC,MAAK,iBAAiB,WAAW,SAAS,QAAQ"}
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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"}
package/dist/types.d.ts CHANGED
@@ -34,6 +34,8 @@ export interface LoggingPluginConfig {
34
34
  logStateChanges?: boolean;
35
35
  /** Include callstack in state change logs (default: false) */
36
36
  includeCallstack?: boolean;
37
+ /** Log changed path names in state change output (default: false) */
38
+ logPaths?: boolean;
37
39
  /** Warn when instance count exceeds threshold (default: 50) */
38
40
  instanceCountWarningThreshold?: number;
39
41
  /** Warn on rapid init/dispose cycles (default: true) */
@@ -55,6 +57,7 @@ export interface ResolvedConfig {
55
57
  logLifecycle: boolean;
56
58
  logStateChanges: boolean;
57
59
  includeCallstack: boolean;
60
+ logPaths: boolean;
58
61
  instanceCountWarningThreshold: number;
59
62
  detectRapidLifecycles: boolean;
60
63
  rapidLifecycleWindowMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEhE,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,IAAI,CAAC;IAClB,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,QAAQ,CAAC;IAEjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,oCAAoC;IACpC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAElB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,wCAAwC;IACxC,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,+DAA+D;IAC/D,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAEvC,wDAAwD;IACxD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,sEAAsE;IACtE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC,iEAAiE;IACjE,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6BAA6B,EAAE,MAAM,CAAC;IACtC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAC3B,MAAM,GAAE,mBAAwB,GAC/B,cAAc,CAiBhB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEhE,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,IAAI,CAAC;IAClB,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,QAAQ,CAAC;IAEjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,oCAAoC;IACpC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAElB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,wCAAwC;IACxC,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,+DAA+D;IAC/D,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAEvC,wDAAwD;IACxD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC,sEAAsE;IACtE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC,iEAAiE;IACjE,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,6BAA6B,EAAE,MAAM,CAAC;IACtC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAC3B,MAAM,GAAE,mBAAwB,GAC/B,cAAc,CAkBhB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blac/logging-plugin",
3
- "version": "2.0.13",
3
+ "version": "2.0.15",
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.12"
49
+ "@blac/core": "^2.0.14"
50
50
  },
51
51
  "devDependencies": {
52
52
  "typescript": "^5.9.2",
53
53
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest",
54
54
  "publint": "^0.3.12",
55
55
  "vite-plus": "latest",
56
- "@blac/core": "2.0.12"
56
+ "@blac/core": "2.0.14"
57
57
  },
58
58
  "scripts": {
59
59
  "dev": "vp pack --watch",