{"version":3,"file":"index.cjs","names":[],"sources":["../../src/utils/worker-performance-tracker.ts"],"sourcesContent":["import { PerformanceTimeEntry } from '@awesome-ecs/abstract/utils';\nimport {\n  WorkerMetricEntry,\n  WorkerMetricsSummary,\n  WorkerSnapshot\n} from '../abstract/worker-performance';\n\ntype AccEntry = {\n  count: number;\n  totalMs: number;\n  minMs: number;\n  maxMs: number;\n  lastMs: number;\n};\n\n/**\n * Tracks performance metrics for worker operations, allowing for aggregation and analysis of execution times across different worker tasks.\n * Metrics are sampled at regular intervals and can be retrieved in summary form for overall or per-worker insights.\n *\n * @injectable\n */\nexport class WorkerPerformanceTracker {\n  static readonly MAX_SNAPSHOTS = 120;\n  static readonly SAMPLE_INTERVAL_MS = 250;\n\n  private readonly _snapshots: WorkerSnapshot[] = [];\n  private readonly _pendingMetrics = new Map<string, WorkerMetricEntry>();\n  private readonly _perWorkerPending = new Map<string, Map<string, WorkerMetricEntry>>();\n\n  private readonly _aggregate = new Map<string, AccEntry>();\n  private readonly _perWorkerAggregate = new Map<string, Map<string, AccEntry>>();\n\n  private _pendingMaxQueueSize = 0;\n  private _pendingStalledCount = 0;\n  private _pendingWaitTimes: number[] = [];\n  private _lastSampleTime = 0;\n  private _enabled = false;\n\n  private _aggregateDirty = true;\n  private _cachedSummaries: WorkerMetricsSummary[] = [];\n  private _perWorkerDirty = true;\n  private _cachedPerWorker = new Map<string, WorkerMetricsSummary[]>();\n\n  get snapshots(): ReadonlyArray<WorkerSnapshot> {\n    return this._snapshots;\n  }\n\n  get enabled(): boolean {\n    return this._enabled;\n  }\n\n  set enabled(value: boolean) {\n    this._enabled = value;\n  }\n\n  get queueHistory(): number[] {\n    return this._snapshots.map((s) => s.queueSize);\n  }\n\n  get stalledHistory(): number[] {\n    return this._snapshots.map((s) => s.stalledCount);\n  }\n\n  get waitTimeHistory(): number[] {\n    return this._snapshots.map((s) => s.avgWaitTimeMs);\n  }\n\n  get eventTypeHistories(): Map<string, number[]> {\n    const allNames = new Set<string>();\n    for (const snap of this._snapshots) {\n      for (const m of snap.metrics) allNames.add(m.name);\n    }\n\n    const result = new Map<string, number[]>();\n    for (const name of allNames) {\n      result.set(\n        name,\n        this._snapshots.map((snap) => {\n          const m = snap.metrics.find((e) => e.name === name);\n          return m && m.count > 0 ? m.totalMs / m.count : 0;\n        })\n      );\n    }\n    return result;\n  }\n\n  get summaries(): WorkerMetricsSummary[] {\n    if (!this._aggregateDirty) return this._cachedSummaries;\n\n    const result: WorkerMetricsSummary[] = [];\n    for (const [name, data] of this._aggregate) {\n      result.push({\n        name,\n        count: data.count,\n        totalMs: data.totalMs,\n        avgMs: data.count > 0 ? data.totalMs / data.count : 0,\n        minMs: data.minMs,\n        maxMs: data.maxMs,\n        lastMs: data.lastMs\n      });\n    }\n    result.sort((a, b) => b.totalMs - a.totalMs);\n    this._cachedSummaries = result;\n    this._aggregateDirty = false;\n    return result;\n  }\n\n  get perWorkerSummaries(): Map<string, WorkerMetricsSummary[]> {\n    if (!this._perWorkerDirty) return this._cachedPerWorker;\n\n    const result = new Map<string, WorkerMetricsSummary[]>();\n    for (const [uid, aggMap] of this._perWorkerAggregate) {\n      const entries: WorkerMetricsSummary[] = [];\n      for (const [name, data] of aggMap) {\n        entries.push({\n          name,\n          count: data.count,\n          totalMs: data.totalMs,\n          avgMs: data.count > 0 ? data.totalMs / data.count : 0,\n          minMs: data.minMs,\n          maxMs: data.maxMs,\n          lastMs: data.lastMs\n        });\n      }\n      entries.sort((a, b) => b.totalMs - a.totalMs);\n      result.set(uid, entries);\n    }\n    this._cachedPerWorker = result;\n    this._perWorkerDirty = false;\n    return result;\n  }\n\n  recordQueueSize(size: number): void {\n    if (!this._enabled) return;\n    this._pendingMaxQueueSize = Math.max(this._pendingMaxQueueSize, size);\n  }\n\n  recordStalledCount(count: number): void {\n    if (!this._enabled) return;\n    this._pendingStalledCount = Math.max(this._pendingStalledCount, count);\n  }\n\n  recordWaitTimes(times: number[]): void {\n    if (!this._enabled) return;\n    for (const t of times) {\n      this._pendingWaitTimes.push(t);\n    }\n  }\n\n  addMetric(workerUid: string, entry: PerformanceTimeEntry): void {\n    if (!this._enabled) return;\n    const ms = entry.msPassed ?? 0;\n    const name = entry.name;\n\n    // Aggregate pending\n    const existing = this._pendingMetrics.get(name);\n    if (existing) {\n      existing.totalMs += ms;\n      existing.count += 1;\n    } else {\n      this._pendingMetrics.set(name, { name, totalMs: ms, count: 1 });\n    }\n\n    // Per-worker pending\n    let workerMap = this._perWorkerPending.get(workerUid);\n    if (!workerMap) {\n      workerMap = new Map();\n      this._perWorkerPending.set(workerUid, workerMap);\n    }\n    const workerExisting = workerMap.get(name);\n    if (workerExisting) {\n      workerExisting.totalMs += ms;\n      workerExisting.count += 1;\n    } else {\n      workerMap.set(name, { name, totalMs: ms, count: 1 });\n    }\n  }\n\n  addMetrics(workerUid: string, entries: PerformanceTimeEntry[]): void {\n    for (const entry of entries) {\n      this.addMetric(workerUid, entry);\n    }\n  }\n\n  sample(now?: number): void {\n    if (!this._enabled) return;\n    const timestamp = now ?? performance.now();\n\n    if (\n      this._lastSampleTime !== 0 &&\n      timestamp - this._lastSampleTime < WorkerPerformanceTracker.SAMPLE_INTERVAL_MS\n    ) {\n      return;\n    }\n\n    const metrics = Array.from(this._pendingMetrics.values());\n\n    const avgWaitTimeMs =\n      this._pendingWaitTimes.length > 0\n        ? this._pendingWaitTimes.reduce((s, t) => s + t, 0) / this._pendingWaitTimes.length\n        : 0;\n\n    const snapshot: WorkerSnapshot = {\n      timestamp,\n      queueSize: this._pendingMaxQueueSize,\n      stalledCount: this._pendingStalledCount,\n      avgWaitTimeMs,\n      metrics\n    };\n\n    this._snapshots.push(snapshot);\n    if (this._snapshots.length > WorkerPerformanceTracker.MAX_SNAPSHOTS) {\n      this._snapshots.shift();\n    }\n\n    // Merge into aggregate summaries\n    this.mergeIntoAggregate(metrics);\n    this.mergePerWorkerAggregate();\n\n    // Reset pending\n    this._pendingMetrics.clear();\n    this._perWorkerPending.clear();\n    this._pendingMaxQueueSize = 0;\n    this._pendingStalledCount = 0;\n    this._pendingWaitTimes = [];\n    this._lastSampleTime = timestamp;\n  }\n\n  clear(): void {\n    this._snapshots.length = 0;\n    this._pendingMetrics.clear();\n    this._perWorkerPending.clear();\n    this._aggregate.clear();\n    this._perWorkerAggregate.clear();\n    this._pendingMaxQueueSize = 0;\n    this._pendingStalledCount = 0;\n    this._pendingWaitTimes = [];\n    this._lastSampleTime = 0;\n    this._aggregateDirty = true;\n    this._perWorkerDirty = true;\n    this._cachedSummaries = [];\n    this._cachedPerWorker = new Map();\n  }\n\n  private mergeIntoAggregate(metrics: WorkerMetricEntry[]): void {\n    for (const m of metrics) {\n      this.mergeEntry(this._aggregate, m.name, m);\n    }\n    this._aggregateDirty = true;\n  }\n\n  private mergePerWorkerAggregate(): void {\n    for (const [uid, workerMap] of this._perWorkerPending) {\n      const aggMap = this.getOrCreateWorkerAgg(uid);\n      for (const [name, m] of workerMap) {\n        this.mergeEntry(aggMap, name, m);\n      }\n    }\n    this._perWorkerDirty = true;\n  }\n\n  private getOrCreateWorkerAgg(uid: string): Map<string, AccEntry> {\n    let aggMap = this._perWorkerAggregate.get(uid);\n    if (!aggMap) {\n      aggMap = new Map();\n      this._perWorkerAggregate.set(uid, aggMap);\n    }\n    return aggMap;\n  }\n\n  private mergeEntry(aggMap: Map<string, AccEntry>, name: string, m: WorkerMetricEntry): void {\n    const avg = m.count > 0 ? m.totalMs / m.count : 0;\n    const existing = aggMap.get(name);\n    if (existing) {\n      existing.count += m.count;\n      existing.totalMs += m.totalMs;\n      existing.minMs = Math.min(avg, existing.minMs);\n      existing.maxMs = Math.max(avg, existing.maxMs);\n      existing.lastMs = avg;\n    } else {\n      aggMap.set(name, { count: m.count, totalMs: m.totalMs, minMs: avg, maxMs: avg, lastMs: avg });\n    }\n  }\n}\n"],"mappings":";;;;;;;;AAqBA,IAAa,2BAAb,MAAa,yBAAyB;CACpC,OAAgB,gBAAgB;CAChC,OAAgB,qBAAqB;CAErC,aAAgD,CAAC;CACjD,kCAAmC,IAAI,IAA+B;CACtE,oCAAqC,IAAI,IAA4C;CAErF,6BAA8B,IAAI,IAAsB;CACxD,sCAAuC,IAAI,IAAmC;CAE9E,uBAA+B;CAC/B,uBAA+B;CAC/B,oBAAsC,CAAC;CACvC,kBAA0B;CAC1B,WAAmB;CAEnB,kBAA0B;CAC1B,mBAAmD,CAAC;CACpD,kBAA0B;CAC1B,mCAA2B,IAAI,IAAoC;CAEnE,IAAI,YAA2C;EAC7C,OAAO,KAAK;CACd;CAEA,IAAI,UAAmB;EACrB,OAAO,KAAK;CACd;CAEA,IAAI,QAAQ,OAAgB;EAC1B,KAAK,WAAW;CAClB;CAEA,IAAI,eAAyB;EAC3B,OAAO,KAAK,WAAW,KAAK,MAAM,EAAE,SAAS;CAC/C;CAEA,IAAI,iBAA2B;EAC7B,OAAO,KAAK,WAAW,KAAK,MAAM,EAAE,YAAY;CAClD;CAEA,IAAI,kBAA4B;EAC9B,OAAO,KAAK,WAAW,KAAK,MAAM,EAAE,aAAa;CACnD;CAEA,IAAI,qBAA4C;EAC9C,MAAM,2BAAW,IAAI,IAAY;EACjC,KAAK,MAAM,QAAQ,KAAK,YACtB,KAAK,MAAM,KAAK,KAAK,SAAS,SAAS,IAAI,EAAE,IAAI;EAGnD,MAAM,yBAAS,IAAI,IAAsB;EACzC,KAAK,MAAM,QAAQ,UACjB,OAAO,IACL,MACA,KAAK,WAAW,KAAK,SAAS;GAC5B,MAAM,IAAI,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,IAAI;GAClD,OAAO,KAAK,EAAE,QAAQ,IAAI,EAAE,UAAU,EAAE,QAAQ;EAClD,CAAC,CACH;EAEF,OAAO;CACT;CAEA,IAAI,YAAoC;EACtC,IAAI,CAAC,KAAK,iBAAiB,OAAO,KAAK;EAEvC,MAAM,SAAiC,CAAC;EACxC,KAAK,MAAM,CAAC,MAAM,SAAS,KAAK,YAC9B,OAAO,KAAK;GACV;GACA,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,OAAO,KAAK,QAAQ,IAAI,KAAK,UAAU,KAAK,QAAQ;GACpD,OAAO,KAAK;GACZ,OAAO,KAAK;GACZ,QAAQ,KAAK;EACf,CAAC;EAEH,OAAO,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;EAC3C,KAAK,mBAAmB;EACxB,KAAK,kBAAkB;EACvB,OAAO;CACT;CAEA,IAAI,qBAA0D;EAC5D,IAAI,CAAC,KAAK,iBAAiB,OAAO,KAAK;EAEvC,MAAM,yBAAS,IAAI,IAAoC;EACvD,KAAK,MAAM,CAAC,KAAK,WAAW,KAAK,qBAAqB;GACpD,MAAM,UAAkC,CAAC;GACzC,KAAK,MAAM,CAAC,MAAM,SAAS,QACzB,QAAQ,KAAK;IACX;IACA,OAAO,KAAK;IACZ,SAAS,KAAK;IACd,OAAO,KAAK,QAAQ,IAAI,KAAK,UAAU,KAAK,QAAQ;IACpD,OAAO,KAAK;IACZ,OAAO,KAAK;IACZ,QAAQ,KAAK;GACf,CAAC;GAEH,QAAQ,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;GAC5C,OAAO,IAAI,KAAK,OAAO;EACzB;EACA,KAAK,mBAAmB;EACxB,KAAK,kBAAkB;EACvB,OAAO;CACT;CAEA,gBAAgB,MAAoB;EAClC,IAAI,CAAC,KAAK,UAAU;EACpB,KAAK,uBAAuB,KAAK,IAAI,KAAK,sBAAsB,IAAI;CACtE;CAEA,mBAAmB,OAAqB;EACtC,IAAI,CAAC,KAAK,UAAU;EACpB,KAAK,uBAAuB,KAAK,IAAI,KAAK,sBAAsB,KAAK;CACvE;CAEA,gBAAgB,OAAuB;EACrC,IAAI,CAAC,KAAK,UAAU;EACpB,KAAK,MAAM,KAAK,OACd,KAAK,kBAAkB,KAAK,CAAC;CAEjC;CAEA,UAAU,WAAmB,OAAmC;EAC9D,IAAI,CAAC,KAAK,UAAU;EACpB,MAAM,KAAK,MAAM,YAAY;EAC7B,MAAM,OAAO,MAAM;EAGnB,MAAM,WAAW,KAAK,gBAAgB,IAAI,IAAI;EAC9C,IAAI,UAAU;GACZ,SAAS,WAAW;GACpB,SAAS,SAAS;EACpB,OACE,KAAK,gBAAgB,IAAI,MAAM;GAAE;GAAM,SAAS;GAAI,OAAO;EAAE,CAAC;EAIhE,IAAI,YAAY,KAAK,kBAAkB,IAAI,SAAS;EACpD,IAAI,CAAC,WAAW;GACd,4BAAY,IAAI,IAAI;GACpB,KAAK,kBAAkB,IAAI,WAAW,SAAS;EACjD;EACA,MAAM,iBAAiB,UAAU,IAAI,IAAI;EACzC,IAAI,gBAAgB;GAClB,eAAe,WAAW;GAC1B,eAAe,SAAS;EAC1B,OACE,UAAU,IAAI,MAAM;GAAE;GAAM,SAAS;GAAI,OAAO;EAAE,CAAC;CAEvD;CAEA,WAAW,WAAmB,SAAuC;EACnE,KAAK,MAAM,SAAS,SAClB,KAAK,UAAU,WAAW,KAAK;CAEnC;CAEA,OAAO,KAAoB;EACzB,IAAI,CAAC,KAAK,UAAU;EACpB,MAAM,YAAY,OAAO,YAAY,IAAI;EAEzC,IACE,KAAK,oBAAoB,KACzB,YAAY,KAAK,kBAAkB,yBAAyB,oBAE5D;EAGF,MAAM,UAAU,MAAM,KAAK,KAAK,gBAAgB,OAAO,CAAC;EAExD,MAAM,gBACJ,KAAK,kBAAkB,SAAS,IAC5B,KAAK,kBAAkB,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,kBAAkB,SAC3E;EAEN,MAAM,WAA2B;GAC/B;GACA,WAAW,KAAK;GAChB,cAAc,KAAK;GACnB;GACA;EACF;EAEA,KAAK,WAAW,KAAK,QAAQ;EAC7B,IAAI,KAAK,WAAW,SAAS,yBAAyB,eACpD,KAAK,WAAW,MAAM;EAIxB,KAAK,mBAAmB,OAAO;EAC/B,KAAK,wBAAwB;EAG7B,KAAK,gBAAgB,MAAM;EAC3B,KAAK,kBAAkB,MAAM;EAC7B,KAAK,uBAAuB;EAC5B,KAAK,uBAAuB;EAC5B,KAAK,oBAAoB,CAAC;EAC1B,KAAK,kBAAkB;CACzB;CAEA,QAAc;EACZ,KAAK,WAAW,SAAS;EACzB,KAAK,gBAAgB,MAAM;EAC3B,KAAK,kBAAkB,MAAM;EAC7B,KAAK,WAAW,MAAM;EACtB,KAAK,oBAAoB,MAAM;EAC/B,KAAK,uBAAuB;EAC5B,KAAK,uBAAuB;EAC5B,KAAK,oBAAoB,CAAC;EAC1B,KAAK,kBAAkB;EACvB,KAAK,kBAAkB;EACvB,KAAK,kBAAkB;EACvB,KAAK,mBAAmB,CAAC;EACzB,KAAK,mCAAmB,IAAI,IAAI;CAClC;CAEA,mBAA2B,SAAoC;EAC7D,KAAK,MAAM,KAAK,SACd,KAAK,WAAW,KAAK,YAAY,EAAE,MAAM,CAAC;EAE5C,KAAK,kBAAkB;CACzB;CAEA,0BAAwC;EACtC,KAAK,MAAM,CAAC,KAAK,cAAc,KAAK,mBAAmB;GACrD,MAAM,SAAS,KAAK,qBAAqB,GAAG;GAC5C,KAAK,MAAM,CAAC,MAAM,MAAM,WACtB,KAAK,WAAW,QAAQ,MAAM,CAAC;EAEnC;EACA,KAAK,kBAAkB;CACzB;CAEA,qBAA6B,KAAoC;EAC/D,IAAI,SAAS,KAAK,oBAAoB,IAAI,GAAG;EAC7C,IAAI,CAAC,QAAQ;GACX,yBAAS,IAAI,IAAI;GACjB,KAAK,oBAAoB,IAAI,KAAK,MAAM;EAC1C;EACA,OAAO;CACT;CAEA,WAAmB,QAA+B,MAAc,GAA4B;EAC1F,MAAM,MAAM,EAAE,QAAQ,IAAI,EAAE,UAAU,EAAE,QAAQ;EAChD,MAAM,WAAW,OAAO,IAAI,IAAI;EAChC,IAAI,UAAU;GACZ,SAAS,SAAS,EAAE;GACpB,SAAS,WAAW,EAAE;GACtB,SAAS,QAAQ,KAAK,IAAI,KAAK,SAAS,KAAK;GAC7C,SAAS,QAAQ,KAAK,IAAI,KAAK,SAAS,KAAK;GAC7C,SAAS,SAAS;EACpB,OACE,OAAO,IAAI,MAAM;GAAE,OAAO,EAAE;GAAO,SAAS,EAAE;GAAS,OAAO;GAAK,OAAO;GAAK,QAAQ;EAAI,CAAC;CAEhG;AACF"}