{"version":3,"file":"index.cjs","names":["createHttpMetrics","createGcMetrics","createRequestRecorder","createGcObserver","defaultNormalizers","Prometheus","timing"],"sources":["../src/middleware/middleware.ts"],"sourcesContent":["import type { TRequestRecorder } from '@promster/metrics';\nimport {\n  createGcMetrics,\n  createGcObserver,\n  createHttpMetrics,\n  createRequestRecorder,\n  defaultNormalizers,\n  Prometheus,\n  skipMetricsInEnvironment,\n  timing,\n} from '@promster/metrics';\nimport type {\n  TDefaultedPromsterOptions,\n  TGcMetrics,\n  THttpMetrics,\n  TLabelValues,\n  TOptionalPromsterOptions,\n} from '@promster/types';\nimport type { Application, NextFunction, Request, Response } from 'express';\nimport merge from 'merge-options';\n\ninterface TApp extends Application {\n  locals: Record<string, unknown>;\n}\n\ntype TLocaleTarget = {\n  app?: TApp;\n  key: string;\n  value: typeof Prometheus | TRequestRecorder;\n};\nconst exposeOnLocals = ({ app, key, value }: TLocaleTarget) => {\n  if (app?.locals) {\n    app.locals[key] = value;\n  }\n};\n\nconst extractPath = (req: Request) => req.originalUrl || req.url;\n\nlet recordRequest: TRequestRecorder;\nlet upMetric: TGcMetrics['up'];\n\nconst getRequestRecorder = () => recordRequest;\nconst signalIsUp = () => {\n  if (!upMetric) {\n    return;\n  }\n\n  for (const upMetricType of upMetric) {\n    upMetricType.set(1);\n  }\n};\n\nconst signalIsNotUp = () => {\n  if (!upMetric) {\n    return;\n  }\n\n  for (const upMetricType of upMetric) {\n    upMetricType.set(0);\n  }\n};\n\ntype TSkipFunction = <TRequest = Request, TResponse = Response>(\n  _req: TRequest,\n  _res: TResponse,\n  _labels: TLabelValues,\n) => boolean;\nexport type TPromsterOptions = TOptionalPromsterOptions & {\n  skip?: TSkipFunction;\n};\ntype TMiddlewareOptions = {\n  app?: TApp;\n  options?: TPromsterOptions;\n};\nconst createMiddleware = (\n  { app, options }: TMiddlewareOptions = { app: undefined, options: undefined },\n) => {\n  const allDefaultedOptions: TDefaultedPromsterOptions & {\n    skip?: TSkipFunction;\n  } = merge(\n    createHttpMetrics.defaultOptions,\n    createGcMetrics.defaultOptions,\n    createRequestRecorder.defaultOptions,\n    // @ts-expect-error\n    createGcObserver.defaultOptions,\n    defaultNormalizers,\n    options,\n  );\n\n  const shouldSkipMetricsByEnvironment =\n    skipMetricsInEnvironment(allDefaultedOptions);\n\n  const httpMetrics: THttpMetrics = createHttpMetrics(allDefaultedOptions);\n  const gcMetrics: TGcMetrics = createGcMetrics(allDefaultedOptions);\n  const observeGc = createGcObserver(gcMetrics, allDefaultedOptions);\n\n  recordRequest = createRequestRecorder(httpMetrics, allDefaultedOptions);\n  upMetric = gcMetrics?.up;\n\n  exposeOnLocals({ app, key: 'Prometheus', value: Prometheus });\n  exposeOnLocals({ app, key: 'recordRequest', value: recordRequest });\n\n  if (!shouldSkipMetricsByEnvironment) {\n    observeGc();\n  }\n\n  signalIsUp();\n\n  return (request: Request, response: Response, next: NextFunction) => {\n    const requestTiming = timing.start();\n\n    response.on('finish', () => {\n      const labels = Object.assign(\n        {},\n        {\n          method: allDefaultedOptions.normalizeMethod(request.method, {\n            req: request,\n            res: response,\n          }),\n          status_code: allDefaultedOptions.normalizeStatusCode(\n            response.statusCode,\n            { req: request, res: response },\n          ),\n          path: allDefaultedOptions.normalizePath(extractPath(request), {\n            req: request,\n            res: response,\n          }),\n        },\n        allDefaultedOptions.getLabelValues?.(request, response),\n      );\n\n      const shouldSkipByRequest = allDefaultedOptions.skip?.(\n        request,\n        response,\n        labels,\n      );\n\n      const requestContentLength = Number(\n        request.headers['content-length'] ?? 0,\n      );\n      const responseContentLength = Number(\n        response.getHeader('content-length') ?? 0,\n      );\n\n      if (!shouldSkipByRequest && !shouldSkipMetricsByEnvironment) {\n        recordRequest(requestTiming, {\n          labels,\n          requestContentLength,\n          responseContentLength,\n        });\n      }\n    });\n\n    next();\n  };\n};\n\nexport {\n  createMiddleware,\n  exposeOnLocals,\n  extractPath,\n  getRequestRecorder,\n  signalIsUp,\n  signalIsNotUp,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,kBAAkB,EAAE,KAAK,KAAK,YAA2B;CAC7D,IAAI,KAAK,QACP,IAAI,OAAO,OAAO;AAEtB;AAEA,MAAM,eAAe,QAAiB,IAAI,eAAe,IAAI;AAE7D,IAAI;AACJ,IAAI;AAEJ,MAAM,2BAA2B;AACjC,MAAM,mBAAmB;CACvB,IAAI,CAAC,UACH;CAGF,KAAK,MAAM,gBAAgB,UACzB,aAAa,IAAI,CAAC;AAEtB;AAEA,MAAM,sBAAsB;CAC1B,IAAI,CAAC,UACH;CAGF,KAAK,MAAM,gBAAgB,UACzB,aAAa,IAAI,CAAC;AAEtB;AAcA,MAAM,oBACJ,EAAE,KAAK,YAAgC;CAAE,KAAK,KAAA;CAAW,SAAS,KAAA;AAAU,MACzE;CACH,MAAM,uBAAA,GAAA,cAAA,QAAA,CAGJA,kBAAAA,kBAAkB,gBAClBC,kBAAAA,gBAAgB,gBAChBC,kBAAAA,sBAAsB,gBAEtBC,kBAAAA,iBAAiB,gBACjBC,kBAAAA,oBACA,OACF;CAEA,MAAM,kCAAA,GAAA,kBAAA,yBAAA,CACqB,mBAAmB;CAE9C,MAAM,eAAA,GAAA,kBAAA,kBAAA,CAA8C,mBAAmB;CACvE,MAAM,aAAA,GAAA,kBAAA,gBAAA,CAAwC,mBAAmB;CACjE,MAAM,aAAA,GAAA,kBAAA,iBAAA,CAA6B,WAAW,mBAAmB;CAEjE,iBAAA,GAAA,kBAAA,sBAAA,CAAsC,aAAa,mBAAmB;CACtE,WAAW,WAAW;CAEtB,eAAe;EAAE;EAAK,KAAK;EAAc,OAAOC,kBAAAA;CAAW,CAAC;CAC5D,eAAe;EAAE;EAAK,KAAK;EAAiB,OAAO;CAAc,CAAC;CAElE,IAAI,CAAC,gCACH,UAAU;CAGZ,WAAW;CAEX,QAAQ,SAAkB,UAAoB,SAAuB;EACnE,MAAM,gBAAgBC,kBAAAA,OAAO,MAAM;EAEnC,SAAS,GAAG,gBAAgB;GAC1B,MAAM,SAAS,OAAO,OACpB,CAAC,GACD;IACE,QAAQ,oBAAoB,gBAAgB,QAAQ,QAAQ;KAC1D,KAAK;KACL,KAAK;IACP,CAAC;IACD,aAAa,oBAAoB,oBAC/B,SAAS,YACT;KAAE,KAAK;KAAS,KAAK;IAAS,CAChC;IACA,MAAM,oBAAoB,cAAc,YAAY,OAAO,GAAG;KAC5D,KAAK;KACL,KAAK;IACP,CAAC;GACH,GACA,oBAAoB,iBAAiB,SAAS,QAAQ,CACxD;GAEA,MAAM,sBAAsB,oBAAoB,OAC9C,SACA,UACA,MACF;GAEA,MAAM,uBAAuB,OAC3B,QAAQ,QAAQ,qBAAqB,CACvC;GACA,MAAM,wBAAwB,OAC5B,SAAS,UAAU,gBAAgB,KAAK,CAC1C;GAEA,IAAI,CAAC,uBAAuB,CAAC,gCAC3B,cAAc,eAAe;IAC3B;IACA;IACA;GACF,CAAC;EAEL,CAAC;EAED,KAAK;CACP;AACF"}