evlog 2.14.1 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -4
  3. package/dist/adapters/axiom.d.mts +18 -27
  4. package/dist/adapters/axiom.d.mts.map +1 -1
  5. package/dist/adapters/axiom.mjs +40 -30
  6. package/dist/adapters/axiom.mjs.map +1 -1
  7. package/dist/adapters/better-stack.d.mts +11 -24
  8. package/dist/adapters/better-stack.d.mts.map +1 -1
  9. package/dist/adapters/better-stack.mjs +34 -29
  10. package/dist/adapters/better-stack.mjs.map +1 -1
  11. package/dist/adapters/datadog.d.mts +1 -1
  12. package/dist/adapters/datadog.d.mts.map +1 -1
  13. package/dist/adapters/datadog.mjs +10 -4
  14. package/dist/adapters/datadog.mjs.map +1 -1
  15. package/dist/adapters/fs.d.mts +2 -2
  16. package/dist/adapters/fs.d.mts.map +1 -1
  17. package/dist/adapters/fs.mjs +19 -7
  18. package/dist/adapters/fs.mjs.map +1 -1
  19. package/dist/adapters/hyperdx.d.mts +1 -1
  20. package/dist/adapters/hyperdx.mjs +1 -2
  21. package/dist/adapters/hyperdx.mjs.map +1 -1
  22. package/dist/adapters/otlp.d.mts +1 -1
  23. package/dist/adapters/otlp.d.mts.map +1 -1
  24. package/dist/adapters/otlp.mjs +36 -31
  25. package/dist/adapters/otlp.mjs.map +1 -1
  26. package/dist/adapters/posthog.d.mts +50 -70
  27. package/dist/adapters/posthog.d.mts.map +1 -1
  28. package/dist/adapters/posthog.mjs +50 -85
  29. package/dist/adapters/posthog.mjs.map +1 -1
  30. package/dist/adapters/sentry.d.mts +1 -1
  31. package/dist/adapters/sentry.d.mts.map +1 -1
  32. package/dist/adapters/sentry.mjs +15 -5
  33. package/dist/adapters/sentry.mjs.map +1 -1
  34. package/dist/ai/index.d.mts +1 -1
  35. package/dist/{audit-DQoBo7Dl.mjs → audit--n0QRR2Y.mjs} +152 -13
  36. package/dist/audit--n0QRR2Y.mjs.map +1 -0
  37. package/dist/{audit-CTIviX3P.d.mts → audit-CJl-wZ10.d.mts} +134 -2
  38. package/dist/audit-CJl-wZ10.d.mts.map +1 -0
  39. package/dist/better-auth/index.d.mts +1 -1
  40. package/dist/browser.d.mts +1 -1
  41. package/dist/define-D6OJdSUH.mjs +63 -0
  42. package/dist/define-D6OJdSUH.mjs.map +1 -0
  43. package/dist/define-Fp8TrdEB.d.mts +57 -0
  44. package/dist/define-Fp8TrdEB.d.mts.map +1 -0
  45. package/dist/{dist-Do8P4zWd.mjs → dist-BIlS38vi.mjs} +1 -1
  46. package/dist/dist-BIlS38vi.mjs.map +1 -0
  47. package/dist/drain-ByWUeOQC.mjs +160 -0
  48. package/dist/drain-ByWUeOQC.mjs.map +1 -0
  49. package/dist/elysia/index.d.mts +25 -2
  50. package/dist/elysia/index.d.mts.map +1 -1
  51. package/dist/elysia/index.mjs +53 -20
  52. package/dist/elysia/index.mjs.map +1 -1
  53. package/dist/enricher-BA6viylF.mjs +95 -0
  54. package/dist/enricher-BA6viylF.mjs.map +1 -0
  55. package/dist/enricher-CLSnrzrr.d.mts +42 -0
  56. package/dist/enricher-CLSnrzrr.d.mts.map +1 -0
  57. package/dist/enrichers.d.mts +16 -9
  58. package/dist/enrichers.d.mts.map +1 -1
  59. package/dist/enrichers.mjs +81 -64
  60. package/dist/enrichers.mjs.map +1 -1
  61. package/dist/{error-C7gSQVqk.d.mts → error-C-66_G2M.d.mts} +2 -2
  62. package/dist/{error-C7gSQVqk.d.mts.map → error-C-66_G2M.d.mts.map} +1 -1
  63. package/dist/error.d.mts +1 -1
  64. package/dist/{errors-BJRXUfMg.mjs → errors-BQgyQ9xe.mjs} +1 -1
  65. package/dist/{errors-BJRXUfMg.mjs.map → errors-BQgyQ9xe.mjs.map} +1 -1
  66. package/dist/{errors-4MPmTzjY.d.mts → errors-DQoYsDW1.d.mts} +2 -2
  67. package/dist/{errors-4MPmTzjY.d.mts.map → errors-DQoYsDW1.d.mts.map} +1 -1
  68. package/dist/event-ef-5Dbxg.mjs +53 -0
  69. package/dist/event-ef-5Dbxg.mjs.map +1 -0
  70. package/dist/express/index.d.mts +2 -2
  71. package/dist/express/index.d.mts.map +1 -1
  72. package/dist/express/index.mjs +17 -15
  73. package/dist/express/index.mjs.map +1 -1
  74. package/dist/fastify/index.d.mts +2 -2
  75. package/dist/fastify/index.d.mts.map +1 -1
  76. package/dist/fastify/index.mjs +19 -20
  77. package/dist/fastify/index.mjs.map +1 -1
  78. package/dist/fork-D44V93-K.mjs +227 -0
  79. package/dist/fork-D44V93-K.mjs.map +1 -0
  80. package/dist/{headers-D74M0wsg.mjs → headers-CU-QqnYg.mjs} +19 -2
  81. package/dist/headers-CU-QqnYg.mjs.map +1 -0
  82. package/dist/hono/index.d.mts +2 -2
  83. package/dist/hono/index.d.mts.map +1 -1
  84. package/dist/hono/index.mjs +14 -10
  85. package/dist/hono/index.mjs.map +1 -1
  86. package/dist/http.d.mts +1 -1
  87. package/dist/index.d.mts +8 -7
  88. package/dist/index.mjs +3 -2
  89. package/dist/integration-Bz8X6_Lb.mjs +75 -0
  90. package/dist/integration-Bz8X6_Lb.mjs.map +1 -0
  91. package/dist/{logger-DttRJRGa.d.mts → logger-Brt5-WMK.d.mts} +9 -3
  92. package/dist/logger-Brt5-WMK.d.mts.map +1 -0
  93. package/dist/logger.d.mts +2 -2
  94. package/dist/logger.mjs +2 -2
  95. package/dist/middleware-CGM-bOvE.d.mts +72 -0
  96. package/dist/middleware-CGM-bOvE.d.mts.map +1 -0
  97. package/dist/nestjs/index.d.mts +2 -2
  98. package/dist/nestjs/index.mjs +3 -4
  99. package/dist/nestjs/index.mjs.map +1 -1
  100. package/dist/next/client.d.mts +1 -1
  101. package/dist/next/index.d.mts +4 -4
  102. package/dist/next/index.mjs +3 -3
  103. package/dist/next/instrumentation.d.mts +1 -1
  104. package/dist/next/instrumentation.mjs +1 -1
  105. package/dist/nitro/errorHandler.mjs +2 -2
  106. package/dist/nitro/module.d.mts +2 -2
  107. package/dist/nitro/plugin.mjs +21 -11
  108. package/dist/nitro/plugin.mjs.map +1 -1
  109. package/dist/nitro/v3/errorHandler.mjs +3 -3
  110. package/dist/nitro/v3/index.d.mts +2 -2
  111. package/dist/nitro/v3/module.d.mts +1 -1
  112. package/dist/nitro/v3/plugin.mjs +29 -17
  113. package/dist/nitro/v3/plugin.mjs.map +1 -1
  114. package/dist/nitro/v3/useLogger.d.mts +1 -1
  115. package/dist/{nitro-CPPRCPbG.d.mts → nitro-DHPb9dXG.d.mts} +2 -2
  116. package/dist/{nitro-CPPRCPbG.d.mts.map → nitro-DHPb9dXG.d.mts.map} +1 -1
  117. package/dist/{nitro-OmT_M4Pb.mjs → nitro-DavLelNz.mjs} +2 -2
  118. package/dist/nitro-DavLelNz.mjs.map +1 -0
  119. package/dist/{nitroConfigBridge-C37lXaNm.mjs → nitroConfigBridge-aZ1e5upQ.mjs} +1 -1
  120. package/dist/nitroConfigBridge-aZ1e5upQ.mjs.map +1 -0
  121. package/dist/nuxt/module.d.mts +1 -1
  122. package/dist/nuxt/module.mjs +2 -2
  123. package/dist/{parseError-o1GpZEOR.d.mts → parseError-B1zJZvQ5.d.mts} +2 -2
  124. package/dist/parseError-B1zJZvQ5.d.mts.map +1 -0
  125. package/dist/react-router/index.d.mts +2 -2
  126. package/dist/react-router/index.mjs +3 -4
  127. package/dist/react-router/index.mjs.map +1 -1
  128. package/dist/{routes-CGPmbzCZ.mjs → routes-B48wm7Pb.mjs} +1 -1
  129. package/dist/{routes-CGPmbzCZ.mjs.map → routes-B48wm7Pb.mjs.map} +1 -1
  130. package/dist/runtime/client/log.d.mts +1 -1
  131. package/dist/runtime/server/routes/_evlog/ingest.post.mjs +21 -10
  132. package/dist/runtime/server/routes/_evlog/ingest.post.mjs.map +1 -1
  133. package/dist/runtime/server/useLogger.d.mts +1 -1
  134. package/dist/runtime/utils/parseError.d.mts +2 -2
  135. package/dist/runtime/utils/parseError.mjs +1 -1
  136. package/dist/{_severity-CQijvfhU.mjs → severity-BYWZ96Sb.mjs} +6 -2
  137. package/dist/severity-BYWZ96Sb.mjs.map +1 -0
  138. package/dist/{source-location-DRvDDqfq.mjs → source-location-Dco0cRTz.mjs} +3 -3
  139. package/dist/source-location-Dco0cRTz.mjs.map +1 -0
  140. package/dist/storage-BT-3fT1-.mjs +27 -0
  141. package/dist/storage-BT-3fT1-.mjs.map +1 -0
  142. package/dist/sveltekit/index.d.mts +2 -2
  143. package/dist/sveltekit/index.mjs +5 -6
  144. package/dist/sveltekit/index.mjs.map +1 -1
  145. package/dist/toolkit.d.mts +288 -12
  146. package/dist/toolkit.d.mts.map +1 -1
  147. package/dist/toolkit.mjs +13 -7
  148. package/dist/types.d.mts +1 -1
  149. package/dist/{useLogger-CyPP1sVB.d.mts → useLogger-Cb1R6bQE.d.mts} +2 -2
  150. package/dist/{useLogger-CyPP1sVB.d.mts.map → useLogger-Cb1R6bQE.d.mts.map} +1 -1
  151. package/dist/{utils-Dmin7wVL.d.mts → utils-gQCeZMbg.d.mts} +2 -2
  152. package/dist/{utils-Dmin7wVL.d.mts.map → utils-gQCeZMbg.d.mts.map} +1 -1
  153. package/dist/utils.d.mts +1 -1
  154. package/dist/vite/index.d.mts +1 -1
  155. package/dist/vite/index.mjs +1 -1
  156. package/dist/workers.d.mts +1 -1
  157. package/dist/workers.mjs +1 -1
  158. package/package.json +22 -19
  159. package/dist/_drain-CmCtsuF6.mjs +0 -23
  160. package/dist/_drain-CmCtsuF6.mjs.map +0 -1
  161. package/dist/_http-BY1e9pwC.mjs +0 -78
  162. package/dist/_http-BY1e9pwC.mjs.map +0 -1
  163. package/dist/_severity-CQijvfhU.mjs.map +0 -1
  164. package/dist/audit-CTIviX3P.d.mts.map +0 -1
  165. package/dist/audit-DQoBo7Dl.mjs.map +0 -1
  166. package/dist/dist-Do8P4zWd.mjs.map +0 -1
  167. package/dist/fork-D1j1Fuzy.mjs +0 -72
  168. package/dist/fork-D1j1Fuzy.mjs.map +0 -1
  169. package/dist/headers-D74M0wsg.mjs.map +0 -1
  170. package/dist/logger-DttRJRGa.d.mts.map +0 -1
  171. package/dist/middleware-CTnDsST-.d.mts +0 -93
  172. package/dist/middleware-CTnDsST-.d.mts.map +0 -1
  173. package/dist/middleware-oAccqyPp.mjs +0 -123
  174. package/dist/middleware-oAccqyPp.mjs.map +0 -1
  175. package/dist/nitro-OmT_M4Pb.mjs.map +0 -1
  176. package/dist/nitroConfigBridge-C37lXaNm.mjs.map +0 -1
  177. package/dist/parseError-o1GpZEOR.d.mts.map +0 -1
  178. package/dist/source-location-DRvDDqfq.mjs.map +0 -1
  179. package/dist/storage-CFGTn37X.mjs +0 -46
  180. package/dist/storage-CFGTn37X.mjs.map +0 -1
@@ -1,11 +1,6 @@
1
+ import { i as normalizeNumber, r as getHeader } from "./headers-CU-QqnYg.mjs";
2
+ import { r as composeEnrichers, t as defineEnricher } from "./enricher-BA6viylF.mjs";
1
3
  //#region src/enrichers/index.ts
2
- function getHeader(headers, name) {
3
- if (!headers) return void 0;
4
- if (headers[name] !== void 0) return headers[name];
5
- const lowerName = name.toLowerCase();
6
- if (headers[lowerName] !== void 0) return headers[lowerName];
7
- for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === lowerName) return value;
8
- }
9
4
  function parseUserAgent(ua) {
10
5
  const lower = ua.toLowerCase();
11
6
  let deviceType = { type: "unknown" };
@@ -75,29 +70,19 @@ function parseTraceparent(traceparent) {
75
70
  spanId: match[2]
76
71
  };
77
72
  }
78
- function mergeEventField(existing, computed, overwrite) {
79
- if (overwrite || existing === void 0 || existing === null || typeof existing !== "object") return computed;
80
- return {
81
- ...computed,
82
- ...existing
83
- };
84
- }
85
- function normalizeNumber(value) {
86
- if (!value) return void 0;
87
- const parsed = Number(value);
88
- return Number.isFinite(parsed) ? parsed : void 0;
89
- }
90
73
  /**
91
74
  * Enrich events with parsed user agent data.
92
75
  * Sets `event.userAgent` with `UserAgentInfo` shape: `{ raw, browser?, os?, device? }`.
93
76
  */
94
77
  function createUserAgentEnricher(options = {}) {
95
- return (ctx) => {
96
- const ua = getHeader(ctx.headers, "user-agent");
97
- if (!ua) return;
98
- const info = parseUserAgent(ua);
99
- ctx.event.userAgent = mergeEventField(ctx.event.userAgent, info, options.overwrite);
100
- };
78
+ return defineEnricher({
79
+ name: "userAgent",
80
+ field: "userAgent",
81
+ compute: ({ headers }) => {
82
+ const ua = getHeader(headers, "user-agent");
83
+ return ua ? parseUserAgent(ua) : void 0;
84
+ }
85
+ }, options);
101
86
  }
102
87
  /**
103
88
  * Enrich events with geo data from platform headers.
@@ -112,36 +97,41 @@ function createUserAgentEnricher(options = {}) {
112
97
  * or use a Workers middleware to copy `cf` properties into custom headers.
113
98
  */
114
99
  function createGeoEnricher(options = {}) {
115
- return (ctx) => {
116
- const { headers } = ctx;
117
- if (!headers) return;
118
- const geo = {
119
- country: getHeader(headers, "x-vercel-ip-country") ?? getHeader(headers, "cf-ipcountry"),
120
- region: getHeader(headers, "x-vercel-ip-country-region") ?? getHeader(headers, "cf-region"),
121
- regionCode: getHeader(headers, "x-vercel-ip-country-region-code") ?? getHeader(headers, "cf-region-code"),
122
- city: getHeader(headers, "x-vercel-ip-city") ?? getHeader(headers, "cf-city"),
123
- latitude: normalizeNumber(getHeader(headers, "x-vercel-ip-latitude") ?? getHeader(headers, "cf-latitude")),
124
- longitude: normalizeNumber(getHeader(headers, "x-vercel-ip-longitude") ?? getHeader(headers, "cf-longitude"))
125
- };
126
- if (Object.values(geo).every((value) => value === void 0)) return;
127
- ctx.event.geo = mergeEventField(ctx.event.geo, geo, options.overwrite);
128
- };
100
+ return defineEnricher({
101
+ name: "geo",
102
+ field: "geo",
103
+ compute: ({ headers }) => {
104
+ if (!headers) return void 0;
105
+ const geo = {
106
+ country: getHeader(headers, "x-vercel-ip-country") ?? getHeader(headers, "cf-ipcountry"),
107
+ region: getHeader(headers, "x-vercel-ip-country-region") ?? getHeader(headers, "cf-region"),
108
+ regionCode: getHeader(headers, "x-vercel-ip-country-region-code") ?? getHeader(headers, "cf-region-code"),
109
+ city: getHeader(headers, "x-vercel-ip-city") ?? getHeader(headers, "cf-city"),
110
+ latitude: normalizeNumber(getHeader(headers, "x-vercel-ip-latitude") ?? getHeader(headers, "cf-latitude")),
111
+ longitude: normalizeNumber(getHeader(headers, "x-vercel-ip-longitude") ?? getHeader(headers, "cf-longitude"))
112
+ };
113
+ return Object.values(geo).every((value) => value === void 0) ? void 0 : geo;
114
+ }
115
+ }, options);
129
116
  }
130
117
  /**
131
118
  * Enrich events with request/response payload sizes.
132
119
  * Sets `event.requestSize` with `RequestSizeInfo` shape: `{ requestBytes?, responseBytes? }`.
133
120
  */
134
121
  function createRequestSizeEnricher(options = {}) {
135
- return (ctx) => {
136
- const requestBytes = normalizeNumber(getHeader(ctx.headers, "content-length"));
137
- const responseBytes = normalizeNumber(getHeader(ctx.response?.headers, "content-length"));
138
- const sizes = {
139
- requestBytes,
140
- responseBytes
141
- };
142
- if (requestBytes === void 0 && responseBytes === void 0) return;
143
- ctx.event.requestSize = mergeEventField(ctx.event.requestSize, sizes, options.overwrite);
144
- };
122
+ return defineEnricher({
123
+ name: "requestSize",
124
+ field: "requestSize",
125
+ compute: ({ headers, response }) => {
126
+ const requestBytes = normalizeNumber(getHeader(headers, "content-length"));
127
+ const responseBytes = normalizeNumber(getHeader(response?.headers, "content-length"));
128
+ if (requestBytes === void 0 && responseBytes === void 0) return void 0;
129
+ return {
130
+ requestBytes,
131
+ responseBytes
132
+ };
133
+ }
134
+ }, options);
145
135
  }
146
136
  /**
147
137
  * Enrich events with W3C trace context data.
@@ -149,24 +139,51 @@ function createRequestSizeEnricher(options = {}) {
149
139
  * Also sets `event.traceId` and `event.spanId` at the top level.
150
140
  */
151
141
  function createTraceContextEnricher(options = {}) {
142
+ const enricher = defineEnricher({
143
+ name: "traceContext",
144
+ field: "traceContext",
145
+ compute: ({ event, headers }) => {
146
+ const traceparent = getHeader(headers, "traceparent");
147
+ const tracestate = getHeader(headers, "tracestate");
148
+ if (!traceparent && !tracestate) return void 0;
149
+ const parsed = traceparent ? parseTraceparent(traceparent) : void 0;
150
+ return {
151
+ traceparent,
152
+ tracestate,
153
+ traceId: parsed?.traceId ?? event.traceId,
154
+ spanId: parsed?.spanId ?? event.spanId
155
+ };
156
+ }
157
+ }, options);
152
158
  return (ctx) => {
153
- const traceparent = getHeader(ctx.headers, "traceparent");
154
- const tracestate = getHeader(ctx.headers, "tracestate");
155
- if (!traceparent && !tracestate) return;
156
- const parsed = traceparent ? parseTraceparent(traceparent) : void 0;
157
- const incomingTraceContext = {
158
- traceparent,
159
- tracestate,
160
- traceId: parsed?.traceId ?? ctx.event.traceId,
161
- spanId: parsed?.spanId ?? ctx.event.spanId
162
- };
163
- const mergedTraceContext = mergeEventField(ctx.event.traceContext, incomingTraceContext, options.overwrite);
164
- ctx.event.traceContext = mergedTraceContext;
165
- if (mergedTraceContext.traceId && (options.overwrite || ctx.event.traceId === void 0)) ctx.event.traceId = mergedTraceContext.traceId;
166
- if (mergedTraceContext.spanId && (options.overwrite || ctx.event.spanId === void 0)) ctx.event.spanId = mergedTraceContext.spanId;
159
+ enricher(ctx);
160
+ const merged = ctx.event.traceContext;
161
+ if (!merged) return;
162
+ if (merged.traceId && (options.overwrite || ctx.event.traceId === void 0)) ctx.event.traceId = merged.traceId;
163
+ if (merged.spanId && (options.overwrite || ctx.event.spanId === void 0)) ctx.event.spanId = merged.spanId;
167
164
  };
168
165
  }
166
+ /**
167
+ * Compose every built-in enricher into a single async enricher, in the order
168
+ * `userAgent → geo → requestSize → traceContext`.
169
+ *
170
+ * Drop-in shorthand for the most common middleware setup:
171
+ *
172
+ * ```ts
173
+ * import { createDefaultEnrichers } from 'evlog/enrichers'
174
+ *
175
+ * app.use(evlog({ enrich: createDefaultEnrichers() }))
176
+ * ```
177
+ */
178
+ function createDefaultEnrichers(options = {}) {
179
+ return composeEnrichers([
180
+ createUserAgentEnricher(options),
181
+ createGeoEnricher(options),
182
+ createRequestSizeEnricher(options),
183
+ createTraceContextEnricher(options)
184
+ ], { name: "default-enrichers" });
185
+ }
169
186
  //#endregion
170
- export { createGeoEnricher, createRequestSizeEnricher, createTraceContextEnricher, createUserAgentEnricher };
187
+ export { createDefaultEnrichers, createGeoEnricher, createRequestSizeEnricher, createTraceContextEnricher, createUserAgentEnricher };
171
188
 
172
189
  //# sourceMappingURL=enrichers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"enrichers.mjs","names":[],"sources":["../src/enrichers/index.ts"],"sourcesContent":["import type { EnrichContext } from '../types'\n\nexport interface EnricherOptions {\n /**\n * When true, overwrite any existing fields in the event.\n * Defaults to false to preserve user-provided data.\n */\n overwrite?: boolean\n}\n\nexport interface UserAgentInfo {\n raw: string\n browser?: { name: string; version?: string }\n os?: { name: string; version?: string }\n device?: { type: 'mobile' | 'tablet' | 'desktop' | 'bot' | 'unknown' }\n}\n\nexport interface GeoInfo {\n country?: string\n region?: string\n regionCode?: string\n city?: string\n latitude?: number\n longitude?: number\n}\n\nexport interface RequestSizeInfo {\n requestBytes?: number\n responseBytes?: number\n}\n\nexport interface TraceContextInfo {\n traceparent?: string\n tracestate?: string\n traceId?: string\n spanId?: string\n}\n\nfunction getHeader(headers: Record<string, string> | undefined, name: string): string | undefined {\n if (!headers) return undefined\n if (headers[name] !== undefined) return headers[name]\n const lowerName = name.toLowerCase()\n if (headers[lowerName] !== undefined) return headers[lowerName]\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lowerName) return value\n }\n return undefined\n}\n\nfunction parseUserAgent(ua: string): UserAgentInfo {\n const lower = ua.toLowerCase()\n\n let deviceType: UserAgentInfo['device'] = { type: 'unknown' }\n if (/bot|crawl|spider|slurp|bingpreview/.test(lower)) {\n deviceType = { type: 'bot' }\n } else if (/ipad|tablet/.test(lower)) {\n deviceType = { type: 'tablet' }\n } else if (/mobi|iphone|android/.test(lower)) {\n deviceType = { type: 'mobile' }\n } else if (ua.length > 0) {\n deviceType = { type: 'desktop' }\n }\n\n const browserMatchers: Array<{ name: string, regex: RegExp }> = [\n { name: 'Edge', regex: /edg\\/([\\d.]+)/i },\n { name: 'Chrome', regex: /chrome\\/([\\d.]+)/i },\n { name: 'Firefox', regex: /firefox\\/([\\d.]+)/i },\n { name: 'Safari', regex: /version\\/([\\d.]+).*safari/i },\n ]\n\n let browser: UserAgentInfo['browser']\n for (const matcher of browserMatchers) {\n const match = ua.match(matcher.regex)\n if (match) {\n browser = { name: matcher.name, version: match[1] }\n break\n }\n }\n\n let os: UserAgentInfo['os']\n if (/windows nt/i.test(ua)) {\n const match = ua.match(/windows nt ([\\d.]+)/i)\n os = { name: 'Windows', version: match?.[1] }\n } else if (/mac os x/i.test(ua) && !/iphone|ipad|ipod/i.test(ua)) {\n const match = ua.match(/mac os x ([\\d_]+)/i)\n os = { name: 'macOS', version: match?.[1]?.replace(/_/g, '.') }\n } else if (/iphone|ipad|ipod/i.test(ua)) {\n const match = ua.match(/os ([\\d_]+)/i)\n os = { name: 'iOS', version: match?.[1]?.replace(/_/g, '.') }\n } else if (/android/i.test(ua)) {\n const match = ua.match(/android ([\\d.]+)/i)\n os = { name: 'Android', version: match?.[1] }\n } else if (/linux/i.test(ua)) {\n os = { name: 'Linux' }\n }\n\n return {\n raw: ua,\n browser,\n os,\n device: deviceType,\n }\n}\n\nfunction parseTraceparent(traceparent: string): Pick<TraceContextInfo, 'traceId' | 'spanId'> | undefined {\n const match = traceparent.match(/^[\\da-f]{2}-([\\da-f]{32})-([\\da-f]{16})-[\\da-f]{2}$/i)\n if (!match) return undefined\n return { traceId: match[1], spanId: match[2] }\n}\n\nfunction mergeEventField<T extends object>(\n existing: unknown,\n computed: T,\n overwrite?: boolean,\n): T {\n if (overwrite || existing === undefined || existing === null || typeof existing !== 'object') {\n return computed\n }\n return { ...computed, ...(existing as T) }\n}\n\nfunction normalizeNumber(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\n/**\n * Enrich events with parsed user agent data.\n * Sets `event.userAgent` with `UserAgentInfo` shape: `{ raw, browser?, os?, device? }`.\n */\nexport function createUserAgentEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return (ctx) => {\n const ua = getHeader(ctx.headers, 'user-agent')\n if (!ua) return\n const info = parseUserAgent(ua)\n ctx.event.userAgent = mergeEventField<UserAgentInfo>(ctx.event.userAgent, info, options.overwrite)\n }\n}\n\n/**\n * Enrich events with geo data from platform headers.\n * Sets `event.geo` with `GeoInfo` shape: `{ country?, region?, regionCode?, city?, latitude?, longitude? }`.\n *\n * Supports Vercel (`x-vercel-ip-*`) headers out of the box.\n *\n * **Cloudflare note:** Only `cf-ipcountry` is an actual HTTP header added by Cloudflare.\n * The `cf-region`, `cf-city`, `cf-latitude`, `cf-longitude` headers are NOT standard\n * Cloudflare headers — they are properties of `request.cf` which is not exposed as HTTP\n * headers. For full geo data on Cloudflare, write a custom enricher that reads `request.cf`\n * or use a Workers middleware to copy `cf` properties into custom headers.\n */\nexport function createGeoEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return (ctx) => {\n const { headers } = ctx\n if (!headers) return\n\n const geo: GeoInfo = {\n country: getHeader(headers, 'x-vercel-ip-country') ?? getHeader(headers, 'cf-ipcountry'),\n region: getHeader(headers, 'x-vercel-ip-country-region') ?? getHeader(headers, 'cf-region'),\n regionCode: getHeader(headers, 'x-vercel-ip-country-region-code') ?? getHeader(headers, 'cf-region-code'),\n city: getHeader(headers, 'x-vercel-ip-city') ?? getHeader(headers, 'cf-city'),\n latitude: normalizeNumber(getHeader(headers, 'x-vercel-ip-latitude') ?? getHeader(headers, 'cf-latitude')),\n longitude: normalizeNumber(getHeader(headers, 'x-vercel-ip-longitude') ?? getHeader(headers, 'cf-longitude')),\n }\n\n if (Object.values(geo).every(value => value === undefined)) return\n ctx.event.geo = mergeEventField<GeoInfo>(ctx.event.geo, geo, options.overwrite)\n }\n}\n\n/**\n * Enrich events with request/response payload sizes.\n * Sets `event.requestSize` with `RequestSizeInfo` shape: `{ requestBytes?, responseBytes? }`.\n */\nexport function createRequestSizeEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return (ctx) => {\n const requestBytes = normalizeNumber(getHeader(ctx.headers, 'content-length'))\n const responseBytes = normalizeNumber(getHeader(ctx.response?.headers, 'content-length'))\n\n const sizes: RequestSizeInfo = {\n requestBytes,\n responseBytes,\n }\n\n if (requestBytes === undefined && responseBytes === undefined) return\n ctx.event.requestSize = mergeEventField<RequestSizeInfo>(ctx.event.requestSize, sizes, options.overwrite)\n }\n}\n\n/**\n * Enrich events with W3C trace context data.\n * Sets `event.traceContext` with `TraceContextInfo` shape: `{ traceparent?, tracestate?, traceId?, spanId? }`.\n * Also sets `event.traceId` and `event.spanId` at the top level.\n */\nexport function createTraceContextEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return (ctx) => {\n const traceparent = getHeader(ctx.headers, 'traceparent')\n const tracestate = getHeader(ctx.headers, 'tracestate')\n if (!traceparent && !tracestate) return\n\n const parsed = traceparent ? parseTraceparent(traceparent) : undefined\n const incomingTraceContext: TraceContextInfo = {\n traceparent,\n tracestate,\n traceId: parsed?.traceId ?? (ctx.event.traceId as string | undefined),\n spanId: parsed?.spanId ?? (ctx.event.spanId as string | undefined),\n }\n\n const mergedTraceContext = mergeEventField<TraceContextInfo>(\n ctx.event.traceContext,\n incomingTraceContext,\n options.overwrite,\n )\n ctx.event.traceContext = mergedTraceContext\n\n if (mergedTraceContext.traceId && (options.overwrite || ctx.event.traceId === undefined)) {\n ctx.event.traceId = mergedTraceContext.traceId\n }\n if (mergedTraceContext.spanId && (options.overwrite || ctx.event.spanId === undefined)) {\n ctx.event.spanId = mergedTraceContext.spanId\n }\n }\n}\n"],"mappings":";AAsCA,SAAS,UAAU,SAA6C,MAAkC;AAChG,KAAI,CAAC,QAAS,QAAO,KAAA;AACrB,KAAI,QAAQ,UAAU,KAAA,EAAW,QAAO,QAAQ;CAChD,MAAM,YAAY,KAAK,aAAa;AACpC,KAAI,QAAQ,eAAe,KAAA,EAAW,QAAO,QAAQ;AACrD,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAChD,KAAI,IAAI,aAAa,KAAK,UAAW,QAAO;;AAKhD,SAAS,eAAe,IAA2B;CACjD,MAAM,QAAQ,GAAG,aAAa;CAE9B,IAAI,aAAsC,EAAE,MAAM,WAAW;AAC7D,KAAI,qCAAqC,KAAK,MAAM,CAClD,cAAa,EAAE,MAAM,OAAO;UACnB,cAAc,KAAK,MAAM,CAClC,cAAa,EAAE,MAAM,UAAU;UACtB,sBAAsB,KAAK,MAAM,CAC1C,cAAa,EAAE,MAAM,UAAU;UACtB,GAAG,SAAS,EACrB,cAAa,EAAE,MAAM,WAAW;CAGlC,MAAM,kBAA0D;EAC9D;GAAE,MAAM;GAAQ,OAAO;GAAkB;EACzC;GAAE,MAAM;GAAU,OAAO;GAAqB;EAC9C;GAAE,MAAM;GAAW,OAAO;GAAsB;EAChD;GAAE,MAAM;GAAU,OAAO;GAA8B;EACxD;CAED,IAAI;AACJ,MAAK,MAAM,WAAW,iBAAiB;EACrC,MAAM,QAAQ,GAAG,MAAM,QAAQ,MAAM;AACrC,MAAI,OAAO;AACT,aAAU;IAAE,MAAM,QAAQ;IAAM,SAAS,MAAM;IAAI;AACnD;;;CAIJ,IAAI;AACJ,KAAI,cAAc,KAAK,GAAG,CAExB,MAAK;EAAE,MAAM;EAAW,SADV,GAAG,MAAM,uBACe,GAAG;EAAI;UACpC,YAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,KAAK,GAAG,CAE9D,MAAK;EAAE,MAAM;EAAS,SADR,GAAG,MAAM,qBACa,GAAG,IAAI,QAAQ,MAAM,IAAI;EAAE;UACtD,oBAAoB,KAAK,GAAG,CAErC,MAAK;EAAE,MAAM;EAAO,SADN,GAAG,MAAM,eACW,GAAG,IAAI,QAAQ,MAAM,IAAI;EAAE;UACpD,WAAW,KAAK,GAAG,CAE5B,MAAK;EAAE,MAAM;EAAW,SADV,GAAG,MAAM,oBACe,GAAG;EAAI;UACpC,SAAS,KAAK,GAAG,CAC1B,MAAK,EAAE,MAAM,SAAS;AAGxB,QAAO;EACL,KAAK;EACL;EACA;EACA,QAAQ;EACT;;AAGH,SAAS,iBAAiB,aAA+E;CACvG,MAAM,QAAQ,YAAY,MAAM,uDAAuD;AACvF,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,QAAO;EAAE,SAAS,MAAM;EAAI,QAAQ,MAAM;EAAI;;AAGhD,SAAS,gBACP,UACA,UACA,WACG;AACH,KAAI,aAAa,aAAa,KAAA,KAAa,aAAa,QAAQ,OAAO,aAAa,SAClF,QAAO;AAET,QAAO;EAAE,GAAG;EAAU,GAAI;EAAgB;;AAG5C,SAAS,gBAAgB,OAA+C;AACtE,KAAI,CAAC,MAAO,QAAO,KAAA;CACnB,MAAM,SAAS,OAAO,MAAM;AAC5B,QAAO,OAAO,SAAS,OAAO,GAAG,SAAS,KAAA;;;;;;AAO5C,SAAgB,wBAAwB,UAA2B,EAAE,EAAgC;AACnG,SAAQ,QAAQ;EACd,MAAM,KAAK,UAAU,IAAI,SAAS,aAAa;AAC/C,MAAI,CAAC,GAAI;EACT,MAAM,OAAO,eAAe,GAAG;AAC/B,MAAI,MAAM,YAAY,gBAA+B,IAAI,MAAM,WAAW,MAAM,QAAQ,UAAU;;;;;;;;;;;;;;;AAgBtG,SAAgB,kBAAkB,UAA2B,EAAE,EAAgC;AAC7F,SAAQ,QAAQ;EACd,MAAM,EAAE,YAAY;AACpB,MAAI,CAAC,QAAS;EAEd,MAAM,MAAe;GACnB,SAAS,UAAU,SAAS,sBAAsB,IAAI,UAAU,SAAS,eAAe;GACxF,QAAQ,UAAU,SAAS,6BAA6B,IAAI,UAAU,SAAS,YAAY;GAC3F,YAAY,UAAU,SAAS,kCAAkC,IAAI,UAAU,SAAS,iBAAiB;GACzG,MAAM,UAAU,SAAS,mBAAmB,IAAI,UAAU,SAAS,UAAU;GAC7E,UAAU,gBAAgB,UAAU,SAAS,uBAAuB,IAAI,UAAU,SAAS,cAAc,CAAC;GAC1G,WAAW,gBAAgB,UAAU,SAAS,wBAAwB,IAAI,UAAU,SAAS,eAAe,CAAC;GAC9G;AAED,MAAI,OAAO,OAAO,IAAI,CAAC,OAAM,UAAS,UAAU,KAAA,EAAU,CAAE;AAC5D,MAAI,MAAM,MAAM,gBAAyB,IAAI,MAAM,KAAK,KAAK,QAAQ,UAAU;;;;;;;AAQnF,SAAgB,0BAA0B,UAA2B,EAAE,EAAgC;AACrG,SAAQ,QAAQ;EACd,MAAM,eAAe,gBAAgB,UAAU,IAAI,SAAS,iBAAiB,CAAC;EAC9E,MAAM,gBAAgB,gBAAgB,UAAU,IAAI,UAAU,SAAS,iBAAiB,CAAC;EAEzF,MAAM,QAAyB;GAC7B;GACA;GACD;AAED,MAAI,iBAAiB,KAAA,KAAa,kBAAkB,KAAA,EAAW;AAC/D,MAAI,MAAM,cAAc,gBAAiC,IAAI,MAAM,aAAa,OAAO,QAAQ,UAAU;;;;;;;;AAS7G,SAAgB,2BAA2B,UAA2B,EAAE,EAAgC;AACtG,SAAQ,QAAQ;EACd,MAAM,cAAc,UAAU,IAAI,SAAS,cAAc;EACzD,MAAM,aAAa,UAAU,IAAI,SAAS,aAAa;AACvD,MAAI,CAAC,eAAe,CAAC,WAAY;EAEjC,MAAM,SAAS,cAAc,iBAAiB,YAAY,GAAG,KAAA;EAC7D,MAAM,uBAAyC;GAC7C;GACA;GACA,SAAS,QAAQ,WAAY,IAAI,MAAM;GACvC,QAAQ,QAAQ,UAAW,IAAI,MAAM;GACtC;EAED,MAAM,qBAAqB,gBACzB,IAAI,MAAM,cACV,sBACA,QAAQ,UACT;AACD,MAAI,MAAM,eAAe;AAEzB,MAAI,mBAAmB,YAAY,QAAQ,aAAa,IAAI,MAAM,YAAY,KAAA,GAC5E,KAAI,MAAM,UAAU,mBAAmB;AAEzC,MAAI,mBAAmB,WAAW,QAAQ,aAAa,IAAI,MAAM,WAAW,KAAA,GAC1E,KAAI,MAAM,SAAS,mBAAmB"}
1
+ {"version":3,"file":"enrichers.mjs","names":[],"sources":["../src/enrichers/index.ts"],"sourcesContent":["import type { EnrichContext } from '../types'\nimport { composeEnrichers } from '../shared/compose'\nimport { defineEnricher, type EnricherOptions } from '../shared/enricher'\nimport { getHeader, normalizeNumber } from '../shared/headers'\n\nexport type { EnricherOptions }\n\nexport interface UserAgentInfo {\n raw: string\n browser?: { name: string; version?: string }\n os?: { name: string; version?: string }\n device?: { type: 'mobile' | 'tablet' | 'desktop' | 'bot' | 'unknown' }\n}\n\nexport interface GeoInfo {\n country?: string\n region?: string\n regionCode?: string\n city?: string\n latitude?: number\n longitude?: number\n}\n\nexport interface RequestSizeInfo {\n requestBytes?: number\n responseBytes?: number\n}\n\nexport interface TraceContextInfo {\n traceparent?: string\n tracestate?: string\n traceId?: string\n spanId?: string\n}\n\nfunction parseUserAgent(ua: string): UserAgentInfo {\n const lower = ua.toLowerCase()\n\n let deviceType: UserAgentInfo['device'] = { type: 'unknown' }\n if (/bot|crawl|spider|slurp|bingpreview/.test(lower)) {\n deviceType = { type: 'bot' }\n } else if (/ipad|tablet/.test(lower)) {\n deviceType = { type: 'tablet' }\n } else if (/mobi|iphone|android/.test(lower)) {\n deviceType = { type: 'mobile' }\n } else if (ua.length > 0) {\n deviceType = { type: 'desktop' }\n }\n\n const browserMatchers: Array<{ name: string, regex: RegExp }> = [\n { name: 'Edge', regex: /edg\\/([\\d.]+)/i },\n { name: 'Chrome', regex: /chrome\\/([\\d.]+)/i },\n { name: 'Firefox', regex: /firefox\\/([\\d.]+)/i },\n { name: 'Safari', regex: /version\\/([\\d.]+).*safari/i },\n ]\n\n let browser: UserAgentInfo['browser']\n for (const matcher of browserMatchers) {\n const match = ua.match(matcher.regex)\n if (match) {\n browser = { name: matcher.name, version: match[1] }\n break\n }\n }\n\n let os: UserAgentInfo['os']\n if (/windows nt/i.test(ua)) {\n const match = ua.match(/windows nt ([\\d.]+)/i)\n os = { name: 'Windows', version: match?.[1] }\n } else if (/mac os x/i.test(ua) && !/iphone|ipad|ipod/i.test(ua)) {\n const match = ua.match(/mac os x ([\\d_]+)/i)\n os = { name: 'macOS', version: match?.[1]?.replace(/_/g, '.') }\n } else if (/iphone|ipad|ipod/i.test(ua)) {\n const match = ua.match(/os ([\\d_]+)/i)\n os = { name: 'iOS', version: match?.[1]?.replace(/_/g, '.') }\n } else if (/android/i.test(ua)) {\n const match = ua.match(/android ([\\d.]+)/i)\n os = { name: 'Android', version: match?.[1] }\n } else if (/linux/i.test(ua)) {\n os = { name: 'Linux' }\n }\n\n return {\n raw: ua,\n browser,\n os,\n device: deviceType,\n }\n}\n\nfunction parseTraceparent(traceparent: string): Pick<TraceContextInfo, 'traceId' | 'spanId'> | undefined {\n const match = traceparent.match(/^[\\da-f]{2}-([\\da-f]{32})-([\\da-f]{16})-[\\da-f]{2}$/i)\n if (!match) return undefined\n return { traceId: match[1], spanId: match[2] }\n}\n\n/**\n * Enrich events with parsed user agent data.\n * Sets `event.userAgent` with `UserAgentInfo` shape: `{ raw, browser?, os?, device? }`.\n */\nexport function createUserAgentEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return defineEnricher<UserAgentInfo>({\n name: 'userAgent',\n field: 'userAgent',\n compute: ({ headers }) => {\n const ua = getHeader(headers, 'user-agent')\n return ua ? parseUserAgent(ua) : undefined\n },\n }, options)\n}\n\n/**\n * Enrich events with geo data from platform headers.\n * Sets `event.geo` with `GeoInfo` shape: `{ country?, region?, regionCode?, city?, latitude?, longitude? }`.\n *\n * Supports Vercel (`x-vercel-ip-*`) headers out of the box.\n *\n * **Cloudflare note:** Only `cf-ipcountry` is an actual HTTP header added by Cloudflare.\n * The `cf-region`, `cf-city`, `cf-latitude`, `cf-longitude` headers are NOT standard\n * Cloudflare headers — they are properties of `request.cf` which is not exposed as HTTP\n * headers. For full geo data on Cloudflare, write a custom enricher that reads `request.cf`\n * or use a Workers middleware to copy `cf` properties into custom headers.\n */\nexport function createGeoEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return defineEnricher<GeoInfo>({\n name: 'geo',\n field: 'geo',\n compute: ({ headers }) => {\n if (!headers) return undefined\n const geo: GeoInfo = {\n country: getHeader(headers, 'x-vercel-ip-country') ?? getHeader(headers, 'cf-ipcountry'),\n region: getHeader(headers, 'x-vercel-ip-country-region') ?? getHeader(headers, 'cf-region'),\n regionCode: getHeader(headers, 'x-vercel-ip-country-region-code') ?? getHeader(headers, 'cf-region-code'),\n city: getHeader(headers, 'x-vercel-ip-city') ?? getHeader(headers, 'cf-city'),\n latitude: normalizeNumber(getHeader(headers, 'x-vercel-ip-latitude') ?? getHeader(headers, 'cf-latitude')),\n longitude: normalizeNumber(getHeader(headers, 'x-vercel-ip-longitude') ?? getHeader(headers, 'cf-longitude')),\n }\n return Object.values(geo).every(value => value === undefined) ? undefined : geo\n },\n }, options)\n}\n\n/**\n * Enrich events with request/response payload sizes.\n * Sets `event.requestSize` with `RequestSizeInfo` shape: `{ requestBytes?, responseBytes? }`.\n */\nexport function createRequestSizeEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n return defineEnricher<RequestSizeInfo>({\n name: 'requestSize',\n field: 'requestSize',\n compute: ({ headers, response }) => {\n const requestBytes = normalizeNumber(getHeader(headers, 'content-length'))\n const responseBytes = normalizeNumber(getHeader(response?.headers, 'content-length'))\n if (requestBytes === undefined && responseBytes === undefined) return undefined\n return { requestBytes, responseBytes }\n },\n }, options)\n}\n\n/**\n * Enrich events with W3C trace context data.\n * Sets `event.traceContext` with `TraceContextInfo` shape: `{ traceparent?, tracestate?, traceId?, spanId? }`.\n * Also sets `event.traceId` and `event.spanId` at the top level.\n */\nexport function createTraceContextEnricher(options: EnricherOptions = {}): (ctx: EnrichContext) => void {\n const enricher = defineEnricher<TraceContextInfo>({\n name: 'traceContext',\n field: 'traceContext',\n compute: ({ event, headers }) => {\n const traceparent = getHeader(headers, 'traceparent')\n const tracestate = getHeader(headers, 'tracestate')\n if (!traceparent && !tracestate) return undefined\n const parsed = traceparent ? parseTraceparent(traceparent) : undefined\n return {\n traceparent,\n tracestate,\n traceId: parsed?.traceId ?? (event.traceId as string | undefined),\n spanId: parsed?.spanId ?? (event.spanId as string | undefined),\n }\n },\n }, options)\n\n // Trace context also pins traceId/spanId at the top level for transports that\n // expect them outside the nested object.\n return (ctx) => {\n enricher(ctx)\n const merged = ctx.event.traceContext as TraceContextInfo | undefined\n if (!merged) return\n if (merged.traceId && (options.overwrite || ctx.event.traceId === undefined)) {\n ctx.event.traceId = merged.traceId\n }\n if (merged.spanId && (options.overwrite || ctx.event.spanId === undefined)) {\n ctx.event.spanId = merged.spanId\n }\n }\n}\n\n/**\n * Compose every built-in enricher into a single async enricher, in the order\n * `userAgent → geo → requestSize → traceContext`.\n *\n * Drop-in shorthand for the most common middleware setup:\n *\n * ```ts\n * import { createDefaultEnrichers } from 'evlog/enrichers'\n *\n * app.use(evlog({ enrich: createDefaultEnrichers() }))\n * ```\n */\nexport function createDefaultEnrichers(options: EnricherOptions = {}): (ctx: EnrichContext) => Promise<void> {\n return composeEnrichers(\n [\n createUserAgentEnricher(options),\n createGeoEnricher(options),\n createRequestSizeEnricher(options),\n createTraceContextEnricher(options),\n ],\n { name: 'default-enrichers' },\n )\n}\n"],"mappings":";;;AAmCA,SAAS,eAAe,IAA2B;CACjD,MAAM,QAAQ,GAAG,aAAa;CAE9B,IAAI,aAAsC,EAAE,MAAM,WAAW;AAC7D,KAAI,qCAAqC,KAAK,MAAM,CAClD,cAAa,EAAE,MAAM,OAAO;UACnB,cAAc,KAAK,MAAM,CAClC,cAAa,EAAE,MAAM,UAAU;UACtB,sBAAsB,KAAK,MAAM,CAC1C,cAAa,EAAE,MAAM,UAAU;UACtB,GAAG,SAAS,EACrB,cAAa,EAAE,MAAM,WAAW;CAGlC,MAAM,kBAA0D;EAC9D;GAAE,MAAM;GAAQ,OAAO;GAAkB;EACzC;GAAE,MAAM;GAAU,OAAO;GAAqB;EAC9C;GAAE,MAAM;GAAW,OAAO;GAAsB;EAChD;GAAE,MAAM;GAAU,OAAO;GAA8B;EACxD;CAED,IAAI;AACJ,MAAK,MAAM,WAAW,iBAAiB;EACrC,MAAM,QAAQ,GAAG,MAAM,QAAQ,MAAM;AACrC,MAAI,OAAO;AACT,aAAU;IAAE,MAAM,QAAQ;IAAM,SAAS,MAAM;IAAI;AACnD;;;CAIJ,IAAI;AACJ,KAAI,cAAc,KAAK,GAAG,CAExB,MAAK;EAAE,MAAM;EAAW,SADV,GAAG,MAAM,uBACe,GAAG;EAAI;UACpC,YAAY,KAAK,GAAG,IAAI,CAAC,oBAAoB,KAAK,GAAG,CAE9D,MAAK;EAAE,MAAM;EAAS,SADR,GAAG,MAAM,qBACa,GAAG,IAAI,QAAQ,MAAM,IAAI;EAAE;UACtD,oBAAoB,KAAK,GAAG,CAErC,MAAK;EAAE,MAAM;EAAO,SADN,GAAG,MAAM,eACW,GAAG,IAAI,QAAQ,MAAM,IAAI;EAAE;UACpD,WAAW,KAAK,GAAG,CAE5B,MAAK;EAAE,MAAM;EAAW,SADV,GAAG,MAAM,oBACe,GAAG;EAAI;UACpC,SAAS,KAAK,GAAG,CAC1B,MAAK,EAAE,MAAM,SAAS;AAGxB,QAAO;EACL,KAAK;EACL;EACA;EACA,QAAQ;EACT;;AAGH,SAAS,iBAAiB,aAA+E;CACvG,MAAM,QAAQ,YAAY,MAAM,uDAAuD;AACvF,KAAI,CAAC,MAAO,QAAO,KAAA;AACnB,QAAO;EAAE,SAAS,MAAM;EAAI,QAAQ,MAAM;EAAI;;;;;;AAOhD,SAAgB,wBAAwB,UAA2B,EAAE,EAAgC;AACnG,QAAO,eAA8B;EACnC,MAAM;EACN,OAAO;EACP,UAAU,EAAE,cAAc;GACxB,MAAM,KAAK,UAAU,SAAS,aAAa;AAC3C,UAAO,KAAK,eAAe,GAAG,GAAG,KAAA;;EAEpC,EAAE,QAAQ;;;;;;;;;;;;;;AAeb,SAAgB,kBAAkB,UAA2B,EAAE,EAAgC;AAC7F,QAAO,eAAwB;EAC7B,MAAM;EACN,OAAO;EACP,UAAU,EAAE,cAAc;AACxB,OAAI,CAAC,QAAS,QAAO,KAAA;GACrB,MAAM,MAAe;IACnB,SAAS,UAAU,SAAS,sBAAsB,IAAI,UAAU,SAAS,eAAe;IACxF,QAAQ,UAAU,SAAS,6BAA6B,IAAI,UAAU,SAAS,YAAY;IAC3F,YAAY,UAAU,SAAS,kCAAkC,IAAI,UAAU,SAAS,iBAAiB;IACzG,MAAM,UAAU,SAAS,mBAAmB,IAAI,UAAU,SAAS,UAAU;IAC7E,UAAU,gBAAgB,UAAU,SAAS,uBAAuB,IAAI,UAAU,SAAS,cAAc,CAAC;IAC1G,WAAW,gBAAgB,UAAU,SAAS,wBAAwB,IAAI,UAAU,SAAS,eAAe,CAAC;IAC9G;AACD,UAAO,OAAO,OAAO,IAAI,CAAC,OAAM,UAAS,UAAU,KAAA,EAAU,GAAG,KAAA,IAAY;;EAE/E,EAAE,QAAQ;;;;;;AAOb,SAAgB,0BAA0B,UAA2B,EAAE,EAAgC;AACrG,QAAO,eAAgC;EACrC,MAAM;EACN,OAAO;EACP,UAAU,EAAE,SAAS,eAAe;GAClC,MAAM,eAAe,gBAAgB,UAAU,SAAS,iBAAiB,CAAC;GAC1E,MAAM,gBAAgB,gBAAgB,UAAU,UAAU,SAAS,iBAAiB,CAAC;AACrF,OAAI,iBAAiB,KAAA,KAAa,kBAAkB,KAAA,EAAW,QAAO,KAAA;AACtE,UAAO;IAAE;IAAc;IAAe;;EAEzC,EAAE,QAAQ;;;;;;;AAQb,SAAgB,2BAA2B,UAA2B,EAAE,EAAgC;CACtG,MAAM,WAAW,eAAiC;EAChD,MAAM;EACN,OAAO;EACP,UAAU,EAAE,OAAO,cAAc;GAC/B,MAAM,cAAc,UAAU,SAAS,cAAc;GACrD,MAAM,aAAa,UAAU,SAAS,aAAa;AACnD,OAAI,CAAC,eAAe,CAAC,WAAY,QAAO,KAAA;GACxC,MAAM,SAAS,cAAc,iBAAiB,YAAY,GAAG,KAAA;AAC7D,UAAO;IACL;IACA;IACA,SAAS,QAAQ,WAAY,MAAM;IACnC,QAAQ,QAAQ,UAAW,MAAM;IAClC;;EAEJ,EAAE,QAAQ;AAIX,SAAQ,QAAQ;AACd,WAAS,IAAI;EACb,MAAM,SAAS,IAAI,MAAM;AACzB,MAAI,CAAC,OAAQ;AACb,MAAI,OAAO,YAAY,QAAQ,aAAa,IAAI,MAAM,YAAY,KAAA,GAChE,KAAI,MAAM,UAAU,OAAO;AAE7B,MAAI,OAAO,WAAW,QAAQ,aAAa,IAAI,MAAM,WAAW,KAAA,GAC9D,KAAI,MAAM,SAAS,OAAO;;;;;;;;;;;;;;;AAiBhC,SAAgB,uBAAuB,UAA2B,EAAE,EAAyC;AAC3G,QAAO,iBACL;EACE,wBAAwB,QAAQ;EAChC,kBAAkB,QAAQ;EAC1B,0BAA0B,QAAQ;EAClC,2BAA2B,QAAQ;EACpC,EACD,EAAE,MAAM,qBAAqB,CAC9B"}
@@ -1,4 +1,4 @@
1
- import { R as ErrorOptions } from "./audit-CTIviX3P.mjs";
1
+ import { R as ErrorOptions } from "./audit-CJl-wZ10.mjs";
2
2
 
3
3
  //#region src/error.d.ts
4
4
  /**
@@ -67,4 +67,4 @@ declare class EvlogError extends Error {
67
67
  declare function createError(options: ErrorOptions | string): EvlogError;
68
68
  //#endregion
69
69
  export { createError as n, EvlogError as t };
70
- //# sourceMappingURL=error-C7gSQVqk.d.mts.map
70
+ //# sourceMappingURL=error-C-66_G2M.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-C7gSQVqk.d.mts","names":[],"sources":["../src/error.ts"],"mappings":";;;;;AAqBA;;;;;;;;;;;;;cAAa,UAAA,SAAmB,KAAA;EAY1B;EAAA,SATK,MAAA;EAAA,SACA,GAAA;EAAA,SACA,GAAA;EAAA,SACA,IAAA;EAqCL;;;;EAAA,IA/BA,QAAA,CAAA,GAAY,MAAA;cAIJ,OAAA,EAAS,YAAA;EA0CqB;EAAA,IAftC,UAAA,CAAA;EAuDJ;EAAA,IAlDI,UAAA,CAAA;EAkDY;EAAA,IA7CZ,aAAA,CAAA;EAiFU;EAAA,IA5EV,IAAA,CAAA;IAAU,GAAA;IAAc,GAAA;IAAc,IAAA;EAAA;EAOjC,QAAA,CAAA;EAiCT,MAAA,CAAA,GAAU,MAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;iBAoCI,WAAA,CAAY,OAAA,EAAS,YAAA,YAAwB,UAAA"}
1
+ {"version":3,"file":"error-C-66_G2M.d.mts","names":[],"sources":["../src/error.ts"],"mappings":";;;;;AAqBA;;;;;;;;;;;;;cAAa,UAAA,SAAmB,KAAA;EAY1B;EAAA,SATK,MAAA;EAAA,SACA,GAAA;EAAA,SACA,GAAA;EAAA,SACA,IAAA;EAqCL;;;;EAAA,IA/BA,QAAA,CAAA,GAAY,MAAA;cAIJ,OAAA,EAAS,YAAA;EA0CqB;EAAA,IAftC,UAAA,CAAA;EAuDJ;EAAA,IAlDI,UAAA,CAAA;EAkDY;EAAA,IA7CZ,aAAA,CAAA;EAiFU;EAAA,IA5EV,IAAA,CAAA;IAAU,GAAA;IAAc,GAAA;IAAc,IAAA;EAAA;EAOjC,QAAA,CAAA;EAiCT,MAAA,CAAA,GAAU,MAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;iBAoCI,WAAA,CAAY,OAAA,EAAS,YAAA,YAAwB,UAAA"}
package/dist/error.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as createError, t as EvlogError } from "./error-C7gSQVqk.mjs";
1
+ import { n as createError, t as EvlogError } from "./error-C-66_G2M.mjs";
2
2
  export { EvlogError, createError, createError as createEvlogError };
@@ -15,4 +15,4 @@ function extractErrorStatus(error) {
15
15
  //#endregion
16
16
  export { extractErrorStatus as t };
17
17
 
18
- //# sourceMappingURL=errors-BJRXUfMg.mjs.map
18
+ //# sourceMappingURL=errors-BQgyQ9xe.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors-BJRXUfMg.mjs","names":[],"sources":["../src/shared/errors.ts"],"sourcesContent":["/**\n * Extract HTTP status from an error, checking both `status` and `statusCode`.\n *\n * Works with any error shape (H3, Nitro, EvlogError, plain objects).\n *\n * @beta This function is part of the evlog toolkit API for building custom framework integrations.\n */\nexport function extractErrorStatus(error: unknown): number {\n if (error === null || typeof error !== 'object') return 500\n const raw = (error as { status?: unknown }).status\n ?? (error as { statusCode?: unknown }).statusCode\n const status = Number(raw)\n return Number.isFinite(status) ? status : 500\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,mBAAmB,OAAwB;AACzD,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;CACxD,MAAM,MAAO,MAA+B,UACtC,MAAmC;CACzC,MAAM,SAAS,OAAO,IAAI;AAC1B,QAAO,OAAO,SAAS,OAAO,GAAG,SAAS"}
1
+ {"version":3,"file":"errors-BQgyQ9xe.mjs","names":[],"sources":["../src/shared/errors.ts"],"sourcesContent":["/**\n * Extract HTTP status from an error, checking both `status` and `statusCode`.\n *\n * Works with any error shape (H3, Nitro, EvlogError, plain objects).\n *\n * @beta This function is part of the evlog toolkit API for building custom framework integrations.\n */\nexport function extractErrorStatus(error: unknown): number {\n if (error === null || typeof error !== 'object') return 500\n const raw = (error as { status?: unknown }).status\n ?? (error as { statusCode?: unknown }).statusCode\n const status = Number(raw)\n return Number.isFinite(status) ? status : 500\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,mBAAmB,OAAwB;AACzD,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;CACxD,MAAM,MAAO,MAA+B,UACtC,MAAmC;CACzC,MAAM,SAAS,OAAO,IAAI;AAC1B,QAAO,OAAO,SAAS,OAAO,GAAG,SAAS"}
@@ -1,4 +1,4 @@
1
- import { Z as RouteConfig } from "./audit-CTIviX3P.mjs";
1
+ import { Z as RouteConfig } from "./audit-CJl-wZ10.mjs";
2
2
 
3
3
  //#region src/shared/routes.d.ts
4
4
  declare function shouldLog(path: string, include?: string[], exclude?: string[]): boolean;
@@ -36,4 +36,4 @@ declare function getServiceForPath(path: string, routes?: Record<string, RouteCo
36
36
  declare function extractErrorStatus(error: unknown): number;
37
37
  //#endregion
38
38
  export { getServiceForPath as n, shouldLog as r, extractErrorStatus as t };
39
- //# sourceMappingURL=errors-4MPmTzjY.d.mts.map
39
+ //# sourceMappingURL=errors-DQoYsDW1.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors-4MPmTzjY.d.mts","names":[],"sources":["../src/shared/routes.ts","../src/shared/errors.ts"],"mappings":";;;iBAGgB,SAAA,CAAU,IAAA,UAAc,OAAA,aAAoB,OAAA;;AAA5D;;;;;;;;;AAsCA;;;;;;;;;;;iBAAgB,iBAAA,CAAkB,IAAA,UAAc,MAAA,GAAS,MAAA,SAAe,WAAA;;;;;;AAtCxE;;;;iBCIgB,kBAAA,CAAmB,KAAA"}
1
+ {"version":3,"file":"errors-DQoYsDW1.d.mts","names":[],"sources":["../src/shared/routes.ts","../src/shared/errors.ts"],"mappings":";;;iBAGgB,SAAA,CAAU,IAAA,UAAc,OAAA,aAAoB,OAAA;;AAA5D;;;;;;;;;AAsCA;;;;;;;;;;;iBAAgB,iBAAA,CAAkB,IAAA,UAAc,MAAA,GAAS,MAAA,SAAe,WAAA;;;;;;AAtCxE;;;;iBCIgB,kBAAA,CAAmB,KAAA"}
@@ -0,0 +1,53 @@
1
+ //#region src/shared/event.ts
2
+ /**
3
+ * Helpers for building / mutating wide events from inside enrichers and adapters.
4
+ */
5
+ /**
6
+ * Merge a computed value onto an existing event field. By default, existing
7
+ * object values win over computed ones — so `log.set({ geo: ... })` keeps
8
+ * precedence over an enricher's automatic detection.
9
+ */
10
+ function mergeEventField(existing, computed, overwrite) {
11
+ if (overwrite || existing === void 0 || existing === null || typeof existing !== "object") return computed;
12
+ return {
13
+ ...computed,
14
+ ...existing
15
+ };
16
+ }
17
+ /** Convert a JS value to a {@link TypedAttributeValue}. Objects are JSON-serialized. */
18
+ function toTypedAttributeValue(value) {
19
+ if (value === null || value === void 0) return void 0;
20
+ if (typeof value === "string") return {
21
+ value,
22
+ type: "string"
23
+ };
24
+ if (typeof value === "boolean") return {
25
+ value,
26
+ type: "boolean"
27
+ };
28
+ if (typeof value === "number") {
29
+ if (Number.isInteger(value)) return {
30
+ value,
31
+ type: "integer"
32
+ };
33
+ return {
34
+ value,
35
+ type: "double"
36
+ };
37
+ }
38
+ return {
39
+ value: JSON.stringify(value),
40
+ type: "string"
41
+ };
42
+ }
43
+ /** Convert a JS value to the OTLP `AnyValue` shape (`stringValue` / `intValue` / `boolValue`). */
44
+ function toOtlpAttributeValue(value) {
45
+ if (typeof value === "boolean") return { boolValue: value };
46
+ if (typeof value === "number" && Number.isInteger(value)) return { intValue: String(value) };
47
+ if (typeof value === "string") return { stringValue: value };
48
+ return { stringValue: JSON.stringify(value) };
49
+ }
50
+ //#endregion
51
+ export { toOtlpAttributeValue as n, toTypedAttributeValue as r, mergeEventField as t };
52
+
53
+ //# sourceMappingURL=event-ef-5Dbxg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-ef-5Dbxg.mjs","names":[],"sources":["../src/shared/event.ts"],"sourcesContent":["/**\n * Helpers for building / mutating wide events from inside enrichers and adapters.\n */\n\n/**\n * Merge a computed value onto an existing event field. By default, existing\n * object values win over computed ones — so `log.set({ geo: ... })` keeps\n * precedence over an enricher's automatic detection.\n */\nexport function mergeEventField<T extends object>(\n existing: unknown,\n computed: T,\n overwrite?: boolean,\n): T {\n if (overwrite || existing === undefined || existing === null || typeof existing !== 'object') {\n return computed\n }\n return { ...computed, ...(existing as T) }\n}\n\n/** Typed attribute used when flattening events for OTLP/Sentry/Datadog/PostHog. */\nexport type AttributeValueKind = 'string' | 'integer' | 'double' | 'boolean'\n\nexport interface TypedAttributeValue {\n value: string | number | boolean\n type: AttributeValueKind\n}\n\n/** Convert a JS value to a {@link TypedAttributeValue}. Objects are JSON-serialized. */\nexport function toTypedAttributeValue(value: unknown): TypedAttributeValue | undefined {\n if (value === null || value === undefined) return undefined\n if (typeof value === 'string') return { value, type: 'string' }\n if (typeof value === 'boolean') return { value, type: 'boolean' }\n if (typeof value === 'number') {\n if (Number.isInteger(value)) return { value, type: 'integer' }\n return { value, type: 'double' }\n }\n return { value: JSON.stringify(value), type: 'string' }\n}\n\n/** Convert a JS value to the OTLP `AnyValue` shape (`stringValue` / `intValue` / `boolValue`). */\nexport function toOtlpAttributeValue(value: unknown): {\n stringValue?: string\n intValue?: string\n boolValue?: boolean\n} {\n if (typeof value === 'boolean') return { boolValue: value }\n if (typeof value === 'number' && Number.isInteger(value)) return { intValue: String(value) }\n if (typeof value === 'string') return { stringValue: value }\n return { stringValue: JSON.stringify(value) }\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,gBACd,UACA,UACA,WACG;AACH,KAAI,aAAa,aAAa,KAAA,KAAa,aAAa,QAAQ,OAAO,aAAa,SAClF,QAAO;AAET,QAAO;EAAE,GAAG;EAAU,GAAI;EAAgB;;;AAY5C,SAAgB,sBAAsB,OAAiD;AACrF,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO,KAAA;AAClD,KAAI,OAAO,UAAU,SAAU,QAAO;EAAE;EAAO,MAAM;EAAU;AAC/D,KAAI,OAAO,UAAU,UAAW,QAAO;EAAE;EAAO,MAAM;EAAW;AACjE,KAAI,OAAO,UAAU,UAAU;AAC7B,MAAI,OAAO,UAAU,MAAM,CAAE,QAAO;GAAE;GAAO,MAAM;GAAW;AAC9D,SAAO;GAAE;GAAO,MAAM;GAAU;;AAElC,QAAO;EAAE,OAAO,KAAK,UAAU,MAAM;EAAE,MAAM;EAAU;;;AAIzD,SAAgB,qBAAqB,OAInC;AACA,KAAI,OAAO,UAAU,UAAW,QAAO,EAAE,WAAW,OAAO;AAC3D,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,MAAM,CAAE,QAAO,EAAE,UAAU,OAAO,MAAM,EAAE;AAC5F,KAAI,OAAO,UAAU,SAAU,QAAO,EAAE,aAAa,OAAO;AAC5D,QAAO,EAAE,aAAa,KAAK,UAAU,MAAM,EAAE"}
@@ -1,5 +1,5 @@
1
- import { Y as RequestLogger } from "../audit-CTIviX3P.mjs";
2
- import { t as BaseEvlogOptions } from "../middleware-CTnDsST-.mjs";
1
+ import { Y as RequestLogger } from "../audit-CJl-wZ10.mjs";
2
+ import { t as BaseEvlogOptions } from "../middleware-CGM-bOvE.mjs";
3
3
  import { RequestHandler } from "express";
4
4
 
5
5
  //#region src/express/index.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/express/index.ts"],"mappings":";;;;;cAOiB,SAAA,sBAAS,MAAA,wBAAA,aAAA,CAAA,CAAA;AAAA,KAId,mBAAA,GAAsB,gBAAA;AAAA;EAAA,UAKtB,OAAA;IACR,GAAA,EAAK,aAAA;EAAA;AAAA;;;;;;;;;AANT;;;;;AAEoB;;;;;iBA0BJ,KAAA,CAAM,OAAA,GAAS,mBAAA,GAA2B,cAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/express/index.ts"],"mappings":";;;;;cAMiB,SAAA,sBAAS,MAAA,wBAAA,aAAA,CAAA,CAAA;AAAA,KAId,mBAAA,GAAsB,gBAAA;AAAA;EAAA,UAKtB,OAAA;IACR,GAAA,EAAK,aAAA;EAAA;AAAA;;;;;;;;;AANT;;;;;AAEoB;;;;;iBAwCJ,KAAA,CAAM,OAAA,GAAS,mBAAA,GAA2B,cAAA"}
@@ -1,9 +1,20 @@
1
- import { t as createMiddlewareLogger } from "../middleware-oAccqyPp.mjs";
2
- import { t as attachForkToLogger } from "../fork-D1j1Fuzy.mjs";
3
- import { n as extractSafeNodeHeaders } from "../headers-D74M0wsg.mjs";
4
- import { t as createLoggerStorage } from "../storage-CFGTn37X.mjs";
1
+ import { t as defineFrameworkIntegration } from "../integration-Bz8X6_Lb.mjs";
2
+ import { t as createLoggerStorage } from "../storage-BT-3fT1-.mjs";
5
3
  //#region src/express/index.ts
6
4
  const { storage, useLogger } = createLoggerStorage("middleware context. Make sure app.use(evlog()) is registered before your routes.");
5
+ const integration = defineFrameworkIntegration({
6
+ name: "express",
7
+ extractRequest: (req) => ({
8
+ method: req.method,
9
+ path: new URL(req.originalUrl || req.url || "/", "http://localhost").pathname,
10
+ headers: req.headers,
11
+ requestId: req.get("x-request-id")
12
+ }),
13
+ attachLogger: (req, logger) => {
14
+ req.log = logger;
15
+ },
16
+ storage
17
+ });
7
18
  /**
8
19
  * Create an evlog middleware for Express.
9
20
  *
@@ -24,24 +35,15 @@ const { storage, useLogger } = createLoggerStorage("middleware context. Make sur
24
35
  */
25
36
  function evlog(options = {}) {
26
37
  return (req, res, next) => {
27
- const middlewareOpts = {
28
- method: req.method,
29
- path: new URL(req.originalUrl || req.url || "/", "http://localhost").pathname,
30
- requestId: req.get("x-request-id") || crypto.randomUUID(),
31
- headers: extractSafeNodeHeaders(req.headers),
32
- ...options
33
- };
34
- const { logger, finish, skipped } = createMiddlewareLogger(middlewareOpts);
38
+ const { finish, skipped, runWith } = integration.start(req, options);
35
39
  if (skipped) {
36
40
  next();
37
41
  return;
38
42
  }
39
- attachForkToLogger(storage, logger, middlewareOpts);
40
- req.log = logger;
41
43
  res.on("finish", () => {
42
44
  finish({ status: res.statusCode }).catch(() => {});
43
45
  });
44
- storage.run(logger, () => next());
46
+ runWith(() => next());
45
47
  };
46
48
  }
47
49
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport type { RequestLogger } from '../types'\nimport { createMiddlewareLogger, type BaseEvlogOptions } from '../shared/middleware'\nimport { attachForkToLogger } from '../shared/fork'\nimport { extractSafeNodeHeaders } from '../shared/headers'\nimport { createLoggerStorage } from '../shared/storage'\n\nconst { storage, useLogger } = createLoggerStorage(\n 'middleware context. Make sure app.use(evlog()) is registered before your routes.',\n)\n\nexport type EvlogExpressOptions = BaseEvlogOptions\n\nexport { useLogger }\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n log: RequestLogger\n }\n}\n\n/**\n * Create an evlog middleware for Express.\n *\n * @example\n * ```ts\n * import express from 'express'\n * import { evlog } from 'evlog/express'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * const app = express()\n * app.use(evlog({\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * }))\n * ```\n */\nexport function evlog(options: EvlogExpressOptions = {}): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n const middlewareOpts = {\n method: req.method,\n path: new URL(req.originalUrl || req.url || '/', 'http://localhost').pathname,\n requestId: req.get('x-request-id') || crypto.randomUUID(),\n headers: extractSafeNodeHeaders(req.headers),\n ...options,\n }\n const { logger, finish, skipped } = createMiddlewareLogger(middlewareOpts)\n\n if (skipped) {\n next()\n return\n }\n\n attachForkToLogger(storage, logger, middlewareOpts)\n req.log = logger\n\n res.on('finish', () => {\n finish({ status: res.statusCode }).catch(() => {})\n })\n\n storage.run(logger, () => next())\n }\n}\n"],"mappings":";;;;;AAOA,MAAM,EAAE,SAAS,cAAc,oBAC7B,mFACD;;;;;;;;;;;;;;;;;;;AA8BD,SAAgB,MAAM,UAA+B,EAAE,EAAkB;AACvE,SAAQ,KAAc,KAAe,SAAuB;EAC1D,MAAM,iBAAiB;GACrB,QAAQ,IAAI;GACZ,MAAM,IAAI,IAAI,IAAI,eAAe,IAAI,OAAO,KAAK,mBAAmB,CAAC;GACrE,WAAW,IAAI,IAAI,eAAe,IAAI,OAAO,YAAY;GACzD,SAAS,uBAAuB,IAAI,QAAQ;GAC5C,GAAG;GACJ;EACD,MAAM,EAAE,QAAQ,QAAQ,YAAY,uBAAuB,eAAe;AAE1E,MAAI,SAAS;AACX,SAAM;AACN;;AAGF,qBAAmB,SAAS,QAAQ,eAAe;AACnD,MAAI,MAAM;AAEV,MAAI,GAAG,gBAAgB;AACrB,UAAO,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,YAAY,GAAG;IAClD;AAEF,UAAQ,IAAI,cAAc,MAAM,CAAC"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/express/index.ts"],"sourcesContent":["import type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport type { RequestLogger } from '../types'\nimport { defineFrameworkIntegration } from '../shared/integration'\nimport type { BaseEvlogOptions } from '../shared/middleware'\nimport { createLoggerStorage } from '../shared/storage'\n\nconst { storage, useLogger } = createLoggerStorage(\n 'middleware context. Make sure app.use(evlog()) is registered before your routes.',\n)\n\nexport type EvlogExpressOptions = BaseEvlogOptions\n\nexport { useLogger }\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n log: RequestLogger\n }\n}\n\nconst integration = defineFrameworkIntegration<Request>({\n name: 'express',\n extractRequest: (req) => ({\n method: req.method,\n path: new URL(req.originalUrl || req.url || '/', 'http://localhost').pathname,\n headers: req.headers,\n requestId: req.get('x-request-id'),\n }),\n attachLogger: (req, logger) => {\n req.log = logger\n },\n storage,\n})\n\n/**\n * Create an evlog middleware for Express.\n *\n * @example\n * ```ts\n * import express from 'express'\n * import { evlog } from 'evlog/express'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * const app = express()\n * app.use(evlog({\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * }))\n * ```\n */\nexport function evlog(options: EvlogExpressOptions = {}): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n const { finish, skipped, runWith } = integration.start(req, options)\n\n if (skipped) {\n next()\n return\n }\n\n res.on('finish', () => {\n finish({ status: res.statusCode }).catch(() => {})\n })\n\n void runWith(() => next())\n }\n}\n"],"mappings":";;;AAMA,MAAM,EAAE,SAAS,cAAc,oBAC7B,mFACD;AAYD,MAAM,cAAc,2BAAoC;CACtD,MAAM;CACN,iBAAiB,SAAS;EACxB,QAAQ,IAAI;EACZ,MAAM,IAAI,IAAI,IAAI,eAAe,IAAI,OAAO,KAAK,mBAAmB,CAAC;EACrE,SAAS,IAAI;EACb,WAAW,IAAI,IAAI,eAAe;EACnC;CACD,eAAe,KAAK,WAAW;AAC7B,MAAI,MAAM;;CAEZ;CACD,CAAC;;;;;;;;;;;;;;;;;;;AAoBF,SAAgB,MAAM,UAA+B,EAAE,EAAkB;AACvE,SAAQ,KAAc,KAAe,SAAuB;EAC1D,MAAM,EAAE,QAAQ,SAAS,YAAY,YAAY,MAAM,KAAK,QAAQ;AAEpE,MAAI,SAAS;AACX,SAAM;AACN;;AAGF,MAAI,GAAG,gBAAgB;AACrB,UAAO,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,YAAY,GAAG;IAClD;AAEG,gBAAc,MAAM,CAAC"}
@@ -1,5 +1,5 @@
1
- import { Y as RequestLogger } from "../audit-CTIviX3P.mjs";
2
- import { t as BaseEvlogOptions } from "../middleware-CTnDsST-.mjs";
1
+ import { Y as RequestLogger } from "../audit-CJl-wZ10.mjs";
2
+ import { t as BaseEvlogOptions } from "../middleware-CGM-bOvE.mjs";
3
3
  import { FastifyPluginCallback } from "fastify";
4
4
 
5
5
  //#region src/fastify/index.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/fastify/index.ts"],"mappings":";;;;;cAMiB,SAAA,sBAAS,MAAA,wBAAA,aAAA,CAAA,CAAA;AAAA,KAId,mBAAA,GAAsB,gBAAA;AAAA;EAAA,UAKtB,cAAA;IAER,GAAA;EAAA;AAAA;;;;;;;;;;;AAPJ;;;;;AAEoB;;;;;;cA4FP,KAAA,EAAK,qBAAA,CAAA,gBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/fastify/index.ts"],"mappings":";;;;;cAKiB,SAAA,sBAAS,MAAA,wBAAA,aAAA,CAAA,CAAA;AAAA,KAId,mBAAA,GAAsB,gBAAA;AAAA;EAAA,UAKtB,cAAA;IAER,GAAA;EAAA;AAAA;;;;;;;;;;;AAPJ;;;;;AAEoB;;;;;;cAsFP,KAAA,EAAK,qBAAA,CAAA,gBAAA"}
@@ -1,32 +1,31 @@
1
- import { t as createMiddlewareLogger } from "../middleware-oAccqyPp.mjs";
2
- import { t as attachForkToLogger } from "../fork-D1j1Fuzy.mjs";
3
- import { n as extractSafeNodeHeaders } from "../headers-D74M0wsg.mjs";
4
- import { t as createLoggerStorage } from "../storage-CFGTn37X.mjs";
1
+ import { t as defineFrameworkIntegration } from "../integration-Bz8X6_Lb.mjs";
2
+ import { t as createLoggerStorage } from "../storage-BT-3fT1-.mjs";
5
3
  //#region src/fastify/index.ts
6
4
  const { storage, useLogger } = createLoggerStorage("plugin context. Make sure app.register(evlog) is called before your routes.");
5
+ const integration = defineFrameworkIntegration({
6
+ name: "fastify",
7
+ extractRequest: (req) => ({
8
+ method: req.method,
9
+ path: new URL(req.url, "http://localhost").pathname,
10
+ headers: req.headers,
11
+ requestId: typeof req.headers["x-request-id"] === "string" ? req.headers["x-request-id"] : void 0
12
+ }),
13
+ attachLogger: (req, logger) => {
14
+ req.log = logger;
15
+ },
16
+ storage
17
+ });
7
18
  const evlogPlugin = (fastify, options, done) => {
8
19
  const emitted = /* @__PURE__ */ new WeakSet();
9
20
  const requestState = /* @__PURE__ */ new WeakMap();
10
- fastify.addHook("onRequest", (request, _reply, done) => {
11
- const headers = extractSafeNodeHeaders(request.headers);
12
- const path = new URL(request.url, "http://localhost").pathname;
13
- const middlewareOpts = {
14
- method: request.method,
15
- path,
16
- requestId: headers["x-request-id"] || crypto.randomUUID(),
17
- headers,
18
- ...options
19
- };
20
- const { logger, finish, skipped } = createMiddlewareLogger(middlewareOpts);
21
+ fastify.addHook("onRequest", (request, _reply, next) => {
22
+ const { finish, skipped, runWith } = integration.start(request, options);
21
23
  if (skipped) {
22
- done();
24
+ next();
23
25
  return;
24
26
  }
25
- attachForkToLogger(storage, logger, middlewareOpts);
26
- const req = request;
27
- req.log = logger;
28
27
  requestState.set(request, { finish });
29
- storage.run(logger, () => done());
28
+ runWith(() => next());
30
29
  });
31
30
  fastify.addHook("onResponse", async (request, reply) => {
32
31
  const state = requestState.get(request);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/fastify/index.ts"],"sourcesContent":["import type { FastifyPluginCallback } from 'fastify'\nimport { createMiddlewareLogger, type BaseEvlogOptions } from '../shared/middleware'\nimport { attachForkToLogger } from '../shared/fork'\nimport { extractSafeNodeHeaders } from '../shared/headers'\nimport { createLoggerStorage } from '../shared/storage'\n\nconst { storage, useLogger } = createLoggerStorage(\n 'plugin context. Make sure app.register(evlog) is called before your routes.',\n)\n\nexport type EvlogFastifyOptions = BaseEvlogOptions\n\nexport { useLogger }\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n // Overrides Fastify's built-in pino logger on the request with evlog's RequestLogger.\n log: any\n }\n}\n\ninterface RequestState {\n finish: (opts?: { status?: number; error?: Error }) => Promise<unknown>\n}\n\nconst evlogPlugin: FastifyPluginCallback<EvlogFastifyOptions> = (fastify, options, done) => {\n const emitted = new WeakSet<object>()\n const requestState = new WeakMap<object, RequestState>()\n\n fastify.addHook('onRequest', (request, _reply, done) => {\n const headers = extractSafeNodeHeaders(request.headers)\n const path = new URL(request.url, 'http://localhost').pathname\n\n const middlewareOpts = {\n method: request.method,\n path,\n requestId: headers['x-request-id'] || crypto.randomUUID(),\n headers,\n ...options,\n }\n const { logger, finish, skipped } = createMiddlewareLogger(middlewareOpts)\n\n if (skipped) {\n done()\n return\n }\n\n attachForkToLogger(storage, logger, middlewareOpts)\n\n // Shadow Fastify's built-in pino logger with evlog's request-scoped logger\n const req = request as any\n req.log = logger\n requestState.set(request, { finish })\n\n storage.run(logger, () => done())\n })\n\n fastify.addHook('onResponse', async (request, reply) => {\n const state = requestState.get(request)\n if (!state || emitted.has(request)) return\n emitted.add(request)\n await state.finish({ status: reply.statusCode })\n })\n\n fastify.addHook('onError', async (request, _reply, error) => {\n const state = requestState.get(request)\n if (!state || emitted.has(request)) return\n emitted.add(request)\n const logger = (request as any).log\n const err = error instanceof Error ? error : new Error(String(error))\n if (logger && typeof logger.error === 'function') logger.error(err)\n await state.finish({ error: err })\n })\n\n done()\n}\n\n// Break Fastify plugin encapsulation without a runtime dependency on fastify-plugin.\n// This is the same mechanism fastify-plugin uses internally.\nconst plugin = evlogPlugin as any\nplugin[Symbol.for('skip-override')] = true\nplugin[Symbol.for('fastify.display-name')] = 'evlog'\n\n/**\n * Create an evlog plugin for Fastify.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify'\n * import { initLogger } from 'evlog'\n * import { evlog } from 'evlog/fastify'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * initLogger({ env: { service: 'fastify-api' } })\n *\n * const app = Fastify()\n * await app.register(evlog, {\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * })\n * ```\n */\nexport const evlog = evlogPlugin\n"],"mappings":";;;;;AAMA,MAAM,EAAE,SAAS,cAAc,oBAC7B,8EACD;AAiBD,MAAM,eAA2D,SAAS,SAAS,SAAS;CAC1F,MAAM,0BAAU,IAAI,SAAiB;CACrC,MAAM,+BAAe,IAAI,SAA+B;AAExD,SAAQ,QAAQ,cAAc,SAAS,QAAQ,SAAS;EACtD,MAAM,UAAU,uBAAuB,QAAQ,QAAQ;EACvD,MAAM,OAAO,IAAI,IAAI,QAAQ,KAAK,mBAAmB,CAAC;EAEtD,MAAM,iBAAiB;GACrB,QAAQ,QAAQ;GAChB;GACA,WAAW,QAAQ,mBAAmB,OAAO,YAAY;GACzD;GACA,GAAG;GACJ;EACD,MAAM,EAAE,QAAQ,QAAQ,YAAY,uBAAuB,eAAe;AAE1E,MAAI,SAAS;AACX,SAAM;AACN;;AAGF,qBAAmB,SAAS,QAAQ,eAAe;EAGnD,MAAM,MAAM;AACZ,MAAI,MAAM;AACV,eAAa,IAAI,SAAS,EAAE,QAAQ,CAAC;AAErC,UAAQ,IAAI,cAAc,MAAM,CAAC;GACjC;AAEF,SAAQ,QAAQ,cAAc,OAAO,SAAS,UAAU;EACtD,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,QAAQ,IAAI,QAAQ,CAAE;AACpC,UAAQ,IAAI,QAAQ;AACpB,QAAM,MAAM,OAAO,EAAE,QAAQ,MAAM,YAAY,CAAC;GAChD;AAEF,SAAQ,QAAQ,WAAW,OAAO,SAAS,QAAQ,UAAU;EAC3D,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,QAAQ,IAAI,QAAQ,CAAE;AACpC,UAAQ,IAAI,QAAQ;EACpB,MAAM,SAAU,QAAgB;EAChC,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,MAAI,UAAU,OAAO,OAAO,UAAU,WAAY,QAAO,MAAM,IAAI;AACnE,QAAM,MAAM,OAAO,EAAE,OAAO,KAAK,CAAC;GAClC;AAEF,OAAM;;AAKR,MAAM,SAAS;AACf,OAAO,OAAO,IAAI,gBAAgB,IAAI;AACtC,OAAO,OAAO,IAAI,uBAAuB,IAAI;;;;;;;;;;;;;;;;;;;;;;AAuB7C,MAAa,QAAQ"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/fastify/index.ts"],"sourcesContent":["import type { FastifyPluginCallback, FastifyRequest } from 'fastify'\nimport { defineFrameworkIntegration } from '../shared/integration'\nimport type { BaseEvlogOptions } from '../shared/middleware'\nimport { createLoggerStorage } from '../shared/storage'\n\nconst { storage, useLogger } = createLoggerStorage(\n 'plugin context. Make sure app.register(evlog) is called before your routes.',\n)\n\nexport type EvlogFastifyOptions = BaseEvlogOptions\n\nexport { useLogger }\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n // Overrides Fastify's built-in pino logger on the request with evlog's RequestLogger.\n log: any\n }\n}\n\ninterface RequestState {\n finish: (opts?: { status?: number; error?: Error }) => Promise<unknown>\n}\n\nconst integration = defineFrameworkIntegration<FastifyRequest>({\n name: 'fastify',\n extractRequest: (req) => ({\n method: req.method,\n path: new URL(req.url, 'http://localhost').pathname,\n headers: req.headers,\n requestId: typeof req.headers['x-request-id'] === 'string' ? req.headers['x-request-id'] : undefined,\n }),\n attachLogger: (req, logger) => {\n (req as any).log = logger\n },\n storage,\n})\n\nconst evlogPlugin: FastifyPluginCallback<EvlogFastifyOptions> = (fastify, options, done) => {\n const emitted = new WeakSet<object>()\n const requestState = new WeakMap<object, RequestState>()\n\n fastify.addHook('onRequest', (request, _reply, next) => {\n const { finish, skipped, runWith } = integration.start(request, options)\n if (skipped) {\n next()\n return\n }\n requestState.set(request, { finish })\n void runWith(() => next())\n })\n\n fastify.addHook('onResponse', async (request, reply) => {\n const state = requestState.get(request)\n if (!state || emitted.has(request)) return\n emitted.add(request)\n await state.finish({ status: reply.statusCode })\n })\n\n fastify.addHook('onError', async (request, _reply, error) => {\n const state = requestState.get(request)\n if (!state || emitted.has(request)) return\n emitted.add(request)\n const logger = (request as any).log\n const err = error instanceof Error ? error : new Error(String(error))\n if (logger && typeof logger.error === 'function') logger.error(err)\n await state.finish({ error: err })\n })\n\n done()\n}\n\nconst plugin = evlogPlugin as any\nplugin[Symbol.for('skip-override')] = true\nplugin[Symbol.for('fastify.display-name')] = 'evlog'\n\n/**\n * Create an evlog plugin for Fastify.\n *\n * @example\n * ```ts\n * import Fastify from 'fastify'\n * import { initLogger } from 'evlog'\n * import { evlog } from 'evlog/fastify'\n * import { createAxiomDrain } from 'evlog/axiom'\n *\n * initLogger({ env: { service: 'fastify-api' } })\n *\n * const app = Fastify()\n * await app.register(evlog, {\n * drain: createAxiomDrain(),\n * enrich: (ctx) => {\n * ctx.event.region = process.env.FLY_REGION\n * },\n * })\n * ```\n */\nexport const evlog = evlogPlugin\n"],"mappings":";;;AAKA,MAAM,EAAE,SAAS,cAAc,oBAC7B,8EACD;AAiBD,MAAM,cAAc,2BAA2C;CAC7D,MAAM;CACN,iBAAiB,SAAS;EACxB,QAAQ,IAAI;EACZ,MAAM,IAAI,IAAI,IAAI,KAAK,mBAAmB,CAAC;EAC3C,SAAS,IAAI;EACb,WAAW,OAAO,IAAI,QAAQ,oBAAoB,WAAW,IAAI,QAAQ,kBAAkB,KAAA;EAC5F;CACD,eAAe,KAAK,WAAW;AAC5B,MAAY,MAAM;;CAErB;CACD,CAAC;AAEF,MAAM,eAA2D,SAAS,SAAS,SAAS;CAC1F,MAAM,0BAAU,IAAI,SAAiB;CACrC,MAAM,+BAAe,IAAI,SAA+B;AAExD,SAAQ,QAAQ,cAAc,SAAS,QAAQ,SAAS;EACtD,MAAM,EAAE,QAAQ,SAAS,YAAY,YAAY,MAAM,SAAS,QAAQ;AACxE,MAAI,SAAS;AACX,SAAM;AACN;;AAEF,eAAa,IAAI,SAAS,EAAE,QAAQ,CAAC;AAChC,gBAAc,MAAM,CAAC;GAC1B;AAEF,SAAQ,QAAQ,cAAc,OAAO,SAAS,UAAU;EACtD,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,QAAQ,IAAI,QAAQ,CAAE;AACpC,UAAQ,IAAI,QAAQ;AACpB,QAAM,MAAM,OAAO,EAAE,QAAQ,MAAM,YAAY,CAAC;GAChD;AAEF,SAAQ,QAAQ,WAAW,OAAO,SAAS,QAAQ,UAAU;EAC3D,MAAM,QAAQ,aAAa,IAAI,QAAQ;AACvC,MAAI,CAAC,SAAS,QAAQ,IAAI,QAAQ,CAAE;AACpC,UAAQ,IAAI,QAAQ;EACpB,MAAM,SAAU,QAAgB;EAChC,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,MAAI,UAAU,OAAO,OAAO,UAAU,WAAY,QAAO,MAAM,IAAI;AACnE,QAAM,MAAM,OAAO,EAAE,OAAO,KAAK,CAAC;GAClC;AAEF,OAAM;;AAGR,MAAM,SAAS;AACf,OAAO,OAAO,IAAI,gBAAgB,IAAI;AACtC,OAAO,OAAO,IAAI,uBAAuB,IAAI;;;;;;;;;;;;;;;;;;;;;;AAuB7C,MAAa,QAAQ"}