{"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { getSentryEventId } from \"./sentry.js\";\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n  private errorCounts: Map<string, number>;\n  private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n  private sentryEventIds: Map<string, string>;\n\n  constructor() {\n    this.errorCounts = new Map();\n    this.errorDetails = new Map();\n    this.sentryEventIds = new Map();\n  }\n\n  public addServerError(serverError: ConsumerMethodPath & ServerError) {\n    const key = this.getKey(serverError);\n    if (!this.errorDetails.has(key)) {\n      this.errorDetails.set(key, serverError);\n    }\n    this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n\n    const sentryEventId = getSentryEventId();\n    if (sentryEventId) {\n      this.sentryEventIds.set(key, sentryEventId);\n    }\n  }\n\n  public getAndResetServerErrors() {\n    const data: Array<ServerErrorsItem> = [];\n    this.errorCounts.forEach((count, key) => {\n      const serverError = this.errorDetails.get(key);\n      if (serverError) {\n        data.push({\n          consumer: serverError.consumer || null,\n          method: serverError.method,\n          path: serverError.path,\n          type: serverError.type,\n          msg: truncateExceptionMessage(serverError.msg),\n          traceback: truncateExceptionStackTrace(serverError.traceback),\n          sentry_event_id: this.sentryEventIds.get(key) || null,\n          error_count: count,\n        });\n      }\n    });\n    this.errorCounts.clear();\n    this.errorDetails.clear();\n    this.sentryEventIds.clear();\n    return data;\n  }\n\n  private getKey(serverError: ConsumerMethodPath & ServerError) {\n    const hashInput = [\n      serverError.consumer || \"\",\n      serverError.method.toUpperCase(),\n      serverError.path,\n      serverError.type,\n      serverError.msg.trim(),\n      serverError.traceback.trim(),\n    ].join(\"|\");\n    return createHash(\"md5\").update(hashInput).digest(\"hex\");\n  }\n}\n\nexport function truncateExceptionMessage(msg: string) {\n  if (msg.length <= MAX_MSG_LENGTH) {\n    return msg;\n  }\n  const suffix = \"... (truncated)\";\n  const cutoff = MAX_MSG_LENGTH - suffix.length;\n  return msg.substring(0, cutoff) + suffix;\n}\n\nexport function truncateExceptionStackTrace(stack: string) {\n  if (stack.length <= MAX_STACKTRACE_LENGTH) {\n    return stack;\n  }\n  const suffix = \"... (truncated) ...\";\n  const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n  const lines = stack.trim().split(\"\\n\");\n  const truncatedLines: string[] = [];\n  let length = 0;\n  for (const line of lines) {\n    if (length + line.length + 1 > cutoff) {\n      truncatedLines.push(suffix);\n      break;\n    }\n    truncatedLines.push(line);\n    length += line.length + 1;\n  }\n  return truncatedLines.join(\"\\n\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;;;;AAAA,yBAA2B;AAE3B,oBAAiC;AAGjC,MAAMA,iBAAiB;AACvB,MAAMC,wBAAwB;AAE9B,MAAqBC,sBAArB,MAAqBA,oBAAAA;EACXC;EACAC;EACAC;EAER,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;AACxB,SAAKD,iBAAiB,oBAAIC,IAAAA;EAC5B;EAEOC,eAAeC,aAA+C;AACnE,UAAMC,MAAM,KAAKC,OAAOF,WAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,WAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;AAE7D,UAAMK,oBAAgBC,gCAAAA;AACtB,QAAID,eAAe;AACjB,WAAKT,eAAeO,IAAIH,KAAKK,aAAAA;IAC/B;EACF;EAEOE,0BAA0B;AAC/B,UAAMC,OAAgC,CAAA;AACtC,SAAKd,YAAYe,QAAQ,CAACC,OAAOV,QAAAA;AAC/B,YAAMD,cAAc,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC1C,UAAID,aAAa;AACfS,aAAKG,KAAK;UACRC,UAAUb,YAAYa,YAAY;UAClCC,QAAQd,YAAYc;UACpBC,MAAMf,YAAYe;UAClBC,MAAMhB,YAAYgB;UAClBC,KAAKC,yBAAyBlB,YAAYiB,GAAG;UAC7CE,WAAWC,4BAA4BpB,YAAYmB,SAAS;UAC5DE,iBAAiB,KAAKxB,eAAeQ,IAAIJ,GAAAA,KAAQ;UACjDqB,aAAaX;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKhB,YAAY4B,MAAK;AACtB,SAAK3B,aAAa2B,MAAK;AACvB,SAAK1B,eAAe0B,MAAK;AACzB,WAAOd;EACT;EAEQP,OAAOF,aAA+C;AAC5D,UAAMwB,YAAY;MAChBxB,YAAYa,YAAY;MACxBb,YAAYc,OAAOW,YAAW;MAC9BzB,YAAYe;MACZf,YAAYgB;MACZhB,YAAYiB,IAAIS,KAAI;MACpB1B,YAAYmB,UAAUO,KAAI;MAC1BC,KAAK,GAAA;AACP,eAAOC,+BAAW,KAAA,EAAOC,OAAOL,SAAAA,EAAWM,OAAO,KAAA;EACpD;AACF;AA1DqBpC;AAArB,IAAqBA,qBAArB;AA4DO,SAASwB,yBAAyBD,KAAW;AAClD,MAAIA,IAAIc,UAAUvC,gBAAgB;AAChC,WAAOyB;EACT;AACA,QAAMe,SAAS;AACf,QAAMC,SAASzC,iBAAiBwC,OAAOD;AACvC,SAAOd,IAAIiB,UAAU,GAAGD,MAAAA,IAAUD;AACpC;AAPgBd;AAST,SAASE,4BAA4Be,OAAa;AACvD,MAAIA,MAAMJ,UAAUtC,uBAAuB;AACzC,WAAO0C;EACT;AACA,QAAMH,SAAS;AACf,QAAMC,SAASxC,wBAAwBuC,OAAOD;AAC9C,QAAMK,QAAQD,MAAMT,KAAI,EAAGW,MAAM,IAAA;AACjC,QAAMC,iBAA2B,CAAA;AACjC,MAAIP,SAAS;AACb,aAAWQ,QAAQH,OAAO;AACxB,QAAIL,SAASQ,KAAKR,SAAS,IAAIE,QAAQ;AACrCK,qBAAe1B,KAAKoB,MAAAA;AACpB;IACF;AACAM,mBAAe1B,KAAK2B,IAAAA;AACpBR,cAAUQ,KAAKR,SAAS;EAC1B;AACA,SAAOO,eAAeX,KAAK,IAAA;AAC7B;AAlBgBP;","names":["MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","Map","addServerError","serverError","key","getKey","has","set","get","sentryEventId","getSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","truncateExceptionMessage","traceback","truncateExceptionStackTrace","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line"]}