{"version":3,"file":"SignalBuffer.cjs","names":[],"sources":["../../../src/processors/utils/SignalBuffer.ts"],"sourcesContent":["export interface SignalEntry {\n  readonly kind: 'span' | 'log';\n  readonly id: string;\n  readonly startTime: number;\n  // The signal's emb.type, e.g. EMB_TYPES.Network - tells the backend which\n  // table to look the id up in. Undefined if the signal has no type at the\n  // point it's recorded (e.g. some spans settle their type only later).\n  readonly type?: string;\n}\n\nexport interface SignalBufferArgs {\n  maxAgeMillis?: number;\n  maxEntries?: number;\n}\n\nexport interface SignalWindowResult {\n  spanIds: string[];\n  spanTypes: string[];\n  logIds: string[];\n  logTypes: string[];\n}\n\n// Best-effort correlation ceiling: a window still open 60s after the newest\n// recorded signal will under-report early signals, since those signals have\n// already been evicted by then.\nconst DEFAULT_MAX_AGE_MILLIS = 60_000;\nconst DEFAULT_MAX_ENTRIES = 4096;\n\n/**\n * A bounded, in-memory rolling buffer of span and log signals. Used to back-fill\n * a span with the ids of signals that started within its window, since that\n * span is built after its window has already closed.\n */\nexport class SignalBuffer {\n  private readonly _maxAgeMillis: number;\n  private readonly _maxEntries: number;\n  private readonly _entries: SignalEntry[] = [];\n  private _latestStartTime = 0;\n\n  public constructor({\n    maxAgeMillis = DEFAULT_MAX_AGE_MILLIS,\n    maxEntries = DEFAULT_MAX_ENTRIES,\n  }: SignalBufferArgs = {}) {\n    this._maxAgeMillis = maxAgeMillis;\n    this._maxEntries = maxEntries;\n  }\n\n  public record(entry: SignalEntry): void {\n    this._entries.push(entry);\n    if (entry.startTime > this._latestStartTime) {\n      this._latestStartTime = entry.startTime;\n    }\n    this._evict();\n  }\n\n  public collectWindow(startTime: number, endTime: number): SignalWindowResult {\n    const matched = this._entries.filter(\n      (entry) => entry.startTime >= startTime && entry.startTime <= endTime,\n    );\n    const spans = matched.filter((entry) => entry.kind === 'span');\n    const logs = matched.filter((entry) => entry.kind === 'log');\n\n    return {\n      spanIds: spans.map((entry) => entry.id),\n      spanTypes: spans.map((entry) => entry.type ?? ''),\n      logIds: logs.map((entry) => entry.id),\n      logTypes: logs.map((entry) => entry.type ?? ''),\n    };\n  }\n\n  // Age eviction is relative to the newest entry seen so the buffer needs no\n  // injected clock and stays deterministic.\n  private _evict(): void {\n    const cutoff = this._latestStartTime - this._maxAgeMillis;\n    while (this._entries.length > 0 && this._entries[0].startTime < cutoff) {\n      this._entries.shift();\n    }\n    while (this._entries.length > this._maxEntries) {\n      this._entries.shift();\n    }\n  }\n}\n"],"mappings":";;AAyBA,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;;;;;;AAO5B,IAAa,eAAb,MAA0B;CACxB;CACA;CACA,WAA2C,CAAC;CAC5C,mBAA2B;CAE3B,YAAmB,EACjB,eAAe,wBACf,aAAa,wBACO,CAAC,GAAG;EACxB,KAAK,gBAAgB;EACrB,KAAK,cAAc;CACrB;CAEA,OAAc,OAA0B;EACtC,KAAK,SAAS,KAAK,KAAK;EACxB,IAAI,MAAM,YAAY,KAAK,kBACzB,KAAK,mBAAmB,MAAM;EAEhC,KAAK,OAAO;CACd;CAEA,cAAqB,WAAmB,SAAqC;EAC3E,MAAM,UAAU,KAAK,SAAS,QAC3B,UAAU,MAAM,aAAa,aAAa,MAAM,aAAa,OAChE;EACA,MAAM,QAAQ,QAAQ,QAAQ,UAAU,MAAM,SAAS,MAAM;EAC7D,MAAM,OAAO,QAAQ,QAAQ,UAAU,MAAM,SAAS,KAAK;EAE3D,OAAO;GACL,SAAS,MAAM,KAAK,UAAU,MAAM,EAAE;GACtC,WAAW,MAAM,KAAK,UAAU,MAAM,QAAQ,EAAE;GAChD,QAAQ,KAAK,KAAK,UAAU,MAAM,EAAE;GACpC,UAAU,KAAK,KAAK,UAAU,MAAM,QAAQ,EAAE;EAChD;CACF;CAIA,SAAuB;EACrB,MAAM,SAAS,KAAK,mBAAmB,KAAK;EAC5C,OAAO,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,EAAE,CAAC,YAAY,QAC9D,KAAK,SAAS,MAAM;EAEtB,OAAO,KAAK,SAAS,SAAS,KAAK,aACjC,KAAK,SAAS,MAAM;CAExB;AACF"}