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,5 +1,4 @@
1
- import { n as resolveAdapterConfig, t as httpPost } from "../_http-BY1e9pwC.mjs";
2
- import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
1
+ import { a as resolveAdapterConfig, n as defineHttpDrain, r as httpPost, t as defineDrain } from "../drain-ByWUeOQC.mjs";
3
2
  import { sendBatchToOTLP } from "./otlp.mjs";
4
3
  //#region src/adapters/posthog.ts
5
4
  const POSTHOG_FIELDS = [
@@ -11,14 +10,12 @@ const POSTHOG_FIELDS = [
11
10
  key: "host",
12
11
  env: ["NUXT_POSTHOG_HOST", "POSTHOG_HOST"]
13
12
  },
13
+ { key: "mode" },
14
+ { key: "eventName" },
15
+ { key: "distinctId" },
14
16
  { key: "timeout" },
15
17
  { key: "retries" }
16
18
  ];
17
- const POSTHOG_EVENTS_FIELDS = [
18
- ...POSTHOG_FIELDS,
19
- { key: "eventName" },
20
- { key: "distinctId" }
21
- ];
22
19
  function resolveHost(config) {
23
20
  return (config.host ?? "https://us.i.posthog.com").replace(/\/$/, "");
24
21
  }
@@ -31,7 +28,7 @@ function toOTLPConfig(config) {
31
28
  };
32
29
  }
33
30
  /**
34
- * Convert a WideEvent to a PostHog custom event format.
31
+ * Convert a WideEvent to a PostHog custom event.
35
32
  */
36
33
  function toPostHogEvent(event, config) {
37
34
  const { timestamp, level, service, ...rest } = event;
@@ -47,7 +44,10 @@ function toPostHogEvent(event, config) {
47
44
  };
48
45
  }
49
46
  /**
50
- * Create a drain function for sending logs to PostHog Logs via OTLP.
47
+ * Create a drain function for sending logs to PostHog.
48
+ *
49
+ * - Default `mode: 'logs'` — sends events to PostHog Logs via OTLP. Recommended.
50
+ * - `mode: 'events'` — sends events to the `/batch/` API as custom events.
51
51
  *
52
52
  * Configuration priority (highest to lowest):
53
53
  * 1. Overrides passed to createPostHogDrain()
@@ -57,123 +57,88 @@ function toPostHogEvent(event, config) {
57
57
  *
58
58
  * @example
59
59
  * ```ts
60
- * // Zero config - just set NUXT_POSTHOG_API_KEY env var
61
- * nitroApp.hooks.hook('evlog:drain', createPostHogDrain())
60
+ * // Default: PostHog Logs (OTLP)
61
+ * initLogger({ drain: createPostHogDrain() })
62
62
  *
63
- * // With overrides
64
- * nitroApp.hooks.hook('evlog:drain', createPostHogDrain({
65
- * apiKey: 'phc_...',
66
- * host: 'https://eu.i.posthog.com',
67
- * }))
63
+ * // Custom events
64
+ * initLogger({ drain: createPostHogDrain({ mode: 'events', eventName: 'server_request' }) })
68
65
  * ```
69
66
  */
70
67
  function createPostHogDrain(overrides) {
68
+ if ((overrides?.mode ?? "logs") === "events") return defineHttpDrain({
69
+ name: "posthog-events",
70
+ resolve: async () => {
71
+ const config = await resolveAdapterConfig("posthog", POSTHOG_FIELDS, overrides);
72
+ if (!config.apiKey) {
73
+ console.error("[evlog/posthog-events] Missing apiKey. Set NUXT_POSTHOG_API_KEY env var or pass to createPostHogDrain({ mode: 'events' })");
74
+ return null;
75
+ }
76
+ return config;
77
+ },
78
+ encode: (events, config) => ({
79
+ url: `${resolveHost(config)}/batch/`,
80
+ headers: { "Content-Type": "application/json" },
81
+ body: JSON.stringify({
82
+ api_key: config.apiKey,
83
+ batch: events.map((event) => toPostHogEvent(event, config))
84
+ })
85
+ })
86
+ });
71
87
  return defineDrain({
72
88
  name: "posthog",
73
89
  resolve: async () => {
74
90
  const config = await resolveAdapterConfig("posthog", POSTHOG_FIELDS, overrides);
75
91
  if (!config.apiKey) {
76
- console.error("[evlog/posthog] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogDrain()");
92
+ console.error("[evlog/posthog] Missing apiKey. Set NUXT_POSTHOG_API_KEY env var or pass to createPostHogDrain()");
77
93
  return null;
78
94
  }
79
95
  return config;
80
96
  },
81
- send: sendBatchToPostHog
97
+ send: async (events, config) => {
98
+ if (events.length === 0) return;
99
+ await sendBatchToOTLP(events, toOTLPConfig(config));
100
+ }
101
+ });
102
+ }
103
+ /**
104
+ * @deprecated Use {@link createPostHogDrain} with `mode: 'events'`.
105
+ */
106
+ function createPostHogEventsDrain(overrides) {
107
+ return createPostHogDrain({
108
+ ...overrides,
109
+ mode: "events"
82
110
  });
83
111
  }
84
112
  /**
85
113
  * Send a single event to PostHog Logs via OTLP.
86
- *
87
- * @example
88
- * ```ts
89
- * await sendToPostHog(event, {
90
- * apiKey: process.env.POSTHOG_API_KEY!,
91
- * })
92
- * ```
93
114
  */
94
115
  async function sendToPostHog(event, config) {
95
116
  await sendBatchToPostHog([event], config);
96
117
  }
97
118
  /**
98
119
  * Send a batch of events to PostHog Logs via OTLP.
99
- *
100
- * @example
101
- * ```ts
102
- * await sendBatchToPostHog(events, {
103
- * apiKey: process.env.POSTHOG_API_KEY!,
104
- * })
105
- * ```
106
120
  */
107
121
  async function sendBatchToPostHog(events, config) {
108
122
  if (events.length === 0) return;
109
123
  await sendBatchToOTLP(events, toOTLPConfig(config));
110
124
  }
111
125
  /**
112
- * Create a drain function for sending logs to PostHog as custom events.
113
- *
114
- * Uses PostHog's `/batch/` API. Consider using `createPostHogDrain()` instead
115
- * which uses PostHog Logs (OTLP) and is significantly cheaper.
116
- *
117
- * Configuration priority (highest to lowest):
118
- * 1. Overrides passed to createPostHogEventsDrain()
119
- * 2. runtimeConfig.evlog.posthog
120
- * 3. runtimeConfig.posthog
121
- * 4. Environment variables: NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY, NUXT_POSTHOG_HOST/POSTHOG_HOST
122
- *
123
- * @example
124
- * ```ts
125
- * nitroApp.hooks.hook('evlog:drain', createPostHogEventsDrain({
126
- * eventName: 'server_request',
127
- * }))
128
- * ```
129
- */
130
- function createPostHogEventsDrain(overrides) {
131
- return defineDrain({
132
- name: "posthog-events",
133
- resolve: async () => {
134
- const config = await resolveAdapterConfig("posthog", POSTHOG_EVENTS_FIELDS, overrides);
135
- if (!config.apiKey) {
136
- console.error("[evlog/posthog-events] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogEventsDrain()");
137
- return null;
138
- }
139
- return config;
140
- },
141
- send: sendBatchToPostHogEvents
142
- });
143
- }
144
- /**
145
- * Send a single event to PostHog as a custom event.
146
- *
147
- * @example
148
- * ```ts
149
- * await sendToPostHogEvents(event, {
150
- * apiKey: process.env.POSTHOG_API_KEY!,
151
- * })
152
- * ```
126
+ * Send a single event to PostHog via the custom-events `/batch/` API.
153
127
  */
154
128
  async function sendToPostHogEvents(event, config) {
155
129
  await sendBatchToPostHogEvents([event], config);
156
130
  }
157
131
  /**
158
- * Send a batch of events to PostHog as custom events via the `/batch/` API.
159
- *
160
- * @example
161
- * ```ts
162
- * await sendBatchToPostHogEvents(events, {
163
- * apiKey: process.env.POSTHOG_API_KEY!,
164
- * })
165
- * ```
132
+ * Send a batch of events to PostHog via the custom-events `/batch/` API.
166
133
  */
167
134
  async function sendBatchToPostHogEvents(events, config) {
168
135
  if (events.length === 0) return;
169
- const url = `${resolveHost(config)}/batch/`;
170
- const batch = events.map((event) => toPostHogEvent(event, config));
171
136
  await httpPost({
172
- url,
137
+ url: `${resolveHost(config)}/batch/`,
173
138
  headers: { "Content-Type": "application/json" },
174
139
  body: JSON.stringify({
175
140
  api_key: config.apiKey,
176
- batch
141
+ batch: events.map((event) => toPostHogEvent(event, config))
177
142
  }),
178
143
  timeout: config.timeout ?? 5e3,
179
144
  retries: config.retries,
@@ -1 +1 @@
1
- {"version":3,"file":"posthog.mjs","names":[],"sources":["../../src/adapters/posthog.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\nimport { sendBatchToOTLP } from './otlp'\nimport type { OTLPConfig } from './otlp'\n\nexport interface PostHogConfig {\n /** PostHog project API key */\n apiKey: string\n /** PostHog host URL. Default: https://us.i.posthog.com */\n host?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\nexport interface PostHogEventsConfig extends PostHogConfig {\n /** PostHog event name. Default: evlog_wide_event */\n eventName?: string\n /** Override distinct_id (defaults to event.service) */\n distinctId?: string\n}\n\n/** PostHog event structure for the batch API */\nexport interface PostHogEvent {\n event: string\n distinct_id: string\n timestamp: string\n properties: Record<string, unknown>\n}\n\nconst POSTHOG_FIELDS: ConfigField<PostHogConfig>[] = [\n { key: 'apiKey', env: ['NUXT_POSTHOG_API_KEY', 'POSTHOG_API_KEY'] },\n { key: 'host', env: ['NUXT_POSTHOG_HOST', 'POSTHOG_HOST'] },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\nconst POSTHOG_EVENTS_FIELDS: ConfigField<PostHogEventsConfig>[] = [\n ...POSTHOG_FIELDS,\n { key: 'eventName' },\n { key: 'distinctId' },\n]\n\nfunction resolveHost(config: PostHogConfig): string {\n return (config.host ?? 'https://us.i.posthog.com').replace(/\\/$/, '')\n}\n\nfunction toOTLPConfig(config: PostHogConfig): OTLPConfig {\n const host = resolveHost(config)\n return {\n endpoint: `${host}/i`,\n headers: { Authorization: `Bearer ${config.apiKey}` },\n timeout: config.timeout,\n retries: config.retries,\n }\n}\n\n/**\n * Convert a WideEvent to a PostHog custom event format.\n */\nexport function toPostHogEvent(event: WideEvent, config: PostHogEventsConfig): PostHogEvent {\n const { timestamp, level, service, ...rest } = event\n\n return {\n event: config.eventName ?? 'evlog_wide_event',\n distinct_id: config.distinctId ?? (typeof event.userId === 'string' ? event.userId : undefined) ?? service,\n timestamp,\n properties: {\n level,\n service,\n ...rest,\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// PostHog Logs (OTLP) — default\n// ---------------------------------------------------------------------------\n\n/**\n * Create a drain function for sending logs to PostHog Logs via OTLP.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createPostHogDrain()\n * 2. runtimeConfig.evlog.posthog\n * 3. runtimeConfig.posthog\n * 4. Environment variables: NUXT_POSTHOG_*, POSTHOG_*\n *\n * @example\n * ```ts\n * // Zero config - just set NUXT_POSTHOG_API_KEY env var\n * nitroApp.hooks.hook('evlog:drain', createPostHogDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog:drain', createPostHogDrain({\n * apiKey: 'phc_...',\n * host: 'https://eu.i.posthog.com',\n * }))\n * ```\n */\nexport function createPostHogDrain(overrides?: Partial<PostHogConfig>) {\n return defineDrain<PostHogConfig>({\n name: 'posthog',\n resolve: async () => {\n const config = await resolveAdapterConfig<PostHogConfig>('posthog', POSTHOG_FIELDS, overrides)\n if (!config.apiKey) {\n console.error('[evlog/posthog] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogDrain()')\n return null\n }\n return config as PostHogConfig\n },\n send: sendBatchToPostHog,\n })\n}\n\n/**\n * Send a single event to PostHog Logs via OTLP.\n *\n * @example\n * ```ts\n * await sendToPostHog(event, {\n * apiKey: process.env.POSTHOG_API_KEY!,\n * })\n * ```\n */\nexport async function sendToPostHog(event: WideEvent, config: PostHogConfig): Promise<void> {\n await sendBatchToPostHog([event], config)\n}\n\n/**\n * Send a batch of events to PostHog Logs via OTLP.\n *\n * @example\n * ```ts\n * await sendBatchToPostHog(events, {\n * apiKey: process.env.POSTHOG_API_KEY!,\n * })\n * ```\n */\nexport async function sendBatchToPostHog(events: WideEvent[], config: PostHogConfig): Promise<void> {\n if (events.length === 0) return\n await sendBatchToOTLP(events, toOTLPConfig(config))\n}\n\n// ---------------------------------------------------------------------------\n// PostHog Events (custom events via /batch/)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a drain function for sending logs to PostHog as custom events.\n *\n * Uses PostHog's `/batch/` API. Consider using `createPostHogDrain()` instead\n * which uses PostHog Logs (OTLP) and is significantly cheaper.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createPostHogEventsDrain()\n * 2. runtimeConfig.evlog.posthog\n * 3. runtimeConfig.posthog\n * 4. Environment variables: NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY, NUXT_POSTHOG_HOST/POSTHOG_HOST\n *\n * @example\n * ```ts\n * nitroApp.hooks.hook('evlog:drain', createPostHogEventsDrain({\n * eventName: 'server_request',\n * }))\n * ```\n */\nexport function createPostHogEventsDrain(overrides?: Partial<PostHogEventsConfig>) {\n return defineDrain<PostHogEventsConfig>({\n name: 'posthog-events',\n resolve: async () => {\n const config = await resolveAdapterConfig<PostHogEventsConfig>('posthog', POSTHOG_EVENTS_FIELDS, overrides)\n if (!config.apiKey) {\n console.error('[evlog/posthog-events] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogEventsDrain()')\n return null\n }\n return config as PostHogEventsConfig\n },\n send: sendBatchToPostHogEvents,\n })\n}\n\n/**\n * Send a single event to PostHog as a custom event.\n *\n * @example\n * ```ts\n * await sendToPostHogEvents(event, {\n * apiKey: process.env.POSTHOG_API_KEY!,\n * })\n * ```\n */\nexport async function sendToPostHogEvents(event: WideEvent, config: PostHogEventsConfig): Promise<void> {\n await sendBatchToPostHogEvents([event], config)\n}\n\n/**\n * Send a batch of events to PostHog as custom events via the `/batch/` API.\n *\n * @example\n * ```ts\n * await sendBatchToPostHogEvents(events, {\n * apiKey: process.env.POSTHOG_API_KEY!,\n * })\n * ```\n */\nexport async function sendBatchToPostHogEvents(events: WideEvent[], config: PostHogEventsConfig): Promise<void> {\n if (events.length === 0) return\n\n const url = `${resolveHost(config)}/batch/`\n const batch = events.map(event => toPostHogEvent(event, config))\n\n await httpPost({\n url,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ api_key: config.apiKey, batch }),\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'PostHog',\n })\n}\n"],"mappings":";;;;AAkCA,MAAM,iBAA+C;CACnD;EAAE,KAAK;EAAU,KAAK,CAAC,wBAAwB,kBAAkB;EAAE;CACnE;EAAE,KAAK;EAAQ,KAAK,CAAC,qBAAqB,eAAe;EAAE;CAC3D,EAAE,KAAK,WAAW;CAClB,EAAE,KAAK,WAAW;CACnB;AAED,MAAM,wBAA4D;CAChE,GAAG;CACH,EAAE,KAAK,aAAa;CACpB,EAAE,KAAK,cAAc;CACtB;AAED,SAAS,YAAY,QAA+B;AAClD,SAAQ,OAAO,QAAQ,4BAA4B,QAAQ,OAAO,GAAG;;AAGvE,SAAS,aAAa,QAAmC;AAEvD,QAAO;EACL,UAAU,GAFC,YAAY,OAEN,CAAC;EAClB,SAAS,EAAE,eAAe,UAAU,OAAO,UAAU;EACrD,SAAS,OAAO;EAChB,SAAS,OAAO;EACjB;;;;;AAMH,SAAgB,eAAe,OAAkB,QAA2C;CAC1F,MAAM,EAAE,WAAW,OAAO,SAAS,GAAG,SAAS;AAE/C,QAAO;EACL,OAAO,OAAO,aAAa;EAC3B,aAAa,OAAO,eAAe,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS,KAAA,MAAc;EACnG;EACA,YAAY;GACV;GACA;GACA,GAAG;GACJ;EACF;;;;;;;;;;;;;;;;;;;;;;;AA4BH,SAAgB,mBAAmB,WAAoC;AACrE,QAAO,YAA2B;EAChC,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAAoC,WAAW,gBAAgB,UAAU;AAC9F,OAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,MAAM,mHAAmH;AACjI,WAAO;;AAET,UAAO;;EAET,MAAM;EACP,CAAC;;;;;;;;;;;;AAaJ,eAAsB,cAAc,OAAkB,QAAsC;AAC1F,OAAM,mBAAmB,CAAC,MAAM,EAAE,OAAO;;;;;;;;;;;;AAa3C,eAAsB,mBAAmB,QAAqB,QAAsC;AAClG,KAAI,OAAO,WAAW,EAAG;AACzB,OAAM,gBAAgB,QAAQ,aAAa,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;AA0BrD,SAAgB,yBAAyB,WAA0C;AACjF,QAAO,YAAiC;EACtC,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAA0C,WAAW,uBAAuB,UAAU;AAC3G,OAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,MAAM,gIAAgI;AAC9I,WAAO;;AAET,UAAO;;EAET,MAAM;EACP,CAAC;;;;;;;;;;;;AAaJ,eAAsB,oBAAoB,OAAkB,QAA4C;AACtG,OAAM,yBAAyB,CAAC,MAAM,EAAE,OAAO;;;;;;;;;;;;AAajD,eAAsB,yBAAyB,QAAqB,QAA4C;AAC9G,KAAI,OAAO,WAAW,EAAG;CAEzB,MAAM,MAAM,GAAG,YAAY,OAAO,CAAC;CACnC,MAAM,QAAQ,OAAO,KAAI,UAAS,eAAe,OAAO,OAAO,CAAC;AAEhE,OAAM,SAAS;EACb;EACA,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU;GAAE,SAAS,OAAO;GAAQ;GAAO,CAAC;EACvD,SAAS,OAAO,WAAW;EAC3B,SAAS,OAAO;EAChB,OAAO;EACR,CAAC"}
1
+ {"version":3,"file":"posthog.mjs","names":[],"sources":["../../src/adapters/posthog.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from '../shared/config'\nimport { resolveAdapterConfig } from '../shared/config'\nimport { defineDrain, defineHttpDrain } from '../shared/drain'\nimport { httpPost } from '../shared/http'\nimport { sendBatchToOTLP } from './otlp'\nimport type { OTLPConfig } from './otlp'\n\n/**\n * Mode for {@link createPostHogDrain}.\n *\n * - `'logs'` (default) — sends events to PostHog Logs via OTLP. Cheapest path\n * and recommended for most teams.\n * - `'events'` — sends events to the `/batch/` API as custom PostHog events.\n * Useful when you want events to appear in PostHog product analytics\n * funnels/dashboards.\n */\nexport type PostHogMode = 'logs' | 'events'\n\nexport interface PostHogConfig {\n /** PostHog project API key */\n apiKey: string\n /** PostHog host URL. Default: https://us.i.posthog.com */\n host?: string\n /**\n * Send mode. `'logs'` (default) uses PostHog Logs (OTLP, cheapest);\n * `'events'` uses the `/batch/` API for custom PostHog events.\n * @default 'logs'\n */\n mode?: PostHogMode\n /**\n * PostHog event name when `mode === 'events'`. Ignored otherwise.\n * @default 'evlog_wide_event'\n */\n eventName?: string\n /**\n * Override `distinct_id` when `mode === 'events'`. Ignored otherwise.\n * Defaults to `event.userId` (when set) or `event.service`.\n */\n distinctId?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\n/**\n * @deprecated Use {@link PostHogConfig} with `mode: 'events'` instead.\n */\nexport type PostHogEventsConfig = PostHogConfig\n\n/** PostHog event structure for the batch API */\nexport interface PostHogEvent {\n event: string\n distinct_id: string\n timestamp: string\n properties: Record<string, unknown>\n}\n\nconst POSTHOG_FIELDS: ConfigField<PostHogConfig>[] = [\n { key: 'apiKey', env: ['NUXT_POSTHOG_API_KEY', 'POSTHOG_API_KEY'] },\n { key: 'host', env: ['NUXT_POSTHOG_HOST', 'POSTHOG_HOST'] },\n { key: 'mode' },\n { key: 'eventName' },\n { key: 'distinctId' },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\nfunction resolveHost(config: PostHogConfig): string {\n return (config.host ?? 'https://us.i.posthog.com').replace(/\\/$/, '')\n}\n\nfunction toOTLPConfig(config: PostHogConfig): OTLPConfig {\n return {\n endpoint: `${resolveHost(config)}/i`,\n headers: { Authorization: `Bearer ${config.apiKey}` },\n timeout: config.timeout,\n retries: config.retries,\n }\n}\n\n/**\n * Convert a WideEvent to a PostHog custom event.\n */\nexport function toPostHogEvent(event: WideEvent, config: PostHogConfig): PostHogEvent {\n const { timestamp, level, service, ...rest } = event\n return {\n event: config.eventName ?? 'evlog_wide_event',\n distinct_id: config.distinctId\n ?? (typeof event.userId === 'string' ? event.userId : undefined)\n ?? service,\n timestamp,\n properties: {\n level,\n service,\n ...rest,\n },\n }\n}\n\n/**\n * Create a drain function for sending logs to PostHog.\n *\n * - Default `mode: 'logs'` — sends events to PostHog Logs via OTLP. Recommended.\n * - `mode: 'events'` — sends events to the `/batch/` API as custom events.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createPostHogDrain()\n * 2. runtimeConfig.evlog.posthog\n * 3. runtimeConfig.posthog\n * 4. Environment variables: NUXT_POSTHOG_*, POSTHOG_*\n *\n * @example\n * ```ts\n * // Default: PostHog Logs (OTLP)\n * initLogger({ drain: createPostHogDrain() })\n *\n * // Custom events\n * initLogger({ drain: createPostHogDrain({ mode: 'events', eventName: 'server_request' }) })\n * ```\n */\nexport function createPostHogDrain(overrides?: Partial<PostHogConfig>) {\n const mode: PostHogMode = overrides?.mode ?? 'logs'\n\n if (mode === 'events') {\n return defineHttpDrain<PostHogConfig>({\n name: 'posthog-events',\n resolve: async () => {\n const config = await resolveAdapterConfig<PostHogConfig>('posthog', POSTHOG_FIELDS, overrides)\n if (!config.apiKey) {\n console.error('[evlog/posthog-events] Missing apiKey. Set NUXT_POSTHOG_API_KEY env var or pass to createPostHogDrain({ mode: \\'events\\' })')\n return null\n }\n return config as PostHogConfig\n },\n encode: (events, config) => ({\n url: `${resolveHost(config)}/batch/`,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n api_key: config.apiKey,\n batch: events.map(event => toPostHogEvent(event, config)),\n }),\n }),\n })\n }\n\n return defineDrain<PostHogConfig>({\n name: 'posthog',\n resolve: async () => {\n const config = await resolveAdapterConfig<PostHogConfig>('posthog', POSTHOG_FIELDS, overrides)\n if (!config.apiKey) {\n console.error('[evlog/posthog] Missing apiKey. Set NUXT_POSTHOG_API_KEY env var or pass to createPostHogDrain()')\n return null\n }\n return config as PostHogConfig\n },\n send: async (events, config) => {\n if (events.length === 0) return\n await sendBatchToOTLP(events, toOTLPConfig(config))\n },\n })\n}\n\n/**\n * @deprecated Use {@link createPostHogDrain} with `mode: 'events'`.\n */\nexport function createPostHogEventsDrain(overrides?: Partial<PostHogConfig>) {\n return createPostHogDrain({ ...overrides, mode: 'events' })\n}\n\n/**\n * Send a single event to PostHog Logs via OTLP.\n */\nexport async function sendToPostHog(event: WideEvent, config: PostHogConfig): Promise<void> {\n await sendBatchToPostHog([event], config)\n}\n\n/**\n * Send a batch of events to PostHog Logs via OTLP.\n */\nexport async function sendBatchToPostHog(events: WideEvent[], config: PostHogConfig): Promise<void> {\n if (events.length === 0) return\n await sendBatchToOTLP(events, toOTLPConfig(config))\n}\n\n/**\n * Send a single event to PostHog via the custom-events `/batch/` API.\n */\nexport async function sendToPostHogEvents(event: WideEvent, config: PostHogConfig): Promise<void> {\n await sendBatchToPostHogEvents([event], config)\n}\n\n/**\n * Send a batch of events to PostHog via the custom-events `/batch/` API.\n */\nexport async function sendBatchToPostHogEvents(events: WideEvent[], config: PostHogConfig): Promise<void> {\n if (events.length === 0) return\n await httpPost({\n url: `${resolveHost(config)}/batch/`,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n api_key: config.apiKey,\n batch: events.map(event => toPostHogEvent(event, config)),\n }),\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'PostHog',\n })\n}\n"],"mappings":";;;AA2DA,MAAM,iBAA+C;CACnD;EAAE,KAAK;EAAU,KAAK,CAAC,wBAAwB,kBAAkB;EAAE;CACnE;EAAE,KAAK;EAAQ,KAAK,CAAC,qBAAqB,eAAe;EAAE;CAC3D,EAAE,KAAK,QAAQ;CACf,EAAE,KAAK,aAAa;CACpB,EAAE,KAAK,cAAc;CACrB,EAAE,KAAK,WAAW;CAClB,EAAE,KAAK,WAAW;CACnB;AAED,SAAS,YAAY,QAA+B;AAClD,SAAQ,OAAO,QAAQ,4BAA4B,QAAQ,OAAO,GAAG;;AAGvE,SAAS,aAAa,QAAmC;AACvD,QAAO;EACL,UAAU,GAAG,YAAY,OAAO,CAAC;EACjC,SAAS,EAAE,eAAe,UAAU,OAAO,UAAU;EACrD,SAAS,OAAO;EAChB,SAAS,OAAO;EACjB;;;;;AAMH,SAAgB,eAAe,OAAkB,QAAqC;CACpF,MAAM,EAAE,WAAW,OAAO,SAAS,GAAG,SAAS;AAC/C,QAAO;EACL,OAAO,OAAO,aAAa;EAC3B,aAAa,OAAO,eACd,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS,KAAA,MACnD;EACL;EACA,YAAY;GACV;GACA;GACA,GAAG;GACJ;EACF;;;;;;;;;;;;;;;;;;;;;;;AAwBH,SAAgB,mBAAmB,WAAoC;AAGrE,MAF0B,WAAW,QAAQ,YAEhC,SACX,QAAO,gBAA+B;EACpC,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAAoC,WAAW,gBAAgB,UAAU;AAC9F,OAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,MAAM,4HAA8H;AAC5I,WAAO;;AAET,UAAO;;EAET,SAAS,QAAQ,YAAY;GAC3B,KAAK,GAAG,YAAY,OAAO,CAAC;GAC5B,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU;IACnB,SAAS,OAAO;IAChB,OAAO,OAAO,KAAI,UAAS,eAAe,OAAO,OAAO,CAAC;IAC1D,CAAC;GACH;EACF,CAAC;AAGJ,QAAO,YAA2B;EAChC,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAAoC,WAAW,gBAAgB,UAAU;AAC9F,OAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,MAAM,mGAAmG;AACjH,WAAO;;AAET,UAAO;;EAET,MAAM,OAAO,QAAQ,WAAW;AAC9B,OAAI,OAAO,WAAW,EAAG;AACzB,SAAM,gBAAgB,QAAQ,aAAa,OAAO,CAAC;;EAEtD,CAAC;;;;;AAMJ,SAAgB,yBAAyB,WAAoC;AAC3E,QAAO,mBAAmB;EAAE,GAAG;EAAW,MAAM;EAAU,CAAC;;;;;AAM7D,eAAsB,cAAc,OAAkB,QAAsC;AAC1F,OAAM,mBAAmB,CAAC,MAAM,EAAE,OAAO;;;;;AAM3C,eAAsB,mBAAmB,QAAqB,QAAsC;AAClG,KAAI,OAAO,WAAW,EAAG;AACzB,OAAM,gBAAgB,QAAQ,aAAa,OAAO,CAAC;;;;;AAMrD,eAAsB,oBAAoB,OAAkB,QAAsC;AAChG,OAAM,yBAAyB,CAAC,MAAM,EAAE,OAAO;;;;;AAMjD,eAAsB,yBAAyB,QAAqB,QAAsC;AACxG,KAAI,OAAO,WAAW,EAAG;AACzB,OAAM,SAAS;EACb,KAAK,GAAG,YAAY,OAAO,CAAC;EAC5B,SAAS,EAAE,gBAAgB,oBAAoB;EAC/C,MAAM,KAAK,UAAU;GACnB,SAAS,OAAO;GAChB,OAAO,OAAO,KAAI,UAAS,eAAe,OAAO,OAAO,CAAC;GAC1D,CAAC;EACF,SAAS,OAAO,WAAW;EAC3B,SAAS,OAAO;EAChB,OAAO;EACR,CAAC"}
@@ -1,4 +1,4 @@
1
- import { F as DrainContext, it as WideEvent } from "../audit-CTIviX3P.mjs";
1
+ import { F as DrainContext, it as WideEvent } from "../audit-CJl-wZ10.mjs";
2
2
  //#region src/adapters/sentry.d.ts
3
3
  interface SentryConfig {
4
4
  /** Sentry DSN */
@@ -1 +1 @@
1
- {"version":3,"file":"sentry.d.mts","names":[],"sources":["../../src/adapters/sentry.ts"],"mappings":";;UAOiB,YAAA;;EAEf,GAAA;EAFe;EAIf,WAAA;;EAEA,OAAA;EAJA;EAMA,IAAA,GAAO,MAAA;EAFP;EAIA,OAAA;EAFO;EAIP,OAAA;AAAA;;UAIe,oBAAA;EACf,KAAA;EACA,IAAA;AAAA;;UAIe,SAAA;EACf,SAAA;EACA,QAAA;EACA,KAAA;EACA,IAAA;EACA,eAAA;EACA,UAAA,GAAa,MAAA,SAAe,oBAAA;AAAA;AAAA,iBA2Fd,WAAA,CAAY,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,YAAA,GAAe,SAAA;;;;;;;AAArE;;;;;;;;;;;;;;;AAiGA;;iBAAgB,iBAAA,CAAkB,SAAA,GAAY,OAAA,CAAQ,YAAA,KAAa,GAAA,EAAd,YAAA,GAAc,YAAA,OAAA,OAAA;;;;;;;;;;;iBAyB7C,YAAA,CAAa,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,YAAA,GAAe,OAAA;;;;;;AAA5E;;;;;iBAcsB,iBAAA,CAAkB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,YAAA,GAAe,OAAA"}
1
+ {"version":3,"file":"sentry.d.mts","names":[],"sources":["../../src/adapters/sentry.ts"],"mappings":";;UAOiB,YAAA;;EAEf,GAAA;EAFe;EAIf,WAAA;;EAEA,OAAA;EAJA;EAMA,IAAA,GAAO,MAAA;EAFP;EAIA,OAAA;EAFO;EAIP,OAAA;AAAA;;UAIe,oBAAA;EACf,KAAA;EACA,IAAA;AAAA;;UAIe,SAAA;EACf,SAAA;EACA,QAAA;EACA,KAAA;EACA,IAAA;EACA,eAAA;EACA,UAAA,GAAa,MAAA,SAAe,oBAAA;AAAA;AAAA,iBA2Fd,WAAA,CAAY,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,YAAA,GAAe,SAAA;;;;;;;AAArE;;;;;;;;;;;;;;;AAiGA;;iBAAgB,iBAAA,CAAkB,SAAA,GAAY,OAAA,CAAQ,YAAA,KAAa,GAAA,EAAd,YAAA,GAAc,YAAA,OAAA,OAAA;;;;;;;;;;;iBAoC7C,YAAA,CAAa,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,YAAA,GAAe,OAAA;;;;;;AAA5E;;;;;iBAcsB,iBAAA,CAAkB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,YAAA,GAAe,OAAA"}
@@ -1,6 +1,5 @@
1
- import { n as resolveAdapterConfig, t as httpPost } from "../_http-BY1e9pwC.mjs";
2
- import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
3
- import { t as OTEL_SEVERITY_NUMBER } from "../_severity-CQijvfhU.mjs";
1
+ import { a as resolveAdapterConfig, n as defineHttpDrain, r as httpPost } from "../drain-ByWUeOQC.mjs";
2
+ import { t as OTEL_SEVERITY_NUMBER } from "../severity-BYWZ96Sb.mjs";
4
3
  //#region src/adapters/sentry.ts
5
4
  const SENTRY_FIELDS = [
6
5
  {
@@ -165,7 +164,7 @@ function buildEnvelopeBody(logs, dsn) {
165
164
  * ```
166
165
  */
167
166
  function createSentryDrain(overrides) {
168
- return defineDrain({
167
+ return defineHttpDrain({
169
168
  name: "sentry",
170
169
  resolve: async () => {
171
170
  const config = await resolveAdapterConfig("sentry", SENTRY_FIELDS, overrides);
@@ -175,7 +174,18 @@ function createSentryDrain(overrides) {
175
174
  }
176
175
  return config;
177
176
  },
178
- send: sendBatchToSentry
177
+ encode: (events, config) => {
178
+ const { url, authHeader } = getSentryEnvelopeUrl(config.dsn);
179
+ const logs = events.map((event) => toSentryLog(event, config));
180
+ return {
181
+ url,
182
+ headers: {
183
+ "Content-Type": "application/x-sentry-envelope",
184
+ "X-Sentry-Auth": authHeader
185
+ },
186
+ body: buildEnvelopeBody(logs, config.dsn)
187
+ };
188
+ }
179
189
  });
180
190
  }
181
191
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sentry.mjs","names":[],"sources":["../../src/adapters/sentry.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\nimport { OTEL_SEVERITY_NUMBER } from './_severity'\n\nexport interface SentryConfig {\n /** Sentry DSN */\n dsn: string\n /** Environment override (defaults to event.environment) */\n environment?: string\n /** Release version override (defaults to event.version) */\n release?: string\n /** Additional tags to attach as attributes */\n tags?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\n/** Sentry Log attribute value with type annotation */\nexport interface SentryAttributeValue {\n value: string | number | boolean\n type: 'string' | 'integer' | 'double' | 'boolean'\n}\n\n/** Sentry Structured Log payload */\nexport interface SentryLog {\n timestamp: number\n trace_id: string\n level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n body: string\n severity_number: number\n attributes?: Record<string, SentryAttributeValue>\n}\n\ninterface SentryDsnParts {\n publicKey: string\n secretKey?: string\n projectId: string\n origin: string\n basePath: string\n}\n\nconst SENTRY_FIELDS: ConfigField<SentryConfig>[] = [\n { key: 'dsn', env: ['NUXT_SENTRY_DSN', 'SENTRY_DSN'] },\n { key: 'environment', env: ['NUXT_SENTRY_ENVIRONMENT', 'SENTRY_ENVIRONMENT'] },\n { key: 'release', env: ['NUXT_SENTRY_RELEASE', 'SENTRY_RELEASE'] },\n { key: 'tags' },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\nfunction parseSentryDsn(dsn: string): SentryDsnParts {\n const url = new URL(dsn)\n const publicKey = url.username\n if (!publicKey) {\n throw new Error('Invalid Sentry DSN: missing public key')\n }\n\n const secretKey = url.password || undefined\n\n const pathParts = url.pathname.split('/').filter(Boolean)\n const projectId = pathParts.pop()\n if (!projectId) {\n throw new Error('Invalid Sentry DSN: missing project ID')\n }\n\n const basePath = pathParts.length > 0 ? `/${pathParts.join('/')}` : ''\n\n return {\n publicKey,\n secretKey,\n projectId,\n origin: `${url.protocol}//${url.host}`,\n basePath,\n }\n}\n\nfunction getSentryEnvelopeUrl(dsn: string): { url: string, authHeader: string } {\n const { publicKey, secretKey, projectId, origin, basePath } = parseSentryDsn(dsn)\n const url = `${origin}${basePath}/api/${projectId}/envelope/`\n let authHeader = `Sentry sentry_version=7, sentry_key=${publicKey}, sentry_client=evlog`\n if (secretKey) {\n authHeader += `, sentry_secret=${secretKey}`\n }\n return { url, authHeader }\n}\n\nfunction createTraceId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') {\n return globalThis.crypto.randomUUID().replace(/-/g, '')\n }\n\n return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('')\n}\n\nfunction getFirstStringValue(event: WideEvent, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = event[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\nfunction toAttributeValue(value: unknown): SentryAttributeValue | undefined {\n if (value === null || value === undefined) {\n return undefined\n }\n if (typeof value === 'string') {\n return { value, type: 'string' }\n }\n if (typeof value === 'boolean') {\n return { value, type: 'boolean' }\n }\n if (typeof value === 'number') {\n if (Number.isInteger(value)) {\n return { value, type: 'integer' }\n }\n return { value, type: 'double' }\n }\n return { value: JSON.stringify(value), type: 'string' }\n}\n\nexport function toSentryLog(event: WideEvent, config: SentryConfig): SentryLog {\n const { timestamp, level, service, environment, version, ...rest } = event\n\n const body = getFirstStringValue(event, ['message', 'action', 'path'])\n ?? 'evlog wide event'\n\n const traceId = (typeof event.traceId === 'string' && event.traceId.length > 0)\n ? event.traceId\n : createTraceId()\n\n const attributes: Record<string, SentryAttributeValue> = {}\n\n const env = config.environment ?? environment\n if (env) {\n attributes['sentry.environment'] = { value: env, type: 'string' }\n }\n\n const rel = config.release ?? version\n if (typeof rel === 'string' && rel.length > 0) {\n attributes['sentry.release'] = { value: rel, type: 'string' }\n }\n\n attributes['service'] = { value: service, type: 'string' }\n\n if (config.tags) {\n for (const [key, value] of Object.entries(config.tags)) {\n attributes[key] = { value, type: 'string' }\n }\n }\n\n for (const [key, value] of Object.entries(rest)) {\n if (key === 'traceId' || key === 'spanId') continue\n if (value === undefined || value === null) continue\n const attr = toAttributeValue(value)\n if (attr) {\n attributes[key] = attr\n }\n }\n\n return {\n timestamp: new Date(timestamp).getTime() / 1000,\n trace_id: traceId,\n level: level as SentryLog['level'],\n body,\n severity_number: OTEL_SEVERITY_NUMBER[level] ?? 9,\n attributes,\n }\n}\n\n/**\n * Build the Sentry Envelope body for a list of logs.\n *\n * Envelope format (line-delimited):\n * - Line 1: Envelope headers (dsn, sent_at)\n * - Line 2: Item header (type: log, item_count, content_type)\n * - Line 3: Item payload ({\"items\": [...]})\n */\nfunction buildEnvelopeBody(logs: SentryLog[], dsn: string): string {\n const envelopeHeader = JSON.stringify({\n dsn,\n sent_at: new Date().toISOString(),\n })\n\n const itemHeader = JSON.stringify({\n type: 'log',\n item_count: logs.length,\n content_type: 'application/vnd.sentry.items.log+json',\n })\n\n const itemPayload = JSON.stringify({ items: logs })\n\n return `${envelopeHeader}\\n${itemHeader}\\n${itemPayload}\\n`\n}\n\n/**\n * Create a drain function for sending logs to Sentry.\n *\n * Sends wide events as Sentry Structured Logs, visible in Explore > Logs\n * in the Sentry dashboard.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createSentryDrain()\n * 2. runtimeConfig.evlog.sentry\n * 3. runtimeConfig.sentry\n * 4. Environment variables: NUXT_SENTRY_*, SENTRY_*\n *\n * @example\n * ```ts\n * // Zero config - just set NUXT_SENTRY_DSN env var\n * nitroApp.hooks.hook('evlog:drain', createSentryDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog:drain', createSentryDrain({\n * dsn: 'https://public@o0.ingest.sentry.io/123',\n * }))\n * ```\n */\nexport function createSentryDrain(overrides?: Partial<SentryConfig>) {\n return defineDrain<SentryConfig>({\n name: 'sentry',\n resolve: async () => {\n const config = await resolveAdapterConfig<SentryConfig>('sentry', SENTRY_FIELDS, overrides)\n if (!config.dsn) {\n console.error('[evlog/sentry] Missing DSN. Set NUXT_SENTRY_DSN/SENTRY_DSN env var or pass to createSentryDrain()')\n return null\n }\n return config as SentryConfig\n },\n send: sendBatchToSentry,\n })\n}\n\n/**\n * Send a single event to Sentry as a structured log.\n *\n * @example\n * ```ts\n * await sendToSentry(event, {\n * dsn: process.env.SENTRY_DSN!,\n * })\n * ```\n */\nexport async function sendToSentry(event: WideEvent, config: SentryConfig): Promise<void> {\n await sendBatchToSentry([event], config)\n}\n\n/**\n * Send a batch of events to Sentry as structured logs via the Envelope endpoint.\n *\n * @example\n * ```ts\n * await sendBatchToSentry(events, {\n * dsn: process.env.SENTRY_DSN!,\n * })\n * ```\n */\nexport async function sendBatchToSentry(events: WideEvent[], config: SentryConfig): Promise<void> {\n if (events.length === 0) return\n\n const { url, authHeader } = getSentryEnvelopeUrl(config.dsn)\n\n const logs = events.map(event => toSentryLog(event, config))\n const body = buildEnvelopeBody(logs, config.dsn)\n\n await httpPost({\n url,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n 'X-Sentry-Auth': authHeader,\n },\n body,\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'Sentry',\n })\n}\n"],"mappings":";;;;AA8CA,MAAM,gBAA6C;CACjD;EAAE,KAAK;EAAO,KAAK,CAAC,mBAAmB,aAAa;EAAE;CACtD;EAAE,KAAK;EAAe,KAAK,CAAC,2BAA2B,qBAAqB;EAAE;CAC9E;EAAE,KAAK;EAAW,KAAK,CAAC,uBAAuB,iBAAiB;EAAE;CAClE,EAAE,KAAK,QAAQ;CACf,EAAE,KAAK,WAAW;CAClB,EAAE,KAAK,WAAW;CACnB;AAED,SAAS,eAAe,KAA6B;CACnD,MAAM,MAAM,IAAI,IAAI,IAAI;CACxB,MAAM,YAAY,IAAI;AACtB,KAAI,CAAC,UACH,OAAM,IAAI,MAAM,yCAAyC;CAG3D,MAAM,YAAY,IAAI,YAAY,KAAA;CAElC,MAAM,YAAY,IAAI,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;CACzD,MAAM,YAAY,UAAU,KAAK;AACjC,KAAI,CAAC,UACH,OAAM,IAAI,MAAM,yCAAyC;CAG3D,MAAM,WAAW,UAAU,SAAS,IAAI,IAAI,UAAU,KAAK,IAAI,KAAK;AAEpE,QAAO;EACL;EACA;EACA;EACA,QAAQ,GAAG,IAAI,SAAS,IAAI,IAAI;EAChC;EACD;;AAGH,SAAS,qBAAqB,KAAkD;CAC9E,MAAM,EAAE,WAAW,WAAW,WAAW,QAAQ,aAAa,eAAe,IAAI;CACjF,MAAM,MAAM,GAAG,SAAS,SAAS,OAAO,UAAU;CAClD,IAAI,aAAa,uCAAuC,UAAU;AAClE,KAAI,UACF,eAAc,mBAAmB;AAEnC,QAAO;EAAE;EAAK;EAAY;;AAG5B,SAAS,gBAAwB;AAC/B,KAAI,OAAO,WAAW,QAAQ,eAAe,WAC3C,QAAO,WAAW,OAAO,YAAY,CAAC,QAAQ,MAAM,GAAG;AAGzD,QAAO,MAAM,KAAK,EAAE,QAAQ,IAAI,QAAQ,KAAK,MAAM,KAAK,QAAQ,GAAG,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,GAAG;;AAG/F,SAAS,oBAAoB,OAAkB,MAAoC;AACjF,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,MAAM;AACpB,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;;;AAK9D,SAAS,iBAAiB,OAAkD;AAC1E,KAAI,UAAU,QAAQ,UAAU,KAAA,EAC9B;AAEF,KAAI,OAAO,UAAU,SACnB,QAAO;EAAE;EAAO,MAAM;EAAU;AAElC,KAAI,OAAO,UAAU,UACnB,QAAO;EAAE;EAAO,MAAM;EAAW;AAEnC,KAAI,OAAO,UAAU,UAAU;AAC7B,MAAI,OAAO,UAAU,MAAM,CACzB,QAAO;GAAE;GAAO,MAAM;GAAW;AAEnC,SAAO;GAAE;GAAO,MAAM;GAAU;;AAElC,QAAO;EAAE,OAAO,KAAK,UAAU,MAAM;EAAE,MAAM;EAAU;;AAGzD,SAAgB,YAAY,OAAkB,QAAiC;CAC7E,MAAM,EAAE,WAAW,OAAO,SAAS,aAAa,SAAS,GAAG,SAAS;CAErE,MAAM,OAAO,oBAAoB,OAAO;EAAC;EAAW;EAAU;EAAO,CAAC,IACjE;CAEL,MAAM,UAAW,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,SAAS,IACzE,MAAM,UACN,eAAe;CAEnB,MAAM,aAAmD,EAAE;CAE3D,MAAM,MAAM,OAAO,eAAe;AAClC,KAAI,IACF,YAAW,wBAAwB;EAAE,OAAO;EAAK,MAAM;EAAU;CAGnE,MAAM,MAAM,OAAO,WAAW;AAC9B,KAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAC1C,YAAW,oBAAoB;EAAE,OAAO;EAAK,MAAM;EAAU;AAG/D,YAAW,aAAa;EAAE,OAAO;EAAS,MAAM;EAAU;AAE1D,KAAI,OAAO,KACT,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,KAAK,CACpD,YAAW,OAAO;EAAE;EAAO,MAAM;EAAU;AAI/C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;AAC/C,MAAI,QAAQ,aAAa,QAAQ,SAAU;AAC3C,MAAI,UAAU,KAAA,KAAa,UAAU,KAAM;EAC3C,MAAM,OAAO,iBAAiB,MAAM;AACpC,MAAI,KACF,YAAW,OAAO;;AAItB,QAAO;EACL,WAAW,IAAI,KAAK,UAAU,CAAC,SAAS,GAAG;EAC3C,UAAU;EACH;EACP;EACA,iBAAiB,qBAAqB,UAAU;EAChD;EACD;;;;;;;;;;AAWH,SAAS,kBAAkB,MAAmB,KAAqB;AAcjE,QAAO,GAbgB,KAAK,UAAU;EACpC;EACA,0BAAS,IAAI,MAAM,EAAC,aAAa;EAClC,CAUuB,CAAC,IARN,KAAK,UAAU;EAChC,MAAM;EACN,YAAY,KAAK;EACjB,cAAc;EACf,CAIsC,CAAC,IAFpB,KAAK,UAAU,EAAE,OAAO,MAAM,CAEK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA0B1D,SAAgB,kBAAkB,WAAmC;AACnE,QAAO,YAA0B;EAC/B,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAAmC,UAAU,eAAe,UAAU;AAC3F,OAAI,CAAC,OAAO,KAAK;AACf,YAAQ,MAAM,oGAAoG;AAClH,WAAO;;AAET,UAAO;;EAET,MAAM;EACP,CAAC;;;;;;;;;;;;AAaJ,eAAsB,aAAa,OAAkB,QAAqC;AACxF,OAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO;;;;;;;;;;;;AAa1C,eAAsB,kBAAkB,QAAqB,QAAqC;AAChG,KAAI,OAAO,WAAW,EAAG;CAEzB,MAAM,EAAE,KAAK,eAAe,qBAAqB,OAAO,IAAI;CAG5D,MAAM,OAAO,kBADA,OAAO,KAAI,UAAS,YAAY,OAAO,OAAO,CACxB,EAAE,OAAO,IAAI;AAEhD,OAAM,SAAS;EACb;EACA,SAAS;GACP,gBAAgB;GAChB,iBAAiB;GAClB;EACD;EACA,SAAS,OAAO,WAAW;EAC3B,SAAS,OAAO;EAChB,OAAO;EACR,CAAC"}
1
+ {"version":3,"file":"sentry.mjs","names":[],"sources":["../../src/adapters/sentry.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from '../shared/config'\nimport { resolveAdapterConfig } from '../shared/config'\nimport { defineHttpDrain } from '../shared/drain'\nimport { httpPost } from '../shared/http'\nimport { OTEL_SEVERITY_NUMBER } from '../shared/severity'\n\nexport interface SentryConfig {\n /** Sentry DSN */\n dsn: string\n /** Environment override (defaults to event.environment) */\n environment?: string\n /** Release version override (defaults to event.version) */\n release?: string\n /** Additional tags to attach as attributes */\n tags?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\n/** Sentry Log attribute value with type annotation */\nexport interface SentryAttributeValue {\n value: string | number | boolean\n type: 'string' | 'integer' | 'double' | 'boolean'\n}\n\n/** Sentry Structured Log payload */\nexport interface SentryLog {\n timestamp: number\n trace_id: string\n level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'\n body: string\n severity_number: number\n attributes?: Record<string, SentryAttributeValue>\n}\n\ninterface SentryDsnParts {\n publicKey: string\n secretKey?: string\n projectId: string\n origin: string\n basePath: string\n}\n\nconst SENTRY_FIELDS: ConfigField<SentryConfig>[] = [\n { key: 'dsn', env: ['NUXT_SENTRY_DSN', 'SENTRY_DSN'] },\n { key: 'environment', env: ['NUXT_SENTRY_ENVIRONMENT', 'SENTRY_ENVIRONMENT'] },\n { key: 'release', env: ['NUXT_SENTRY_RELEASE', 'SENTRY_RELEASE'] },\n { key: 'tags' },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\nfunction parseSentryDsn(dsn: string): SentryDsnParts {\n const url = new URL(dsn)\n const publicKey = url.username\n if (!publicKey) {\n throw new Error('Invalid Sentry DSN: missing public key')\n }\n\n const secretKey = url.password || undefined\n\n const pathParts = url.pathname.split('/').filter(Boolean)\n const projectId = pathParts.pop()\n if (!projectId) {\n throw new Error('Invalid Sentry DSN: missing project ID')\n }\n\n const basePath = pathParts.length > 0 ? `/${pathParts.join('/')}` : ''\n\n return {\n publicKey,\n secretKey,\n projectId,\n origin: `${url.protocol}//${url.host}`,\n basePath,\n }\n}\n\nfunction getSentryEnvelopeUrl(dsn: string): { url: string, authHeader: string } {\n const { publicKey, secretKey, projectId, origin, basePath } = parseSentryDsn(dsn)\n const url = `${origin}${basePath}/api/${projectId}/envelope/`\n let authHeader = `Sentry sentry_version=7, sentry_key=${publicKey}, sentry_client=evlog`\n if (secretKey) {\n authHeader += `, sentry_secret=${secretKey}`\n }\n return { url, authHeader }\n}\n\nfunction createTraceId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') {\n return globalThis.crypto.randomUUID().replace(/-/g, '')\n }\n\n return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('')\n}\n\nfunction getFirstStringValue(event: WideEvent, keys: string[]): string | undefined {\n for (const key of keys) {\n const value = event[key]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\nfunction toAttributeValue(value: unknown): SentryAttributeValue | undefined {\n if (value === null || value === undefined) {\n return undefined\n }\n if (typeof value === 'string') {\n return { value, type: 'string' }\n }\n if (typeof value === 'boolean') {\n return { value, type: 'boolean' }\n }\n if (typeof value === 'number') {\n if (Number.isInteger(value)) {\n return { value, type: 'integer' }\n }\n return { value, type: 'double' }\n }\n return { value: JSON.stringify(value), type: 'string' }\n}\n\nexport function toSentryLog(event: WideEvent, config: SentryConfig): SentryLog {\n const { timestamp, level, service, environment, version, ...rest } = event\n\n const body = getFirstStringValue(event, ['message', 'action', 'path'])\n ?? 'evlog wide event'\n\n const traceId = (typeof event.traceId === 'string' && event.traceId.length > 0)\n ? event.traceId\n : createTraceId()\n\n const attributes: Record<string, SentryAttributeValue> = {}\n\n const env = config.environment ?? environment\n if (env) {\n attributes['sentry.environment'] = { value: env, type: 'string' }\n }\n\n const rel = config.release ?? version\n if (typeof rel === 'string' && rel.length > 0) {\n attributes['sentry.release'] = { value: rel, type: 'string' }\n }\n\n attributes['service'] = { value: service, type: 'string' }\n\n if (config.tags) {\n for (const [key, value] of Object.entries(config.tags)) {\n attributes[key] = { value, type: 'string' }\n }\n }\n\n for (const [key, value] of Object.entries(rest)) {\n if (key === 'traceId' || key === 'spanId') continue\n if (value === undefined || value === null) continue\n const attr = toAttributeValue(value)\n if (attr) {\n attributes[key] = attr\n }\n }\n\n return {\n timestamp: new Date(timestamp).getTime() / 1000,\n trace_id: traceId,\n level: level as SentryLog['level'],\n body,\n severity_number: OTEL_SEVERITY_NUMBER[level] ?? 9,\n attributes,\n }\n}\n\n/**\n * Build the Sentry Envelope body for a list of logs.\n *\n * Envelope format (line-delimited):\n * - Line 1: Envelope headers (dsn, sent_at)\n * - Line 2: Item header (type: log, item_count, content_type)\n * - Line 3: Item payload ({\"items\": [...]})\n */\nfunction buildEnvelopeBody(logs: SentryLog[], dsn: string): string {\n const envelopeHeader = JSON.stringify({\n dsn,\n sent_at: new Date().toISOString(),\n })\n\n const itemHeader = JSON.stringify({\n type: 'log',\n item_count: logs.length,\n content_type: 'application/vnd.sentry.items.log+json',\n })\n\n const itemPayload = JSON.stringify({ items: logs })\n\n return `${envelopeHeader}\\n${itemHeader}\\n${itemPayload}\\n`\n}\n\n/**\n * Create a drain function for sending logs to Sentry.\n *\n * Sends wide events as Sentry Structured Logs, visible in Explore > Logs\n * in the Sentry dashboard.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createSentryDrain()\n * 2. runtimeConfig.evlog.sentry\n * 3. runtimeConfig.sentry\n * 4. Environment variables: NUXT_SENTRY_*, SENTRY_*\n *\n * @example\n * ```ts\n * // Zero config - just set NUXT_SENTRY_DSN env var\n * nitroApp.hooks.hook('evlog:drain', createSentryDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog:drain', createSentryDrain({\n * dsn: 'https://public@o0.ingest.sentry.io/123',\n * }))\n * ```\n */\nexport function createSentryDrain(overrides?: Partial<SentryConfig>) {\n return defineHttpDrain<SentryConfig>({\n name: 'sentry',\n resolve: async () => {\n const config = await resolveAdapterConfig<SentryConfig>('sentry', SENTRY_FIELDS, overrides)\n if (!config.dsn) {\n console.error('[evlog/sentry] Missing DSN. Set NUXT_SENTRY_DSN/SENTRY_DSN env var or pass to createSentryDrain()')\n return null\n }\n return config as SentryConfig\n },\n encode: (events, config) => {\n const { url, authHeader } = getSentryEnvelopeUrl(config.dsn)\n const logs = events.map(event => toSentryLog(event, config))\n return {\n url,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n 'X-Sentry-Auth': authHeader,\n },\n body: buildEnvelopeBody(logs, config.dsn),\n }\n },\n })\n}\n\n/**\n * Send a single event to Sentry as a structured log.\n *\n * @example\n * ```ts\n * await sendToSentry(event, {\n * dsn: process.env.SENTRY_DSN!,\n * })\n * ```\n */\nexport async function sendToSentry(event: WideEvent, config: SentryConfig): Promise<void> {\n await sendBatchToSentry([event], config)\n}\n\n/**\n * Send a batch of events to Sentry as structured logs via the Envelope endpoint.\n *\n * @example\n * ```ts\n * await sendBatchToSentry(events, {\n * dsn: process.env.SENTRY_DSN!,\n * })\n * ```\n */\nexport async function sendBatchToSentry(events: WideEvent[], config: SentryConfig): Promise<void> {\n if (events.length === 0) return\n\n const { url, authHeader } = getSentryEnvelopeUrl(config.dsn)\n\n const logs = events.map(event => toSentryLog(event, config))\n const body = buildEnvelopeBody(logs, config.dsn)\n\n await httpPost({\n url,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n 'X-Sentry-Auth': authHeader,\n },\n body,\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'Sentry',\n })\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Y as RequestLogger } from "../audit-CTIviX3P.mjs";
1
+ import { Y as RequestLogger } from "../audit-CJl-wZ10.mjs";
2
2
  import { GatewayModelId, TelemetryIntegration } from "ai";
3
3
  import { LanguageModelV3, LanguageModelV3Middleware } from "@ai-sdk/provider";
4
4
 
@@ -1,4 +1,5 @@
1
1
  import { colors, cssColors, detectEnvironment, escapeFormatString, formatDuration, getConsoleMethod, getCssLevelColor, getLevelColor, isBrowser, isDev, isLevelEnabled, matchesPattern } from "./utils.mjs";
2
+ import { r as getHeader$1 } from "./headers-CU-QqnYg.mjs";
2
3
  //#region src/redact.ts
3
4
  const DEFAULT_REPLACEMENT = "[REDACTED]";
4
5
  /**
@@ -188,6 +189,130 @@ function normalizeRedactConfig(raw) {
188
189
  return resolveRedactConfig(config);
189
190
  }
190
191
  //#endregion
192
+ //#region src/shared/plugin.ts
193
+ /** Identity helper preserving plugin type inference. */
194
+ function definePlugin(plugin) {
195
+ return plugin;
196
+ }
197
+ /** Wrap a standalone drain callback as an {@link EvlogPlugin}. */
198
+ function drainPlugin(name, drain) {
199
+ return {
200
+ name,
201
+ drain
202
+ };
203
+ }
204
+ /** Wrap a standalone enricher callback as an {@link EvlogPlugin}. */
205
+ function enricherPlugin(name, enrich) {
206
+ return {
207
+ name,
208
+ enrich
209
+ };
210
+ }
211
+ function logPluginError(name, hook, err) {
212
+ console.error(`[evlog/${name}] ${hook} failed:`, err);
213
+ }
214
+ /** De-duplicates by `name` — last registration wins. */
215
+ function createPluginRunner(plugins = []) {
216
+ const byName = /* @__PURE__ */ new Map();
217
+ for (const plugin of plugins) byName.set(plugin.name, plugin);
218
+ const list = Array.from(byName.values());
219
+ return {
220
+ plugins: list,
221
+ hasEnrich: list.some((p) => typeof p.enrich === "function"),
222
+ hasDrain: list.some((p) => typeof p.drain === "function"),
223
+ hasKeep: list.some((p) => typeof p.keep === "function"),
224
+ hasRequestLifecycle: list.some((p) => typeof p.onRequestStart === "function" || typeof p.onRequestFinish === "function"),
225
+ hasClientLog: list.some((p) => typeof p.onClientLog === "function"),
226
+ hasExtendLogger: list.some((p) => typeof p.extendLogger === "function"),
227
+ applyExtendLogger(logger) {
228
+ for (const plugin of list) {
229
+ if (!plugin.extendLogger) continue;
230
+ try {
231
+ plugin.extendLogger(logger);
232
+ } catch (err) {
233
+ logPluginError(plugin.name, "extendLogger", err);
234
+ }
235
+ }
236
+ },
237
+ runOnRequestStart(ctx) {
238
+ for (const plugin of list) {
239
+ if (!plugin.onRequestStart) continue;
240
+ try {
241
+ plugin.onRequestStart(ctx);
242
+ } catch (err) {
243
+ logPluginError(plugin.name, "onRequestStart", err);
244
+ }
245
+ }
246
+ },
247
+ runOnRequestFinish(ctx) {
248
+ for (const plugin of list) {
249
+ if (!plugin.onRequestFinish) continue;
250
+ try {
251
+ plugin.onRequestFinish(ctx);
252
+ } catch (err) {
253
+ logPluginError(plugin.name, "onRequestFinish", err);
254
+ }
255
+ }
256
+ },
257
+ async runEnrich(ctx) {
258
+ for (const plugin of list) {
259
+ if (!plugin.enrich) continue;
260
+ try {
261
+ await plugin.enrich(ctx);
262
+ } catch (err) {
263
+ logPluginError(plugin.name, "enrich", err);
264
+ }
265
+ }
266
+ },
267
+ async runDrain(ctx) {
268
+ const drains = list.filter((p) => typeof p.drain === "function");
269
+ if (drains.length === 0) return;
270
+ await Promise.allSettled(drains.map(async (plugin) => {
271
+ try {
272
+ await plugin.drain(ctx);
273
+ } catch (err) {
274
+ logPluginError(plugin.name, "drain", err);
275
+ }
276
+ }));
277
+ },
278
+ async runKeep(ctx) {
279
+ for (const plugin of list) {
280
+ if (!plugin.keep) continue;
281
+ try {
282
+ await plugin.keep(ctx);
283
+ } catch (err) {
284
+ logPluginError(plugin.name, "keep", err);
285
+ }
286
+ }
287
+ },
288
+ runOnClientLog(ctx) {
289
+ for (const plugin of list) {
290
+ if (!plugin.onClientLog) continue;
291
+ try {
292
+ plugin.onClientLog(ctx);
293
+ } catch (err) {
294
+ logPluginError(plugin.name, "onClientLog", err);
295
+ }
296
+ }
297
+ },
298
+ async runSetup(ctx) {
299
+ for (const plugin of list) {
300
+ if (!plugin.setup) continue;
301
+ try {
302
+ await plugin.setup(ctx);
303
+ } catch (err) {
304
+ logPluginError(plugin.name, "setup", err);
305
+ }
306
+ }
307
+ }
308
+ };
309
+ }
310
+ const emptyRunner = createPluginRunner([]);
311
+ /** Shared no-op runner used when no plugins are registered. */
312
+ function getEmptyPluginRunner() {
313
+ return emptyRunner;
314
+ }
315
+ //#endregion
191
316
  //#region src/logger.ts
192
317
  function isPlainObject(val) {
193
318
  return val !== null && typeof val === "object" && !Array.isArray(val);
@@ -226,6 +351,7 @@ let globalSilent = false;
226
351
  /** Minimum level for the global `log` API only (`ownsEvent === false`). Default: all levels. */
227
352
  let globalMinLevel = "debug";
228
353
  let _locked = false;
354
+ let globalPluginRunner = getEmptyPluginRunner();
229
355
  /**
230
356
  * Initialize the logger with configuration.
231
357
  * Call this once at application startup.
@@ -247,7 +373,18 @@ function initLogger(config = {}) {
247
373
  globalRedact = resolveRedactConfig(config.redact ?? !isDev());
248
374
  globalSilent = config.silent ?? false;
249
375
  globalMinLevel = config.minLevel ?? "debug";
250
- if (globalSilent && !globalDrain && !config._suppressDrainWarning) console.warn("[evlog] silent mode is enabled but no drain is configured. Events will be built and sampled but not output anywhere. Set a drain via initLogger({ drain }) or a framework hook (evlog:drain).");
376
+ globalPluginRunner = config.plugins?.length ? createPluginRunner(config.plugins) : getEmptyPluginRunner();
377
+ if (globalPluginRunner.plugins.length > 0) globalPluginRunner.runSetup({ env: { ...globalEnv } });
378
+ const hasAnyDrain = !!globalDrain || globalPluginRunner.hasDrain;
379
+ if (globalSilent && !hasAnyDrain && !config._suppressDrainWarning) console.warn("[evlog] silent mode is enabled but no drain is configured. Events will be built and sampled but not output anywhere. Set a drain via initLogger({ drain }) or a framework hook (evlog:drain).");
380
+ }
381
+ /**
382
+ * @internal Get the globally registered plugin runner.
383
+ * Used by framework middleware so plugins also fire on routes that pre-date
384
+ * the middleware-level options.
385
+ */
386
+ function getGlobalPluginRunner() {
387
+ return globalPluginRunner;
251
388
  }
252
389
  /**
253
390
  * Check if logging is globally enabled.
@@ -331,11 +468,17 @@ function emitWideEvent(level, event, options = {}) {
331
468
  if (!globalSilent) if (globalPretty) prettyPrintWideEvent(formatted);
332
469
  else if (globalStringify) console[getConsoleMethod(level)](JSON.stringify(formatted));
333
470
  else console[getConsoleMethod(level)](formatted);
334
- if (globalDrain && !deferDrain) {
335
- const drainPromise = Promise.resolve(globalDrain({ event: formatted })).catch((err) => {
336
- console.error("[evlog] drain failed:", err);
337
- });
338
- if (waitUntil) waitUntil(drainPromise);
471
+ if (!deferDrain) {
472
+ const drainPromises = [];
473
+ if (globalDrain) drainPromises.push((async () => {
474
+ try {
475
+ await globalDrain({ event: formatted });
476
+ } catch (err) {
477
+ console.error("[evlog] drain failed:", err);
478
+ }
479
+ })());
480
+ if (globalPluginRunner.hasDrain) drainPromises.push(globalPluginRunner.runDrain({ event: formatted }));
481
+ if (drainPromises.length > 0 && waitUntil) waitUntil(Promise.all(drainPromises));
339
482
  }
340
483
  return formatted;
341
484
  }
@@ -1262,11 +1405,7 @@ function auditEnricher(options = {}) {
1262
1405
  };
1263
1406
  }
1264
1407
  function getHeader(headers, name) {
1265
- if (!headers) return void 0;
1266
- if (headers[name] !== void 0) return headers[name];
1267
- const lower = name.toLowerCase();
1268
- if (headers[lower] !== void 0) return headers[lower];
1269
- for (const [k, v] of Object.entries(headers)) if (k.toLowerCase() === lower) return v;
1408
+ return getHeader$1(headers, name);
1270
1409
  }
1271
1410
  /**
1272
1411
  * Wrap any drain so it only receives events that carry an `audit` field.
@@ -1478,6 +1617,6 @@ const auditRedactPreset = { paths: [
1478
1617
  "audit.context.headers.cookie"
1479
1618
  ] };
1480
1619
  //#endregion
1481
- export { shouldKeep as C, resolveRedactConfig as E, lockLogger as S, redactEvent as T, getEnvironment as _, auditEnricher as a, isEnabled as b, buildAuditFields as c, signed as d, withAudit as f, createRequestLogger as g, createLogger as h, auditDiff as i, defineAuditAction as l, _log as m, AuditDeniedError as n, auditOnly as o, withAuditMethods as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, mockAudit as u, getGlobalDrain as v, normalizeRedactConfig as w, isLoggerLocked as x, initLogger as y };
1620
+ export { normalizeRedactConfig as A, lockLogger as C, drainPlugin as D, definePlugin as E, resolveRedactConfig as M, enricherPlugin as O, isLoggerLocked as S, createPluginRunner as T, getEnvironment as _, auditEnricher as a, initLogger as b, buildAuditFields as c, signed as d, withAudit as f, createRequestLogger as g, createLogger as h, auditDiff as i, redactEvent as j, getEmptyPluginRunner as k, defineAuditAction as l, _log as m, AuditDeniedError as n, auditOnly as o, withAuditMethods as p, audit as r, auditRedactPreset as s, AUDIT_SCHEMA_VERSION as t, mockAudit as u, getGlobalDrain as v, shouldKeep as w, isEnabled as x, getGlobalPluginRunner as y };
1482
1621
 
1483
- //# sourceMappingURL=audit-DQoBo7Dl.mjs.map
1622
+ //# sourceMappingURL=audit--n0QRR2Y.mjs.map