apitally 1.0.0-beta.5 → 1.0.0

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.
Files changed (75) hide show
  1. package/README.md +12 -8
  2. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  3. package/dist/activation.cjs +30 -0
  4. package/dist/activation.cjs.map +1 -1
  5. package/dist/activation.d.cts +1 -0
  6. package/dist/activation.d.ts +1 -0
  7. package/dist/activation.js +28 -1
  8. package/dist/activation.js.map +1 -1
  9. package/dist/adonisjs/middleware.cjs +1 -1
  10. package/dist/adonisjs/middleware.cjs.map +1 -1
  11. package/dist/adonisjs/middleware.js +1 -1
  12. package/dist/adonisjs/middleware.js.map +1 -1
  13. package/dist/elysia/index.cjs.map +1 -1
  14. package/dist/elysia/index.js.map +1 -1
  15. package/dist/elysia/middleware.cjs +8 -10
  16. package/dist/elysia/middleware.cjs.map +1 -1
  17. package/dist/elysia/middleware.js +8 -10
  18. package/dist/elysia/middleware.js.map +1 -1
  19. package/dist/elysia/routes.cjs +2 -1
  20. package/dist/elysia/routes.cjs.map +1 -1
  21. package/dist/elysia/routes.js +2 -1
  22. package/dist/elysia/routes.js.map +1 -1
  23. package/dist/express/index.cjs.map +1 -1
  24. package/dist/express/index.js.map +1 -1
  25. package/dist/express/install.cjs +1 -0
  26. package/dist/express/install.cjs.map +1 -1
  27. package/dist/express/install.js +2 -1
  28. package/dist/express/install.js.map +1 -1
  29. package/dist/express/middleware.cjs +1 -1
  30. package/dist/express/middleware.cjs.map +1 -1
  31. package/dist/express/middleware.js +1 -1
  32. package/dist/express/middleware.js.map +1 -1
  33. package/dist/fastify/middleware.cjs +1 -1
  34. package/dist/fastify/middleware.cjs.map +1 -1
  35. package/dist/fastify/middleware.js +1 -1
  36. package/dist/fastify/middleware.js.map +1 -1
  37. package/dist/h3/index.cjs +1 -0
  38. package/dist/h3/index.cjs.map +1 -1
  39. package/dist/h3/index.js +2 -1
  40. package/dist/h3/index.js.map +1 -1
  41. package/dist/h3/middleware.cjs +1 -1
  42. package/dist/h3/middleware.cjs.map +1 -1
  43. package/dist/h3/middleware.js +1 -1
  44. package/dist/h3/middleware.js.map +1 -1
  45. package/dist/hapi/middleware.cjs +1 -1
  46. package/dist/hapi/middleware.cjs.map +1 -1
  47. package/dist/hapi/middleware.js +1 -1
  48. package/dist/hapi/middleware.js.map +1 -1
  49. package/dist/hono/index.cjs.map +1 -1
  50. package/dist/hono/index.js.map +1 -1
  51. package/dist/hono/middleware.cjs +2 -1
  52. package/dist/hono/middleware.cjs.map +1 -1
  53. package/dist/hono/middleware.js +3 -2
  54. package/dist/hono/middleware.js.map +1 -1
  55. package/dist/koa/index.cjs +1 -0
  56. package/dist/koa/index.cjs.map +1 -1
  57. package/dist/koa/index.js +2 -1
  58. package/dist/koa/index.js.map +1 -1
  59. package/dist/koa/middleware.cjs +1 -1
  60. package/dist/koa/middleware.cjs.map +1 -1
  61. package/dist/koa/middleware.js +1 -1
  62. package/dist/koa/middleware.js.map +1 -1
  63. package/dist/requestAttributes.cjs +7 -1
  64. package/dist/requestAttributes.cjs.map +1 -1
  65. package/dist/requestAttributes.js +7 -1
  66. package/dist/requestAttributes.js.map +1 -1
  67. package/dist/requestObservation.cjs +2 -1
  68. package/dist/requestObservation.cjs.map +1 -1
  69. package/dist/requestObservation.js +2 -1
  70. package/dist/requestObservation.js.map +1 -1
  71. package/dist/tracing.cjs +2 -1
  72. package/dist/tracing.cjs.map +1 -1
  73. package/dist/tracing.js +2 -1
  74. package/dist/tracing.js.map +1 -1
  75. package/package.json +14 -14
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/h3/index.ts"],"sourcesContent":["import type { H3, H3Plugin } from \"h3\";\nimport { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installH3RequestObservation } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\nconst INSTALL_MARKER = Symbol.for(\"apitally.installMarker\");\n\nexport function apitallyPlugin(options?: ApitallyOptions): H3Plugin {\n return (app) => install(app, options);\n}\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: H3, options?: ApitallyOptions): void {\n install(app, options);\n}\n\nfunction install(app: H3, options?: ApitallyOptions): void {\n configure(options);\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[INSTALL_MARKER] === true) {\n return;\n }\n markedApp[INSTALL_MARKER] = true;\n registerStartupEventInfo({\n framework: \"h3\",\n frameworkVersion: resolvePackageVersion(\"h3\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n installH3RequestObservation(app);\n}\n"],"mappings":";;;;;AASA,MAAM,iBAAiB,OAAO,IAAI,wBAAwB;AAE1D,SAAgB,eAAe,SAAqC;CAClE,QAAQ,QAAQ,QAAQ,KAAK,OAAO;AACtC;AAGA,SAAgB,YAAY,KAAS,SAAiC;CACpE,QAAQ,KAAK,OAAO;AACtB;AAEA,SAAS,QAAQ,KAAS,SAAiC;CACzD,UAAU,OAAO;CACjB,MAAM,YAAY;CAClB,IAAI,UAAU,oBAAoB,MAChC;CAEF,UAAU,kBAAkB;CAC5B,yBAAyB;EACvB,WAAW;EACX,kBAAkB,sBAAsB,IAAI;EAC5C,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,4BAA4B,GAAG;AACjC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/h3/index.ts"],"sourcesContent":["import type { H3, H3Plugin } from \"h3\";\nimport {\n activateWhenHttpServerListens,\n configure,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installH3RequestObservation } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\nconst INSTALL_MARKER = Symbol.for(\"apitally.installMarker\");\n\nexport function apitallyPlugin(options?: ApitallyOptions): H3Plugin {\n return (app) => install(app, options);\n}\n\n// Setup stays synchronous; activation begins when an HTTP server starts\n// listening, or on the first request.\nexport function useApitally(app: H3, options?: ApitallyOptions): void {\n install(app, options);\n}\n\nfunction install(app: H3, options?: ApitallyOptions): void {\n configure(options);\n activateWhenHttpServerListens();\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[INSTALL_MARKER] === true) {\n return;\n }\n markedApp[INSTALL_MARKER] = true;\n registerStartupEventInfo({\n framework: \"h3\",\n frameworkVersion: resolvePackageVersion(\"h3\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n installH3RequestObservation(app);\n}\n"],"mappings":";;;;;AAaA,MAAM,iBAAiB,OAAO,IAAI,wBAAwB;AAE1D,SAAgB,eAAe,SAAqC;CAClE,QAAQ,QAAQ,QAAQ,KAAK,OAAO;AACtC;AAIA,SAAgB,YAAY,KAAS,SAAiC;CACpE,QAAQ,KAAK,OAAO;AACtB;AAEA,SAAS,QAAQ,KAAS,SAAiC;CACzD,UAAU,OAAO;CACjB,8BAA8B;CAC9B,MAAM,YAAY;CAClB,IAAI,UAAU,oBAAoB,MAChC;CAEF,UAAU,kBAAkB;CAC5B,yBAAyB;EACvB,WAAW;EACX,kBAAkB,sBAAsB,IAAI;EAC5C,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,4BAA4B,GAAG;AACjC"}
@@ -10,7 +10,7 @@ const require_requestObservationWeb = require("../requestObservationWeb.cjs");
10
10
  const require_h3_routes = require("./routes.cjs");
11
11
  let _opentelemetry_api = require("@opentelemetry/api");
12
12
  //#region src/h3/middleware.ts
13
- const TRACER_NAME = "apitally.h3";
13
+ const TRACER_NAME = "apitally/h3";
14
14
  function installH3RequestObservation(app) {
15
15
  const appInternals = app;
16
16
  const originalRequest = appInternals["~request"];
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.cjs","names":["getRequestRecord","isActivated","isWebSocketUpgrade","startWebRequestObservation","captureWebRequestBody","context","formatIssues","resolveMatchedRoute","captureWebResponse","getConfig","finalizeRequestObservation"],"sources":["../../src/h3/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { H3 } from \"h3\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebRequestBody,\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n} from \"../requestObservationWeb.js\";\nimport { formatIssues } from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally.h3\";\n\ntype H3RequestFunction = H3[\"~request\"];\ntype H3Request = Parameters<H3RequestFunction>[0];\n\ninterface H3RequestObservation extends WebRequestObservation {\n requestBodyCompletion: Promise<void>;\n route?: string;\n}\n\nexport function installH3RequestObservation(app: H3): void {\n const appInternals = app as unknown as { \"~request\"?: unknown };\n const originalRequest = appInternals[\"~request\"];\n if (typeof originalRequest !== \"function\") {\n logWarning(\n \"The H3 request dispatcher is unavailable, so Apitally request instrumentation is disabled.\",\n );\n return;\n }\n\n const observations = new WeakMap<object, H3RequestObservation>();\n const requestFunction = originalRequest as H3RequestFunction;\n const originalOnError = app.config.onError;\n const wrappedRequest: H3RequestFunction = function (this: H3, request, requestContext) {\n if (getRequestRecord()) {\n return requestFunction.call(this, request, requestContext);\n }\n\n let observation: H3RequestObservation;\n let observationContext: ReturnType<typeof startWebRequestObservation>[\"requestContext\"];\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return requestFunction.call(this, request, requestContext);\n }\n const requestAddress = (request as { context?: { clientAddress?: unknown } }).context\n ?.clientAddress;\n const clientAddress = requestContext?.clientAddress;\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress:\n typeof clientAddress === \"string\"\n ? clientAddress\n : typeof requestAddress === \"string\"\n ? requestAddress\n : typeof request.ip === \"string\"\n ? request.ip\n : undefined,\n });\n observation = {\n ...started.observation,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observationContext = started.requestContext;\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n return requestFunction.call(this, request, requestContext);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observationContext, () =>\n requestFunction.call(this, request, requestContext),\n );\n } catch (error) {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n }\n if (typeof (result as PromiseLike<Response>)?.then === \"function\") {\n return Promise.resolve(result).then(\n (response) => observeResponse(observations, request, response),\n (error: unknown) => {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n },\n );\n }\n return observeResponse(observations, request, result as Response);\n };\n\n try {\n app.config.onError = (error, event) => {\n let status: number | undefined;\n if (typeof error === \"number\") {\n status = error;\n } else if (typeof error === \"object\" && error !== null) {\n const candidate = error as { status?: unknown; statusCode?: unknown };\n status =\n typeof candidate.status === \"number\"\n ? candidate.status\n : typeof candidate.statusCode === \"number\"\n ? candidate.statusCode\n : undefined;\n }\n try {\n if (status === undefined || status >= 500) {\n const cause =\n typeof error === \"object\" && error !== null\n ? (error as { cause?: unknown }).cause\n : undefined;\n captureException(cause === undefined ? error : cause);\n }\n const issues = (error as { data?: { issues?: unknown } } | undefined)?.data?.issues;\n const requestRecord = getRequestRecord();\n if (requestRecord && Array.isArray(issues)) {\n requestRecord.validationErrors = formatIssues(issues);\n }\n } catch (captureError) {\n logDebug(`Error recording an H3 exception: ${String(captureError)}`);\n }\n return originalOnError?.(error, event);\n };\n appInternals[\"~request\"] = wrappedRequest;\n app.use((event, next) => {\n const observation = observations.get(event.req);\n if (observation) {\n observation.route = resolveMatchedRoute(event);\n }\n return next();\n });\n } catch (error) {\n try {\n app.config.onError = originalOnError;\n appInternals[\"~request\"] = originalRequest;\n } catch {\n // H3 may have frozen the app before setup was attempted.\n }\n logWarning(`Error installing the Apitally H3 middleware: ${String(error)}`);\n }\n}\n\nfunction observeResponse(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n response: Response,\n): Response {\n const observation = observations.get(request);\n if (!observation) {\n return response;\n }\n observations.delete(request);\n const statusCode = response.status;\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const captured = captureWebResponse(\n response,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n );\n captured.completion\n .then(async (completion) => {\n await observation.requestBodyCompletion;\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n observation.requestBodyCompletion\n .then(() =>\n finalizeRequestObservation({\n observation,\n completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n }),\n )\n .catch((completionError: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n error: unknown,\n): void {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n observations.delete(request);\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally H3 middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"middleware.cjs","names":["getRequestRecord","isActivated","isWebSocketUpgrade","startWebRequestObservation","captureWebRequestBody","context","formatIssues","resolveMatchedRoute","captureWebResponse","getConfig","finalizeRequestObservation"],"sources":["../../src/h3/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { H3 } from \"h3\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebRequestBody,\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n} from \"../requestObservationWeb.js\";\nimport { formatIssues } from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally/h3\";\n\ntype H3RequestFunction = H3[\"~request\"];\ntype H3Request = Parameters<H3RequestFunction>[0];\n\ninterface H3RequestObservation extends WebRequestObservation {\n requestBodyCompletion: Promise<void>;\n route?: string;\n}\n\nexport function installH3RequestObservation(app: H3): void {\n const appInternals = app as unknown as { \"~request\"?: unknown };\n const originalRequest = appInternals[\"~request\"];\n if (typeof originalRequest !== \"function\") {\n logWarning(\n \"The H3 request dispatcher is unavailable, so Apitally request instrumentation is disabled.\",\n );\n return;\n }\n\n const observations = new WeakMap<object, H3RequestObservation>();\n const requestFunction = originalRequest as H3RequestFunction;\n const originalOnError = app.config.onError;\n const wrappedRequest: H3RequestFunction = function (this: H3, request, requestContext) {\n if (getRequestRecord()) {\n return requestFunction.call(this, request, requestContext);\n }\n\n let observation: H3RequestObservation;\n let observationContext: ReturnType<typeof startWebRequestObservation>[\"requestContext\"];\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return requestFunction.call(this, request, requestContext);\n }\n const requestAddress = (request as { context?: { clientAddress?: unknown } }).context\n ?.clientAddress;\n const clientAddress = requestContext?.clientAddress;\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress:\n typeof clientAddress === \"string\"\n ? clientAddress\n : typeof requestAddress === \"string\"\n ? requestAddress\n : typeof request.ip === \"string\"\n ? request.ip\n : undefined,\n });\n observation = {\n ...started.observation,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observationContext = started.requestContext;\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n return requestFunction.call(this, request, requestContext);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observationContext, () =>\n requestFunction.call(this, request, requestContext),\n );\n } catch (error) {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n }\n if (typeof (result as PromiseLike<Response>)?.then === \"function\") {\n return Promise.resolve(result).then(\n (response) => observeResponse(observations, request, response),\n (error: unknown) => {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n },\n );\n }\n return observeResponse(observations, request, result as Response);\n };\n\n try {\n app.config.onError = (error, event) => {\n let status: number | undefined;\n if (typeof error === \"number\") {\n status = error;\n } else if (typeof error === \"object\" && error !== null) {\n const candidate = error as { status?: unknown; statusCode?: unknown };\n status =\n typeof candidate.status === \"number\"\n ? candidate.status\n : typeof candidate.statusCode === \"number\"\n ? candidate.statusCode\n : undefined;\n }\n try {\n if (status === undefined || status >= 500) {\n const cause =\n typeof error === \"object\" && error !== null\n ? (error as { cause?: unknown }).cause\n : undefined;\n captureException(cause === undefined ? error : cause);\n }\n const issues = (error as { data?: { issues?: unknown } } | undefined)?.data?.issues;\n const requestRecord = getRequestRecord();\n if (requestRecord && Array.isArray(issues)) {\n requestRecord.validationErrors = formatIssues(issues);\n }\n } catch (captureError) {\n logDebug(`Error recording an H3 exception: ${String(captureError)}`);\n }\n return originalOnError?.(error, event);\n };\n appInternals[\"~request\"] = wrappedRequest;\n app.use((event, next) => {\n const observation = observations.get(event.req);\n if (observation) {\n observation.route = resolveMatchedRoute(event);\n }\n return next();\n });\n } catch (error) {\n try {\n app.config.onError = originalOnError;\n appInternals[\"~request\"] = originalRequest;\n } catch {\n // H3 may have frozen the app before setup was attempted.\n }\n logWarning(`Error installing the Apitally H3 middleware: ${String(error)}`);\n }\n}\n\nfunction observeResponse(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n response: Response,\n): Response {\n const observation = observations.get(request);\n if (!observation) {\n return response;\n }\n observations.delete(request);\n const statusCode = response.status;\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const captured = captureWebResponse(\n response,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n );\n captured.completion\n .then(async (completion) => {\n await observation.requestBodyCompletion;\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n observation.requestBodyCompletion\n .then(() =>\n finalizeRequestObservation({\n observation,\n completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n }),\n )\n .catch((completionError: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n error: unknown,\n): void {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n observations.delete(request);\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally H3 middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { captureWebRequestBody, captureWebResponse, isWebSocketUpgrade, startWeb
9
9
  import { resolveMatchedRoute } from "./routes.js";
10
10
  import { context } from "@opentelemetry/api";
11
11
  //#region src/h3/middleware.ts
12
- const TRACER_NAME = "apitally.h3";
12
+ const TRACER_NAME = "apitally/h3";
13
13
  function installH3RequestObservation(app) {
14
14
  const appInternals = app;
15
15
  const originalRequest = appInternals["~request"];
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":[],"sources":["../../src/h3/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { H3 } from \"h3\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebRequestBody,\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n} from \"../requestObservationWeb.js\";\nimport { formatIssues } from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally.h3\";\n\ntype H3RequestFunction = H3[\"~request\"];\ntype H3Request = Parameters<H3RequestFunction>[0];\n\ninterface H3RequestObservation extends WebRequestObservation {\n requestBodyCompletion: Promise<void>;\n route?: string;\n}\n\nexport function installH3RequestObservation(app: H3): void {\n const appInternals = app as unknown as { \"~request\"?: unknown };\n const originalRequest = appInternals[\"~request\"];\n if (typeof originalRequest !== \"function\") {\n logWarning(\n \"The H3 request dispatcher is unavailable, so Apitally request instrumentation is disabled.\",\n );\n return;\n }\n\n const observations = new WeakMap<object, H3RequestObservation>();\n const requestFunction = originalRequest as H3RequestFunction;\n const originalOnError = app.config.onError;\n const wrappedRequest: H3RequestFunction = function (this: H3, request, requestContext) {\n if (getRequestRecord()) {\n return requestFunction.call(this, request, requestContext);\n }\n\n let observation: H3RequestObservation;\n let observationContext: ReturnType<typeof startWebRequestObservation>[\"requestContext\"];\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return requestFunction.call(this, request, requestContext);\n }\n const requestAddress = (request as { context?: { clientAddress?: unknown } }).context\n ?.clientAddress;\n const clientAddress = requestContext?.clientAddress;\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress:\n typeof clientAddress === \"string\"\n ? clientAddress\n : typeof requestAddress === \"string\"\n ? requestAddress\n : typeof request.ip === \"string\"\n ? request.ip\n : undefined,\n });\n observation = {\n ...started.observation,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observationContext = started.requestContext;\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n return requestFunction.call(this, request, requestContext);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observationContext, () =>\n requestFunction.call(this, request, requestContext),\n );\n } catch (error) {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n }\n if (typeof (result as PromiseLike<Response>)?.then === \"function\") {\n return Promise.resolve(result).then(\n (response) => observeResponse(observations, request, response),\n (error: unknown) => {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n },\n );\n }\n return observeResponse(observations, request, result as Response);\n };\n\n try {\n app.config.onError = (error, event) => {\n let status: number | undefined;\n if (typeof error === \"number\") {\n status = error;\n } else if (typeof error === \"object\" && error !== null) {\n const candidate = error as { status?: unknown; statusCode?: unknown };\n status =\n typeof candidate.status === \"number\"\n ? candidate.status\n : typeof candidate.statusCode === \"number\"\n ? candidate.statusCode\n : undefined;\n }\n try {\n if (status === undefined || status >= 500) {\n const cause =\n typeof error === \"object\" && error !== null\n ? (error as { cause?: unknown }).cause\n : undefined;\n captureException(cause === undefined ? error : cause);\n }\n const issues = (error as { data?: { issues?: unknown } } | undefined)?.data?.issues;\n const requestRecord = getRequestRecord();\n if (requestRecord && Array.isArray(issues)) {\n requestRecord.validationErrors = formatIssues(issues);\n }\n } catch (captureError) {\n logDebug(`Error recording an H3 exception: ${String(captureError)}`);\n }\n return originalOnError?.(error, event);\n };\n appInternals[\"~request\"] = wrappedRequest;\n app.use((event, next) => {\n const observation = observations.get(event.req);\n if (observation) {\n observation.route = resolveMatchedRoute(event);\n }\n return next();\n });\n } catch (error) {\n try {\n app.config.onError = originalOnError;\n appInternals[\"~request\"] = originalRequest;\n } catch {\n // H3 may have frozen the app before setup was attempted.\n }\n logWarning(`Error installing the Apitally H3 middleware: ${String(error)}`);\n }\n}\n\nfunction observeResponse(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n response: Response,\n): Response {\n const observation = observations.get(request);\n if (!observation) {\n return response;\n }\n observations.delete(request);\n const statusCode = response.status;\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const captured = captureWebResponse(\n response,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n );\n captured.completion\n .then(async (completion) => {\n await observation.requestBodyCompletion;\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n observation.requestBodyCompletion\n .then(() =>\n finalizeRequestObservation({\n observation,\n completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n }),\n )\n .catch((completionError: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n error: unknown,\n): void {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n observations.delete(request);\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally H3 middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","names":[],"sources":["../../src/h3/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { H3 } from \"h3\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebRequestBody,\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n} from \"../requestObservationWeb.js\";\nimport { formatIssues } from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally/h3\";\n\ntype H3RequestFunction = H3[\"~request\"];\ntype H3Request = Parameters<H3RequestFunction>[0];\n\ninterface H3RequestObservation extends WebRequestObservation {\n requestBodyCompletion: Promise<void>;\n route?: string;\n}\n\nexport function installH3RequestObservation(app: H3): void {\n const appInternals = app as unknown as { \"~request\"?: unknown };\n const originalRequest = appInternals[\"~request\"];\n if (typeof originalRequest !== \"function\") {\n logWarning(\n \"The H3 request dispatcher is unavailable, so Apitally request instrumentation is disabled.\",\n );\n return;\n }\n\n const observations = new WeakMap<object, H3RequestObservation>();\n const requestFunction = originalRequest as H3RequestFunction;\n const originalOnError = app.config.onError;\n const wrappedRequest: H3RequestFunction = function (this: H3, request, requestContext) {\n if (getRequestRecord()) {\n return requestFunction.call(this, request, requestContext);\n }\n\n let observation: H3RequestObservation;\n let observationContext: ReturnType<typeof startWebRequestObservation>[\"requestContext\"];\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return requestFunction.call(this, request, requestContext);\n }\n const requestAddress = (request as { context?: { clientAddress?: unknown } }).context\n ?.clientAddress;\n const clientAddress = requestContext?.clientAddress;\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress:\n typeof clientAddress === \"string\"\n ? clientAddress\n : typeof requestAddress === \"string\"\n ? requestAddress\n : typeof request.ip === \"string\"\n ? request.ip\n : undefined,\n });\n observation = {\n ...started.observation,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observationContext = started.requestContext;\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n return requestFunction.call(this, request, requestContext);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observationContext, () =>\n requestFunction.call(this, request, requestContext),\n );\n } catch (error) {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n }\n if (typeof (result as PromiseLike<Response>)?.then === \"function\") {\n return Promise.resolve(result).then(\n (response) => observeResponse(observations, request, response),\n (error: unknown) => {\n finalizeFailedRequestObservation(observations, request, error);\n throw error;\n },\n );\n }\n return observeResponse(observations, request, result as Response);\n };\n\n try {\n app.config.onError = (error, event) => {\n let status: number | undefined;\n if (typeof error === \"number\") {\n status = error;\n } else if (typeof error === \"object\" && error !== null) {\n const candidate = error as { status?: unknown; statusCode?: unknown };\n status =\n typeof candidate.status === \"number\"\n ? candidate.status\n : typeof candidate.statusCode === \"number\"\n ? candidate.statusCode\n : undefined;\n }\n try {\n if (status === undefined || status >= 500) {\n const cause =\n typeof error === \"object\" && error !== null\n ? (error as { cause?: unknown }).cause\n : undefined;\n captureException(cause === undefined ? error : cause);\n }\n const issues = (error as { data?: { issues?: unknown } } | undefined)?.data?.issues;\n const requestRecord = getRequestRecord();\n if (requestRecord && Array.isArray(issues)) {\n requestRecord.validationErrors = formatIssues(issues);\n }\n } catch (captureError) {\n logDebug(`Error recording an H3 exception: ${String(captureError)}`);\n }\n return originalOnError?.(error, event);\n };\n appInternals[\"~request\"] = wrappedRequest;\n app.use((event, next) => {\n const observation = observations.get(event.req);\n if (observation) {\n observation.route = resolveMatchedRoute(event);\n }\n return next();\n });\n } catch (error) {\n try {\n app.config.onError = originalOnError;\n appInternals[\"~request\"] = originalRequest;\n } catch {\n // H3 may have frozen the app before setup was attempted.\n }\n logWarning(`Error installing the Apitally H3 middleware: ${String(error)}`);\n }\n}\n\nfunction observeResponse(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n response: Response,\n): Response {\n const observation = observations.get(request);\n if (!observation) {\n return response;\n }\n observations.delete(request);\n const statusCode = response.status;\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const captured = captureWebResponse(\n response,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n );\n captured.completion\n .then(async (completion) => {\n await observation.requestBodyCompletion;\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally H3 middleware: ${String(error)}`);\n observation.requestBodyCompletion\n .then(() =>\n finalizeRequestObservation({\n observation,\n completedAtMillis,\n statusCode,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders,\n capturedRequestBody: observation.requestBodyCapture,\n }),\n )\n .catch((completionError: unknown) => {\n logWarning(`Error in the Apitally H3 middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<object, H3RequestObservation>,\n request: H3Request,\n error: unknown,\n): void {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n observations.delete(request);\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally H3 middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
@@ -11,7 +11,7 @@ let _opentelemetry_api = require("@opentelemetry/api");
11
11
  let node_util = require("node:util");
12
12
  let _opentelemetry_api_logs = require("@opentelemetry/api-logs");
13
13
  //#region src/hapi/middleware.ts
14
- const TRACER_NAME = "apitally.hapi";
14
+ const TRACER_NAME = "apitally/hapi";
15
15
  const LOG_LEVEL_SEVERITIES = {
16
16
  trace: _opentelemetry_api_logs.SeverityNumber.TRACE,
17
17
  debug: _opentelemetry_api_logs.SeverityNumber.DEBUG,
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.cjs","names":["SeverityNumber","isActivated","startNodeRequestObservation","getConfig","resolveRequestRoute","context","flushTelemetry","getActivationHandles","format"],"sources":["../../src/hapi/middleware.ts"],"sourcesContent":["import { format } from \"node:util\";\nimport type { Request, RequestEvent, Server } from \"@hapi/hapi\";\nimport { context } from \"@opentelemetry/api\";\nimport { SeverityNumber } from \"@opentelemetry/api-logs\";\nimport { activate, flushTelemetry, getActivationHandles, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { emitCapturedLogRecord } from \"../logCapture.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveRequestRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally.hapi\";\n\nconst LOG_LEVEL_SEVERITIES: Record<string, SeverityNumber> = {\n trace: SeverityNumber.TRACE,\n debug: SeverityNumber.DEBUG,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n warning: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n fatal: SeverityNumber.FATAL,\n};\n\ntype RequestWithLifecycle = Request & {\n _lifecycle?: () => Promise<void>;\n};\n\nexport function installHapiHooks(server: Server): void {\n const observations = new WeakMap<Request, StartedNodeRequestObservation>();\n\n server.ext(\"onRequest\", (request, h) => {\n activate();\n if (!isActivated() || request.raw.req.headers.upgrade?.toLowerCase() === \"websocket\") {\n return h.continue;\n }\n\n try {\n const state = startNodeRequestObservation({\n request: request.raw.req,\n tracerName: TRACER_NAME,\n });\n observations.set(request, state);\n captureNodeResponse(\n request.raw.res,\n getConfig().captureResponseBody && state.observation.requestRecord.dropReason === undefined,\n state.observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation: state.observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: request.raw.res.statusCode,\n route: resolveRequestRoute(request),\n requestHeaders: request.raw.req.headers,\n responseHeaders: request.raw.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n });\n\n const requestWithLifecycle = request as RequestWithLifecycle;\n const lifecycle = requestWithLifecycle._lifecycle;\n if (typeof lifecycle === \"function\") {\n requestWithLifecycle._lifecycle = function (this: RequestWithLifecycle) {\n return context.with(state.requestContext, () => lifecycle.call(this));\n };\n }\n } catch (error) {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n }\n return h.continue;\n });\n\n server.ext(\"onPreResponse\", (request, h) => {\n const state = observations.get(request);\n if (state) {\n context.with(state.requestContext, () => {\n const statusCode = resolveBoomStatusCode(request.response);\n if (statusCode !== undefined && statusCode >= 500) {\n captureException(request.response);\n }\n });\n }\n return h.continue;\n });\n\n server.events.on(\"request\", (request, event) => {\n captureRequestLog(server, observations, request, event);\n });\n server.ext(\"onPostStop\", () => flushTelemetry());\n try {\n server.ext(\"onPreStart\", () => {\n activate();\n });\n } catch {\n activate();\n }\n}\n\nfunction captureRequestLog(\n server: Server,\n observations: WeakMap<Request, StartedNodeRequestObservation>,\n request: Request,\n event: RequestEvent,\n): void {\n try {\n if (\n event.channel !== \"app\" ||\n !getConfig().captureLogs ||\n (server.registrations as Record<string, unknown>)[\"hapi-pino\"] !== undefined\n ) {\n return;\n }\n const state = observations.get(request);\n const loggerProvider = getActivationHandles()?.loggerProvider;\n if (!state || !loggerProvider) {\n return;\n }\n const { severityNumber, severityText } = resolveLogSeverity(event.tags);\n context.with(state.requestContext, () => {\n const logger = loggerProvider.getLogger(\"hapi\");\n if (logger.enabled({ severityNumber })) {\n emitCapturedLogRecord(logger, {\n severityNumber,\n severityText,\n body: format(event.error ?? event.data),\n timestamp: Number(event.timestamp),\n });\n }\n });\n } catch {\n // Capture must never throw into request.log().\n }\n}\n\nfunction resolveLogSeverity(tags: string[]): {\n severityNumber: SeverityNumber;\n severityText: string;\n} {\n for (const tag of tags) {\n const severityText = tag.toLowerCase();\n const severityNumber = LOG_LEVEL_SEVERITIES[severityText];\n if (severityNumber !== undefined) {\n return { severityNumber, severityText };\n }\n }\n return { severityNumber: SeverityNumber.INFO, severityText: \"log\" };\n}\n\nfunction resolveBoomStatusCode(response: unknown): number | undefined {\n if (typeof response !== \"object\" || response === null) {\n return undefined;\n }\n const candidate = response as {\n isBoom?: unknown;\n output?: { statusCode?: unknown };\n };\n return candidate.isBoom === true && typeof candidate.output?.statusCode === \"number\"\n ? candidate.output.statusCode\n : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"middleware.cjs","names":["SeverityNumber","isActivated","startNodeRequestObservation","getConfig","resolveRequestRoute","context","flushTelemetry","getActivationHandles","format"],"sources":["../../src/hapi/middleware.ts"],"sourcesContent":["import { format } from \"node:util\";\nimport type { Request, RequestEvent, Server } from \"@hapi/hapi\";\nimport { context } from \"@opentelemetry/api\";\nimport { SeverityNumber } from \"@opentelemetry/api-logs\";\nimport { activate, flushTelemetry, getActivationHandles, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { emitCapturedLogRecord } from \"../logCapture.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveRequestRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally/hapi\";\n\nconst LOG_LEVEL_SEVERITIES: Record<string, SeverityNumber> = {\n trace: SeverityNumber.TRACE,\n debug: SeverityNumber.DEBUG,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n warning: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n fatal: SeverityNumber.FATAL,\n};\n\ntype RequestWithLifecycle = Request & {\n _lifecycle?: () => Promise<void>;\n};\n\nexport function installHapiHooks(server: Server): void {\n const observations = new WeakMap<Request, StartedNodeRequestObservation>();\n\n server.ext(\"onRequest\", (request, h) => {\n activate();\n if (!isActivated() || request.raw.req.headers.upgrade?.toLowerCase() === \"websocket\") {\n return h.continue;\n }\n\n try {\n const state = startNodeRequestObservation({\n request: request.raw.req,\n tracerName: TRACER_NAME,\n });\n observations.set(request, state);\n captureNodeResponse(\n request.raw.res,\n getConfig().captureResponseBody && state.observation.requestRecord.dropReason === undefined,\n state.observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation: state.observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: request.raw.res.statusCode,\n route: resolveRequestRoute(request),\n requestHeaders: request.raw.req.headers,\n responseHeaders: request.raw.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n });\n\n const requestWithLifecycle = request as RequestWithLifecycle;\n const lifecycle = requestWithLifecycle._lifecycle;\n if (typeof lifecycle === \"function\") {\n requestWithLifecycle._lifecycle = function (this: RequestWithLifecycle) {\n return context.with(state.requestContext, () => lifecycle.call(this));\n };\n }\n } catch (error) {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n }\n return h.continue;\n });\n\n server.ext(\"onPreResponse\", (request, h) => {\n const state = observations.get(request);\n if (state) {\n context.with(state.requestContext, () => {\n const statusCode = resolveBoomStatusCode(request.response);\n if (statusCode !== undefined && statusCode >= 500) {\n captureException(request.response);\n }\n });\n }\n return h.continue;\n });\n\n server.events.on(\"request\", (request, event) => {\n captureRequestLog(server, observations, request, event);\n });\n server.ext(\"onPostStop\", () => flushTelemetry());\n try {\n server.ext(\"onPreStart\", () => {\n activate();\n });\n } catch {\n activate();\n }\n}\n\nfunction captureRequestLog(\n server: Server,\n observations: WeakMap<Request, StartedNodeRequestObservation>,\n request: Request,\n event: RequestEvent,\n): void {\n try {\n if (\n event.channel !== \"app\" ||\n !getConfig().captureLogs ||\n (server.registrations as Record<string, unknown>)[\"hapi-pino\"] !== undefined\n ) {\n return;\n }\n const state = observations.get(request);\n const loggerProvider = getActivationHandles()?.loggerProvider;\n if (!state || !loggerProvider) {\n return;\n }\n const { severityNumber, severityText } = resolveLogSeverity(event.tags);\n context.with(state.requestContext, () => {\n const logger = loggerProvider.getLogger(\"hapi\");\n if (logger.enabled({ severityNumber })) {\n emitCapturedLogRecord(logger, {\n severityNumber,\n severityText,\n body: format(event.error ?? event.data),\n timestamp: Number(event.timestamp),\n });\n }\n });\n } catch {\n // Capture must never throw into request.log().\n }\n}\n\nfunction resolveLogSeverity(tags: string[]): {\n severityNumber: SeverityNumber;\n severityText: string;\n} {\n for (const tag of tags) {\n const severityText = tag.toLowerCase();\n const severityNumber = LOG_LEVEL_SEVERITIES[severityText];\n if (severityNumber !== undefined) {\n return { severityNumber, severityText };\n }\n }\n return { severityNumber: SeverityNumber.INFO, severityText: \"log\" };\n}\n\nfunction resolveBoomStatusCode(response: unknown): number | undefined {\n if (typeof response !== \"object\" || response === null) {\n return undefined;\n }\n const candidate = response as {\n isBoom?: unknown;\n output?: { statusCode?: unknown };\n };\n return candidate.isBoom === true && typeof candidate.output?.statusCode === \"number\"\n ? candidate.output.statusCode\n : undefined;\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,MAAM,cAAc;AAEpB,MAAM,uBAAuD;CAC3D,OAAOA,wBAAAA,eAAe;CACtB,OAAOA,wBAAAA,eAAe;CACtB,MAAMA,wBAAAA,eAAe;CACrB,MAAMA,wBAAAA,eAAe;CACrB,SAASA,wBAAAA,eAAe;CACxB,OAAOA,wBAAAA,eAAe;CACtB,OAAOA,wBAAAA,eAAe;AACxB;AAMA,SAAgB,iBAAiB,QAAsB;CACrD,MAAM,+BAAe,IAAI,QAAgD;CAEzE,OAAO,IAAI,cAAc,SAAS,MAAM;EACtC,mBAAA,SAAS;EACT,IAAI,CAACC,mBAAAA,YAAY,KAAK,QAAQ,IAAI,IAAI,QAAQ,SAAS,YAAY,MAAM,aACvE,OAAO,EAAE;EAGX,IAAI;GACF,MAAM,QAAQC,+BAAAA,4BAA4B;IACxC,SAAS,QAAQ,IAAI;IACrB,YAAY;GACd,CAAC;GACD,aAAa,IAAI,SAAS,KAAK;GAC/B,+BAAA,oBACE,QAAQ,IAAI,KACZC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,MAAM,YAAY,cAAc,eAAe,KAAA,GAClF,MAAM,YAAY,kBACpB,CAAC,CACE,MAAM,eAAe;IACpB,2BAAA,2BAA2B;KACzB,aAAa,MAAM;KACnB,mBAAmB,WAAW;KAC9B,YAAY,QAAQ,IAAI,IAAI;KAC5B,OAAOC,oBAAAA,oBAAoB,OAAO;KAClC,gBAAgB,QAAQ,IAAI,IAAI;KAChC,iBAAiB,QAAQ,IAAI,IAAI,WAAW;KAC5C,qBAAqB,WAAW;KAChC,sBAAsB;IACxB,CAAC;GACH,CAAC,CAAC,CACD,OAAO,UAAmB;IACzB,eAAA,WAAW,0CAA0C,OAAO,KAAK,GAAG;GACtE,CAAC;GAEH,MAAM,uBAAuB;GAC7B,MAAM,YAAY,qBAAqB;GACvC,IAAI,OAAO,cAAc,YACvB,qBAAqB,aAAa,WAAsC;IACtE,OAAOC,mBAAAA,QAAQ,KAAK,MAAM,sBAAsB,UAAU,KAAK,IAAI,CAAC;GACtE;EAEJ,SAAS,OAAO;GACd,eAAA,WAAW,0CAA0C,OAAO,KAAK,GAAG;EACtE;EACA,OAAO,EAAE;CACX,CAAC;CAED,OAAO,IAAI,kBAAkB,SAAS,MAAM;EAC1C,MAAM,QAAQ,aAAa,IAAI,OAAO;EACtC,IAAI,OACF,mBAAA,QAAQ,KAAK,MAAM,sBAAsB;GACvC,MAAM,aAAa,sBAAsB,QAAQ,QAAQ;GACzD,IAAI,eAAe,KAAA,KAAa,cAAc,KAC5C,mBAAA,iBAAiB,QAAQ,QAAQ;EAErC,CAAC;EAEH,OAAO,EAAE;CACX,CAAC;CAED,OAAO,OAAO,GAAG,YAAY,SAAS,UAAU;EAC9C,kBAAkB,QAAQ,cAAc,SAAS,KAAK;CACxD,CAAC;CACD,OAAO,IAAI,oBAAoBC,mBAAAA,eAAe,CAAC;CAC/C,IAAI;EACF,OAAO,IAAI,oBAAoB;GAC7B,mBAAA,SAAS;EACX,CAAC;CACH,QAAQ;EACN,mBAAA,SAAS;CACX;AACF;AAEA,SAAS,kBACP,QACA,cACA,SACA,OACM;CACN,IAAI;EACF,IACE,MAAM,YAAY,SAClB,CAACH,eAAAA,UAAU,CAAC,CAAC,eACZ,OAAO,cAA0C,iBAAiB,KAAA,GAEnE;EAEF,MAAM,QAAQ,aAAa,IAAI,OAAO;EACtC,MAAM,iBAAiBI,mBAAAA,qBAAqB,CAAC,EAAE;EAC/C,IAAI,CAAC,SAAS,CAAC,gBACb;EAEF,MAAM,EAAE,gBAAgB,iBAAiB,mBAAmB,MAAM,IAAI;EACtE,mBAAA,QAAQ,KAAK,MAAM,sBAAsB;GACvC,MAAM,SAAS,eAAe,UAAU,MAAM;GAC9C,IAAI,OAAO,QAAQ,EAAE,eAAe,CAAC,GACnC,mBAAA,sBAAsB,QAAQ;IAC5B;IACA;IACA,OAAA,GAAMC,UAAAA,OAAAA,CAAO,MAAM,SAAS,MAAM,IAAI;IACtC,WAAW,OAAO,MAAM,SAAS;GACnC,CAAC;EAEL,CAAC;CACH,QAAQ,CAER;AACF;AAEA,SAAS,mBAAmB,MAG1B;CACA,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,eAAe,IAAI,YAAY;EACrC,MAAM,iBAAiB,qBAAqB;EAC5C,IAAI,mBAAmB,KAAA,GACrB,OAAO;GAAE;GAAgB;EAAa;CAE1C;CACA,OAAO;EAAE,gBAAgBR,wBAAAA,eAAe;EAAM,cAAc;CAAM;AACpE;AAEA,SAAS,sBAAsB,UAAuC;CACpE,IAAI,OAAO,aAAa,YAAY,aAAa,MAC/C;CAEF,MAAM,YAAY;CAIlB,OAAO,UAAU,WAAW,QAAQ,OAAO,UAAU,QAAQ,eAAe,WACxE,UAAU,OAAO,aACjB,KAAA;AACN"}
@@ -10,7 +10,7 @@ import { context } from "@opentelemetry/api";
10
10
  import { format } from "node:util";
11
11
  import { SeverityNumber } from "@opentelemetry/api-logs";
12
12
  //#region src/hapi/middleware.ts
13
- const TRACER_NAME = "apitally.hapi";
13
+ const TRACER_NAME = "apitally/hapi";
14
14
  const LOG_LEVEL_SEVERITIES = {
15
15
  trace: SeverityNumber.TRACE,
16
16
  debug: SeverityNumber.DEBUG,
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":[],"sources":["../../src/hapi/middleware.ts"],"sourcesContent":["import { format } from \"node:util\";\nimport type { Request, RequestEvent, Server } from \"@hapi/hapi\";\nimport { context } from \"@opentelemetry/api\";\nimport { SeverityNumber } from \"@opentelemetry/api-logs\";\nimport { activate, flushTelemetry, getActivationHandles, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { emitCapturedLogRecord } from \"../logCapture.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveRequestRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally.hapi\";\n\nconst LOG_LEVEL_SEVERITIES: Record<string, SeverityNumber> = {\n trace: SeverityNumber.TRACE,\n debug: SeverityNumber.DEBUG,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n warning: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n fatal: SeverityNumber.FATAL,\n};\n\ntype RequestWithLifecycle = Request & {\n _lifecycle?: () => Promise<void>;\n};\n\nexport function installHapiHooks(server: Server): void {\n const observations = new WeakMap<Request, StartedNodeRequestObservation>();\n\n server.ext(\"onRequest\", (request, h) => {\n activate();\n if (!isActivated() || request.raw.req.headers.upgrade?.toLowerCase() === \"websocket\") {\n return h.continue;\n }\n\n try {\n const state = startNodeRequestObservation({\n request: request.raw.req,\n tracerName: TRACER_NAME,\n });\n observations.set(request, state);\n captureNodeResponse(\n request.raw.res,\n getConfig().captureResponseBody && state.observation.requestRecord.dropReason === undefined,\n state.observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation: state.observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: request.raw.res.statusCode,\n route: resolveRequestRoute(request),\n requestHeaders: request.raw.req.headers,\n responseHeaders: request.raw.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n });\n\n const requestWithLifecycle = request as RequestWithLifecycle;\n const lifecycle = requestWithLifecycle._lifecycle;\n if (typeof lifecycle === \"function\") {\n requestWithLifecycle._lifecycle = function (this: RequestWithLifecycle) {\n return context.with(state.requestContext, () => lifecycle.call(this));\n };\n }\n } catch (error) {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n }\n return h.continue;\n });\n\n server.ext(\"onPreResponse\", (request, h) => {\n const state = observations.get(request);\n if (state) {\n context.with(state.requestContext, () => {\n const statusCode = resolveBoomStatusCode(request.response);\n if (statusCode !== undefined && statusCode >= 500) {\n captureException(request.response);\n }\n });\n }\n return h.continue;\n });\n\n server.events.on(\"request\", (request, event) => {\n captureRequestLog(server, observations, request, event);\n });\n server.ext(\"onPostStop\", () => flushTelemetry());\n try {\n server.ext(\"onPreStart\", () => {\n activate();\n });\n } catch {\n activate();\n }\n}\n\nfunction captureRequestLog(\n server: Server,\n observations: WeakMap<Request, StartedNodeRequestObservation>,\n request: Request,\n event: RequestEvent,\n): void {\n try {\n if (\n event.channel !== \"app\" ||\n !getConfig().captureLogs ||\n (server.registrations as Record<string, unknown>)[\"hapi-pino\"] !== undefined\n ) {\n return;\n }\n const state = observations.get(request);\n const loggerProvider = getActivationHandles()?.loggerProvider;\n if (!state || !loggerProvider) {\n return;\n }\n const { severityNumber, severityText } = resolveLogSeverity(event.tags);\n context.with(state.requestContext, () => {\n const logger = loggerProvider.getLogger(\"hapi\");\n if (logger.enabled({ severityNumber })) {\n emitCapturedLogRecord(logger, {\n severityNumber,\n severityText,\n body: format(event.error ?? event.data),\n timestamp: Number(event.timestamp),\n });\n }\n });\n } catch {\n // Capture must never throw into request.log().\n }\n}\n\nfunction resolveLogSeverity(tags: string[]): {\n severityNumber: SeverityNumber;\n severityText: string;\n} {\n for (const tag of tags) {\n const severityText = tag.toLowerCase();\n const severityNumber = LOG_LEVEL_SEVERITIES[severityText];\n if (severityNumber !== undefined) {\n return { severityNumber, severityText };\n }\n }\n return { severityNumber: SeverityNumber.INFO, severityText: \"log\" };\n}\n\nfunction resolveBoomStatusCode(response: unknown): number | undefined {\n if (typeof response !== \"object\" || response === null) {\n return undefined;\n }\n const candidate = response as {\n isBoom?: unknown;\n output?: { statusCode?: unknown };\n };\n return candidate.isBoom === true && typeof candidate.output?.statusCode === \"number\"\n ? candidate.output.statusCode\n : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","names":[],"sources":["../../src/hapi/middleware.ts"],"sourcesContent":["import { format } from \"node:util\";\nimport type { Request, RequestEvent, Server } from \"@hapi/hapi\";\nimport { context } from \"@opentelemetry/api\";\nimport { SeverityNumber } from \"@opentelemetry/api-logs\";\nimport { activate, flushTelemetry, getActivationHandles, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { emitCapturedLogRecord } from \"../logCapture.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveRequestRoute } from \"./routes.js\";\n\nconst TRACER_NAME = \"apitally/hapi\";\n\nconst LOG_LEVEL_SEVERITIES: Record<string, SeverityNumber> = {\n trace: SeverityNumber.TRACE,\n debug: SeverityNumber.DEBUG,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n warning: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n fatal: SeverityNumber.FATAL,\n};\n\ntype RequestWithLifecycle = Request & {\n _lifecycle?: () => Promise<void>;\n};\n\nexport function installHapiHooks(server: Server): void {\n const observations = new WeakMap<Request, StartedNodeRequestObservation>();\n\n server.ext(\"onRequest\", (request, h) => {\n activate();\n if (!isActivated() || request.raw.req.headers.upgrade?.toLowerCase() === \"websocket\") {\n return h.continue;\n }\n\n try {\n const state = startNodeRequestObservation({\n request: request.raw.req,\n tracerName: TRACER_NAME,\n });\n observations.set(request, state);\n captureNodeResponse(\n request.raw.res,\n getConfig().captureResponseBody && state.observation.requestRecord.dropReason === undefined,\n state.observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation: state.observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: request.raw.res.statusCode,\n route: resolveRequestRoute(request),\n requestHeaders: request.raw.req.headers,\n responseHeaders: request.raw.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n });\n\n const requestWithLifecycle = request as RequestWithLifecycle;\n const lifecycle = requestWithLifecycle._lifecycle;\n if (typeof lifecycle === \"function\") {\n requestWithLifecycle._lifecycle = function (this: RequestWithLifecycle) {\n return context.with(state.requestContext, () => lifecycle.call(this));\n };\n }\n } catch (error) {\n logWarning(`Error in the Apitally Hapi middleware: ${String(error)}`);\n }\n return h.continue;\n });\n\n server.ext(\"onPreResponse\", (request, h) => {\n const state = observations.get(request);\n if (state) {\n context.with(state.requestContext, () => {\n const statusCode = resolveBoomStatusCode(request.response);\n if (statusCode !== undefined && statusCode >= 500) {\n captureException(request.response);\n }\n });\n }\n return h.continue;\n });\n\n server.events.on(\"request\", (request, event) => {\n captureRequestLog(server, observations, request, event);\n });\n server.ext(\"onPostStop\", () => flushTelemetry());\n try {\n server.ext(\"onPreStart\", () => {\n activate();\n });\n } catch {\n activate();\n }\n}\n\nfunction captureRequestLog(\n server: Server,\n observations: WeakMap<Request, StartedNodeRequestObservation>,\n request: Request,\n event: RequestEvent,\n): void {\n try {\n if (\n event.channel !== \"app\" ||\n !getConfig().captureLogs ||\n (server.registrations as Record<string, unknown>)[\"hapi-pino\"] !== undefined\n ) {\n return;\n }\n const state = observations.get(request);\n const loggerProvider = getActivationHandles()?.loggerProvider;\n if (!state || !loggerProvider) {\n return;\n }\n const { severityNumber, severityText } = resolveLogSeverity(event.tags);\n context.with(state.requestContext, () => {\n const logger = loggerProvider.getLogger(\"hapi\");\n if (logger.enabled({ severityNumber })) {\n emitCapturedLogRecord(logger, {\n severityNumber,\n severityText,\n body: format(event.error ?? event.data),\n timestamp: Number(event.timestamp),\n });\n }\n });\n } catch {\n // Capture must never throw into request.log().\n }\n}\n\nfunction resolveLogSeverity(tags: string[]): {\n severityNumber: SeverityNumber;\n severityText: string;\n} {\n for (const tag of tags) {\n const severityText = tag.toLowerCase();\n const severityNumber = LOG_LEVEL_SEVERITIES[severityText];\n if (severityNumber !== undefined) {\n return { severityNumber, severityText };\n }\n }\n return { severityNumber: SeverityNumber.INFO, severityText: \"log\" };\n}\n\nfunction resolveBoomStatusCode(response: unknown): number | undefined {\n if (typeof response !== \"object\" || response === null) {\n return undefined;\n }\n const candidate = response as {\n isBoom?: unknown;\n output?: { statusCode?: unknown };\n };\n return candidate.isBoom === true && typeof candidate.output?.statusCode === \"number\"\n ? candidate.output.statusCode\n : undefined;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["resolvePackageVersion","resolveStartupPaths"],"sources":["../../src/hono/index.ts"],"sourcesContent":["import type { Hono } from \"hono\";\nimport { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { wrapAppFetch } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: Hono, options?: ApitallyOptions): void {\n configure(options);\n registerStartupEventInfo({\n framework: \"hono\",\n frameworkVersion: resolvePackageVersion(\"hono\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n wrapAppFetch(app);\n}\n"],"mappings":";;;;;;AAUA,SAAgB,YAAY,KAAW,SAAiC;CACtE,mBAAA,UAAU,OAAO;CACjB,mBAAA,yBAAyB;EACvB,WAAW;EACX,kBAAkBA,uBAAAA,sBAAsB,MAAM;EAC9C,oBAAoBC,oBAAAA,oBAAoB,GAAG;CAC7C,CAAC;CACD,wBAAA,aAAa,GAAG;AAClB"}
1
+ {"version":3,"file":"index.cjs","names":["resolvePackageVersion","resolveStartupPaths"],"sources":["../../src/hono/index.ts"],"sourcesContent":["import type { Hono } from \"hono\";\nimport { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { wrapAppFetch } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins when an HTTP server starts\n// listening, or on the first request.\nexport function useApitally(app: Hono, options?: ApitallyOptions): void {\n configure(options);\n registerStartupEventInfo({\n framework: \"hono\",\n frameworkVersion: resolvePackageVersion(\"hono\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n wrapAppFetch(app);\n}\n"],"mappings":";;;;;;AAWA,SAAgB,YAAY,KAAW,SAAiC;CACtE,mBAAA,UAAU,OAAO;CACjB,mBAAA,yBAAyB;EACvB,WAAW;EACX,kBAAkBA,uBAAAA,sBAAsB,MAAM;EAC9C,oBAAoBC,oBAAAA,oBAAoB,GAAG;CAC7C,CAAC;CACD,wBAAA,aAAa,GAAG;AAClB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/hono/index.ts"],"sourcesContent":["import type { Hono } from \"hono\";\nimport { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { wrapAppFetch } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: Hono, options?: ApitallyOptions): void {\n configure(options);\n registerStartupEventInfo({\n framework: \"hono\",\n frameworkVersion: resolvePackageVersion(\"hono\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n wrapAppFetch(app);\n}\n"],"mappings":";;;;;AAUA,SAAgB,YAAY,KAAW,SAAiC;CACtE,UAAU,OAAO;CACjB,yBAAyB;EACvB,WAAW;EACX,kBAAkB,sBAAsB,MAAM;EAC9C,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,aAAa,GAAG;AAClB"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/hono/index.ts"],"sourcesContent":["import type { Hono } from \"hono\";\nimport { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { wrapAppFetch } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins when an HTTP server starts\n// listening, or on the first request.\nexport function useApitally(app: Hono, options?: ApitallyOptions): void {\n configure(options);\n registerStartupEventInfo({\n framework: \"hono\",\n frameworkVersion: resolvePackageVersion(\"hono\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n wrapAppFetch(app);\n}\n"],"mappings":";;;;;AAWA,SAAgB,YAAY,KAAW,SAAiC;CACtE,UAAU,OAAO;CACjB,yBAAyB;EACvB,WAAW;EACX,kBAAkB,sBAAsB,MAAM;EAC9C,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,aAAa,GAAG;AAClB"}
@@ -12,7 +12,7 @@ let _opentelemetry_api = require("@opentelemetry/api");
12
12
  //#region src/hono/middleware.ts
13
13
  const FETCH_WRAP_MARKER = Symbol.for("apitally.honoFetchWrap");
14
14
  const ERROR_HANDLER_WRAP_MARKER = Symbol.for("apitally.honoErrorHandlerWrap");
15
- const TRACER_NAME = "apitally.hono";
15
+ const TRACER_NAME = "apitally/hono";
16
16
  const observationsByRequestRecord = /* @__PURE__ */ new WeakMap();
17
17
  function wrapAppFetch(app) {
18
18
  const markedApp = app;
@@ -22,6 +22,7 @@ function wrapAppFetch(app) {
22
22
  return;
23
23
  }
24
24
  markedApp[FETCH_WRAP_MARKER] = true;
25
+ require_activation.activateWhenHttpServerListens();
25
26
  app.use(recordMatchedRouteAfterNext);
26
27
  const originalFetch = app.fetch;
27
28
  let errorHandlerWrapPending = true;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.cjs","names":["context","getRequestRecord","resolveMatchedRoute","isActivated","isWebSocketUpgrade","startWebRequestObservation","captureWebResponse","getConfig","isValidationResponseStatus","extractZodValidationErrors","parseJsonResponseBody","markedHandler"],"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { Hono, Context as HonoContext, MiddlewareHandler } from \"hono\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord, type RequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n type WebResponseCompletion,\n} from \"../requestObservationWeb.js\";\nimport {\n extractZodValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst FETCH_WRAP_MARKER = Symbol.for(\"apitally.honoFetchWrap\");\nconst ERROR_HANDLER_WRAP_MARKER = Symbol.for(\"apitally.honoErrorHandlerWrap\");\nconst TRACER_NAME = \"apitally.hono\";\n\ntype FetchFunction = (request: Request, ...rest: unknown[]) => Response | Promise<Response>;\n\ninterface RequestObservation extends WebRequestObservation {\n honoContext?: HonoContext;\n route?: string;\n}\n\n// WeakMap associates route middleware with transport observation without\n// retaining completed request records.\nconst observationsByRequestRecord = new WeakMap<RequestRecord, RequestObservation>();\n\n// Wrapping app.fetch covers every response, including onError. Route middleware\n// precedes later registrations, and the marker prevents duplicate observation.\nexport function wrapAppFetch(app: Hono): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[FETCH_WRAP_MARKER] === true) {\n return;\n }\n if (hasRegisteredRoutes(app)) {\n logWarning(\n \"useApitally() was called after routes were registered on the Hono app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes.\",\n );\n return;\n }\n markedApp[FETCH_WRAP_MARKER] = true;\n app.use(recordMatchedRouteAfterNext);\n const originalFetch = app.fetch as FetchFunction;\n let errorHandlerWrapPending = true;\n // First-request wrapping follows route() mounts and onError registration.\n // Replacing the handler afterward disables exception capture.\n const wrapErrorHandlerOnce = () => {\n if (errorHandlerWrapPending) {\n errorHandlerWrapPending = false;\n wrapErrorHandler(app);\n }\n };\n const wrappedFetch: FetchFunction = function (\n this: unknown,\n request: Request,\n ...rest: unknown[]\n ) {\n let observed: ObservedRequestStart | undefined;\n try {\n observed = observeRequest(request, rest[0], wrapErrorHandlerOnce);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!observed) {\n return originalFetch.call(this, request, ...rest);\n }\n const { observation, requestContext } = observed;\n let dispatchResult: Response | Promise<Response>;\n try {\n dispatchResult = context.with(requestContext, () =>\n originalFetch.call(this, request, ...rest),\n );\n } catch (error) {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n }\n return Promise.resolve(dispatchResult).then(\n (response) => observeResponse(response, observation),\n (error: unknown) => {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n },\n );\n };\n (app as { fetch: FetchFunction }).fetch = wrappedFetch;\n}\n\n// Middleware entries register as method \"ALL\" and lose nothing when they\n// precede the wrap; only concrete-method entries need the route middleware\n// that is registered after them.\nfunction hasRegisteredRoutes(app: Hono): boolean {\n try {\n const routes = app.routes;\n return (\n Array.isArray(routes) &&\n routes.some((route) => typeof route.method === \"string\" && route.method !== \"ALL\")\n );\n } catch (error) {\n logDebug(`Error inspecting the hono app's routes: ${String(error)}`);\n return false;\n }\n}\n\n// Registered before later routes, this middleware records the final context\n// and resolved route after next().\nconst recordMatchedRouteAfterNext: MiddlewareHandler = async (c, next) => {\n const requestRecord = getRequestRecord();\n await next();\n if (!requestRecord) {\n return;\n }\n try {\n const observation = observationsByRequestRecord.get(requestRecord);\n if (observation) {\n observation.honoContext = c;\n observation.route = resolveMatchedRoute(c);\n const error = c.error;\n if (error && c.res.status >= 500 && observation.requestRecord.exception !== error) {\n captureException(error);\n }\n }\n } catch (error) {\n logDebug(`Error recording the matched hono route: ${String(error)}`);\n }\n};\n\ninterface ObservedRequestStart {\n observation: RequestObservation;\n requestContext: Context;\n}\n\nfunction observeRequest(\n request: Request,\n env: unknown,\n wrapErrorHandlerOnce: () => void,\n): ObservedRequestStart | undefined {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return undefined;\n }\n wrapErrorHandlerOnce();\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress: resolveClientAddress(request, env),\n });\n const observation: RequestObservation = started.observation;\n observationsByRequestRecord.set(observation.requestRecord, observation);\n return { observation, requestContext: started.requestContext };\n}\n\n// A tee after compression observes bytes sent to the client. Finalization follows\n// tee completion, and bodiless responses complete immediately.\nfunction observeResponse(response: Response, observation: RequestObservation): Response {\n try {\n const captured = captureWebResponse(\n response,\n (getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined) ||\n isValidationResponseStatus(response.status),\n );\n captured.completion\n .then((completion) => finalizeRequestFromResponse(observation, response, completion))\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n return response;\n }\n}\n\nasync function finalizeRequestFromResponse(\n observation: RequestObservation,\n response: Response,\n capturedResponseBody: WebResponseCompletion,\n): Promise<void> {\n await captureRequestBodyFromCache(observation);\n if (isValidationResponseStatus(response.status)) {\n observation.requestRecord.validationErrors ??= extractZodValidationErrors(\n parseJsonResponseBody(capturedResponseBody.body, response.headers.get(\"content-encoding\")),\n );\n }\n finalizeRequestObservation({\n observation,\n completedAtMillis: capturedResponseBody.completedAtMillis,\n statusCode: response.status,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders: response.headers,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody,\n });\n}\n\n// A fetch rejection has no response, so the request record is finalized and\n// the rejection propagates unchanged.\nfunction finalizeRequestObservationAfterFetchRejection(\n observation: RequestObservation,\n error: unknown,\n): void {\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally middleware: ${String(finalizeError)}`);\n }\n}\n\n// Only cache entries preserving the original request bytes are captured. If no\n// such entry exists, the SDK leaves the request stream untouched.\nasync function captureRequestBodyFromCache(observation: RequestObservation): Promise<void> {\n const bodyCache = observation.honoContext?.req.bodyCache as Record<string, unknown> | undefined;\n if (typeof bodyCache !== \"object\" || bodyCache === null) {\n return;\n }\n try {\n const chunk = await resolveCachedBodyChunk(bodyCache);\n if (chunk !== undefined) {\n observation.requestBodyCapture.addChunk(chunk);\n observation.requestBodyCapture.markComplete();\n }\n } catch (error) {\n logDebug(`Error reading the hono request body cache: ${String(error)}`);\n }\n}\n\nasync function resolveCachedBodyChunk(\n bodyCache: Record<string, unknown>,\n): Promise<Uint8Array | string | undefined> {\n if (bodyCache.arrayBuffer !== undefined) {\n const value: unknown = await bodyCache.arrayBuffer;\n return value instanceof ArrayBuffer ? new Uint8Array(value) : undefined;\n }\n if (bodyCache.text !== undefined) {\n const value: unknown = await bodyCache.text;\n return typeof value === \"string\" ? value : undefined;\n }\n if (bodyCache.blob !== undefined) {\n const value: unknown = await bodyCache.blob;\n if (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Blob).arrayBuffer === \"function\"\n ) {\n return new Uint8Array(await (value as Blob).arrayBuffer());\n }\n }\n return undefined;\n}\n\n// Hono exposes errorHandler at runtime; wrapping it records the exception before\n// Hono converts it into a response.\nfunction wrapErrorHandler(app: Hono): void {\n try {\n const appWithHandler = app as unknown as { errorHandler?: unknown };\n const originalHandler = appWithHandler.errorHandler;\n if (typeof originalHandler !== \"function\") {\n return;\n }\n const markedHandler = originalHandler as unknown as Record<symbol, boolean | undefined>;\n if (markedHandler[ERROR_HANDLER_WRAP_MARKER] === true) {\n return;\n }\n const wrappedHandler = function (this: unknown, ...args: unknown[]) {\n const result = (originalHandler as (...handlerArgs: unknown[]) => unknown).apply(this, args);\n if (result instanceof Promise) {\n return result.then((response) => {\n captureExceptionIfServerErrorResponse(args[0], response);\n return response;\n });\n }\n captureExceptionIfServerErrorResponse(args[0], result);\n return result;\n };\n (wrappedHandler as unknown as Record<symbol, boolean>)[ERROR_HANDLER_WRAP_MARKER] = true;\n appWithHandler.errorHandler = wrappedHandler;\n } catch (error) {\n logDebug(`Error wrapping the hono onError handler: ${String(error)}`);\n }\n}\n\nfunction captureExceptionIfServerErrorResponse(error: unknown, response: unknown): void {\n const status =\n typeof response === \"object\" && response !== null\n ? (response as { status?: unknown }).status\n : undefined;\n if (typeof status !== \"number\" || status >= 500) {\n captureException(error);\n }\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: Hono's fetch environment has no shared adapter type.\nfunction resolveClientAddress(request: Request, env: any): string | undefined {\n const address =\n env?.incoming?.socket?.remoteAddress ?? (env?.server ?? env)?.requestIP?.(request)?.address;\n return typeof address === \"string\" ? address : undefined;\n}\n"],"mappings":";;;;;;;;;;;;AAyBA,MAAM,oBAAoB,OAAO,IAAI,wBAAwB;AAC7D,MAAM,4BAA4B,OAAO,IAAI,+BAA+B;AAC5E,MAAM,cAAc;AAWpB,MAAM,8CAA8B,IAAI,QAA2C;AAInF,SAAgB,aAAa,KAAiB;CAC5C,MAAM,YAAY;CAClB,IAAI,UAAU,uBAAuB,MACnC;CAEF,IAAI,oBAAoB,GAAG,GAAG;EAC5B,eAAA,WACE,0MACF;EACA;CACF;CACA,UAAU,qBAAqB;CAC/B,IAAI,IAAI,2BAA2B;CACnC,MAAM,gBAAgB,IAAI;CAC1B,IAAI,0BAA0B;CAG9B,MAAM,6BAA6B;EACjC,IAAI,yBAAyB;GAC3B,0BAA0B;GAC1B,iBAAiB,GAAG;EACtB;CACF;CACA,MAAM,eAA8B,SAElC,SACA,GAAG,MACH;EACA,IAAI;EACJ,IAAI;GACF,WAAW,eAAe,SAAS,KAAK,IAAI,oBAAoB;EAClE,SAAS,OAAO;GACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,UACH,OAAO,cAAc,KAAK,MAAM,SAAS,GAAG,IAAI;EAElD,MAAM,EAAE,aAAa,mBAAmB;EACxC,IAAI;EACJ,IAAI;GACF,iBAAiBA,mBAAAA,QAAQ,KAAK,sBAC5B,cAAc,KAAK,MAAM,SAAS,GAAG,IAAI,CAC3C;EACF,SAAS,OAAO;GACd,8CAA8C,aAAa,KAAK;GAChE,MAAM;EACR;EACA,OAAO,QAAQ,QAAQ,cAAc,CAAC,CAAC,MACpC,aAAa,gBAAgB,UAAU,WAAW,IAClD,UAAmB;GAClB,8CAA8C,aAAa,KAAK;GAChE,MAAM;EACR,CACF;CACF;CACA,IAAkC,QAAQ;AAC5C;AAKA,SAAS,oBAAoB,KAAoB;CAC/C,IAAI;EACF,MAAM,SAAS,IAAI;EACnB,OACE,MAAM,QAAQ,MAAM,KACpB,OAAO,MAAM,UAAU,OAAO,MAAM,WAAW,YAAY,MAAM,WAAW,KAAK;CAErF,SAAS,OAAO;EACd,eAAA,SAAS,2CAA2C,OAAO,KAAK,GAAG;EACnE,OAAO;CACT;AACF;AAIA,MAAM,8BAAiD,OAAO,GAAG,SAAS;CACxE,MAAM,gBAAgBC,gBAAAA,iBAAiB;CACvC,MAAM,KAAK;CACX,IAAI,CAAC,eACH;CAEF,IAAI;EACF,MAAM,cAAc,4BAA4B,IAAI,aAAa;EACjE,IAAI,aAAa;GACf,YAAY,cAAc;GAC1B,YAAY,QAAQC,oBAAAA,oBAAoB,CAAC;GACzC,MAAM,QAAQ,EAAE;GAChB,IAAI,SAAS,EAAE,IAAI,UAAU,OAAO,YAAY,cAAc,cAAc,OAC1E,mBAAA,iBAAiB,KAAK;EAE1B;CACF,SAAS,OAAO;EACd,eAAA,SAAS,2CAA2C,OAAO,KAAK,GAAG;CACrE;AACF;AAOA,SAAS,eACP,SACA,KACA,sBACkC;CAClC,mBAAA,SAAS;CACT,IAAI,CAACC,mBAAAA,YAAY,KAAKC,8BAAAA,mBAAmB,OAAO,GAC9C;CAEF,qBAAqB;CACrB,MAAM,UAAUC,8BAAAA,2BAA2B;EACzC;EACA,YAAY;EACZ,eAAe,qBAAqB,SAAS,GAAG;CAClD,CAAC;CACD,MAAM,cAAkC,QAAQ;CAChD,4BAA4B,IAAI,YAAY,eAAe,WAAW;CACtE,OAAO;EAAE;EAAa,gBAAgB,QAAQ;CAAe;AAC/D;AAIA,SAAS,gBAAgB,UAAoB,aAA2C;CACtF,IAAI;EACF,MAAM,WAAWC,8BAAAA,mBACf,UACCC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA,KAC3EC,yBAAAA,2BAA2B,SAAS,MAAM,CAC9C;EACA,SAAS,WACN,MAAM,eAAe,4BAA4B,aAAa,UAAU,UAAU,CAAC,CAAC,CACpF,OAAO,UAAmB;GACzB,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE,CAAC;EACH,OAAO,SAAS;CAClB,SAAS,OAAO;EACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EAC/D,OAAO;CACT;AACF;AAEA,eAAe,4BACb,aACA,UACA,sBACe;CACf,MAAM,4BAA4B,WAAW;CAC7C,IAAIA,yBAAAA,2BAA2B,SAAS,MAAM,GAC5C,YAAY,cAAc,qBAAqBC,yBAAAA,2BAC7CC,yBAAAA,sBAAsB,qBAAqB,MAAM,SAAS,QAAQ,IAAI,kBAAkB,CAAC,CAC3F;CAEF,2BAAA,2BAA2B;EACzB;EACA,mBAAmB,qBAAqB;EACxC,YAAY,SAAS;EACrB,OAAO,YAAY;EACnB,gBAAgB,YAAY;EAC5B,iBAAiB,SAAS;EAC1B,qBAAqB,YAAY;EACjC;CACF,CAAC;AACH;AAIA,SAAS,8CACP,aACA,OACM;CACN,IAAI;EACF,2BAAA,oCAAoC;GAClC,eAAe,YAAY;GAC3B,YAAY,YAAY;GACxB;GACA,kBAAkB,YAAY,IAAI,IAAI,YAAY,mBAAmB;EACvE,CAAC;CACH,SAAS,eAAe;EACtB,eAAA,WAAW,qCAAqC,OAAO,aAAa,GAAG;CACzE;AACF;AAIA,eAAe,4BAA4B,aAAgD;CACzF,MAAM,YAAY,YAAY,aAAa,IAAI;CAC/C,IAAI,OAAO,cAAc,YAAY,cAAc,MACjD;CAEF,IAAI;EACF,MAAM,QAAQ,MAAM,uBAAuB,SAAS;EACpD,IAAI,UAAU,KAAA,GAAW;GACvB,YAAY,mBAAmB,SAAS,KAAK;GAC7C,YAAY,mBAAmB,aAAa;EAC9C;CACF,SAAS,OAAO;EACd,eAAA,SAAS,8CAA8C,OAAO,KAAK,GAAG;CACxE;AACF;AAEA,eAAe,uBACb,WAC0C;CAC1C,IAAI,UAAU,gBAAgB,KAAA,GAAW;EACvC,MAAM,QAAiB,MAAM,UAAU;EACvC,OAAO,iBAAiB,cAAc,IAAI,WAAW,KAAK,IAAI,KAAA;CAChE;CACA,IAAI,UAAU,SAAS,KAAA,GAAW;EAChC,MAAM,QAAiB,MAAM,UAAU;EACvC,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;CAC7C;CACA,IAAI,UAAU,SAAS,KAAA,GAAW;EAChC,MAAM,QAAiB,MAAM,UAAU;EACvC,IACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAe,gBAAgB,YAEvC,OAAO,IAAI,WAAW,MAAO,MAAe,YAAY,CAAC;CAE7D;AAEF;AAIA,SAAS,iBAAiB,KAAiB;CACzC,IAAI;EACF,MAAM,iBAAiB;EACvB,MAAM,kBAAkB,eAAe;EACvC,IAAI,OAAO,oBAAoB,YAC7B;EAGF,IAAIC,gBAAc,+BAA+B,MAC/C;EAEF,MAAM,iBAAiB,SAAyB,GAAG,MAAiB;GAClE,MAAM,SAAU,gBAA2D,MAAM,MAAM,IAAI;GAC3F,IAAI,kBAAkB,SACpB,OAAO,OAAO,MAAM,aAAa;IAC/B,sCAAsC,KAAK,IAAI,QAAQ;IACvD,OAAO;GACT,CAAC;GAEH,sCAAsC,KAAK,IAAI,MAAM;GACrD,OAAO;EACT;EACA,eAAuD,6BAA6B;EACpF,eAAe,eAAe;CAChC,SAAS,OAAO;EACd,eAAA,SAAS,4CAA4C,OAAO,KAAK,GAAG;CACtE;AACF;AAEA,SAAS,sCAAsC,OAAgB,UAAyB;CACtF,MAAM,SACJ,OAAO,aAAa,YAAY,aAAa,OACxC,SAAkC,SACnC,KAAA;CACN,IAAI,OAAO,WAAW,YAAY,UAAU,KAC1C,mBAAA,iBAAiB,KAAK;AAE1B;AAGA,SAAS,qBAAqB,SAAkB,KAA8B;CAC5E,MAAM,UACJ,KAAK,UAAU,QAAQ,kBAAkB,KAAK,UAAU,IAAA,EAAM,YAAY,OAAO,CAAC,EAAE;CACtF,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;AACjD"}
1
+ {"version":3,"file":"middleware.cjs","names":["context","getRequestRecord","resolveMatchedRoute","isActivated","isWebSocketUpgrade","startWebRequestObservation","captureWebResponse","getConfig","isValidationResponseStatus","extractZodValidationErrors","parseJsonResponseBody","markedHandler"],"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { Hono, Context as HonoContext, MiddlewareHandler } from \"hono\";\nimport { activate, activateWhenHttpServerListens, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord, type RequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n type WebResponseCompletion,\n} from \"../requestObservationWeb.js\";\nimport {\n extractZodValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst FETCH_WRAP_MARKER = Symbol.for(\"apitally.honoFetchWrap\");\nconst ERROR_HANDLER_WRAP_MARKER = Symbol.for(\"apitally.honoErrorHandlerWrap\");\nconst TRACER_NAME = \"apitally/hono\";\n\ntype FetchFunction = (request: Request, ...rest: unknown[]) => Response | Promise<Response>;\n\ninterface RequestObservation extends WebRequestObservation {\n honoContext?: HonoContext;\n route?: string;\n}\n\n// WeakMap associates route middleware with transport observation without\n// retaining completed request records.\nconst observationsByRequestRecord = new WeakMap<RequestRecord, RequestObservation>();\n\n// Wrapping app.fetch covers every response, including onError. Route middleware\n// precedes later registrations, and the marker prevents duplicate observation.\nexport function wrapAppFetch(app: Hono): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[FETCH_WRAP_MARKER] === true) {\n return;\n }\n if (hasRegisteredRoutes(app)) {\n logWarning(\n \"useApitally() was called after routes were registered on the Hono app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes.\",\n );\n return;\n }\n markedApp[FETCH_WRAP_MARKER] = true;\n activateWhenHttpServerListens();\n app.use(recordMatchedRouteAfterNext);\n const originalFetch = app.fetch as FetchFunction;\n let errorHandlerWrapPending = true;\n // First-request wrapping follows route() mounts and onError registration.\n // Replacing the handler afterward disables exception capture.\n const wrapErrorHandlerOnce = () => {\n if (errorHandlerWrapPending) {\n errorHandlerWrapPending = false;\n wrapErrorHandler(app);\n }\n };\n const wrappedFetch: FetchFunction = function (\n this: unknown,\n request: Request,\n ...rest: unknown[]\n ) {\n let observed: ObservedRequestStart | undefined;\n try {\n observed = observeRequest(request, rest[0], wrapErrorHandlerOnce);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!observed) {\n return originalFetch.call(this, request, ...rest);\n }\n const { observation, requestContext } = observed;\n let dispatchResult: Response | Promise<Response>;\n try {\n dispatchResult = context.with(requestContext, () =>\n originalFetch.call(this, request, ...rest),\n );\n } catch (error) {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n }\n return Promise.resolve(dispatchResult).then(\n (response) => observeResponse(response, observation),\n (error: unknown) => {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n },\n );\n };\n (app as { fetch: FetchFunction }).fetch = wrappedFetch;\n}\n\n// Middleware entries register as method \"ALL\" and lose nothing when they\n// precede the wrap; only concrete-method entries need the route middleware\n// that is registered after them.\nfunction hasRegisteredRoutes(app: Hono): boolean {\n try {\n const routes = app.routes;\n return (\n Array.isArray(routes) &&\n routes.some((route) => typeof route.method === \"string\" && route.method !== \"ALL\")\n );\n } catch (error) {\n logDebug(`Error inspecting the hono app's routes: ${String(error)}`);\n return false;\n }\n}\n\n// Registered before later routes, this middleware records the final context\n// and resolved route after next().\nconst recordMatchedRouteAfterNext: MiddlewareHandler = async (c, next) => {\n const requestRecord = getRequestRecord();\n await next();\n if (!requestRecord) {\n return;\n }\n try {\n const observation = observationsByRequestRecord.get(requestRecord);\n if (observation) {\n observation.honoContext = c;\n observation.route = resolveMatchedRoute(c);\n const error = c.error;\n if (error && c.res.status >= 500 && observation.requestRecord.exception !== error) {\n captureException(error);\n }\n }\n } catch (error) {\n logDebug(`Error recording the matched hono route: ${String(error)}`);\n }\n};\n\ninterface ObservedRequestStart {\n observation: RequestObservation;\n requestContext: Context;\n}\n\nfunction observeRequest(\n request: Request,\n env: unknown,\n wrapErrorHandlerOnce: () => void,\n): ObservedRequestStart | undefined {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return undefined;\n }\n wrapErrorHandlerOnce();\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress: resolveClientAddress(request, env),\n });\n const observation: RequestObservation = started.observation;\n observationsByRequestRecord.set(observation.requestRecord, observation);\n return { observation, requestContext: started.requestContext };\n}\n\n// A tee after compression observes bytes sent to the client. Finalization follows\n// tee completion, and bodiless responses complete immediately.\nfunction observeResponse(response: Response, observation: RequestObservation): Response {\n try {\n const captured = captureWebResponse(\n response,\n (getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined) ||\n isValidationResponseStatus(response.status),\n );\n captured.completion\n .then((completion) => finalizeRequestFromResponse(observation, response, completion))\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n return response;\n }\n}\n\nasync function finalizeRequestFromResponse(\n observation: RequestObservation,\n response: Response,\n capturedResponseBody: WebResponseCompletion,\n): Promise<void> {\n await captureRequestBodyFromCache(observation);\n if (isValidationResponseStatus(response.status)) {\n observation.requestRecord.validationErrors ??= extractZodValidationErrors(\n parseJsonResponseBody(capturedResponseBody.body, response.headers.get(\"content-encoding\")),\n );\n }\n finalizeRequestObservation({\n observation,\n completedAtMillis: capturedResponseBody.completedAtMillis,\n statusCode: response.status,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders: response.headers,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody,\n });\n}\n\n// A fetch rejection has no response, so the request record is finalized and\n// the rejection propagates unchanged.\nfunction finalizeRequestObservationAfterFetchRejection(\n observation: RequestObservation,\n error: unknown,\n): void {\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally middleware: ${String(finalizeError)}`);\n }\n}\n\n// Only cache entries preserving the original request bytes are captured. If no\n// such entry exists, the SDK leaves the request stream untouched.\nasync function captureRequestBodyFromCache(observation: RequestObservation): Promise<void> {\n const bodyCache = observation.honoContext?.req.bodyCache as Record<string, unknown> | undefined;\n if (typeof bodyCache !== \"object\" || bodyCache === null) {\n return;\n }\n try {\n const chunk = await resolveCachedBodyChunk(bodyCache);\n if (chunk !== undefined) {\n observation.requestBodyCapture.addChunk(chunk);\n observation.requestBodyCapture.markComplete();\n }\n } catch (error) {\n logDebug(`Error reading the hono request body cache: ${String(error)}`);\n }\n}\n\nasync function resolveCachedBodyChunk(\n bodyCache: Record<string, unknown>,\n): Promise<Uint8Array | string | undefined> {\n if (bodyCache.arrayBuffer !== undefined) {\n const value: unknown = await bodyCache.arrayBuffer;\n return value instanceof ArrayBuffer ? new Uint8Array(value) : undefined;\n }\n if (bodyCache.text !== undefined) {\n const value: unknown = await bodyCache.text;\n return typeof value === \"string\" ? value : undefined;\n }\n if (bodyCache.blob !== undefined) {\n const value: unknown = await bodyCache.blob;\n if (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Blob).arrayBuffer === \"function\"\n ) {\n return new Uint8Array(await (value as Blob).arrayBuffer());\n }\n }\n return undefined;\n}\n\n// Hono exposes errorHandler at runtime; wrapping it records the exception before\n// Hono converts it into a response.\nfunction wrapErrorHandler(app: Hono): void {\n try {\n const appWithHandler = app as unknown as { errorHandler?: unknown };\n const originalHandler = appWithHandler.errorHandler;\n if (typeof originalHandler !== \"function\") {\n return;\n }\n const markedHandler = originalHandler as unknown as Record<symbol, boolean | undefined>;\n if (markedHandler[ERROR_HANDLER_WRAP_MARKER] === true) {\n return;\n }\n const wrappedHandler = function (this: unknown, ...args: unknown[]) {\n const result = (originalHandler as (...handlerArgs: unknown[]) => unknown).apply(this, args);\n if (result instanceof Promise) {\n return result.then((response) => {\n captureExceptionIfServerErrorResponse(args[0], response);\n return response;\n });\n }\n captureExceptionIfServerErrorResponse(args[0], result);\n return result;\n };\n (wrappedHandler as unknown as Record<symbol, boolean>)[ERROR_HANDLER_WRAP_MARKER] = true;\n appWithHandler.errorHandler = wrappedHandler;\n } catch (error) {\n logDebug(`Error wrapping the hono onError handler: ${String(error)}`);\n }\n}\n\nfunction captureExceptionIfServerErrorResponse(error: unknown, response: unknown): void {\n const status =\n typeof response === \"object\" && response !== null\n ? (response as { status?: unknown }).status\n : undefined;\n if (typeof status !== \"number\" || status >= 500) {\n captureException(error);\n }\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: Hono's fetch environment has no shared adapter type.\nfunction resolveClientAddress(request: Request, env: any): string | undefined {\n const address =\n env?.incoming?.socket?.remoteAddress ?? (env?.server ?? env)?.requestIP?.(request)?.address;\n return typeof address === \"string\" ? address : undefined;\n}\n"],"mappings":";;;;;;;;;;;;AAyBA,MAAM,oBAAoB,OAAO,IAAI,wBAAwB;AAC7D,MAAM,4BAA4B,OAAO,IAAI,+BAA+B;AAC5E,MAAM,cAAc;AAWpB,MAAM,8CAA8B,IAAI,QAA2C;AAInF,SAAgB,aAAa,KAAiB;CAC5C,MAAM,YAAY;CAClB,IAAI,UAAU,uBAAuB,MACnC;CAEF,IAAI,oBAAoB,GAAG,GAAG;EAC5B,eAAA,WACE,0MACF;EACA;CACF;CACA,UAAU,qBAAqB;CAC/B,mBAAA,8BAA8B;CAC9B,IAAI,IAAI,2BAA2B;CACnC,MAAM,gBAAgB,IAAI;CAC1B,IAAI,0BAA0B;CAG9B,MAAM,6BAA6B;EACjC,IAAI,yBAAyB;GAC3B,0BAA0B;GAC1B,iBAAiB,GAAG;EACtB;CACF;CACA,MAAM,eAA8B,SAElC,SACA,GAAG,MACH;EACA,IAAI;EACJ,IAAI;GACF,WAAW,eAAe,SAAS,KAAK,IAAI,oBAAoB;EAClE,SAAS,OAAO;GACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,UACH,OAAO,cAAc,KAAK,MAAM,SAAS,GAAG,IAAI;EAElD,MAAM,EAAE,aAAa,mBAAmB;EACxC,IAAI;EACJ,IAAI;GACF,iBAAiBA,mBAAAA,QAAQ,KAAK,sBAC5B,cAAc,KAAK,MAAM,SAAS,GAAG,IAAI,CAC3C;EACF,SAAS,OAAO;GACd,8CAA8C,aAAa,KAAK;GAChE,MAAM;EACR;EACA,OAAO,QAAQ,QAAQ,cAAc,CAAC,CAAC,MACpC,aAAa,gBAAgB,UAAU,WAAW,IAClD,UAAmB;GAClB,8CAA8C,aAAa,KAAK;GAChE,MAAM;EACR,CACF;CACF;CACA,IAAkC,QAAQ;AAC5C;AAKA,SAAS,oBAAoB,KAAoB;CAC/C,IAAI;EACF,MAAM,SAAS,IAAI;EACnB,OACE,MAAM,QAAQ,MAAM,KACpB,OAAO,MAAM,UAAU,OAAO,MAAM,WAAW,YAAY,MAAM,WAAW,KAAK;CAErF,SAAS,OAAO;EACd,eAAA,SAAS,2CAA2C,OAAO,KAAK,GAAG;EACnE,OAAO;CACT;AACF;AAIA,MAAM,8BAAiD,OAAO,GAAG,SAAS;CACxE,MAAM,gBAAgBC,gBAAAA,iBAAiB;CACvC,MAAM,KAAK;CACX,IAAI,CAAC,eACH;CAEF,IAAI;EACF,MAAM,cAAc,4BAA4B,IAAI,aAAa;EACjE,IAAI,aAAa;GACf,YAAY,cAAc;GAC1B,YAAY,QAAQC,oBAAAA,oBAAoB,CAAC;GACzC,MAAM,QAAQ,EAAE;GAChB,IAAI,SAAS,EAAE,IAAI,UAAU,OAAO,YAAY,cAAc,cAAc,OAC1E,mBAAA,iBAAiB,KAAK;EAE1B;CACF,SAAS,OAAO;EACd,eAAA,SAAS,2CAA2C,OAAO,KAAK,GAAG;CACrE;AACF;AAOA,SAAS,eACP,SACA,KACA,sBACkC;CAClC,mBAAA,SAAS;CACT,IAAI,CAACC,mBAAAA,YAAY,KAAKC,8BAAAA,mBAAmB,OAAO,GAC9C;CAEF,qBAAqB;CACrB,MAAM,UAAUC,8BAAAA,2BAA2B;EACzC;EACA,YAAY;EACZ,eAAe,qBAAqB,SAAS,GAAG;CAClD,CAAC;CACD,MAAM,cAAkC,QAAQ;CAChD,4BAA4B,IAAI,YAAY,eAAe,WAAW;CACtE,OAAO;EAAE;EAAa,gBAAgB,QAAQ;CAAe;AAC/D;AAIA,SAAS,gBAAgB,UAAoB,aAA2C;CACtF,IAAI;EACF,MAAM,WAAWC,8BAAAA,mBACf,UACCC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA,KAC3EC,yBAAAA,2BAA2B,SAAS,MAAM,CAC9C;EACA,SAAS,WACN,MAAM,eAAe,4BAA4B,aAAa,UAAU,UAAU,CAAC,CAAC,CACpF,OAAO,UAAmB;GACzB,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE,CAAC;EACH,OAAO,SAAS;CAClB,SAAS,OAAO;EACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EAC/D,OAAO;CACT;AACF;AAEA,eAAe,4BACb,aACA,UACA,sBACe;CACf,MAAM,4BAA4B,WAAW;CAC7C,IAAIA,yBAAAA,2BAA2B,SAAS,MAAM,GAC5C,YAAY,cAAc,qBAAqBC,yBAAAA,2BAC7CC,yBAAAA,sBAAsB,qBAAqB,MAAM,SAAS,QAAQ,IAAI,kBAAkB,CAAC,CAC3F;CAEF,2BAAA,2BAA2B;EACzB;EACA,mBAAmB,qBAAqB;EACxC,YAAY,SAAS;EACrB,OAAO,YAAY;EACnB,gBAAgB,YAAY;EAC5B,iBAAiB,SAAS;EAC1B,qBAAqB,YAAY;EACjC;CACF,CAAC;AACH;AAIA,SAAS,8CACP,aACA,OACM;CACN,IAAI;EACF,2BAAA,oCAAoC;GAClC,eAAe,YAAY;GAC3B,YAAY,YAAY;GACxB;GACA,kBAAkB,YAAY,IAAI,IAAI,YAAY,mBAAmB;EACvE,CAAC;CACH,SAAS,eAAe;EACtB,eAAA,WAAW,qCAAqC,OAAO,aAAa,GAAG;CACzE;AACF;AAIA,eAAe,4BAA4B,aAAgD;CACzF,MAAM,YAAY,YAAY,aAAa,IAAI;CAC/C,IAAI,OAAO,cAAc,YAAY,cAAc,MACjD;CAEF,IAAI;EACF,MAAM,QAAQ,MAAM,uBAAuB,SAAS;EACpD,IAAI,UAAU,KAAA,GAAW;GACvB,YAAY,mBAAmB,SAAS,KAAK;GAC7C,YAAY,mBAAmB,aAAa;EAC9C;CACF,SAAS,OAAO;EACd,eAAA,SAAS,8CAA8C,OAAO,KAAK,GAAG;CACxE;AACF;AAEA,eAAe,uBACb,WAC0C;CAC1C,IAAI,UAAU,gBAAgB,KAAA,GAAW;EACvC,MAAM,QAAiB,MAAM,UAAU;EACvC,OAAO,iBAAiB,cAAc,IAAI,WAAW,KAAK,IAAI,KAAA;CAChE;CACA,IAAI,UAAU,SAAS,KAAA,GAAW;EAChC,MAAM,QAAiB,MAAM,UAAU;EACvC,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;CAC7C;CACA,IAAI,UAAU,SAAS,KAAA,GAAW;EAChC,MAAM,QAAiB,MAAM,UAAU;EACvC,IACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAe,gBAAgB,YAEvC,OAAO,IAAI,WAAW,MAAO,MAAe,YAAY,CAAC;CAE7D;AAEF;AAIA,SAAS,iBAAiB,KAAiB;CACzC,IAAI;EACF,MAAM,iBAAiB;EACvB,MAAM,kBAAkB,eAAe;EACvC,IAAI,OAAO,oBAAoB,YAC7B;EAGF,IAAIC,gBAAc,+BAA+B,MAC/C;EAEF,MAAM,iBAAiB,SAAyB,GAAG,MAAiB;GAClE,MAAM,SAAU,gBAA2D,MAAM,MAAM,IAAI;GAC3F,IAAI,kBAAkB,SACpB,OAAO,OAAO,MAAM,aAAa;IAC/B,sCAAsC,KAAK,IAAI,QAAQ;IACvD,OAAO;GACT,CAAC;GAEH,sCAAsC,KAAK,IAAI,MAAM;GACrD,OAAO;EACT;EACA,eAAuD,6BAA6B;EACpF,eAAe,eAAe;CAChC,SAAS,OAAO;EACd,eAAA,SAAS,4CAA4C,OAAO,KAAK,GAAG;CACtE;AACF;AAEA,SAAS,sCAAsC,OAAgB,UAAyB;CACtF,MAAM,SACJ,OAAO,aAAa,YAAY,aAAa,OACxC,SAAkC,SACnC,KAAA;CACN,IAAI,OAAO,WAAW,YAAY,UAAU,KAC1C,mBAAA,iBAAiB,KAAK;AAE1B;AAGA,SAAS,qBAAqB,SAAkB,KAA8B;CAC5E,MAAM,UACJ,KAAK,UAAU,QAAQ,kBAAkB,KAAK,UAAU,IAAA,EAAM,YAAY,OAAO,CAAC,EAAE;CACtF,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;AACjD"}
@@ -3,7 +3,7 @@ import { getConfig } from "../config.js";
3
3
  import { getRequestRecord } from "../context.js";
4
4
  import { captureException } from "../exceptions.js";
5
5
  import { extractZodValidationErrors, isValidationResponseStatus, parseJsonResponseBody } from "../validationErrors.js";
6
- import { activate, isActivated } from "../activation.js";
6
+ import { activate, activateWhenHttpServerListens, isActivated } from "../activation.js";
7
7
  import { finalizeRequestObservation, finalizeRequestObservationWithError } from "../requestObservation.js";
8
8
  import { captureWebResponse, isWebSocketUpgrade, startWebRequestObservation } from "../requestObservationWeb.js";
9
9
  import { resolveMatchedRoute } from "./routes.js";
@@ -11,7 +11,7 @@ import { context } from "@opentelemetry/api";
11
11
  //#region src/hono/middleware.ts
12
12
  const FETCH_WRAP_MARKER = Symbol.for("apitally.honoFetchWrap");
13
13
  const ERROR_HANDLER_WRAP_MARKER = Symbol.for("apitally.honoErrorHandlerWrap");
14
- const TRACER_NAME = "apitally.hono";
14
+ const TRACER_NAME = "apitally/hono";
15
15
  const observationsByRequestRecord = /* @__PURE__ */ new WeakMap();
16
16
  function wrapAppFetch(app) {
17
17
  const markedApp = app;
@@ -21,6 +21,7 @@ function wrapAppFetch(app) {
21
21
  return;
22
22
  }
23
23
  markedApp[FETCH_WRAP_MARKER] = true;
24
+ activateWhenHttpServerListens();
24
25
  app.use(recordMatchedRouteAfterNext);
25
26
  const originalFetch = app.fetch;
26
27
  let errorHandlerWrapPending = true;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":["markedHandler"],"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { Hono, Context as HonoContext, MiddlewareHandler } from \"hono\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord, type RequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n type WebResponseCompletion,\n} from \"../requestObservationWeb.js\";\nimport {\n extractZodValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst FETCH_WRAP_MARKER = Symbol.for(\"apitally.honoFetchWrap\");\nconst ERROR_HANDLER_WRAP_MARKER = Symbol.for(\"apitally.honoErrorHandlerWrap\");\nconst TRACER_NAME = \"apitally.hono\";\n\ntype FetchFunction = (request: Request, ...rest: unknown[]) => Response | Promise<Response>;\n\ninterface RequestObservation extends WebRequestObservation {\n honoContext?: HonoContext;\n route?: string;\n}\n\n// WeakMap associates route middleware with transport observation without\n// retaining completed request records.\nconst observationsByRequestRecord = new WeakMap<RequestRecord, RequestObservation>();\n\n// Wrapping app.fetch covers every response, including onError. Route middleware\n// precedes later registrations, and the marker prevents duplicate observation.\nexport function wrapAppFetch(app: Hono): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[FETCH_WRAP_MARKER] === true) {\n return;\n }\n if (hasRegisteredRoutes(app)) {\n logWarning(\n \"useApitally() was called after routes were registered on the Hono app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes.\",\n );\n return;\n }\n markedApp[FETCH_WRAP_MARKER] = true;\n app.use(recordMatchedRouteAfterNext);\n const originalFetch = app.fetch as FetchFunction;\n let errorHandlerWrapPending = true;\n // First-request wrapping follows route() mounts and onError registration.\n // Replacing the handler afterward disables exception capture.\n const wrapErrorHandlerOnce = () => {\n if (errorHandlerWrapPending) {\n errorHandlerWrapPending = false;\n wrapErrorHandler(app);\n }\n };\n const wrappedFetch: FetchFunction = function (\n this: unknown,\n request: Request,\n ...rest: unknown[]\n ) {\n let observed: ObservedRequestStart | undefined;\n try {\n observed = observeRequest(request, rest[0], wrapErrorHandlerOnce);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!observed) {\n return originalFetch.call(this, request, ...rest);\n }\n const { observation, requestContext } = observed;\n let dispatchResult: Response | Promise<Response>;\n try {\n dispatchResult = context.with(requestContext, () =>\n originalFetch.call(this, request, ...rest),\n );\n } catch (error) {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n }\n return Promise.resolve(dispatchResult).then(\n (response) => observeResponse(response, observation),\n (error: unknown) => {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n },\n );\n };\n (app as { fetch: FetchFunction }).fetch = wrappedFetch;\n}\n\n// Middleware entries register as method \"ALL\" and lose nothing when they\n// precede the wrap; only concrete-method entries need the route middleware\n// that is registered after them.\nfunction hasRegisteredRoutes(app: Hono): boolean {\n try {\n const routes = app.routes;\n return (\n Array.isArray(routes) &&\n routes.some((route) => typeof route.method === \"string\" && route.method !== \"ALL\")\n );\n } catch (error) {\n logDebug(`Error inspecting the hono app's routes: ${String(error)}`);\n return false;\n }\n}\n\n// Registered before later routes, this middleware records the final context\n// and resolved route after next().\nconst recordMatchedRouteAfterNext: MiddlewareHandler = async (c, next) => {\n const requestRecord = getRequestRecord();\n await next();\n if (!requestRecord) {\n return;\n }\n try {\n const observation = observationsByRequestRecord.get(requestRecord);\n if (observation) {\n observation.honoContext = c;\n observation.route = resolveMatchedRoute(c);\n const error = c.error;\n if (error && c.res.status >= 500 && observation.requestRecord.exception !== error) {\n captureException(error);\n }\n }\n } catch (error) {\n logDebug(`Error recording the matched hono route: ${String(error)}`);\n }\n};\n\ninterface ObservedRequestStart {\n observation: RequestObservation;\n requestContext: Context;\n}\n\nfunction observeRequest(\n request: Request,\n env: unknown,\n wrapErrorHandlerOnce: () => void,\n): ObservedRequestStart | undefined {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return undefined;\n }\n wrapErrorHandlerOnce();\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress: resolveClientAddress(request, env),\n });\n const observation: RequestObservation = started.observation;\n observationsByRequestRecord.set(observation.requestRecord, observation);\n return { observation, requestContext: started.requestContext };\n}\n\n// A tee after compression observes bytes sent to the client. Finalization follows\n// tee completion, and bodiless responses complete immediately.\nfunction observeResponse(response: Response, observation: RequestObservation): Response {\n try {\n const captured = captureWebResponse(\n response,\n (getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined) ||\n isValidationResponseStatus(response.status),\n );\n captured.completion\n .then((completion) => finalizeRequestFromResponse(observation, response, completion))\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n return response;\n }\n}\n\nasync function finalizeRequestFromResponse(\n observation: RequestObservation,\n response: Response,\n capturedResponseBody: WebResponseCompletion,\n): Promise<void> {\n await captureRequestBodyFromCache(observation);\n if (isValidationResponseStatus(response.status)) {\n observation.requestRecord.validationErrors ??= extractZodValidationErrors(\n parseJsonResponseBody(capturedResponseBody.body, response.headers.get(\"content-encoding\")),\n );\n }\n finalizeRequestObservation({\n observation,\n completedAtMillis: capturedResponseBody.completedAtMillis,\n statusCode: response.status,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders: response.headers,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody,\n });\n}\n\n// A fetch rejection has no response, so the request record is finalized and\n// the rejection propagates unchanged.\nfunction finalizeRequestObservationAfterFetchRejection(\n observation: RequestObservation,\n error: unknown,\n): void {\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally middleware: ${String(finalizeError)}`);\n }\n}\n\n// Only cache entries preserving the original request bytes are captured. If no\n// such entry exists, the SDK leaves the request stream untouched.\nasync function captureRequestBodyFromCache(observation: RequestObservation): Promise<void> {\n const bodyCache = observation.honoContext?.req.bodyCache as Record<string, unknown> | undefined;\n if (typeof bodyCache !== \"object\" || bodyCache === null) {\n return;\n }\n try {\n const chunk = await resolveCachedBodyChunk(bodyCache);\n if (chunk !== undefined) {\n observation.requestBodyCapture.addChunk(chunk);\n observation.requestBodyCapture.markComplete();\n }\n } catch (error) {\n logDebug(`Error reading the hono request body cache: ${String(error)}`);\n }\n}\n\nasync function resolveCachedBodyChunk(\n bodyCache: Record<string, unknown>,\n): Promise<Uint8Array | string | undefined> {\n if (bodyCache.arrayBuffer !== undefined) {\n const value: unknown = await bodyCache.arrayBuffer;\n return value instanceof ArrayBuffer ? new Uint8Array(value) : undefined;\n }\n if (bodyCache.text !== undefined) {\n const value: unknown = await bodyCache.text;\n return typeof value === \"string\" ? value : undefined;\n }\n if (bodyCache.blob !== undefined) {\n const value: unknown = await bodyCache.blob;\n if (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Blob).arrayBuffer === \"function\"\n ) {\n return new Uint8Array(await (value as Blob).arrayBuffer());\n }\n }\n return undefined;\n}\n\n// Hono exposes errorHandler at runtime; wrapping it records the exception before\n// Hono converts it into a response.\nfunction wrapErrorHandler(app: Hono): void {\n try {\n const appWithHandler = app as unknown as { errorHandler?: unknown };\n const originalHandler = appWithHandler.errorHandler;\n if (typeof originalHandler !== \"function\") {\n return;\n }\n const markedHandler = originalHandler as unknown as Record<symbol, boolean | undefined>;\n if (markedHandler[ERROR_HANDLER_WRAP_MARKER] === true) {\n return;\n }\n const wrappedHandler = function (this: unknown, ...args: unknown[]) {\n const result = (originalHandler as (...handlerArgs: unknown[]) => unknown).apply(this, args);\n if (result instanceof Promise) {\n return result.then((response) => {\n captureExceptionIfServerErrorResponse(args[0], response);\n return response;\n });\n }\n captureExceptionIfServerErrorResponse(args[0], result);\n return result;\n };\n (wrappedHandler as unknown as Record<symbol, boolean>)[ERROR_HANDLER_WRAP_MARKER] = true;\n appWithHandler.errorHandler = wrappedHandler;\n } catch (error) {\n logDebug(`Error wrapping the hono onError handler: ${String(error)}`);\n }\n}\n\nfunction captureExceptionIfServerErrorResponse(error: unknown, response: unknown): void {\n const status =\n typeof response === \"object\" && response !== null\n ? (response as { status?: unknown }).status\n : undefined;\n if (typeof status !== \"number\" || status >= 500) {\n captureException(error);\n }\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: Hono's fetch environment has no shared adapter type.\nfunction resolveClientAddress(request: Request, env: any): string | undefined {\n const address =\n env?.incoming?.socket?.remoteAddress ?? (env?.server ?? env)?.requestIP?.(request)?.address;\n return typeof address === \"string\" ? address : undefined;\n}\n"],"mappings":";;;;;;;;;;;AAyBA,MAAM,oBAAoB,OAAO,IAAI,wBAAwB;AAC7D,MAAM,4BAA4B,OAAO,IAAI,+BAA+B;AAC5E,MAAM,cAAc;AAWpB,MAAM,8CAA8B,IAAI,QAA2C;AAInF,SAAgB,aAAa,KAAiB;CAC5C,MAAM,YAAY;CAClB,IAAI,UAAU,uBAAuB,MACnC;CAEF,IAAI,oBAAoB,GAAG,GAAG;EAC5B,WACE,0MACF;EACA;CACF;CACA,UAAU,qBAAqB;CAC/B,IAAI,IAAI,2BAA2B;CACnC,MAAM,gBAAgB,IAAI;CAC1B,IAAI,0BAA0B;CAG9B,MAAM,6BAA6B;EACjC,IAAI,yBAAyB;GAC3B,0BAA0B;GAC1B,iBAAiB,GAAG;EACtB;CACF;CACA,MAAM,eAA8B,SAElC,SACA,GAAG,MACH;EACA,IAAI;EACJ,IAAI;GACF,WAAW,eAAe,SAAS,KAAK,IAAI,oBAAoB;EAClE,SAAS,OAAO;GACd,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,UACH,OAAO,cAAc,KAAK,MAAM,SAAS,GAAG,IAAI;EAElD,MAAM,EAAE,aAAa,mBAAmB;EACxC,IAAI;EACJ,IAAI;GACF,iBAAiB,QAAQ,KAAK,sBAC5B,cAAc,KAAK,MAAM,SAAS,GAAG,IAAI,CAC3C;EACF,SAAS,OAAO;GACd,8CAA8C,aAAa,KAAK;GAChE,MAAM;EACR;EACA,OAAO,QAAQ,QAAQ,cAAc,CAAC,CAAC,MACpC,aAAa,gBAAgB,UAAU,WAAW,IAClD,UAAmB;GAClB,8CAA8C,aAAa,KAAK;GAChE,MAAM;EACR,CACF;CACF;CACA,IAAkC,QAAQ;AAC5C;AAKA,SAAS,oBAAoB,KAAoB;CAC/C,IAAI;EACF,MAAM,SAAS,IAAI;EACnB,OACE,MAAM,QAAQ,MAAM,KACpB,OAAO,MAAM,UAAU,OAAO,MAAM,WAAW,YAAY,MAAM,WAAW,KAAK;CAErF,SAAS,OAAO;EACd,SAAS,2CAA2C,OAAO,KAAK,GAAG;EACnE,OAAO;CACT;AACF;AAIA,MAAM,8BAAiD,OAAO,GAAG,SAAS;CACxE,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,KAAK;CACX,IAAI,CAAC,eACH;CAEF,IAAI;EACF,MAAM,cAAc,4BAA4B,IAAI,aAAa;EACjE,IAAI,aAAa;GACf,YAAY,cAAc;GAC1B,YAAY,QAAQ,oBAAoB,CAAC;GACzC,MAAM,QAAQ,EAAE;GAChB,IAAI,SAAS,EAAE,IAAI,UAAU,OAAO,YAAY,cAAc,cAAc,OAC1E,iBAAiB,KAAK;EAE1B;CACF,SAAS,OAAO;EACd,SAAS,2CAA2C,OAAO,KAAK,GAAG;CACrE;AACF;AAOA,SAAS,eACP,SACA,KACA,sBACkC;CAClC,SAAS;CACT,IAAI,CAAC,YAAY,KAAK,mBAAmB,OAAO,GAC9C;CAEF,qBAAqB;CACrB,MAAM,UAAU,2BAA2B;EACzC;EACA,YAAY;EACZ,eAAe,qBAAqB,SAAS,GAAG;CAClD,CAAC;CACD,MAAM,cAAkC,QAAQ;CAChD,4BAA4B,IAAI,YAAY,eAAe,WAAW;CACtE,OAAO;EAAE;EAAa,gBAAgB,QAAQ;CAAe;AAC/D;AAIA,SAAS,gBAAgB,UAAoB,aAA2C;CACtF,IAAI;EACF,MAAM,WAAW,mBACf,UACC,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA,KAC3E,2BAA2B,SAAS,MAAM,CAC9C;EACA,SAAS,WACN,MAAM,eAAe,4BAA4B,aAAa,UAAU,UAAU,CAAC,CAAC,CACpF,OAAO,UAAmB;GACzB,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE,CAAC;EACH,OAAO,SAAS;CAClB,SAAS,OAAO;EACd,WAAW,qCAAqC,OAAO,KAAK,GAAG;EAC/D,OAAO;CACT;AACF;AAEA,eAAe,4BACb,aACA,UACA,sBACe;CACf,MAAM,4BAA4B,WAAW;CAC7C,IAAI,2BAA2B,SAAS,MAAM,GAC5C,YAAY,cAAc,qBAAqB,2BAC7C,sBAAsB,qBAAqB,MAAM,SAAS,QAAQ,IAAI,kBAAkB,CAAC,CAC3F;CAEF,2BAA2B;EACzB;EACA,mBAAmB,qBAAqB;EACxC,YAAY,SAAS;EACrB,OAAO,YAAY;EACnB,gBAAgB,YAAY;EAC5B,iBAAiB,SAAS;EAC1B,qBAAqB,YAAY;EACjC;CACF,CAAC;AACH;AAIA,SAAS,8CACP,aACA,OACM;CACN,IAAI;EACF,oCAAoC;GAClC,eAAe,YAAY;GAC3B,YAAY,YAAY;GACxB;GACA,kBAAkB,YAAY,IAAI,IAAI,YAAY,mBAAmB;EACvE,CAAC;CACH,SAAS,eAAe;EACtB,WAAW,qCAAqC,OAAO,aAAa,GAAG;CACzE;AACF;AAIA,eAAe,4BAA4B,aAAgD;CACzF,MAAM,YAAY,YAAY,aAAa,IAAI;CAC/C,IAAI,OAAO,cAAc,YAAY,cAAc,MACjD;CAEF,IAAI;EACF,MAAM,QAAQ,MAAM,uBAAuB,SAAS;EACpD,IAAI,UAAU,KAAA,GAAW;GACvB,YAAY,mBAAmB,SAAS,KAAK;GAC7C,YAAY,mBAAmB,aAAa;EAC9C;CACF,SAAS,OAAO;EACd,SAAS,8CAA8C,OAAO,KAAK,GAAG;CACxE;AACF;AAEA,eAAe,uBACb,WAC0C;CAC1C,IAAI,UAAU,gBAAgB,KAAA,GAAW;EACvC,MAAM,QAAiB,MAAM,UAAU;EACvC,OAAO,iBAAiB,cAAc,IAAI,WAAW,KAAK,IAAI,KAAA;CAChE;CACA,IAAI,UAAU,SAAS,KAAA,GAAW;EAChC,MAAM,QAAiB,MAAM,UAAU;EACvC,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;CAC7C;CACA,IAAI,UAAU,SAAS,KAAA,GAAW;EAChC,MAAM,QAAiB,MAAM,UAAU;EACvC,IACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAe,gBAAgB,YAEvC,OAAO,IAAI,WAAW,MAAO,MAAe,YAAY,CAAC;CAE7D;AAEF;AAIA,SAAS,iBAAiB,KAAiB;CACzC,IAAI;EACF,MAAM,iBAAiB;EACvB,MAAM,kBAAkB,eAAe;EACvC,IAAI,OAAO,oBAAoB,YAC7B;EAGF,IAAIA,gBAAc,+BAA+B,MAC/C;EAEF,MAAM,iBAAiB,SAAyB,GAAG,MAAiB;GAClE,MAAM,SAAU,gBAA2D,MAAM,MAAM,IAAI;GAC3F,IAAI,kBAAkB,SACpB,OAAO,OAAO,MAAM,aAAa;IAC/B,sCAAsC,KAAK,IAAI,QAAQ;IACvD,OAAO;GACT,CAAC;GAEH,sCAAsC,KAAK,IAAI,MAAM;GACrD,OAAO;EACT;EACA,eAAuD,6BAA6B;EACpF,eAAe,eAAe;CAChC,SAAS,OAAO;EACd,SAAS,4CAA4C,OAAO,KAAK,GAAG;CACtE;AACF;AAEA,SAAS,sCAAsC,OAAgB,UAAyB;CACtF,MAAM,SACJ,OAAO,aAAa,YAAY,aAAa,OACxC,SAAkC,SACnC,KAAA;CACN,IAAI,OAAO,WAAW,YAAY,UAAU,KAC1C,iBAAiB,KAAK;AAE1B;AAGA,SAAS,qBAAqB,SAAkB,KAA8B;CAC5E,MAAM,UACJ,KAAK,UAAU,QAAQ,kBAAkB,KAAK,UAAU,IAAA,EAAM,YAAY,OAAO,CAAC,EAAE;CACtF,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;AACjD"}
1
+ {"version":3,"file":"middleware.js","names":["markedHandler"],"sources":["../../src/hono/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { Hono, Context as HonoContext, MiddlewareHandler } from \"hono\";\nimport { activate, activateWhenHttpServerListens, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { getRequestRecord, type RequestRecord } from \"../context.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport {\n finalizeRequestObservation,\n finalizeRequestObservationWithError,\n} from \"../requestObservation.js\";\nimport {\n captureWebResponse,\n isWebSocketUpgrade,\n startWebRequestObservation,\n type WebRequestObservation,\n type WebResponseCompletion,\n} from \"../requestObservationWeb.js\";\nimport {\n extractZodValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst FETCH_WRAP_MARKER = Symbol.for(\"apitally.honoFetchWrap\");\nconst ERROR_HANDLER_WRAP_MARKER = Symbol.for(\"apitally.honoErrorHandlerWrap\");\nconst TRACER_NAME = \"apitally/hono\";\n\ntype FetchFunction = (request: Request, ...rest: unknown[]) => Response | Promise<Response>;\n\ninterface RequestObservation extends WebRequestObservation {\n honoContext?: HonoContext;\n route?: string;\n}\n\n// WeakMap associates route middleware with transport observation without\n// retaining completed request records.\nconst observationsByRequestRecord = new WeakMap<RequestRecord, RequestObservation>();\n\n// Wrapping app.fetch covers every response, including onError. Route middleware\n// precedes later registrations, and the marker prevents duplicate observation.\nexport function wrapAppFetch(app: Hono): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[FETCH_WRAP_MARKER] === true) {\n return;\n }\n if (hasRegisteredRoutes(app)) {\n logWarning(\n \"useApitally() was called after routes were registered on the Hono app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes.\",\n );\n return;\n }\n markedApp[FETCH_WRAP_MARKER] = true;\n activateWhenHttpServerListens();\n app.use(recordMatchedRouteAfterNext);\n const originalFetch = app.fetch as FetchFunction;\n let errorHandlerWrapPending = true;\n // First-request wrapping follows route() mounts and onError registration.\n // Replacing the handler afterward disables exception capture.\n const wrapErrorHandlerOnce = () => {\n if (errorHandlerWrapPending) {\n errorHandlerWrapPending = false;\n wrapErrorHandler(app);\n }\n };\n const wrappedFetch: FetchFunction = function (\n this: unknown,\n request: Request,\n ...rest: unknown[]\n ) {\n let observed: ObservedRequestStart | undefined;\n try {\n observed = observeRequest(request, rest[0], wrapErrorHandlerOnce);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!observed) {\n return originalFetch.call(this, request, ...rest);\n }\n const { observation, requestContext } = observed;\n let dispatchResult: Response | Promise<Response>;\n try {\n dispatchResult = context.with(requestContext, () =>\n originalFetch.call(this, request, ...rest),\n );\n } catch (error) {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n }\n return Promise.resolve(dispatchResult).then(\n (response) => observeResponse(response, observation),\n (error: unknown) => {\n finalizeRequestObservationAfterFetchRejection(observation, error);\n throw error;\n },\n );\n };\n (app as { fetch: FetchFunction }).fetch = wrappedFetch;\n}\n\n// Middleware entries register as method \"ALL\" and lose nothing when they\n// precede the wrap; only concrete-method entries need the route middleware\n// that is registered after them.\nfunction hasRegisteredRoutes(app: Hono): boolean {\n try {\n const routes = app.routes;\n return (\n Array.isArray(routes) &&\n routes.some((route) => typeof route.method === \"string\" && route.method !== \"ALL\")\n );\n } catch (error) {\n logDebug(`Error inspecting the hono app's routes: ${String(error)}`);\n return false;\n }\n}\n\n// Registered before later routes, this middleware records the final context\n// and resolved route after next().\nconst recordMatchedRouteAfterNext: MiddlewareHandler = async (c, next) => {\n const requestRecord = getRequestRecord();\n await next();\n if (!requestRecord) {\n return;\n }\n try {\n const observation = observationsByRequestRecord.get(requestRecord);\n if (observation) {\n observation.honoContext = c;\n observation.route = resolveMatchedRoute(c);\n const error = c.error;\n if (error && c.res.status >= 500 && observation.requestRecord.exception !== error) {\n captureException(error);\n }\n }\n } catch (error) {\n logDebug(`Error recording the matched hono route: ${String(error)}`);\n }\n};\n\ninterface ObservedRequestStart {\n observation: RequestObservation;\n requestContext: Context;\n}\n\nfunction observeRequest(\n request: Request,\n env: unknown,\n wrapErrorHandlerOnce: () => void,\n): ObservedRequestStart | undefined {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return undefined;\n }\n wrapErrorHandlerOnce();\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress: resolveClientAddress(request, env),\n });\n const observation: RequestObservation = started.observation;\n observationsByRequestRecord.set(observation.requestRecord, observation);\n return { observation, requestContext: started.requestContext };\n}\n\n// A tee after compression observes bytes sent to the client. Finalization follows\n// tee completion, and bodiless responses complete immediately.\nfunction observeResponse(response: Response, observation: RequestObservation): Response {\n try {\n const captured = captureWebResponse(\n response,\n (getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined) ||\n isValidationResponseStatus(response.status),\n );\n captured.completion\n .then((completion) => finalizeRequestFromResponse(observation, response, completion))\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return captured.response;\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n return response;\n }\n}\n\nasync function finalizeRequestFromResponse(\n observation: RequestObservation,\n response: Response,\n capturedResponseBody: WebResponseCompletion,\n): Promise<void> {\n await captureRequestBodyFromCache(observation);\n if (isValidationResponseStatus(response.status)) {\n observation.requestRecord.validationErrors ??= extractZodValidationErrors(\n parseJsonResponseBody(capturedResponseBody.body, response.headers.get(\"content-encoding\")),\n );\n }\n finalizeRequestObservation({\n observation,\n completedAtMillis: capturedResponseBody.completedAtMillis,\n statusCode: response.status,\n route: observation.route,\n requestHeaders: observation.requestHeaders,\n responseHeaders: response.headers,\n capturedRequestBody: observation.requestBodyCapture,\n capturedResponseBody,\n });\n}\n\n// A fetch rejection has no response, so the request record is finalized and\n// the rejection propagates unchanged.\nfunction finalizeRequestObservationAfterFetchRejection(\n observation: RequestObservation,\n error: unknown,\n): void {\n try {\n finalizeRequestObservationWithError({\n requestRecord: observation.requestRecord,\n spanHandle: observation.spanHandle,\n error,\n durationSeconds: (performance.now() - observation.startTimeMillis) / 1000,\n });\n } catch (finalizeError) {\n logWarning(`Error in the Apitally middleware: ${String(finalizeError)}`);\n }\n}\n\n// Only cache entries preserving the original request bytes are captured. If no\n// such entry exists, the SDK leaves the request stream untouched.\nasync function captureRequestBodyFromCache(observation: RequestObservation): Promise<void> {\n const bodyCache = observation.honoContext?.req.bodyCache as Record<string, unknown> | undefined;\n if (typeof bodyCache !== \"object\" || bodyCache === null) {\n return;\n }\n try {\n const chunk = await resolveCachedBodyChunk(bodyCache);\n if (chunk !== undefined) {\n observation.requestBodyCapture.addChunk(chunk);\n observation.requestBodyCapture.markComplete();\n }\n } catch (error) {\n logDebug(`Error reading the hono request body cache: ${String(error)}`);\n }\n}\n\nasync function resolveCachedBodyChunk(\n bodyCache: Record<string, unknown>,\n): Promise<Uint8Array | string | undefined> {\n if (bodyCache.arrayBuffer !== undefined) {\n const value: unknown = await bodyCache.arrayBuffer;\n return value instanceof ArrayBuffer ? new Uint8Array(value) : undefined;\n }\n if (bodyCache.text !== undefined) {\n const value: unknown = await bodyCache.text;\n return typeof value === \"string\" ? value : undefined;\n }\n if (bodyCache.blob !== undefined) {\n const value: unknown = await bodyCache.blob;\n if (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Blob).arrayBuffer === \"function\"\n ) {\n return new Uint8Array(await (value as Blob).arrayBuffer());\n }\n }\n return undefined;\n}\n\n// Hono exposes errorHandler at runtime; wrapping it records the exception before\n// Hono converts it into a response.\nfunction wrapErrorHandler(app: Hono): void {\n try {\n const appWithHandler = app as unknown as { errorHandler?: unknown };\n const originalHandler = appWithHandler.errorHandler;\n if (typeof originalHandler !== \"function\") {\n return;\n }\n const markedHandler = originalHandler as unknown as Record<symbol, boolean | undefined>;\n if (markedHandler[ERROR_HANDLER_WRAP_MARKER] === true) {\n return;\n }\n const wrappedHandler = function (this: unknown, ...args: unknown[]) {\n const result = (originalHandler as (...handlerArgs: unknown[]) => unknown).apply(this, args);\n if (result instanceof Promise) {\n return result.then((response) => {\n captureExceptionIfServerErrorResponse(args[0], response);\n return response;\n });\n }\n captureExceptionIfServerErrorResponse(args[0], result);\n return result;\n };\n (wrappedHandler as unknown as Record<symbol, boolean>)[ERROR_HANDLER_WRAP_MARKER] = true;\n appWithHandler.errorHandler = wrappedHandler;\n } catch (error) {\n logDebug(`Error wrapping the hono onError handler: ${String(error)}`);\n }\n}\n\nfunction captureExceptionIfServerErrorResponse(error: unknown, response: unknown): void {\n const status =\n typeof response === \"object\" && response !== null\n ? (response as { status?: unknown }).status\n : undefined;\n if (typeof status !== \"number\" || status >= 500) {\n captureException(error);\n }\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: Hono's fetch environment has no shared adapter type.\nfunction resolveClientAddress(request: Request, env: any): string | undefined {\n const address =\n env?.incoming?.socket?.remoteAddress ?? (env?.server ?? env)?.requestIP?.(request)?.address;\n return typeof address === \"string\" ? address : undefined;\n}\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ const require_koa_middleware = require("./middleware.cjs");
6
6
  //#region src/koa/index.ts
7
7
  function useApitally(app, options) {
8
8
  require_activation.configure(options);
9
+ require_activation.activateWhenHttpServerListens();
9
10
  require_activation.registerStartupEventInfo({
10
11
  framework: "koa",
11
12
  frameworkVersion: require_packageVersion.resolvePackageVersion("koa"),
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["resolvePackageVersion","resolveStartupPaths"],"sources":["../../src/koa/index.ts"],"sourcesContent":["import { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installKoaMiddleware } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: import(\"koa\"), options?: ApitallyOptions): void {\n configure(options);\n registerStartupEventInfo({\n framework: \"koa\",\n frameworkVersion: resolvePackageVersion(\"koa\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n installKoaMiddleware(app);\n}\n"],"mappings":";;;;;;AASA,SAAgB,YAAY,KAAoB,SAAiC;CAC/E,mBAAA,UAAU,OAAO;CACjB,mBAAA,yBAAyB;EACvB,WAAW;EACX,kBAAkBA,uBAAAA,sBAAsB,KAAK;EAC7C,oBAAoBC,mBAAAA,oBAAoB,GAAG;CAC7C,CAAC;CACD,uBAAA,qBAAqB,GAAG;AAC1B"}
1
+ {"version":3,"file":"index.cjs","names":["resolvePackageVersion","resolveStartupPaths"],"sources":["../../src/koa/index.ts"],"sourcesContent":["import {\n activateWhenHttpServerListens,\n configure,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installKoaMiddleware } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins when an HTTP server starts\n// listening, or on the first request.\nexport function useApitally(app: import(\"koa\"), options?: ApitallyOptions): void {\n configure(options);\n activateWhenHttpServerListens();\n registerStartupEventInfo({\n framework: \"koa\",\n frameworkVersion: resolvePackageVersion(\"koa\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n installKoaMiddleware(app);\n}\n"],"mappings":";;;;;;AAcA,SAAgB,YAAY,KAAoB,SAAiC;CAC/E,mBAAA,UAAU,OAAO;CACjB,mBAAA,8BAA8B;CAC9B,mBAAA,yBAAyB;EACvB,WAAW;EACX,kBAAkBA,uBAAAA,sBAAsB,KAAK;EAC7C,oBAAoBC,mBAAAA,oBAAoB,GAAG;CAC7C,CAAC;CACD,uBAAA,qBAAqB,GAAG;AAC1B"}
package/dist/koa/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { resolvePackageVersion } from "../packageVersion.js";
2
- import { configure, registerStartupEventInfo } from "../activation.js";
2
+ import { activateWhenHttpServerListens, configure, registerStartupEventInfo } from "../activation.js";
3
3
  import { resolveStartupPaths } from "./routes.js";
4
4
  import { installKoaMiddleware } from "./middleware.js";
5
5
  //#region src/koa/index.ts
6
6
  function useApitally(app, options) {
7
7
  configure(options);
8
+ activateWhenHttpServerListens();
8
9
  registerStartupEventInfo({
9
10
  framework: "koa",
10
11
  frameworkVersion: resolvePackageVersion("koa"),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/koa/index.ts"],"sourcesContent":["import { configure, registerStartupEventInfo } from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installKoaMiddleware } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: import(\"koa\"), options?: ApitallyOptions): void {\n configure(options);\n registerStartupEventInfo({\n framework: \"koa\",\n frameworkVersion: resolvePackageVersion(\"koa\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n installKoaMiddleware(app);\n}\n"],"mappings":";;;;;AASA,SAAgB,YAAY,KAAoB,SAAiC;CAC/E,UAAU,OAAO;CACjB,yBAAyB;EACvB,WAAW;EACX,kBAAkB,sBAAsB,KAAK;EAC7C,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,qBAAqB,GAAG;AAC1B"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/koa/index.ts"],"sourcesContent":["import {\n activateWhenHttpServerListens,\n configure,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installKoaMiddleware } from \"./middleware.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins when an HTTP server starts\n// listening, or on the first request.\nexport function useApitally(app: import(\"koa\"), options?: ApitallyOptions): void {\n configure(options);\n activateWhenHttpServerListens();\n registerStartupEventInfo({\n framework: \"koa\",\n frameworkVersion: resolvePackageVersion(\"koa\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n installKoaMiddleware(app);\n}\n"],"mappings":";;;;;AAcA,SAAgB,YAAY,KAAoB,SAAiC;CAC/E,UAAU,OAAO;CACjB,8BAA8B;CAC9B,yBAAyB;EACvB,WAAW;EACX,kBAAkB,sBAAsB,KAAK;EAC7C,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,qBAAqB,GAAG;AAC1B"}
@@ -9,7 +9,7 @@ const require_koa_routes = require("./routes.cjs");
9
9
  let _opentelemetry_api = require("@opentelemetry/api");
10
10
  //#region src/koa/middleware.ts
11
11
  const MIDDLEWARE_MARKER = Symbol.for("apitally.koaMiddleware");
12
- const TRACER_NAME = "apitally.koa";
12
+ const TRACER_NAME = "apitally/koa";
13
13
  function installKoaMiddleware(app) {
14
14
  const markedApp = app;
15
15
  if (markedApp[MIDDLEWARE_MARKER] === true) return;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.cjs","names":["resolveErrorStatus","context","captureException","resolveMatchedRoute","isActivated","startNodeRequestObservation","getConfig"],"sources":["../../src/koa/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\n\nimport type Koa = require(\"koa\");\n\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst MIDDLEWARE_MARKER = Symbol.for(\"apitally.koaMiddleware\");\nconst TRACER_NAME = \"apitally.koa\";\n\ninterface KoaRequestObservation extends NodeRequestObservation {\n route?: string;\n}\n\ninterface ObservedRequestStart {\n observation: KoaRequestObservation;\n requestContext: Context;\n}\n\nexport function installKoaMiddleware(app: Koa): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[MIDDLEWARE_MARKER] === true) {\n return;\n }\n markedApp[MIDDLEWARE_MARKER] = true;\n warnIfMiddlewareAlreadyRegistered(app);\n\n let isErrorListenerRegistered = false;\n app.use(async (ctx, next) => {\n if (!isErrorListenerRegistered) {\n // Register after callback() so our listener does not suppress Koa's default error logger.\n app.on(\"error\", (error) => {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n captureException(error);\n }\n });\n isErrorListenerRegistered = true;\n }\n let started: ObservedRequestStart | undefined;\n try {\n started = observeRequest(ctx);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!started) {\n await next();\n return;\n }\n\n try {\n await context.with(started.requestContext, next);\n } catch (error) {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n context.with(started.requestContext, () => captureException(error));\n }\n throw error;\n } finally {\n started.observation.route = resolveMatchedRoute(ctx);\n }\n });\n}\n\nfunction observeRequest(ctx: Koa.Context): ObservedRequestStart | undefined {\n activate();\n if (!isActivated()) {\n return undefined;\n }\n registerServerCloseFlush(ctx.req);\n const started = startNodeRequestObservation({ request: ctx.req, tracerName: TRACER_NAME });\n context.bind(started.requestContext, ctx.req);\n context.bind(started.requestContext, ctx.res);\n const observation: KoaRequestObservation = started.observation;\n captureNodeResponse(\n ctx.res,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: ctx.res.statusCode,\n route: observation.route ?? resolveMatchedRoute(ctx),\n clientAddress: ctx.ip,\n requestHeaders: ctx.req.headers,\n responseHeaders: ctx.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return { observation, requestContext: started.requestContext };\n}\n\nfunction warnIfMiddlewareAlreadyRegistered(app: Koa): void {\n try {\n if (app.middleware.length > 0) {\n logWarning(\n \"useApitally() was called after middleware was registered on the Koa app, so earlier middleware may handle or consume requests before Apitally observes them. To resolve this, call useApitally() immediately after creating the app, before registering middleware and routes.\",\n );\n }\n } catch (error) {\n logDebug(`Error inspecting the koa app's middleware: ${String(error)}`);\n }\n}\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,oBAAoB,OAAO,IAAI,wBAAwB;AAC7D,MAAM,cAAc;AAWpB,SAAgB,qBAAqB,KAAgB;CACnD,MAAM,YAAY;CAClB,IAAI,UAAU,uBAAuB,MACnC;CAEF,UAAU,qBAAqB;CAC/B,kCAAkC,GAAG;CAErC,IAAI,4BAA4B;CAChC,IAAI,IAAI,OAAO,KAAK,SAAS;EAC3B,IAAI,CAAC,2BAA2B;GAE9B,IAAI,GAAG,UAAU,UAAU;IACzB,MAAM,SAASA,mBAAAA,mBAAmB,KAAK;IACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,mBAAA,iBAAiB,KAAK;GAE1B,CAAC;GACD,4BAA4B;EAC9B;EACA,IAAI;EACJ,IAAI;GACF,UAAU,eAAe,GAAG;EAC9B,SAAS,OAAO;GACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,SAAS;GACZ,MAAM,KAAK;GACX;EACF;EAEA,IAAI;GACF,MAAMC,mBAAAA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI;EACjD,SAAS,OAAO;GACd,MAAM,SAASD,mBAAAA,mBAAmB,KAAK;GACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,mBAAA,QAAQ,KAAK,QAAQ,sBAAsBE,mBAAAA,iBAAiB,KAAK,CAAC;GAEpE,MAAM;EACR,UAAU;GACR,QAAQ,YAAY,QAAQC,mBAAAA,oBAAoB,GAAG;EACrD;CACF,CAAC;AACH;AAEA,SAAS,eAAe,KAAoD;CAC1E,mBAAA,SAAS;CACT,IAAI,CAACC,mBAAAA,YAAY,GACf;CAEF,+BAAA,yBAAyB,IAAI,GAAG;CAChC,MAAM,UAAUC,+BAAAA,4BAA4B;EAAE,SAAS,IAAI;EAAK,YAAY;CAAY,CAAC;CACzF,mBAAA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,GAAG;CAC5C,mBAAA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,GAAG;CAC5C,MAAM,cAAqC,QAAQ;CACnD,+BAAA,oBACE,IAAI,KACJC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA,GAC5E,YAAY,kBACd,CAAC,CACE,MAAM,eAAe;EACpB,2BAAA,2BAA2B;GACzB;GACA,mBAAmB,WAAW;GAC9B,YAAY,IAAI,IAAI;GACpB,OAAO,YAAY,SAASH,mBAAAA,oBAAoB,GAAG;GACnD,eAAe,IAAI;GACnB,gBAAgB,IAAI,IAAI;GACxB,iBAAiB,IAAI,IAAI,WAAW;GACpC,qBAAqB,WAAW;GAChC,sBAAsB;EACxB,CAAC;CACH,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;CACjE,CAAC;CACH,OAAO;EAAE;EAAa,gBAAgB,QAAQ;CAAe;AAC/D;AAEA,SAAS,kCAAkC,KAAgB;CACzD,IAAI;EACF,IAAI,IAAI,WAAW,SAAS,GAC1B,eAAA,WACE,gRACF;CAEJ,SAAS,OAAO;EACd,eAAA,SAAS,8CAA8C,OAAO,KAAK,GAAG;CACxE;AACF"}
1
+ {"version":3,"file":"middleware.cjs","names":["resolveErrorStatus","context","captureException","resolveMatchedRoute","isActivated","startNodeRequestObservation","getConfig"],"sources":["../../src/koa/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\n\nimport type Koa = require(\"koa\");\n\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport { resolveMatchedRoute } from \"./routes.js\";\n\nconst MIDDLEWARE_MARKER = Symbol.for(\"apitally.koaMiddleware\");\nconst TRACER_NAME = \"apitally/koa\";\n\ninterface KoaRequestObservation extends NodeRequestObservation {\n route?: string;\n}\n\ninterface ObservedRequestStart {\n observation: KoaRequestObservation;\n requestContext: Context;\n}\n\nexport function installKoaMiddleware(app: Koa): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[MIDDLEWARE_MARKER] === true) {\n return;\n }\n markedApp[MIDDLEWARE_MARKER] = true;\n warnIfMiddlewareAlreadyRegistered(app);\n\n let isErrorListenerRegistered = false;\n app.use(async (ctx, next) => {\n if (!isErrorListenerRegistered) {\n // Register after callback() so our listener does not suppress Koa's default error logger.\n app.on(\"error\", (error) => {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n captureException(error);\n }\n });\n isErrorListenerRegistered = true;\n }\n let started: ObservedRequestStart | undefined;\n try {\n started = observeRequest(ctx);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!started) {\n await next();\n return;\n }\n\n try {\n await context.with(started.requestContext, next);\n } catch (error) {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n context.with(started.requestContext, () => captureException(error));\n }\n throw error;\n } finally {\n started.observation.route = resolveMatchedRoute(ctx);\n }\n });\n}\n\nfunction observeRequest(ctx: Koa.Context): ObservedRequestStart | undefined {\n activate();\n if (!isActivated()) {\n return undefined;\n }\n registerServerCloseFlush(ctx.req);\n const started = startNodeRequestObservation({ request: ctx.req, tracerName: TRACER_NAME });\n context.bind(started.requestContext, ctx.req);\n context.bind(started.requestContext, ctx.res);\n const observation: KoaRequestObservation = started.observation;\n captureNodeResponse(\n ctx.res,\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined,\n observation.requestBodyCapture,\n )\n .then((completion) => {\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: ctx.res.statusCode,\n route: observation.route ?? resolveMatchedRoute(ctx),\n clientAddress: ctx.ip,\n requestHeaders: ctx.req.headers,\n responseHeaders: ctx.res.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return { observation, requestContext: started.requestContext };\n}\n\nfunction warnIfMiddlewareAlreadyRegistered(app: Koa): void {\n try {\n if (app.middleware.length > 0) {\n logWarning(\n \"useApitally() was called after middleware was registered on the Koa app, so earlier middleware may handle or consume requests before Apitally observes them. To resolve this, call useApitally() immediately after creating the app, before registering middleware and routes.\",\n );\n }\n } catch (error) {\n logDebug(`Error inspecting the koa app's middleware: ${String(error)}`);\n }\n}\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,oBAAoB,OAAO,IAAI,wBAAwB;AAC7D,MAAM,cAAc;AAWpB,SAAgB,qBAAqB,KAAgB;CACnD,MAAM,YAAY;CAClB,IAAI,UAAU,uBAAuB,MACnC;CAEF,UAAU,qBAAqB;CAC/B,kCAAkC,GAAG;CAErC,IAAI,4BAA4B;CAChC,IAAI,IAAI,OAAO,KAAK,SAAS;EAC3B,IAAI,CAAC,2BAA2B;GAE9B,IAAI,GAAG,UAAU,UAAU;IACzB,MAAM,SAASA,mBAAAA,mBAAmB,KAAK;IACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,mBAAA,iBAAiB,KAAK;GAE1B,CAAC;GACD,4BAA4B;EAC9B;EACA,IAAI;EACJ,IAAI;GACF,UAAU,eAAe,GAAG;EAC9B,SAAS,OAAO;GACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,SAAS;GACZ,MAAM,KAAK;GACX;EACF;EAEA,IAAI;GACF,MAAMC,mBAAAA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI;EACjD,SAAS,OAAO;GACd,MAAM,SAASD,mBAAAA,mBAAmB,KAAK;GACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,mBAAA,QAAQ,KAAK,QAAQ,sBAAsBE,mBAAAA,iBAAiB,KAAK,CAAC;GAEpE,MAAM;EACR,UAAU;GACR,QAAQ,YAAY,QAAQC,mBAAAA,oBAAoB,GAAG;EACrD;CACF,CAAC;AACH;AAEA,SAAS,eAAe,KAAoD;CAC1E,mBAAA,SAAS;CACT,IAAI,CAACC,mBAAAA,YAAY,GACf;CAEF,+BAAA,yBAAyB,IAAI,GAAG;CAChC,MAAM,UAAUC,+BAAAA,4BAA4B;EAAE,SAAS,IAAI;EAAK,YAAY;CAAY,CAAC;CACzF,mBAAA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,GAAG;CAC5C,mBAAA,QAAQ,KAAK,QAAQ,gBAAgB,IAAI,GAAG;CAC5C,MAAM,cAAqC,QAAQ;CACnD,+BAAA,oBACE,IAAI,KACJC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA,GAC5E,YAAY,kBACd,CAAC,CACE,MAAM,eAAe;EACpB,2BAAA,2BAA2B;GACzB;GACA,mBAAmB,WAAW;GAC9B,YAAY,IAAI,IAAI;GACpB,OAAO,YAAY,SAASH,mBAAAA,oBAAoB,GAAG;GACnD,eAAe,IAAI;GACnB,gBAAgB,IAAI,IAAI;GACxB,iBAAiB,IAAI,IAAI,WAAW;GACpC,qBAAqB,WAAW;GAChC,sBAAsB;EACxB,CAAC;CACH,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;CACjE,CAAC;CACH,OAAO;EAAE;EAAa,gBAAgB,QAAQ;CAAe;AAC/D;AAEA,SAAS,kCAAkC,KAAgB;CACzD,IAAI;EACF,IAAI,IAAI,WAAW,SAAS,GAC1B,eAAA,WACE,gRACF;CAEJ,SAAS,OAAO;EACd,eAAA,SAAS,8CAA8C,OAAO,KAAK,GAAG;CACxE;AACF"}
@@ -8,7 +8,7 @@ import { resolveMatchedRoute } from "./routes.js";
8
8
  import { context } from "@opentelemetry/api";
9
9
  //#region src/koa/middleware.ts
10
10
  const MIDDLEWARE_MARKER = Symbol.for("apitally.koaMiddleware");
11
- const TRACER_NAME = "apitally.koa";
11
+ const TRACER_NAME = "apitally/koa";
12
12
  function installKoaMiddleware(app) {
13
13
  const markedApp = app;
14
14
  if (markedApp[MIDDLEWARE_MARKER] === true) return;