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.
- package/README.md +12 -8
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/activation.cjs +30 -0
- package/dist/activation.cjs.map +1 -1
- package/dist/activation.d.cts +1 -0
- package/dist/activation.d.ts +1 -0
- package/dist/activation.js +28 -1
- package/dist/activation.js.map +1 -1
- package/dist/adonisjs/middleware.cjs +1 -1
- package/dist/adonisjs/middleware.cjs.map +1 -1
- package/dist/adonisjs/middleware.js +1 -1
- package/dist/adonisjs/middleware.js.map +1 -1
- package/dist/elysia/index.cjs.map +1 -1
- package/dist/elysia/index.js.map +1 -1
- package/dist/elysia/middleware.cjs +8 -10
- package/dist/elysia/middleware.cjs.map +1 -1
- package/dist/elysia/middleware.js +8 -10
- package/dist/elysia/middleware.js.map +1 -1
- package/dist/elysia/routes.cjs +2 -1
- package/dist/elysia/routes.cjs.map +1 -1
- package/dist/elysia/routes.js +2 -1
- package/dist/elysia/routes.js.map +1 -1
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js.map +1 -1
- package/dist/express/install.cjs +1 -0
- package/dist/express/install.cjs.map +1 -1
- package/dist/express/install.js +2 -1
- package/dist/express/install.js.map +1 -1
- package/dist/express/middleware.cjs +1 -1
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.js +1 -1
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/middleware.cjs +1 -1
- package/dist/fastify/middleware.cjs.map +1 -1
- package/dist/fastify/middleware.js +1 -1
- package/dist/fastify/middleware.js.map +1 -1
- package/dist/h3/index.cjs +1 -0
- package/dist/h3/index.cjs.map +1 -1
- package/dist/h3/index.js +2 -1
- package/dist/h3/index.js.map +1 -1
- package/dist/h3/middleware.cjs +1 -1
- package/dist/h3/middleware.cjs.map +1 -1
- package/dist/h3/middleware.js +1 -1
- package/dist/h3/middleware.js.map +1 -1
- package/dist/hapi/middleware.cjs +1 -1
- package/dist/hapi/middleware.cjs.map +1 -1
- package/dist/hapi/middleware.js +1 -1
- package/dist/hapi/middleware.js.map +1 -1
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +2 -1
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.js +3 -2
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +1 -0
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.js +2 -1
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +1 -1
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.js +1 -1
- package/dist/koa/middleware.js.map +1 -1
- package/dist/requestAttributes.cjs +7 -1
- package/dist/requestAttributes.cjs.map +1 -1
- package/dist/requestAttributes.js +7 -1
- package/dist/requestAttributes.js.map +1 -1
- package/dist/requestObservation.cjs +2 -1
- package/dist/requestObservation.cjs.map +1 -1
- package/dist/requestObservation.js +2 -1
- package/dist/requestObservation.js.map +1 -1
- package/dist/tracing.cjs +2 -1
- package/dist/tracing.cjs.map +1 -1
- package/dist/tracing.js +2 -1
- package/dist/tracing.js.map +1 -1
- package/package.json +14 -14
|
@@ -12,18 +12,16 @@ let _opentelemetry_api = require("@opentelemetry/api");
|
|
|
12
12
|
//#region src/elysia/middleware.ts
|
|
13
13
|
const INSTALL_MARKER = Symbol.for("apitally.elysiaInstall");
|
|
14
14
|
const PLUGIN_NAME = "apitally";
|
|
15
|
-
const TRACER_NAME = "apitally
|
|
15
|
+
const TRACER_NAME = "apitally/elysia";
|
|
16
16
|
function createElysiaPlugin(ElysiaClass, options) {
|
|
17
17
|
require_activation.configure(options);
|
|
18
|
-
|
|
18
|
+
const plugin = buildElysiaPlugin(ElysiaClass);
|
|
19
19
|
require_activation.registerStartupEventInfo({
|
|
20
20
|
framework: "elysia",
|
|
21
21
|
frameworkVersion: require_packageVersion.resolvePackageVersion("elysia"),
|
|
22
|
-
resolvePaths: () =>
|
|
23
|
-
});
|
|
24
|
-
return buildElysiaPlugin(ElysiaClass, (startedApp) => {
|
|
25
|
-
app = startedApp;
|
|
22
|
+
resolvePaths: () => require_elysia_routes.resolveStartupPaths(plugin)
|
|
26
23
|
});
|
|
24
|
+
return plugin;
|
|
27
25
|
}
|
|
28
26
|
function installElysiaIntegration(app, options) {
|
|
29
27
|
require_activation.configure(options);
|
|
@@ -42,7 +40,7 @@ function installElysiaIntegration(app, options) {
|
|
|
42
40
|
const ElysiaClass = app.constructor;
|
|
43
41
|
app.use(buildElysiaPlugin(ElysiaClass));
|
|
44
42
|
}
|
|
45
|
-
function buildElysiaPlugin(ElysiaClass
|
|
43
|
+
function buildElysiaPlugin(ElysiaClass) {
|
|
46
44
|
const observations = /* @__PURE__ */ new WeakMap();
|
|
47
45
|
const plugin = new ElysiaClass({ name: PLUGIN_NAME });
|
|
48
46
|
let startedApp;
|
|
@@ -54,7 +52,7 @@ function buildElysiaPlugin(ElysiaClass, recordStartedApp) {
|
|
|
54
52
|
const started = require_requestObservationWeb.startWebRequestObservation({
|
|
55
53
|
request,
|
|
56
54
|
tracerName: TRACER_NAME,
|
|
57
|
-
clientAddress: startedApp?.server?.requestIP(request)?.address
|
|
55
|
+
clientAddress: startedApp?.server?.requestIP(request)?.address ?? request.ip
|
|
58
56
|
});
|
|
59
57
|
observation = {
|
|
60
58
|
...started.observation,
|
|
@@ -81,8 +79,8 @@ function buildElysiaPlugin(ElysiaClass, recordStartedApp) {
|
|
|
81
79
|
}));
|
|
82
80
|
plugin.onStart((app) => {
|
|
83
81
|
startedApp = app;
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
if (app.config?.aot === false) require_logger.logWarning("Apitally does not observe requests on an Elysia app with aot: false. To resolve this, remove the aot: false option.");
|
|
83
|
+
require_activation.activate();
|
|
86
84
|
});
|
|
87
85
|
plugin.onTransform({ as: "global" }, ({ request, route }) => {
|
|
88
86
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.cjs","names":["resolvePackageVersion","resolveStartupPaths","isActivated","isWebSocketUpgrade","startWebRequestObservation","captureWebRequestBody","context","formatIssues","normalizeSource","captureException","captureWebResponse","getConfig","finalizeRequestObservation"],"sources":["../../src/elysia/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { AnyElysia } from \"elysia\";\nimport {\n activate,\n configure,\n flushTelemetry,\n isActivated,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { resolvePackageVersion } from \"../packageVersion.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, normalizeSource } from \"../validationErrors.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nconst INSTALL_MARKER = Symbol.for(\"apitally.elysiaInstall\");\nconst PLUGIN_NAME = \"apitally\";\nconst TRACER_NAME = \"apitally.elysia\";\n\ninterface ElysiaConstructor {\n new (config?: { name?: string }): AnyElysia;\n}\n\ntype ElysiaDispatcher = (this: unknown, ...args: unknown[]) => Response | Promise<Response>;\ntype ElysiaWrap = Parameters<AnyElysia[\"wrap\"]>[0];\n\ninterface ElysiaRequestObservation extends WebRequestObservation {\n requestContext: Context;\n requestBodyCompletion: Promise<void>;\n route?: string;\n error?: unknown;\n}\n\nexport function createElysiaPlugin(\n ElysiaClass: ElysiaConstructor,\n options?: ApitallyOptions,\n): AnyElysia {\n configure(options);\n let app: AnyElysia | undefined;\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => (app ? resolveStartupPaths(app) : []),\n });\n return buildElysiaPlugin(ElysiaClass, (startedApp) => {\n app = startedApp;\n });\n}\n\nexport function installElysiaIntegration(app: AnyElysia, 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 if (app.routes.length > 0) {\n logWarning(\n \"useApitally() was called after routes were registered on the Elysia app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes or plugins that contain routes.\",\n );\n return;\n }\n markedApp[INSTALL_MARKER] = true;\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n const ElysiaClass = app.constructor as ElysiaConstructor;\n app.use(buildElysiaPlugin(ElysiaClass));\n}\n\nfunction buildElysiaPlugin(\n ElysiaClass: ElysiaConstructor,\n recordStartedApp?: (app: AnyElysia) => void,\n): AnyElysia {\n const observations = new WeakMap<Request, ElysiaRequestObservation>();\n const plugin = new ElysiaClass({ name: PLUGIN_NAME });\n let startedApp: AnyElysia | undefined;\n\n plugin.wrap(\n ((dispatcher: ElysiaDispatcher, request: Request) =>\n function (this: unknown, ...args: unknown[]): Response | Promise<Response> {\n let observation: ElysiaRequestObservation;\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return dispatcher.apply(this, args);\n }\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress: startedApp?.server?.requestIP(request)?.address,\n });\n observation = {\n ...started.observation,\n requestContext: started.requestContext,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally Elysia middleware: ${String(error)}`);\n return dispatcher.apply(this, args);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observation.requestContext, () => dispatcher.apply(this, args));\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 }) as unknown as ElysiaWrap,\n );\n\n plugin.onStart((app) => {\n startedApp = app;\n recordStartedApp?.(app);\n // Elysia's dynamic handler for aot: false never applies plugin wrap() functions.\n if (app.config.aot === false) {\n logWarning(\n \"Apitally does not observe requests on an Elysia app with aot: false. To resolve this, remove the aot: false option.\",\n );\n }\n });\n plugin.onTransform({ as: \"global\" }, ({ request, route }) => {\n try {\n const observation = observations.get(request);\n if (observation && typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n } catch (error) {\n logDebug(`Error recording the matched Elysia route: ${String(error)}`);\n }\n });\n plugin.onError({ as: \"global\" }, ({ request, route, error }) => {\n try {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n if (typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n observation.error = error;\n const { code, all, type } = error as { code?: unknown; all?: unknown; type?: unknown };\n if (code === \"VALIDATION\") {\n observation.requestRecord.validationErrors = formatIssues(all, normalizeSource(type));\n }\n } catch (hookError) {\n logDebug(`Error recording an Elysia error: ${String(hookError)}`);\n }\n });\n plugin.onStop(() => {\n flushTelemetry().catch((error: unknown) => {\n logWarning(`Error flushing Apitally telemetry: ${String(error)}`);\n });\n });\n\n return plugin;\n}\n\nfunction observeResponse(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 // A non-404 response without a recorded route matched a route registered\n // before the plugin was applied; the plugin cannot detect this at setup.\n if (observation.route === undefined && statusCode !== 404) {\n logWarning(\n \"A request matched a route registered before the Apitally plugin was applied, so it is exported without a route template and is not counted in the request metrics. To resolve this, apply the Apitally plugin immediately after creating the app, before registering routes.\",\n );\n }\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const shouldAddContentType = shouldAddBunErrorContentType(observation, responseHeaders);\n if (shouldAddContentType) {\n responseHeaders.set(\"content-type\", \"text/plain;charset=utf-8\");\n }\n if (observation.error !== undefined && statusCode >= 500) {\n context.with(observation.requestContext, () => captureException(observation.error));\n }\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 Elysia middleware: ${String(error)}`);\n });\n if (!shouldAddContentType) {\n return captured.response;\n }\n // Bun adds this header after .wrap(); retain it when replacing the body stream.\n return new Response(captured.response.body, {\n status: captured.response.status,\n statusText: captured.response.statusText,\n headers: responseHeaders,\n });\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally Elysia 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 Elysia middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction shouldAddBunErrorContentType(\n observation: ElysiaRequestObservation,\n responseHeaders: Headers,\n): boolean {\n if (\n typeof (process.versions as { bun?: unknown }).bun !== \"string\" ||\n responseHeaders.has(\"content-type\")\n ) {\n return false;\n }\n const errorCode = (observation.error as { code?: unknown } | undefined)?.code;\n return errorCode === \"NOT_FOUND\" || errorCode === \"VALIDATION\";\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 Elysia middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware.cjs","names":["resolvePackageVersion","resolveStartupPaths","isActivated","isWebSocketUpgrade","startWebRequestObservation","captureWebRequestBody","context","formatIssues","normalizeSource","captureException","captureWebResponse","getConfig","finalizeRequestObservation"],"sources":["../../src/elysia/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { AnyElysia } from \"elysia\";\nimport {\n activate,\n configure,\n flushTelemetry,\n isActivated,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { resolvePackageVersion } from \"../packageVersion.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, normalizeSource } from \"../validationErrors.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nconst INSTALL_MARKER = Symbol.for(\"apitally.elysiaInstall\");\nconst PLUGIN_NAME = \"apitally\";\nconst TRACER_NAME = \"apitally/elysia\";\n\ninterface ElysiaConstructor {\n new (config?: { name?: string }): AnyElysia;\n}\n\ntype ElysiaDispatcher = (this: unknown, ...args: unknown[]) => Response | Promise<Response>;\ntype ElysiaWrap = Parameters<AnyElysia[\"wrap\"]>[0];\n\ninterface ElysiaRequestObservation extends WebRequestObservation {\n requestContext: Context;\n requestBodyCompletion: Promise<void>;\n route?: string;\n error?: unknown;\n}\n\nexport function createElysiaPlugin(\n ElysiaClass: ElysiaConstructor,\n options?: ApitallyOptions,\n): AnyElysia {\n configure(options);\n const plugin = buildElysiaPlugin(ElysiaClass);\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => resolveStartupPaths(plugin),\n });\n return plugin;\n}\n\nexport function installElysiaIntegration(app: AnyElysia, 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 if (app.routes.length > 0) {\n logWarning(\n \"useApitally() was called after routes were registered on the Elysia app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes or plugins that contain routes.\",\n );\n return;\n }\n markedApp[INSTALL_MARKER] = true;\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n const ElysiaClass = app.constructor as ElysiaConstructor;\n app.use(buildElysiaPlugin(ElysiaClass));\n}\n\nfunction buildElysiaPlugin(ElysiaClass: ElysiaConstructor): AnyElysia {\n const observations = new WeakMap<Request, ElysiaRequestObservation>();\n const plugin = new ElysiaClass({ name: PLUGIN_NAME });\n let startedApp: AnyElysia | undefined;\n\n plugin.wrap(\n ((dispatcher: ElysiaDispatcher, request: Request) =>\n function (this: unknown, ...args: unknown[]): Response | Promise<Response> {\n let observation: ElysiaRequestObservation;\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return dispatcher.apply(this, args);\n }\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n // @elysiajs/node has no requestIP(); its srvx request carries the client address as ip.\n clientAddress:\n startedApp?.server?.requestIP(request)?.address ?? (request as { ip?: string }).ip,\n });\n observation = {\n ...started.observation,\n requestContext: started.requestContext,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally Elysia middleware: ${String(error)}`);\n return dispatcher.apply(this, args);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observation.requestContext, () => dispatcher.apply(this, args));\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 }) as unknown as ElysiaWrap,\n );\n\n plugin.onStart((app) => {\n startedApp = app;\n // Elysia's dynamic handler for aot: false never applies plugin wrap() functions.\n // @elysiajs/node passes its adapter instead of the app, and the adapter has no config.\n if (app.config?.aot === false) {\n logWarning(\n \"Apitally does not observe requests on an Elysia app with aot: false. To resolve this, remove the aot: false option.\",\n );\n }\n // Elysia runs onStart hooks once listen() has started the server.\n activate();\n });\n plugin.onTransform({ as: \"global\" }, ({ request, route }) => {\n try {\n const observation = observations.get(request);\n if (observation && typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n } catch (error) {\n logDebug(`Error recording the matched Elysia route: ${String(error)}`);\n }\n });\n plugin.onError({ as: \"global\" }, ({ request, route, error }) => {\n try {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n if (typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n observation.error = error;\n const { code, all, type } = error as { code?: unknown; all?: unknown; type?: unknown };\n if (code === \"VALIDATION\") {\n observation.requestRecord.validationErrors = formatIssues(all, normalizeSource(type));\n }\n } catch (hookError) {\n logDebug(`Error recording an Elysia error: ${String(hookError)}`);\n }\n });\n plugin.onStop(() => {\n flushTelemetry().catch((error: unknown) => {\n logWarning(`Error flushing Apitally telemetry: ${String(error)}`);\n });\n });\n\n return plugin;\n}\n\nfunction observeResponse(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 // A non-404 response without a recorded route matched a route registered\n // before the plugin was applied; the plugin cannot detect this at setup.\n if (observation.route === undefined && statusCode !== 404) {\n logWarning(\n \"A request matched a route registered before the Apitally plugin was applied, so it is exported without a route template and is not counted in the request metrics. To resolve this, apply the Apitally plugin immediately after creating the app, before registering routes.\",\n );\n }\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const shouldAddContentType = shouldAddBunErrorContentType(observation, responseHeaders);\n if (shouldAddContentType) {\n responseHeaders.set(\"content-type\", \"text/plain;charset=utf-8\");\n }\n if (observation.error !== undefined && statusCode >= 500) {\n context.with(observation.requestContext, () => captureException(observation.error));\n }\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 Elysia middleware: ${String(error)}`);\n });\n if (!shouldAddContentType) {\n return captured.response;\n }\n // Bun adds this header after .wrap(); retain it when replacing the body stream.\n return new Response(captured.response.body, {\n status: captured.response.status,\n statusText: captured.response.statusText,\n headers: responseHeaders,\n });\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally Elysia 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 Elysia middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction shouldAddBunErrorContentType(\n observation: ElysiaRequestObservation,\n responseHeaders: Headers,\n): boolean {\n if (\n typeof (process.versions as { bun?: unknown }).bun !== \"string\" ||\n responseHeaders.has(\"content-type\")\n ) {\n return false;\n }\n const errorCode = (observation.error as { code?: unknown } | undefined)?.code;\n return errorCode === \"NOT_FOUND\" || errorCode === \"VALIDATION\";\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 Elysia middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -11,18 +11,16 @@ import { context } from "@opentelemetry/api";
|
|
|
11
11
|
//#region src/elysia/middleware.ts
|
|
12
12
|
const INSTALL_MARKER = Symbol.for("apitally.elysiaInstall");
|
|
13
13
|
const PLUGIN_NAME = "apitally";
|
|
14
|
-
const TRACER_NAME = "apitally
|
|
14
|
+
const TRACER_NAME = "apitally/elysia";
|
|
15
15
|
function createElysiaPlugin(ElysiaClass, options) {
|
|
16
16
|
configure(options);
|
|
17
|
-
|
|
17
|
+
const plugin = buildElysiaPlugin(ElysiaClass);
|
|
18
18
|
registerStartupEventInfo({
|
|
19
19
|
framework: "elysia",
|
|
20
20
|
frameworkVersion: resolvePackageVersion("elysia"),
|
|
21
|
-
resolvePaths: () =>
|
|
22
|
-
});
|
|
23
|
-
return buildElysiaPlugin(ElysiaClass, (startedApp) => {
|
|
24
|
-
app = startedApp;
|
|
21
|
+
resolvePaths: () => resolveStartupPaths(plugin)
|
|
25
22
|
});
|
|
23
|
+
return plugin;
|
|
26
24
|
}
|
|
27
25
|
function installElysiaIntegration(app, options) {
|
|
28
26
|
configure(options);
|
|
@@ -41,7 +39,7 @@ function installElysiaIntegration(app, options) {
|
|
|
41
39
|
const ElysiaClass = app.constructor;
|
|
42
40
|
app.use(buildElysiaPlugin(ElysiaClass));
|
|
43
41
|
}
|
|
44
|
-
function buildElysiaPlugin(ElysiaClass
|
|
42
|
+
function buildElysiaPlugin(ElysiaClass) {
|
|
45
43
|
const observations = /* @__PURE__ */ new WeakMap();
|
|
46
44
|
const plugin = new ElysiaClass({ name: PLUGIN_NAME });
|
|
47
45
|
let startedApp;
|
|
@@ -53,7 +51,7 @@ function buildElysiaPlugin(ElysiaClass, recordStartedApp) {
|
|
|
53
51
|
const started = startWebRequestObservation({
|
|
54
52
|
request,
|
|
55
53
|
tracerName: TRACER_NAME,
|
|
56
|
-
clientAddress: startedApp?.server?.requestIP(request)?.address
|
|
54
|
+
clientAddress: startedApp?.server?.requestIP(request)?.address ?? request.ip
|
|
57
55
|
});
|
|
58
56
|
observation = {
|
|
59
57
|
...started.observation,
|
|
@@ -80,8 +78,8 @@ function buildElysiaPlugin(ElysiaClass, recordStartedApp) {
|
|
|
80
78
|
}));
|
|
81
79
|
plugin.onStart((app) => {
|
|
82
80
|
startedApp = app;
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
if (app.config?.aot === false) logWarning("Apitally does not observe requests on an Elysia app with aot: false. To resolve this, remove the aot: false option.");
|
|
82
|
+
activate();
|
|
85
83
|
});
|
|
86
84
|
plugin.onTransform({ as: "global" }, ({ request, route }) => {
|
|
87
85
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/elysia/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { AnyElysia } from \"elysia\";\nimport {\n activate,\n configure,\n flushTelemetry,\n isActivated,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { resolvePackageVersion } from \"../packageVersion.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, normalizeSource } from \"../validationErrors.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nconst INSTALL_MARKER = Symbol.for(\"apitally.elysiaInstall\");\nconst PLUGIN_NAME = \"apitally\";\nconst TRACER_NAME = \"apitally.elysia\";\n\ninterface ElysiaConstructor {\n new (config?: { name?: string }): AnyElysia;\n}\n\ntype ElysiaDispatcher = (this: unknown, ...args: unknown[]) => Response | Promise<Response>;\ntype ElysiaWrap = Parameters<AnyElysia[\"wrap\"]>[0];\n\ninterface ElysiaRequestObservation extends WebRequestObservation {\n requestContext: Context;\n requestBodyCompletion: Promise<void>;\n route?: string;\n error?: unknown;\n}\n\nexport function createElysiaPlugin(\n ElysiaClass: ElysiaConstructor,\n options?: ApitallyOptions,\n): AnyElysia {\n configure(options);\n let app: AnyElysia | undefined;\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => (app ? resolveStartupPaths(app) : []),\n });\n return buildElysiaPlugin(ElysiaClass, (startedApp) => {\n app = startedApp;\n });\n}\n\nexport function installElysiaIntegration(app: AnyElysia, 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 if (app.routes.length > 0) {\n logWarning(\n \"useApitally() was called after routes were registered on the Elysia app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes or plugins that contain routes.\",\n );\n return;\n }\n markedApp[INSTALL_MARKER] = true;\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n const ElysiaClass = app.constructor as ElysiaConstructor;\n app.use(buildElysiaPlugin(ElysiaClass));\n}\n\nfunction buildElysiaPlugin(\n ElysiaClass: ElysiaConstructor,\n recordStartedApp?: (app: AnyElysia) => void,\n): AnyElysia {\n const observations = new WeakMap<Request, ElysiaRequestObservation>();\n const plugin = new ElysiaClass({ name: PLUGIN_NAME });\n let startedApp: AnyElysia | undefined;\n\n plugin.wrap(\n ((dispatcher: ElysiaDispatcher, request: Request) =>\n function (this: unknown, ...args: unknown[]): Response | Promise<Response> {\n let observation: ElysiaRequestObservation;\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return dispatcher.apply(this, args);\n }\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n clientAddress: startedApp?.server?.requestIP(request)?.address,\n });\n observation = {\n ...started.observation,\n requestContext: started.requestContext,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally Elysia middleware: ${String(error)}`);\n return dispatcher.apply(this, args);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observation.requestContext, () => dispatcher.apply(this, args));\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 }) as unknown as ElysiaWrap,\n );\n\n plugin.onStart((app) => {\n startedApp = app;\n recordStartedApp?.(app);\n // Elysia's dynamic handler for aot: false never applies plugin wrap() functions.\n if (app.config.aot === false) {\n logWarning(\n \"Apitally does not observe requests on an Elysia app with aot: false. To resolve this, remove the aot: false option.\",\n );\n }\n });\n plugin.onTransform({ as: \"global\" }, ({ request, route }) => {\n try {\n const observation = observations.get(request);\n if (observation && typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n } catch (error) {\n logDebug(`Error recording the matched Elysia route: ${String(error)}`);\n }\n });\n plugin.onError({ as: \"global\" }, ({ request, route, error }) => {\n try {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n if (typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n observation.error = error;\n const { code, all, type } = error as { code?: unknown; all?: unknown; type?: unknown };\n if (code === \"VALIDATION\") {\n observation.requestRecord.validationErrors = formatIssues(all, normalizeSource(type));\n }\n } catch (hookError) {\n logDebug(`Error recording an Elysia error: ${String(hookError)}`);\n }\n });\n plugin.onStop(() => {\n flushTelemetry().catch((error: unknown) => {\n logWarning(`Error flushing Apitally telemetry: ${String(error)}`);\n });\n });\n\n return plugin;\n}\n\nfunction observeResponse(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 // A non-404 response without a recorded route matched a route registered\n // before the plugin was applied; the plugin cannot detect this at setup.\n if (observation.route === undefined && statusCode !== 404) {\n logWarning(\n \"A request matched a route registered before the Apitally plugin was applied, so it is exported without a route template and is not counted in the request metrics. To resolve this, apply the Apitally plugin immediately after creating the app, before registering routes.\",\n );\n }\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const shouldAddContentType = shouldAddBunErrorContentType(observation, responseHeaders);\n if (shouldAddContentType) {\n responseHeaders.set(\"content-type\", \"text/plain;charset=utf-8\");\n }\n if (observation.error !== undefined && statusCode >= 500) {\n context.with(observation.requestContext, () => captureException(observation.error));\n }\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 Elysia middleware: ${String(error)}`);\n });\n if (!shouldAddContentType) {\n return captured.response;\n }\n // Bun adds this header after .wrap(); retain it when replacing the body stream.\n return new Response(captured.response.body, {\n status: captured.response.status,\n statusText: captured.response.statusText,\n headers: responseHeaders,\n });\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally Elysia 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 Elysia middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction shouldAddBunErrorContentType(\n observation: ElysiaRequestObservation,\n responseHeaders: Headers,\n): boolean {\n if (\n typeof (process.versions as { bun?: unknown }).bun !== \"string\" ||\n responseHeaders.has(\"content-type\")\n ) {\n return false;\n }\n const errorCode = (observation.error as { code?: unknown } | undefined)?.code;\n return errorCode === \"NOT_FOUND\" || errorCode === \"VALIDATION\";\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 Elysia middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/elysia/middleware.ts"],"sourcesContent":["import { type Context, context } from \"@opentelemetry/api\";\nimport type { AnyElysia } from \"elysia\";\nimport {\n activate,\n configure,\n flushTelemetry,\n isActivated,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logDebug, logWarning } from \"../logger.js\";\nimport { resolvePackageVersion } from \"../packageVersion.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, normalizeSource } from \"../validationErrors.js\";\nimport { resolveStartupPaths } from \"./routes.js\";\n\nconst INSTALL_MARKER = Symbol.for(\"apitally.elysiaInstall\");\nconst PLUGIN_NAME = \"apitally\";\nconst TRACER_NAME = \"apitally/elysia\";\n\ninterface ElysiaConstructor {\n new (config?: { name?: string }): AnyElysia;\n}\n\ntype ElysiaDispatcher = (this: unknown, ...args: unknown[]) => Response | Promise<Response>;\ntype ElysiaWrap = Parameters<AnyElysia[\"wrap\"]>[0];\n\ninterface ElysiaRequestObservation extends WebRequestObservation {\n requestContext: Context;\n requestBodyCompletion: Promise<void>;\n route?: string;\n error?: unknown;\n}\n\nexport function createElysiaPlugin(\n ElysiaClass: ElysiaConstructor,\n options?: ApitallyOptions,\n): AnyElysia {\n configure(options);\n const plugin = buildElysiaPlugin(ElysiaClass);\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => resolveStartupPaths(plugin),\n });\n return plugin;\n}\n\nexport function installElysiaIntegration(app: AnyElysia, 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 if (app.routes.length > 0) {\n logWarning(\n \"useApitally() was called after routes were registered on the Elysia app, so Apitally was not installed. To resolve this, call useApitally() immediately after creating the app, before registering routes or plugins that contain routes.\",\n );\n return;\n }\n markedApp[INSTALL_MARKER] = true;\n registerStartupEventInfo({\n framework: \"elysia\",\n frameworkVersion: resolvePackageVersion(\"elysia\"),\n resolvePaths: () => resolveStartupPaths(app),\n });\n const ElysiaClass = app.constructor as ElysiaConstructor;\n app.use(buildElysiaPlugin(ElysiaClass));\n}\n\nfunction buildElysiaPlugin(ElysiaClass: ElysiaConstructor): AnyElysia {\n const observations = new WeakMap<Request, ElysiaRequestObservation>();\n const plugin = new ElysiaClass({ name: PLUGIN_NAME });\n let startedApp: AnyElysia | undefined;\n\n plugin.wrap(\n ((dispatcher: ElysiaDispatcher, request: Request) =>\n function (this: unknown, ...args: unknown[]): Response | Promise<Response> {\n let observation: ElysiaRequestObservation;\n try {\n activate();\n if (!isActivated() || isWebSocketUpgrade(request)) {\n return dispatcher.apply(this, args);\n }\n const started = startWebRequestObservation({\n request,\n tracerName: TRACER_NAME,\n // @elysiajs/node has no requestIP(); its srvx request carries the client address as ip.\n clientAddress:\n startedApp?.server?.requestIP(request)?.address ?? (request as { ip?: string }).ip,\n });\n observation = {\n ...started.observation,\n requestContext: started.requestContext,\n requestBodyCompletion: captureWebRequestBody(\n request,\n started.observation.requestBodyCapture,\n ),\n };\n observations.set(request, observation);\n } catch (error) {\n logWarning(`Error in the Apitally Elysia middleware: ${String(error)}`);\n return dispatcher.apply(this, args);\n }\n\n let result: Response | Promise<Response>;\n try {\n result = context.with(observation.requestContext, () => dispatcher.apply(this, args));\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 }) as unknown as ElysiaWrap,\n );\n\n plugin.onStart((app) => {\n startedApp = app;\n // Elysia's dynamic handler for aot: false never applies plugin wrap() functions.\n // @elysiajs/node passes its adapter instead of the app, and the adapter has no config.\n if (app.config?.aot === false) {\n logWarning(\n \"Apitally does not observe requests on an Elysia app with aot: false. To resolve this, remove the aot: false option.\",\n );\n }\n // Elysia runs onStart hooks once listen() has started the server.\n activate();\n });\n plugin.onTransform({ as: \"global\" }, ({ request, route }) => {\n try {\n const observation = observations.get(request);\n if (observation && typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n } catch (error) {\n logDebug(`Error recording the matched Elysia route: ${String(error)}`);\n }\n });\n plugin.onError({ as: \"global\" }, ({ request, route, error }) => {\n try {\n const observation = observations.get(request);\n if (!observation) {\n return;\n }\n if (typeof route === \"string\" && route.length > 0) {\n observation.route = route;\n }\n observation.error = error;\n const { code, all, type } = error as { code?: unknown; all?: unknown; type?: unknown };\n if (code === \"VALIDATION\") {\n observation.requestRecord.validationErrors = formatIssues(all, normalizeSource(type));\n }\n } catch (hookError) {\n logDebug(`Error recording an Elysia error: ${String(hookError)}`);\n }\n });\n plugin.onStop(() => {\n flushTelemetry().catch((error: unknown) => {\n logWarning(`Error flushing Apitally telemetry: ${String(error)}`);\n });\n });\n\n return plugin;\n}\n\nfunction observeResponse(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 // A non-404 response without a recorded route matched a route registered\n // before the plugin was applied; the plugin cannot detect this at setup.\n if (observation.route === undefined && statusCode !== 404) {\n logWarning(\n \"A request matched a route registered before the Apitally plugin was applied, so it is exported without a route template and is not counted in the request metrics. To resolve this, apply the Apitally plugin immediately after creating the app, before registering routes.\",\n );\n }\n let responseHeaders = response.headers;\n try {\n responseHeaders = new Headers(response.headers);\n const shouldAddContentType = shouldAddBunErrorContentType(observation, responseHeaders);\n if (shouldAddContentType) {\n responseHeaders.set(\"content-type\", \"text/plain;charset=utf-8\");\n }\n if (observation.error !== undefined && statusCode >= 500) {\n context.with(observation.requestContext, () => captureException(observation.error));\n }\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 Elysia middleware: ${String(error)}`);\n });\n if (!shouldAddContentType) {\n return captured.response;\n }\n // Bun adds this header after .wrap(); retain it when replacing the body stream.\n return new Response(captured.response.body, {\n status: captured.response.status,\n statusText: captured.response.statusText,\n headers: responseHeaders,\n });\n } catch (error) {\n const completedAtMillis = performance.now();\n logWarning(`Error in the Apitally Elysia 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 Elysia middleware: ${String(completionError)}`);\n });\n return response;\n }\n}\n\nfunction shouldAddBunErrorContentType(\n observation: ElysiaRequestObservation,\n responseHeaders: Headers,\n): boolean {\n if (\n typeof (process.versions as { bun?: unknown }).bun !== \"string\" ||\n responseHeaders.has(\"content-type\")\n ) {\n return false;\n }\n const errorCode = (observation.error as { code?: unknown } | undefined)?.code;\n return errorCode === \"NOT_FOUND\" || errorCode === \"VALIDATION\";\n}\n\nfunction finalizeFailedRequestObservation(\n observations: WeakMap<Request, ElysiaRequestObservation>,\n request: Request,\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 Elysia middleware: ${String(finalizeError)}`);\n }\n}\n"],"mappings":"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"}
|
package/dist/elysia/routes.cjs
CHANGED
|
@@ -2,8 +2,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
//#region src/elysia/routes.ts
|
|
3
3
|
const WEBSOCKET_METHODS = /* @__PURE__ */ new Set(["WS", "$INTERNALWS"]);
|
|
4
4
|
function resolveStartupPaths(app) {
|
|
5
|
+
const routes = app.getGlobalRoutes();
|
|
5
6
|
const paths = [];
|
|
6
|
-
for (const route of
|
|
7
|
+
for (const route of routes) if (typeof route.method === "string" && typeof route.path === "string" && !WEBSOCKET_METHODS.has(route.method.toUpperCase())) paths.push({
|
|
7
8
|
method: route.method,
|
|
8
9
|
path: route.path
|
|
9
10
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.cjs","names":[],"sources":["../../src/elysia/routes.ts"],"sourcesContent":["import type { AnyElysia } from \"elysia\";\nimport type { RoutePath } from \"../startup.js\";\n\nconst WEBSOCKET_METHODS = new Set([\"WS\", \"$INTERNALWS\"]);\n\nexport function resolveStartupPaths(app: AnyElysia): RoutePath[] {\n const paths: RoutePath[] = [];\n for (const route of
|
|
1
|
+
{"version":3,"file":"routes.cjs","names":[],"sources":["../../src/elysia/routes.ts"],"sourcesContent":["import type { AnyElysia } from \"elysia\";\nimport type { RoutePath } from \"../startup.js\";\n\nconst WEBSOCKET_METHODS = new Set([\"WS\", \"$INTERNALWS\"]);\n\n// For a plugin, Elysia's getGlobalRoutes() returns the routes of the app it is\n// mounted on. The official @elysiajs/openapi plugin relies on the same method.\nexport function resolveStartupPaths(app: AnyElysia): RoutePath[] {\n const routes = (app as unknown as { getGlobalRoutes(): AnyElysia[\"routes\"] }).getGlobalRoutes();\n const paths: RoutePath[] = [];\n for (const route of routes) {\n if (\n typeof route.method === \"string\" &&\n typeof route.path === \"string\" &&\n !WEBSOCKET_METHODS.has(route.method.toUpperCase())\n ) {\n paths.push({ method: route.method, path: route.path });\n }\n }\n return paths;\n}\n"],"mappings":";;AAGA,MAAM,oCAAoB,IAAI,IAAI,CAAC,MAAM,aAAa,CAAC;AAIvD,SAAgB,oBAAoB,KAA6B;CAC/D,MAAM,SAAU,IAA8D,gBAAgB;CAC9F,MAAM,QAAqB,CAAC;CAC5B,KAAK,MAAM,SAAS,QAClB,IACE,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,SAAS,YACtB,CAAC,kBAAkB,IAAI,MAAM,OAAO,YAAY,CAAC,GAEjD,MAAM,KAAK;EAAE,QAAQ,MAAM;EAAQ,MAAM,MAAM;CAAK,CAAC;CAGzD,OAAO;AACT"}
|
package/dist/elysia/routes.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
//#region src/elysia/routes.ts
|
|
2
2
|
const WEBSOCKET_METHODS = /* @__PURE__ */ new Set(["WS", "$INTERNALWS"]);
|
|
3
3
|
function resolveStartupPaths(app) {
|
|
4
|
+
const routes = app.getGlobalRoutes();
|
|
4
5
|
const paths = [];
|
|
5
|
-
for (const route of
|
|
6
|
+
for (const route of routes) if (typeof route.method === "string" && typeof route.path === "string" && !WEBSOCKET_METHODS.has(route.method.toUpperCase())) paths.push({
|
|
6
7
|
method: route.method,
|
|
7
8
|
path: route.path
|
|
8
9
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","names":[],"sources":["../../src/elysia/routes.ts"],"sourcesContent":["import type { AnyElysia } from \"elysia\";\nimport type { RoutePath } from \"../startup.js\";\n\nconst WEBSOCKET_METHODS = new Set([\"WS\", \"$INTERNALWS\"]);\n\nexport function resolveStartupPaths(app: AnyElysia): RoutePath[] {\n const paths: RoutePath[] = [];\n for (const route of
|
|
1
|
+
{"version":3,"file":"routes.js","names":[],"sources":["../../src/elysia/routes.ts"],"sourcesContent":["import type { AnyElysia } from \"elysia\";\nimport type { RoutePath } from \"../startup.js\";\n\nconst WEBSOCKET_METHODS = new Set([\"WS\", \"$INTERNALWS\"]);\n\n// For a plugin, Elysia's getGlobalRoutes() returns the routes of the app it is\n// mounted on. The official @elysiajs/openapi plugin relies on the same method.\nexport function resolveStartupPaths(app: AnyElysia): RoutePath[] {\n const routes = (app as unknown as { getGlobalRoutes(): AnyElysia[\"routes\"] }).getGlobalRoutes();\n const paths: RoutePath[] = [];\n for (const route of routes) {\n if (\n typeof route.method === \"string\" &&\n typeof route.path === \"string\" &&\n !WEBSOCKET_METHODS.has(route.method.toUpperCase())\n ) {\n paths.push({ method: route.method, path: route.path });\n }\n }\n return paths;\n}\n"],"mappings":";AAGA,MAAM,oCAAoB,IAAI,IAAI,CAAC,MAAM,aAAa,CAAC;AAIvD,SAAgB,oBAAoB,KAA6B;CAC/D,MAAM,SAAU,IAA8D,gBAAgB;CAC9F,MAAM,QAAqB,CAAC;CAC5B,KAAK,MAAM,SAAS,QAClB,IACE,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,SAAS,YACtB,CAAC,kBAAkB,IAAI,MAAM,OAAO,YAAY,CAAC,GAEjD,MAAM,KAAK;EAAE,QAAQ,MAAM;EAAQ,MAAM,MAAM;CAAK,CAAC;CAGzD,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["resolvePackageVersion"],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installExpressIntegration } from \"./install.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: Express, options?: ApitallyOptions): void {\n installExpressIntegration(app, options, {\n framework: \"express\",\n frameworkVersion: resolvePackageVersion(\"express\"),\n });\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["resolvePackageVersion"],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installExpressIntegration } from \"./install.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: Express, options?: ApitallyOptions): void {\n installExpressIntegration(app, options, {\n framework: \"express\",\n frameworkVersion: resolvePackageVersion(\"express\"),\n });\n}\n"],"mappings":";;;;AASA,SAAgB,YAAY,KAAc,SAAiC;CACzE,wBAAA,0BAA0B,KAAK,SAAS;EACtC,WAAW;EACX,kBAAkBA,uBAAAA,sBAAsB,SAAS;CACnD,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installExpressIntegration } from \"./install.js\";\n\nexport type { ApitallyOptions };\n\n// Setup stays synchronous; activation begins on the first request.\nexport function useApitally(app: Express, options?: ApitallyOptions): void {\n installExpressIntegration(app, options, {\n framework: \"express\",\n frameworkVersion: resolvePackageVersion(\"express\"),\n });\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { resolvePackageVersion } from \"../packageVersion.js\";\nimport { installExpressIntegration } from \"./install.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: Express, options?: ApitallyOptions): void {\n installExpressIntegration(app, options, {\n framework: \"express\",\n frameworkVersion: resolvePackageVersion(\"express\"),\n });\n}\n"],"mappings":";;;AASA,SAAgB,YAAY,KAAc,SAAiC;CACzE,0BAA0B,KAAK,SAAS;EACtC,WAAW;EACX,kBAAkB,sBAAsB,SAAS;CACnD,CAAC;AACH"}
|
package/dist/express/install.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const require_express_middleware = require("./middleware.cjs");
|
|
|
6
6
|
//#region src/express/install.ts
|
|
7
7
|
function installExpressIntegration(app, options, frameworkInfo) {
|
|
8
8
|
require_activation.configure(options);
|
|
9
|
+
require_activation.activateWhenHttpServerListens();
|
|
9
10
|
require_activation.registerStartupEventInfo({
|
|
10
11
|
...frameworkInfo,
|
|
11
12
|
resolvePaths: () => require_express_routes.resolveStartupPaths(app)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.cjs","names":["resolveStartupPaths"],"sources":["../../src/express/install.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport {
|
|
1
|
+
{"version":3,"file":"install.cjs","names":["resolveStartupPaths"],"sources":["../../src/express/install.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport {\n activateWhenHttpServerListens,\n configure,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { logDebug } from \"../logger.js\";\nimport type { StartupEventInfo } from \"../startup.js\";\nimport { wrapAppHandle } from \"./middleware.js\";\nimport { installRouteCaptureFromApp, resolveStartupPaths } from \"./routes.js\";\n\nexport function installExpressIntegration(\n app: Express,\n options: ApitallyOptions | undefined,\n frameworkInfo: Pick<StartupEventInfo, \"framework\" | \"frameworkVersion\">,\n): void {\n configure(options);\n activateWhenHttpServerListens();\n registerStartupEventInfo({\n ...frameworkInfo,\n resolvePaths: () => resolveStartupPaths(app),\n });\n try {\n installRouteCaptureFromApp(app);\n } catch (error) {\n logDebug(`Error installing the express route capture: ${String(error)}`);\n }\n wrapAppHandle(app);\n}\n"],"mappings":";;;;;;AAYA,SAAgB,0BACd,KACA,SACA,eACM;CACN,mBAAA,UAAU,OAAO;CACjB,mBAAA,8BAA8B;CAC9B,mBAAA,yBAAyB;EACvB,GAAG;EACH,oBAAoBA,uBAAAA,oBAAoB,GAAG;CAC7C,CAAC;CACD,IAAI;EACF,uBAAA,2BAA2B,GAAG;CAChC,SAAS,OAAO;EACd,eAAA,SAAS,+CAA+C,OAAO,KAAK,GAAG;CACzE;CACA,2BAAA,cAAc,GAAG;AACnB"}
|
package/dist/express/install.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { logDebug } from "../logger.js";
|
|
2
|
-
import { configure, registerStartupEventInfo } from "../activation.js";
|
|
2
|
+
import { activateWhenHttpServerListens, configure, registerStartupEventInfo } from "../activation.js";
|
|
3
3
|
import { installRouteCaptureFromApp, resolveStartupPaths } from "./routes.js";
|
|
4
4
|
import { wrapAppHandle } from "./middleware.js";
|
|
5
5
|
//#region src/express/install.ts
|
|
6
6
|
function installExpressIntegration(app, options, frameworkInfo) {
|
|
7
7
|
configure(options);
|
|
8
|
+
activateWhenHttpServerListens();
|
|
8
9
|
registerStartupEventInfo({
|
|
9
10
|
...frameworkInfo,
|
|
10
11
|
resolvePaths: () => resolveStartupPaths(app)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","names":[],"sources":["../../src/express/install.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport {
|
|
1
|
+
{"version":3,"file":"install.js","names":[],"sources":["../../src/express/install.ts"],"sourcesContent":["import type { Express } from \"express\";\nimport {\n activateWhenHttpServerListens,\n configure,\n registerStartupEventInfo,\n} from \"../activation.js\";\nimport type { ApitallyOptions } from \"../config.js\";\nimport { logDebug } from \"../logger.js\";\nimport type { StartupEventInfo } from \"../startup.js\";\nimport { wrapAppHandle } from \"./middleware.js\";\nimport { installRouteCaptureFromApp, resolveStartupPaths } from \"./routes.js\";\n\nexport function installExpressIntegration(\n app: Express,\n options: ApitallyOptions | undefined,\n frameworkInfo: Pick<StartupEventInfo, \"framework\" | \"frameworkVersion\">,\n): void {\n configure(options);\n activateWhenHttpServerListens();\n registerStartupEventInfo({\n ...frameworkInfo,\n resolvePaths: () => resolveStartupPaths(app),\n });\n try {\n installRouteCaptureFromApp(app);\n } catch (error) {\n logDebug(`Error installing the express route capture: ${String(error)}`);\n }\n wrapAppHandle(app);\n}\n"],"mappings":";;;;;AAYA,SAAgB,0BACd,KACA,SACA,eACM;CACN,UAAU,OAAO;CACjB,8BAA8B;CAC9B,yBAAyB;EACvB,GAAG;EACH,oBAAoB,oBAAoB,GAAG;CAC7C,CAAC;CACD,IAAI;EACF,2BAA2B,GAAG;CAChC,SAAS,OAAO;EACd,SAAS,+CAA+C,OAAO,KAAK,GAAG;CACzE;CACA,cAAc,GAAG;AACnB"}
|
|
@@ -10,7 +10,7 @@ const require_express_routes = require("./routes.cjs");
|
|
|
10
10
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
11
11
|
//#region src/express/middleware.ts
|
|
12
12
|
const HANDLE_WRAP_MARKER = Symbol.for("apitally.expressHandleWrap");
|
|
13
|
-
const TRACER_NAME = "apitally
|
|
13
|
+
const TRACER_NAME = "apitally/express";
|
|
14
14
|
function wrapAppHandle(app) {
|
|
15
15
|
const markedApp = app;
|
|
16
16
|
if (markedApp[HANDLE_WRAP_MARKER] === true) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.cjs","names":["resolveErrorStatus","context","isActivated","startNodeRequestObservation","getConfig","isValidationResponseStatus","finishRouteTracking","extractExpressValidationErrors","parseJsonResponseBody"],"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type Context, context } from \"@opentelemetry/api\";\nimport type { Express, NextFunction, Request, Response } from \"express\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n type NodeResponseCompletion,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport {\n extractExpressValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { beginRouteTracking, finishRouteTracking } from \"./routes.js\";\n\nconst HANDLE_WRAP_MARKER = Symbol.for(\"apitally.expressHandleWrap\");\nconst TRACER_NAME = \"apitally
|
|
1
|
+
{"version":3,"file":"middleware.cjs","names":["resolveErrorStatus","context","isActivated","startNodeRequestObservation","getConfig","isValidationResponseStatus","finishRouteTracking","extractExpressValidationErrors","parseJsonResponseBody"],"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type Context, context } from \"@opentelemetry/api\";\nimport type { Express, NextFunction, Request, Response } from \"express\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n type NodeResponseCompletion,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport {\n extractExpressValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { beginRouteTracking, finishRouteTracking } from \"./routes.js\";\n\nconst HANDLE_WRAP_MARKER = Symbol.for(\"apitally.expressHandleWrap\");\nconst TRACER_NAME = \"apitally/express\";\n\ntype HandleFunction = (\n req: IncomingMessage,\n res: ServerResponse,\n callback?: (error?: unknown) => void,\n) => unknown;\n\n// Wrapping app.handle observes unmatched routes and error responses regardless\n// of middleware order. The marker prevents duplicate observation.\nexport function wrapAppHandle(app: Express): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[HANDLE_WRAP_MARKER] === true) {\n return;\n }\n markedApp[HANDLE_WRAP_MARKER] = true;\n const originalHandle = (app as unknown as { handle: HandleFunction }).handle;\n let errorMiddlewareAppended = false;\n const appendErrorMiddlewareOnce = () => {\n if (errorMiddlewareAppended) {\n return;\n }\n errorMiddlewareAppended = true;\n // Appended on the first request so it sits below every handler the app\n // registered at setup; it records the exception and always passes it on.\n app.use((error: unknown, _req: Request, _res: Response, next: NextFunction) => {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n captureException(error);\n }\n next(error);\n });\n };\n (app as unknown as { handle: HandleFunction }).handle = function (\n this: unknown,\n req: IncomingMessage,\n res: ServerResponse,\n callback?: (error?: unknown) => void,\n ): unknown {\n let requestContext: Context | undefined;\n try {\n requestContext = observeRequest(req, res, appendErrorMiddlewareOnce);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!requestContext) {\n return originalHandle.call(this, req, res, callback);\n }\n return context.with(requestContext, () => originalHandle.call(this, req, res, callback));\n };\n}\n\ninterface ExpressRequestObservation extends NodeRequestObservation {\n request: IncomingMessage;\n response: ServerResponse;\n}\n\n// Request setup precedes Express middleware so span, body, response, and route\n// observation share one context.\nfunction observeRequest(\n request: IncomingMessage,\n response: ServerResponse,\n appendErrorMiddlewareOnce: () => void,\n): Context | undefined {\n activate();\n if (!isActivated()) {\n return undefined;\n }\n appendErrorMiddlewareOnce();\n registerServerCloseFlush(request);\n const started = startNodeRequestObservation({ request, tracerName: TRACER_NAME });\n context.bind(started.requestContext, request);\n context.bind(started.requestContext, response);\n beginRouteTracking(request);\n const observation: ExpressRequestObservation = {\n ...started.observation,\n request,\n response,\n };\n const shouldCaptureBody =\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined;\n captureNodeResponse(\n response,\n (statusCode) => shouldCaptureBody || isValidationResponseStatus(statusCode),\n observation.requestBodyCapture,\n )\n .then((completion) => finalizeRequestFromResponse(observation, completion))\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return started.requestContext;\n}\n\nfunction finalizeRequestFromResponse(\n observation: ExpressRequestObservation,\n completion: NodeResponseCompletion,\n): void {\n const { request, response } = observation;\n const routeResult = finishRouteTracking(request, observation.requestUrl.split(\"?\")[0]);\n if (routeResult.matchedUncapturedRegistration) {\n logWarning(\n 'Some requests matched routes that Apitally did not capture at registration time. These requests are exported without a route template and are not counted in the request metrics. To resolve this, add `import \"apitally/express/register\";` as the first line of your application\\'s entry module.',\n );\n }\n if (isValidationResponseStatus(response.statusCode)) {\n observation.requestRecord.validationErrors ??= extractExpressValidationErrors(\n parseJsonResponseBody(completion.body, response.getHeader(\"content-encoding\")),\n );\n }\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: response.statusCode,\n route: routeResult.route,\n clientAddress: (request as Request).ip,\n requestHeaders: request.headers,\n responseHeaders: response.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n}\n"],"mappings":";;;;;;;;;;;AAsBA,MAAM,qBAAqB,OAAO,IAAI,4BAA4B;AAClE,MAAM,cAAc;AAUpB,SAAgB,cAAc,KAAoB;CAChD,MAAM,YAAY;CAClB,IAAI,UAAU,wBAAwB,MACpC;CAEF,UAAU,sBAAsB;CAChC,MAAM,iBAAkB,IAA8C;CACtE,IAAI,0BAA0B;CAC9B,MAAM,kCAAkC;EACtC,IAAI,yBACF;EAEF,0BAA0B;EAG1B,IAAI,KAAK,OAAgB,MAAe,MAAgB,SAAuB;GAC7E,MAAM,SAASA,mBAAAA,mBAAmB,KAAK;GACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,mBAAA,iBAAiB,KAAK;GAExB,KAAK,KAAK;EACZ,CAAC;CACH;CACA,IAA+C,SAAS,SAEtD,KACA,KACA,UACS;EACT,IAAI;EACJ,IAAI;GACF,iBAAiB,eAAe,KAAK,KAAK,yBAAyB;EACrE,SAAS,OAAO;GACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,gBACH,OAAO,eAAe,KAAK,MAAM,KAAK,KAAK,QAAQ;EAErD,OAAOC,mBAAAA,QAAQ,KAAK,sBAAsB,eAAe,KAAK,MAAM,KAAK,KAAK,QAAQ,CAAC;CACzF;AACF;AASA,SAAS,eACP,SACA,UACA,2BACqB;CACrB,mBAAA,SAAS;CACT,IAAI,CAACC,mBAAAA,YAAY,GACf;CAEF,0BAA0B;CAC1B,+BAAA,yBAAyB,OAAO;CAChC,MAAM,UAAUC,+BAAAA,4BAA4B;EAAE;EAAS,YAAY;CAAY,CAAC;CAChF,mBAAA,QAAQ,KAAK,QAAQ,gBAAgB,OAAO;CAC5C,mBAAA,QAAQ,KAAK,QAAQ,gBAAgB,QAAQ;CAC7C,uBAAA,mBAAmB,OAAO;CAC1B,MAAM,cAAyC;EAC7C,GAAG,QAAQ;EACX;EACA;CACF;CACA,MAAM,oBACJC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA;CAC9E,+BAAA,oBACE,WACC,eAAe,qBAAqBC,yBAAAA,2BAA2B,UAAU,GAC1E,YAAY,kBACd,CAAC,CACE,MAAM,eAAe,4BAA4B,aAAa,UAAU,CAAC,CAAC,CAC1E,OAAO,UAAmB;EACzB,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;CACjE,CAAC;CACH,OAAO,QAAQ;AACjB;AAEA,SAAS,4BACP,aACA,YACM;CACN,MAAM,EAAE,SAAS,aAAa;CAC9B,MAAM,cAAcC,uBAAAA,oBAAoB,SAAS,YAAY,WAAW,MAAM,GAAG,CAAC,CAAC,EAAE;CACrF,IAAI,YAAY,+BACd,eAAA,WACE,sSACF;CAEF,IAAID,yBAAAA,2BAA2B,SAAS,UAAU,GAChD,YAAY,cAAc,qBAAqBE,yBAAAA,+BAC7CC,yBAAAA,sBAAsB,WAAW,MAAM,SAAS,UAAU,kBAAkB,CAAC,CAC/E;CAEF,2BAAA,2BAA2B;EACzB;EACA,mBAAmB,WAAW;EAC9B,YAAY,SAAS;EACrB,OAAO,YAAY;EACnB,eAAgB,QAAoB;EACpC,gBAAgB,QAAQ;EACxB,iBAAiB,SAAS,WAAW;EACrC,qBAAqB,WAAW;EAChC,sBAAsB;CACxB,CAAC;AACH"}
|
|
@@ -9,7 +9,7 @@ import { beginRouteTracking, finishRouteTracking } from "./routes.js";
|
|
|
9
9
|
import { context } from "@opentelemetry/api";
|
|
10
10
|
//#region src/express/middleware.ts
|
|
11
11
|
const HANDLE_WRAP_MARKER = Symbol.for("apitally.expressHandleWrap");
|
|
12
|
-
const TRACER_NAME = "apitally
|
|
12
|
+
const TRACER_NAME = "apitally/express";
|
|
13
13
|
function wrapAppHandle(app) {
|
|
14
14
|
const markedApp = app;
|
|
15
15
|
if (markedApp[HANDLE_WRAP_MARKER] === true) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type Context, context } from \"@opentelemetry/api\";\nimport type { Express, NextFunction, Request, Response } from \"express\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n type NodeResponseCompletion,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport {\n extractExpressValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { beginRouteTracking, finishRouteTracking } from \"./routes.js\";\n\nconst HANDLE_WRAP_MARKER = Symbol.for(\"apitally.expressHandleWrap\");\nconst TRACER_NAME = \"apitally
|
|
1
|
+
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/express/middleware.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type Context, context } from \"@opentelemetry/api\";\nimport type { Express, NextFunction, Request, Response } from \"express\";\nimport { activate, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException, resolveErrorStatus } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type NodeRequestObservation,\n type NodeResponseCompletion,\n registerServerCloseFlush,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport {\n extractExpressValidationErrors,\n isValidationResponseStatus,\n parseJsonResponseBody,\n} from \"../validationErrors.js\";\nimport { beginRouteTracking, finishRouteTracking } from \"./routes.js\";\n\nconst HANDLE_WRAP_MARKER = Symbol.for(\"apitally.expressHandleWrap\");\nconst TRACER_NAME = \"apitally/express\";\n\ntype HandleFunction = (\n req: IncomingMessage,\n res: ServerResponse,\n callback?: (error?: unknown) => void,\n) => unknown;\n\n// Wrapping app.handle observes unmatched routes and error responses regardless\n// of middleware order. The marker prevents duplicate observation.\nexport function wrapAppHandle(app: Express): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[HANDLE_WRAP_MARKER] === true) {\n return;\n }\n markedApp[HANDLE_WRAP_MARKER] = true;\n const originalHandle = (app as unknown as { handle: HandleFunction }).handle;\n let errorMiddlewareAppended = false;\n const appendErrorMiddlewareOnce = () => {\n if (errorMiddlewareAppended) {\n return;\n }\n errorMiddlewareAppended = true;\n // Appended on the first request so it sits below every handler the app\n // registered at setup; it records the exception and always passes it on.\n app.use((error: unknown, _req: Request, _res: Response, next: NextFunction) => {\n const status = resolveErrorStatus(error);\n if (status === undefined || status >= 500) {\n captureException(error);\n }\n next(error);\n });\n };\n (app as unknown as { handle: HandleFunction }).handle = function (\n this: unknown,\n req: IncomingMessage,\n res: ServerResponse,\n callback?: (error?: unknown) => void,\n ): unknown {\n let requestContext: Context | undefined;\n try {\n requestContext = observeRequest(req, res, appendErrorMiddlewareOnce);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n }\n if (!requestContext) {\n return originalHandle.call(this, req, res, callback);\n }\n return context.with(requestContext, () => originalHandle.call(this, req, res, callback));\n };\n}\n\ninterface ExpressRequestObservation extends NodeRequestObservation {\n request: IncomingMessage;\n response: ServerResponse;\n}\n\n// Request setup precedes Express middleware so span, body, response, and route\n// observation share one context.\nfunction observeRequest(\n request: IncomingMessage,\n response: ServerResponse,\n appendErrorMiddlewareOnce: () => void,\n): Context | undefined {\n activate();\n if (!isActivated()) {\n return undefined;\n }\n appendErrorMiddlewareOnce();\n registerServerCloseFlush(request);\n const started = startNodeRequestObservation({ request, tracerName: TRACER_NAME });\n context.bind(started.requestContext, request);\n context.bind(started.requestContext, response);\n beginRouteTracking(request);\n const observation: ExpressRequestObservation = {\n ...started.observation,\n request,\n response,\n };\n const shouldCaptureBody =\n getConfig().captureResponseBody && observation.requestRecord.dropReason === undefined;\n captureNodeResponse(\n response,\n (statusCode) => shouldCaptureBody || isValidationResponseStatus(statusCode),\n observation.requestBodyCapture,\n )\n .then((completion) => finalizeRequestFromResponse(observation, completion))\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n return started.requestContext;\n}\n\nfunction finalizeRequestFromResponse(\n observation: ExpressRequestObservation,\n completion: NodeResponseCompletion,\n): void {\n const { request, response } = observation;\n const routeResult = finishRouteTracking(request, observation.requestUrl.split(\"?\")[0]);\n if (routeResult.matchedUncapturedRegistration) {\n logWarning(\n 'Some requests matched routes that Apitally did not capture at registration time. These requests are exported without a route template and are not counted in the request metrics. To resolve this, add `import \"apitally/express/register\";` as the first line of your application\\'s entry module.',\n );\n }\n if (isValidationResponseStatus(response.statusCode)) {\n observation.requestRecord.validationErrors ??= extractExpressValidationErrors(\n parseJsonResponseBody(completion.body, response.getHeader(\"content-encoding\")),\n );\n }\n finalizeRequestObservation({\n observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: response.statusCode,\n route: routeResult.route,\n clientAddress: (request as Request).ip,\n requestHeaders: request.headers,\n responseHeaders: response.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n}\n"],"mappings":";;;;;;;;;;AAsBA,MAAM,qBAAqB,OAAO,IAAI,4BAA4B;AAClE,MAAM,cAAc;AAUpB,SAAgB,cAAc,KAAoB;CAChD,MAAM,YAAY;CAClB,IAAI,UAAU,wBAAwB,MACpC;CAEF,UAAU,sBAAsB;CAChC,MAAM,iBAAkB,IAA8C;CACtE,IAAI,0BAA0B;CAC9B,MAAM,kCAAkC;EACtC,IAAI,yBACF;EAEF,0BAA0B;EAG1B,IAAI,KAAK,OAAgB,MAAe,MAAgB,SAAuB;GAC7E,MAAM,SAAS,mBAAmB,KAAK;GACvC,IAAI,WAAW,KAAA,KAAa,UAAU,KACpC,iBAAiB,KAAK;GAExB,KAAK,KAAK;EACZ,CAAC;CACH;CACA,IAA+C,SAAS,SAEtD,KACA,KACA,UACS;EACT,IAAI;EACJ,IAAI;GACF,iBAAiB,eAAe,KAAK,KAAK,yBAAyB;EACrE,SAAS,OAAO;GACd,WAAW,qCAAqC,OAAO,KAAK,GAAG;EACjE;EACA,IAAI,CAAC,gBACH,OAAO,eAAe,KAAK,MAAM,KAAK,KAAK,QAAQ;EAErD,OAAO,QAAQ,KAAK,sBAAsB,eAAe,KAAK,MAAM,KAAK,KAAK,QAAQ,CAAC;CACzF;AACF;AASA,SAAS,eACP,SACA,UACA,2BACqB;CACrB,SAAS;CACT,IAAI,CAAC,YAAY,GACf;CAEF,0BAA0B;CAC1B,yBAAyB,OAAO;CAChC,MAAM,UAAU,4BAA4B;EAAE;EAAS,YAAY;CAAY,CAAC;CAChF,QAAQ,KAAK,QAAQ,gBAAgB,OAAO;CAC5C,QAAQ,KAAK,QAAQ,gBAAgB,QAAQ;CAC7C,mBAAmB,OAAO;CAC1B,MAAM,cAAyC;EAC7C,GAAG,QAAQ;EACX;EACA;CACF;CACA,MAAM,oBACJ,UAAU,CAAC,CAAC,uBAAuB,YAAY,cAAc,eAAe,KAAA;CAC9E,oBACE,WACC,eAAe,qBAAqB,2BAA2B,UAAU,GAC1E,YAAY,kBACd,CAAC,CACE,MAAM,eAAe,4BAA4B,aAAa,UAAU,CAAC,CAAC,CAC1E,OAAO,UAAmB;EACzB,WAAW,qCAAqC,OAAO,KAAK,GAAG;CACjE,CAAC;CACH,OAAO,QAAQ;AACjB;AAEA,SAAS,4BACP,aACA,YACM;CACN,MAAM,EAAE,SAAS,aAAa;CAC9B,MAAM,cAAc,oBAAoB,SAAS,YAAY,WAAW,MAAM,GAAG,CAAC,CAAC,EAAE;CACrF,IAAI,YAAY,+BACd,WACE,sSACF;CAEF,IAAI,2BAA2B,SAAS,UAAU,GAChD,YAAY,cAAc,qBAAqB,+BAC7C,sBAAsB,WAAW,MAAM,SAAS,UAAU,kBAAkB,CAAC,CAC/E;CAEF,2BAA2B;EACzB;EACA,mBAAmB,WAAW;EAC9B,YAAY,SAAS;EACrB,OAAO,YAAY;EACnB,eAAgB,QAAoB;EACpC,gBAAgB,QAAQ;EACxB,iBAAiB,SAAS,WAAW;EACrC,qBAAqB,WAAW;EAChC,sBAAsB;CACxB,CAAC;AACH"}
|
|
@@ -10,7 +10,7 @@ const require_fastify_routes = require("./routes.cjs");
|
|
|
10
10
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
11
11
|
//#region src/fastify/middleware.ts
|
|
12
12
|
const HOOKS_MARKER = Symbol.for("apitally.fastifyHooks");
|
|
13
|
-
const TRACER_NAME = "apitally
|
|
13
|
+
const TRACER_NAME = "apitally/fastify";
|
|
14
14
|
function installFastifyHooks(app, startupPaths) {
|
|
15
15
|
const markedApp = app;
|
|
16
16
|
if (markedApp[HOOKS_MARKER] === true) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.cjs","names":["isActivated","startNodeRequestObservation","getConfig","resolveRequestRoute","normalizeSource","flushTelemetry","formatIssues"],"sources":["../../src/fastify/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { activate, flushTelemetry, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport type { RoutePath } from \"../startup.js\";\nimport { formatIssues, normalizeSource } from \"../validationErrors.js\";\nimport { addStartupPaths, resolveRequestRoute } from \"./routes.js\";\n\nconst HOOKS_MARKER = Symbol.for(\"apitally.fastifyHooks\");\nconst TRACER_NAME = \"apitally
|
|
1
|
+
{"version":3,"file":"middleware.cjs","names":["isActivated","startNodeRequestObservation","getConfig","resolveRequestRoute","normalizeSource","flushTelemetry","formatIssues"],"sources":["../../src/fastify/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { activate, flushTelemetry, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport type { RoutePath } from \"../startup.js\";\nimport { formatIssues, normalizeSource } from \"../validationErrors.js\";\nimport { addStartupPaths, resolveRequestRoute } from \"./routes.js\";\n\nconst HOOKS_MARKER = Symbol.for(\"apitally.fastifyHooks\");\nconst TRACER_NAME = \"apitally/fastify\";\n\nexport function installFastifyHooks(app: FastifyInstance, startupPaths: RoutePath[]): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[HOOKS_MARKER] === true) {\n return;\n }\n markedApp[HOOKS_MARKER] = true;\n const observations = new WeakMap<FastifyRequest, StartedNodeRequestObservation>();\n\n app.addHook(\"onRoute\", (routeOptions) => {\n addStartupPaths(startupPaths, routeOptions);\n });\n app.addHook(\"onReady\", (done) => {\n activate();\n done();\n });\n app.addHook(\"onRequest\", (request, reply, done) => {\n activate();\n if (!isActivated() || request.raw.headers.upgrade?.toLowerCase() === \"websocket\") {\n done();\n return;\n }\n\n try {\n const state = startNodeRequestObservation({\n request: request.raw,\n tracerName: TRACER_NAME,\n });\n observations.set(request, state);\n captureNodeResponse(\n reply.raw,\n getConfig().captureResponseBody && state.observation.requestRecord.dropReason === undefined,\n state.observation.requestBodyCapture,\n (name) => reply.getHeader(name),\n )\n .then((completion) => {\n try {\n finalizeRequestObservation({\n observation: state.observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: reply.statusCode,\n route: resolveRequestRoute(request),\n clientAddress: request.ip,\n requestHeaders: request.raw.headers,\n responseHeaders: reply.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n } finally {\n observations.delete(request);\n }\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n context.with(state.requestContext, done);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n done();\n }\n });\n app.addHook(\"preValidation\", (request, _reply, done) => {\n const state = observations.get(request);\n if (state) {\n context.with(state.requestContext, done);\n } else {\n done();\n }\n });\n app.addHook(\"onError\", (request, _reply, error, done) => {\n const state = observations.get(request);\n if (!state) {\n done();\n return;\n }\n context.with(state.requestContext, () => {\n const { statusCode, validation, validationContext } = error as {\n statusCode?: unknown;\n validation?: unknown;\n validationContext?: unknown;\n };\n if (typeof statusCode !== \"number\" || statusCode >= 500) {\n captureException(error);\n }\n if (Array.isArray(validation)) {\n state.observation.requestRecord.validationErrors = formatAjvErrors(\n validation,\n normalizeSource(validationContext),\n );\n }\n done();\n });\n });\n app.addHook(\"onClose\", () => flushTelemetry());\n}\n\nfunction formatAjvErrors(errors: unknown[], source: string) {\n return formatIssues(\n errors.map((error) => {\n const { instancePath, keyword, params, message } = error as {\n instancePath?: unknown;\n keyword?: unknown;\n params?: { missingProperty?: unknown };\n message?: unknown;\n };\n // AJV returns instancePath as a JSON Pointer but missingProperty as a raw property name.\n const missing =\n typeof params?.missingProperty === \"string\"\n ? `/${params.missingProperty.replace(/~/g, \"~0\").replace(/\\//g, \"~1\")}`\n : \"\";\n return { path: `${instancePath ?? \"\"}${missing}`, message, code: keyword };\n }),\n source,\n );\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAM,eAAe,OAAO,IAAI,uBAAuB;AACvD,MAAM,cAAc;AAEpB,SAAgB,oBAAoB,KAAsB,cAAiC;CACzF,MAAM,YAAY;CAClB,IAAI,UAAU,kBAAkB,MAC9B;CAEF,UAAU,gBAAgB;CAC1B,MAAM,+BAAe,IAAI,QAAuD;CAEhF,IAAI,QAAQ,YAAY,iBAAiB;EACvC,uBAAA,gBAAgB,cAAc,YAAY;CAC5C,CAAC;CACD,IAAI,QAAQ,YAAY,SAAS;EAC/B,mBAAA,SAAS;EACT,KAAK;CACP,CAAC;CACD,IAAI,QAAQ,cAAc,SAAS,OAAO,SAAS;EACjD,mBAAA,SAAS;EACT,IAAI,CAACA,mBAAAA,YAAY,KAAK,QAAQ,IAAI,QAAQ,SAAS,YAAY,MAAM,aAAa;GAChF,KAAK;GACL;EACF;EAEA,IAAI;GACF,MAAM,QAAQC,+BAAAA,4BAA4B;IACxC,SAAS,QAAQ;IACjB,YAAY;GACd,CAAC;GACD,aAAa,IAAI,SAAS,KAAK;GAC/B,+BAAA,oBACE,MAAM,KACNC,eAAAA,UAAU,CAAC,CAAC,uBAAuB,MAAM,YAAY,cAAc,eAAe,KAAA,GAClF,MAAM,YAAY,qBACjB,SAAS,MAAM,UAAU,IAAI,CAChC,CAAC,CACE,MAAM,eAAe;IACpB,IAAI;KACF,2BAAA,2BAA2B;MACzB,aAAa,MAAM;MACnB,mBAAmB,WAAW;MAC9B,YAAY,MAAM;MAClB,OAAOC,uBAAAA,oBAAoB,OAAO;MAClC,eAAe,QAAQ;MACvB,gBAAgB,QAAQ,IAAI;MAC5B,iBAAiB,MAAM,WAAW;MAClC,qBAAqB,WAAW;MAChC,sBAAsB;KACxB,CAAC;IACH,UAAU;KACR,aAAa,OAAO,OAAO;IAC7B;GACF,CAAC,CAAC,CACD,OAAO,UAAmB;IACzB,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;GACjE,CAAC;GACH,mBAAA,QAAQ,KAAK,MAAM,gBAAgB,IAAI;EACzC,SAAS,OAAO;GACd,eAAA,WAAW,qCAAqC,OAAO,KAAK,GAAG;GAC/D,KAAK;EACP;CACF,CAAC;CACD,IAAI,QAAQ,kBAAkB,SAAS,QAAQ,SAAS;EACtD,MAAM,QAAQ,aAAa,IAAI,OAAO;EACtC,IAAI,OACF,mBAAA,QAAQ,KAAK,MAAM,gBAAgB,IAAI;OAEvC,KAAK;CAET,CAAC;CACD,IAAI,QAAQ,YAAY,SAAS,QAAQ,OAAO,SAAS;EACvD,MAAM,QAAQ,aAAa,IAAI,OAAO;EACtC,IAAI,CAAC,OAAO;GACV,KAAK;GACL;EACF;EACA,mBAAA,QAAQ,KAAK,MAAM,sBAAsB;GACvC,MAAM,EAAE,YAAY,YAAY,sBAAsB;GAKtD,IAAI,OAAO,eAAe,YAAY,cAAc,KAClD,mBAAA,iBAAiB,KAAK;GAExB,IAAI,MAAM,QAAQ,UAAU,GAC1B,MAAM,YAAY,cAAc,mBAAmB,gBACjD,YACAC,yBAAAA,gBAAgB,iBAAiB,CACnC;GAEF,KAAK;EACP,CAAC;CACH,CAAC;CACD,IAAI,QAAQ,iBAAiBC,mBAAAA,eAAe,CAAC;AAC/C;AAEA,SAAS,gBAAgB,QAAmB,QAAgB;CAC1D,OAAOC,yBAAAA,aACL,OAAO,KAAK,UAAU;EACpB,MAAM,EAAE,cAAc,SAAS,QAAQ,YAAY;EAOnD,MAAM,UACJ,OAAO,QAAQ,oBAAoB,WAC/B,IAAI,OAAO,gBAAgB,QAAQ,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,IAAI,MAClE;EACN,OAAO;GAAE,MAAM,GAAG,gBAAgB,KAAK;GAAW;GAAS,MAAM;EAAQ;CAC3E,CAAC,GACD,MACF;AACF"}
|
|
@@ -9,7 +9,7 @@ import { addStartupPaths, resolveRequestRoute } from "./routes.js";
|
|
|
9
9
|
import { context } from "@opentelemetry/api";
|
|
10
10
|
//#region src/fastify/middleware.ts
|
|
11
11
|
const HOOKS_MARKER = Symbol.for("apitally.fastifyHooks");
|
|
12
|
-
const TRACER_NAME = "apitally
|
|
12
|
+
const TRACER_NAME = "apitally/fastify";
|
|
13
13
|
function installFastifyHooks(app, startupPaths) {
|
|
14
14
|
const markedApp = app;
|
|
15
15
|
if (markedApp[HOOKS_MARKER] === true) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/fastify/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { activate, flushTelemetry, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport type { RoutePath } from \"../startup.js\";\nimport { formatIssues, normalizeSource } from \"../validationErrors.js\";\nimport { addStartupPaths, resolveRequestRoute } from \"./routes.js\";\n\nconst HOOKS_MARKER = Symbol.for(\"apitally.fastifyHooks\");\nconst TRACER_NAME = \"apitally
|
|
1
|
+
{"version":3,"file":"middleware.js","names":[],"sources":["../../src/fastify/middleware.ts"],"sourcesContent":["import { context } from \"@opentelemetry/api\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { activate, flushTelemetry, isActivated } from \"../activation.js\";\nimport { getConfig } from \"../config.js\";\nimport { captureException } from \"../exceptions.js\";\nimport { logWarning } from \"../logger.js\";\nimport { finalizeRequestObservation } from \"../requestObservation.js\";\nimport {\n captureNodeResponse,\n type StartedNodeRequestObservation,\n startNodeRequestObservation,\n} from \"../requestObservationNode.js\";\nimport type { RoutePath } from \"../startup.js\";\nimport { formatIssues, normalizeSource } from \"../validationErrors.js\";\nimport { addStartupPaths, resolveRequestRoute } from \"./routes.js\";\n\nconst HOOKS_MARKER = Symbol.for(\"apitally.fastifyHooks\");\nconst TRACER_NAME = \"apitally/fastify\";\n\nexport function installFastifyHooks(app: FastifyInstance, startupPaths: RoutePath[]): void {\n const markedApp = app as unknown as Record<symbol, boolean | undefined>;\n if (markedApp[HOOKS_MARKER] === true) {\n return;\n }\n markedApp[HOOKS_MARKER] = true;\n const observations = new WeakMap<FastifyRequest, StartedNodeRequestObservation>();\n\n app.addHook(\"onRoute\", (routeOptions) => {\n addStartupPaths(startupPaths, routeOptions);\n });\n app.addHook(\"onReady\", (done) => {\n activate();\n done();\n });\n app.addHook(\"onRequest\", (request, reply, done) => {\n activate();\n if (!isActivated() || request.raw.headers.upgrade?.toLowerCase() === \"websocket\") {\n done();\n return;\n }\n\n try {\n const state = startNodeRequestObservation({\n request: request.raw,\n tracerName: TRACER_NAME,\n });\n observations.set(request, state);\n captureNodeResponse(\n reply.raw,\n getConfig().captureResponseBody && state.observation.requestRecord.dropReason === undefined,\n state.observation.requestBodyCapture,\n (name) => reply.getHeader(name),\n )\n .then((completion) => {\n try {\n finalizeRequestObservation({\n observation: state.observation,\n completedAtMillis: completion.completedAtMillis,\n statusCode: reply.statusCode,\n route: resolveRequestRoute(request),\n clientAddress: request.ip,\n requestHeaders: request.raw.headers,\n responseHeaders: reply.getHeaders(),\n capturedRequestBody: completion.requestBody,\n capturedResponseBody: completion,\n });\n } finally {\n observations.delete(request);\n }\n })\n .catch((error: unknown) => {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n });\n context.with(state.requestContext, done);\n } catch (error) {\n logWarning(`Error in the Apitally middleware: ${String(error)}`);\n done();\n }\n });\n app.addHook(\"preValidation\", (request, _reply, done) => {\n const state = observations.get(request);\n if (state) {\n context.with(state.requestContext, done);\n } else {\n done();\n }\n });\n app.addHook(\"onError\", (request, _reply, error, done) => {\n const state = observations.get(request);\n if (!state) {\n done();\n return;\n }\n context.with(state.requestContext, () => {\n const { statusCode, validation, validationContext } = error as {\n statusCode?: unknown;\n validation?: unknown;\n validationContext?: unknown;\n };\n if (typeof statusCode !== \"number\" || statusCode >= 500) {\n captureException(error);\n }\n if (Array.isArray(validation)) {\n state.observation.requestRecord.validationErrors = formatAjvErrors(\n validation,\n normalizeSource(validationContext),\n );\n }\n done();\n });\n });\n app.addHook(\"onClose\", () => flushTelemetry());\n}\n\nfunction formatAjvErrors(errors: unknown[], source: string) {\n return formatIssues(\n errors.map((error) => {\n const { instancePath, keyword, params, message } = error as {\n instancePath?: unknown;\n keyword?: unknown;\n params?: { missingProperty?: unknown };\n message?: unknown;\n };\n // AJV returns instancePath as a JSON Pointer but missingProperty as a raw property name.\n const missing =\n typeof params?.missingProperty === \"string\"\n ? `/${params.missingProperty.replace(/~/g, \"~0\").replace(/\\//g, \"~1\")}`\n : \"\";\n return { path: `${instancePath ?? \"\"}${missing}`, message, code: keyword };\n }),\n source,\n );\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,eAAe,OAAO,IAAI,uBAAuB;AACvD,MAAM,cAAc;AAEpB,SAAgB,oBAAoB,KAAsB,cAAiC;CACzF,MAAM,YAAY;CAClB,IAAI,UAAU,kBAAkB,MAC9B;CAEF,UAAU,gBAAgB;CAC1B,MAAM,+BAAe,IAAI,QAAuD;CAEhF,IAAI,QAAQ,YAAY,iBAAiB;EACvC,gBAAgB,cAAc,YAAY;CAC5C,CAAC;CACD,IAAI,QAAQ,YAAY,SAAS;EAC/B,SAAS;EACT,KAAK;CACP,CAAC;CACD,IAAI,QAAQ,cAAc,SAAS,OAAO,SAAS;EACjD,SAAS;EACT,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,QAAQ,SAAS,YAAY,MAAM,aAAa;GAChF,KAAK;GACL;EACF;EAEA,IAAI;GACF,MAAM,QAAQ,4BAA4B;IACxC,SAAS,QAAQ;IACjB,YAAY;GACd,CAAC;GACD,aAAa,IAAI,SAAS,KAAK;GAC/B,oBACE,MAAM,KACN,UAAU,CAAC,CAAC,uBAAuB,MAAM,YAAY,cAAc,eAAe,KAAA,GAClF,MAAM,YAAY,qBACjB,SAAS,MAAM,UAAU,IAAI,CAChC,CAAC,CACE,MAAM,eAAe;IACpB,IAAI;KACF,2BAA2B;MACzB,aAAa,MAAM;MACnB,mBAAmB,WAAW;MAC9B,YAAY,MAAM;MAClB,OAAO,oBAAoB,OAAO;MAClC,eAAe,QAAQ;MACvB,gBAAgB,QAAQ,IAAI;MAC5B,iBAAiB,MAAM,WAAW;MAClC,qBAAqB,WAAW;MAChC,sBAAsB;KACxB,CAAC;IACH,UAAU;KACR,aAAa,OAAO,OAAO;IAC7B;GACF,CAAC,CAAC,CACD,OAAO,UAAmB;IACzB,WAAW,qCAAqC,OAAO,KAAK,GAAG;GACjE,CAAC;GACH,QAAQ,KAAK,MAAM,gBAAgB,IAAI;EACzC,SAAS,OAAO;GACd,WAAW,qCAAqC,OAAO,KAAK,GAAG;GAC/D,KAAK;EACP;CACF,CAAC;CACD,IAAI,QAAQ,kBAAkB,SAAS,QAAQ,SAAS;EACtD,MAAM,QAAQ,aAAa,IAAI,OAAO;EACtC,IAAI,OACF,QAAQ,KAAK,MAAM,gBAAgB,IAAI;OAEvC,KAAK;CAET,CAAC;CACD,IAAI,QAAQ,YAAY,SAAS,QAAQ,OAAO,SAAS;EACvD,MAAM,QAAQ,aAAa,IAAI,OAAO;EACtC,IAAI,CAAC,OAAO;GACV,KAAK;GACL;EACF;EACA,QAAQ,KAAK,MAAM,sBAAsB;GACvC,MAAM,EAAE,YAAY,YAAY,sBAAsB;GAKtD,IAAI,OAAO,eAAe,YAAY,cAAc,KAClD,iBAAiB,KAAK;GAExB,IAAI,MAAM,QAAQ,UAAU,GAC1B,MAAM,YAAY,cAAc,mBAAmB,gBACjD,YACA,gBAAgB,iBAAiB,CACnC;GAEF,KAAK;EACP,CAAC;CACH,CAAC;CACD,IAAI,QAAQ,iBAAiB,eAAe,CAAC;AAC/C;AAEA,SAAS,gBAAgB,QAAmB,QAAgB;CAC1D,OAAO,aACL,OAAO,KAAK,UAAU;EACpB,MAAM,EAAE,cAAc,SAAS,QAAQ,YAAY;EAOnD,MAAM,UACJ,OAAO,QAAQ,oBAAoB,WAC/B,IAAI,OAAO,gBAAgB,QAAQ,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,IAAI,MAClE;EACN,OAAO;GAAE,MAAM,GAAG,gBAAgB,KAAK;GAAW;GAAS,MAAM;EAAQ;CAC3E,CAAC,GACD,MACF;AACF"}
|
package/dist/h3/index.cjs
CHANGED
|
@@ -13,6 +13,7 @@ function useApitally(app, options) {
|
|
|
13
13
|
}
|
|
14
14
|
function install(app, options) {
|
|
15
15
|
require_activation.configure(options);
|
|
16
|
+
require_activation.activateWhenHttpServerListens();
|
|
16
17
|
const markedApp = app;
|
|
17
18
|
if (markedApp[INSTALL_MARKER] === true) return;
|
|
18
19
|
markedApp[INSTALL_MARKER] = true;
|
package/dist/h3/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["resolvePackageVersion","resolveStartupPaths"],"sources":["../../src/h3/index.ts"],"sourcesContent":["import type { H3, H3Plugin } from \"h3\";\nimport {
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["resolvePackageVersion","resolveStartupPaths"],"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,mBAAA,UAAU,OAAO;CACjB,mBAAA,8BAA8B;CAC9B,MAAM,YAAY;CAClB,IAAI,UAAU,oBAAoB,MAChC;CAEF,UAAU,kBAAkB;CAC5B,mBAAA,yBAAyB;EACvB,WAAW;EACX,kBAAkBA,uBAAAA,sBAAsB,IAAI;EAC5C,oBAAoBC,kBAAAA,oBAAoB,GAAG;CAC7C,CAAC;CACD,sBAAA,4BAA4B,GAAG;AACjC"}
|
package/dist/h3/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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 { installH3RequestObservation } from "./middleware.js";
|
|
5
5
|
//#region src/h3/index.ts
|
|
@@ -12,6 +12,7 @@ function useApitally(app, options) {
|
|
|
12
12
|
}
|
|
13
13
|
function install(app, options) {
|
|
14
14
|
configure(options);
|
|
15
|
+
activateWhenHttpServerListens();
|
|
15
16
|
const markedApp = app;
|
|
16
17
|
if (markedApp[INSTALL_MARKER] === true) return;
|
|
17
18
|
markedApp[INSTALL_MARKER] = true;
|