evlog 2.7.0 → 2.9.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 (129) hide show
  1. package/README.md +54 -0
  2. package/dist/{_drain-FZVTbHn9.mjs → _drain-C9Nr-6Wc.mjs} +1 -1
  3. package/dist/{_drain-FZVTbHn9.mjs.map → _drain-C9Nr-6Wc.mjs.map} +1 -1
  4. package/dist/_http-C2UoHWgm.mjs +82 -0
  5. package/dist/_http-C2UoHWgm.mjs.map +1 -0
  6. package/dist/{_severity-fhhv2Nq0.mjs → _severity-BLiOKoxh.mjs} +1 -1
  7. package/dist/{_severity-fhhv2Nq0.mjs.map → _severity-BLiOKoxh.mjs.map} +1 -1
  8. package/dist/adapters/axiom.d.mts +3 -1
  9. package/dist/adapters/axiom.d.mts.map +1 -1
  10. package/dist/adapters/axiom.mjs +5 -3
  11. package/dist/adapters/axiom.mjs.map +1 -1
  12. package/dist/adapters/better-stack.d.mts +3 -1
  13. package/dist/adapters/better-stack.d.mts.map +1 -1
  14. package/dist/adapters/better-stack.mjs +5 -3
  15. package/dist/adapters/better-stack.mjs.map +1 -1
  16. package/dist/adapters/fs.d.mts +1 -1
  17. package/dist/adapters/fs.mjs +1 -1
  18. package/dist/adapters/otlp.d.mts +3 -1
  19. package/dist/adapters/otlp.d.mts.map +1 -1
  20. package/dist/adapters/otlp.mjs +6 -4
  21. package/dist/adapters/otlp.mjs.map +1 -1
  22. package/dist/adapters/posthog.d.mts +3 -1
  23. package/dist/adapters/posthog.d.mts.map +1 -1
  24. package/dist/adapters/posthog.mjs +7 -4
  25. package/dist/adapters/posthog.mjs.map +1 -1
  26. package/dist/adapters/sentry.d.mts +3 -1
  27. package/dist/adapters/sentry.d.mts.map +1 -1
  28. package/dist/adapters/sentry.mjs +6 -4
  29. package/dist/adapters/sentry.mjs.map +1 -1
  30. package/dist/ai/index.d.mts +165 -0
  31. package/dist/ai/index.d.mts.map +1 -0
  32. package/dist/ai/index.mjs +340 -0
  33. package/dist/ai/index.mjs.map +1 -0
  34. package/dist/browser.d.mts +1 -1
  35. package/dist/client.d.mts +2 -0
  36. package/dist/client.mjs +2 -0
  37. package/dist/{dist-BsWcv7B8.mjs → dist-BFn8qsRC.mjs} +1 -1
  38. package/dist/{dist-BsWcv7B8.mjs.map → dist-BFn8qsRC.mjs.map} +1 -1
  39. package/dist/elysia/index.d.mts +2 -2
  40. package/dist/elysia/index.d.mts.map +1 -1
  41. package/dist/elysia/index.mjs +7 -7
  42. package/dist/elysia/index.mjs.map +1 -1
  43. package/dist/enrichers.d.mts +1 -1
  44. package/dist/{error-BuPbEDGO.d.mts → error-BjaGNgoo.d.mts} +2 -2
  45. package/dist/{error-BuPbEDGO.d.mts.map → error-BjaGNgoo.d.mts.map} +1 -1
  46. package/dist/error.d.mts +1 -1
  47. package/dist/{errors-nScQVwHW.d.mts → errors-BBJmxg3d.d.mts} +2 -2
  48. package/dist/{errors-nScQVwHW.d.mts.map → errors-BBJmxg3d.d.mts.map} +1 -1
  49. package/dist/express/index.d.mts +2 -2
  50. package/dist/express/index.mjs +2 -2
  51. package/dist/fastify/index.d.mts +2 -2
  52. package/dist/fastify/index.mjs +2 -2
  53. package/dist/{headers-BSgpnR0c.mjs → headers-DmzJ3sQ-.mjs} +2 -2
  54. package/dist/{headers-BSgpnR0c.mjs.map → headers-DmzJ3sQ-.mjs.map} +1 -1
  55. package/dist/hono/index.d.mts +2 -2
  56. package/dist/hono/index.mjs +1 -1
  57. package/dist/index.d.mts +5 -5
  58. package/dist/{logger-BH1gOdNP.d.mts → logger-3ZE3g6GW.d.mts} +2 -2
  59. package/dist/{logger-BH1gOdNP.d.mts.map → logger-3ZE3g6GW.d.mts.map} +1 -1
  60. package/dist/logger.d.mts +1 -1
  61. package/dist/logger.mjs +89 -99
  62. package/dist/logger.mjs.map +1 -1
  63. package/dist/{middleware-CDLTVOBs.d.mts → middleware-B9uwQ5B4.d.mts} +2 -2
  64. package/dist/{middleware-CDLTVOBs.d.mts.map → middleware-B9uwQ5B4.d.mts.map} +1 -1
  65. package/dist/nestjs/index.d.mts +2 -2
  66. package/dist/nestjs/index.mjs +2 -2
  67. package/dist/next/client.d.mts +1 -1
  68. package/dist/next/index.d.mts +4 -4
  69. package/dist/next/index.mjs +1 -1
  70. package/dist/nitro/errorHandler.mjs +1 -1
  71. package/dist/nitro/module.d.mts +2 -2
  72. package/dist/nitro/plugin.mjs +4 -3
  73. package/dist/nitro/plugin.mjs.map +1 -1
  74. package/dist/nitro/v3/errorHandler.mjs +2 -2
  75. package/dist/nitro/v3/middleware.d.mts +3 -1
  76. package/dist/nitro/v3/middleware.d.mts.map +1 -1
  77. package/dist/nitro/v3/middleware.mjs +2 -1
  78. package/dist/nitro/v3/middleware.mjs.map +1 -1
  79. package/dist/nitro/v3/module.d.mts +1 -1
  80. package/dist/nitro/v3/plugin.mjs +7 -6
  81. package/dist/nitro/v3/plugin.mjs.map +1 -1
  82. package/dist/nitro/v3/useLogger.d.mts +1 -1
  83. package/dist/{nitro-CaIIZfdG.d.mts → nitro-BbTINVdZ.d.mts} +2 -2
  84. package/dist/{nitro-CaIIZfdG.d.mts.map → nitro-BbTINVdZ.d.mts.map} +1 -1
  85. package/dist/{nitro-D1pPm37T.mjs → nitro-OmT_M4Pb.mjs} +1 -1
  86. package/dist/{nitro-D1pPm37T.mjs.map → nitro-OmT_M4Pb.mjs.map} +1 -1
  87. package/dist/nuxt/module.d.mts +13 -1
  88. package/dist/nuxt/module.d.mts.map +1 -1
  89. package/dist/nuxt/module.mjs +7 -2
  90. package/dist/nuxt/module.mjs.map +1 -1
  91. package/dist/{parseError-DYrhDD1r.d.mts → parseError-DO1qtmGL.d.mts} +2 -2
  92. package/dist/parseError-DO1qtmGL.d.mts.map +1 -0
  93. package/dist/react-router/index.d.mts +47 -0
  94. package/dist/react-router/index.d.mts.map +1 -0
  95. package/dist/react-router/index.mjs +59 -0
  96. package/dist/react-router/index.mjs.map +1 -0
  97. package/dist/{routes-CE3_c-iZ.mjs → routes-CGPmbzCZ.mjs} +1 -1
  98. package/dist/{routes-CE3_c-iZ.mjs.map → routes-CGPmbzCZ.mjs.map} +1 -1
  99. package/dist/runtime/client/log.d.mts +1 -1
  100. package/dist/runtime/server/useLogger.d.mts +1 -1
  101. package/dist/runtime/utils/parseError.d.mts +2 -2
  102. package/dist/runtime/utils/parseError.mjs +2 -1
  103. package/dist/runtime/utils/parseError.mjs.map +1 -1
  104. package/dist/source-location-DRvDDqfq.mjs +1165 -0
  105. package/dist/source-location-DRvDDqfq.mjs.map +1 -0
  106. package/dist/{storage-DHU1gGqV.mjs → storage-DsueXspk.mjs} +1 -1
  107. package/dist/{storage-DHU1gGqV.mjs.map → storage-DsueXspk.mjs.map} +1 -1
  108. package/dist/sveltekit/index.d.mts +2 -2
  109. package/dist/sveltekit/index.mjs +3 -3
  110. package/dist/toolkit.d.mts +3 -3
  111. package/dist/toolkit.mjs +3 -3
  112. package/dist/{types-US-1hJZl.d.mts → types-BpsDbwHU.d.mts} +3 -1
  113. package/dist/{types-US-1hJZl.d.mts.map → types-BpsDbwHU.d.mts.map} +1 -1
  114. package/dist/types.d.mts +1 -1
  115. package/dist/{useLogger-DBHI03WK.d.mts → useLogger-C_8vgz0g.d.mts} +2 -2
  116. package/dist/{useLogger-DBHI03WK.d.mts.map → useLogger-C_8vgz0g.d.mts.map} +1 -1
  117. package/dist/utils.d.mts +1 -1
  118. package/dist/utils.d.mts.map +1 -1
  119. package/dist/utils.mjs +22 -16
  120. package/dist/utils.mjs.map +1 -1
  121. package/dist/vite/index.d.mts +80 -0
  122. package/dist/vite/index.d.mts.map +1 -0
  123. package/dist/vite/index.mjs +213 -0
  124. package/dist/vite/index.mjs.map +1 -0
  125. package/dist/workers.d.mts +1 -1
  126. package/package.json +56 -5
  127. package/dist/_http-DpL_o_Wk.mjs +0 -57
  128. package/dist/_http-DpL_o_Wk.mjs.map +0 -1
  129. package/dist/parseError-DYrhDD1r.d.mts.map +0 -1
@@ -1,6 +1,6 @@
1
- import { n as resolveAdapterConfig, t as httpPost } from "../_http-DpL_o_Wk.mjs";
2
- import { t as defineDrain } from "../_drain-FZVTbHn9.mjs";
3
- import { t as OTEL_SEVERITY_NUMBER } from "../_severity-fhhv2Nq0.mjs";
1
+ import { n as resolveAdapterConfig, t as httpPost } from "../_http-C2UoHWgm.mjs";
2
+ import { t as defineDrain } from "../_drain-C9Nr-6Wc.mjs";
3
+ import { t as OTEL_SEVERITY_NUMBER } from "../_severity-BLiOKoxh.mjs";
4
4
  //#region src/adapters/sentry.ts
5
5
  const SENTRY_FIELDS = [
6
6
  {
@@ -16,7 +16,8 @@ const SENTRY_FIELDS = [
16
16
  env: ["NUXT_SENTRY_RELEASE", "SENTRY_RELEASE"]
17
17
  },
18
18
  { key: "tags" },
19
- { key: "timeout" }
19
+ { key: "timeout" },
20
+ { key: "retries" }
20
21
  ];
21
22
  function parseSentryDsn(dsn) {
22
23
  const url = new URL(dsn);
@@ -212,6 +213,7 @@ async function sendBatchToSentry(events, config) {
212
213
  },
213
214
  body,
214
215
  timeout: config.timeout ?? 5e3,
216
+ retries: config.retries,
215
217
  label: "Sentry"
216
218
  });
217
219
  }
@@ -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}\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]\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: () => {\n const config = 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 label: 'Sentry',\n })\n}\n"],"mappings":"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"}
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: () => {\n const config = 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":"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"}
@@ -0,0 +1,165 @@
1
+ import { g as RequestLogger } from "../types-BpsDbwHU.mjs";
2
+ import { GatewayModelId } from "ai";
3
+ import { LanguageModelV3, LanguageModelV3Middleware } from "@ai-sdk/provider";
4
+
5
+ //#region src/ai/index.d.ts
6
+ /**
7
+ * Fine-grained control over tool call input capture.
8
+ */
9
+ interface ToolInputsOptions {
10
+ /**
11
+ * Max character length for the stringified input JSON.
12
+ * Inputs exceeding this limit are truncated with a `…` suffix.
13
+ */
14
+ maxLength?: number;
15
+ /**
16
+ * Custom transform applied to each captured input before storing.
17
+ * Receives the parsed input and tool name; return value is stored.
18
+ * Runs before `maxLength` truncation.
19
+ */
20
+ transform?: (input: unknown, toolName: string) => unknown;
21
+ }
22
+ /**
23
+ * Options for `createAILogger` and `createAIMiddleware`.
24
+ */
25
+ interface AILoggerOptions {
26
+ /**
27
+ * When enabled, `toolCalls` contains `{ name, input }` objects instead of plain tool name strings.
28
+ * Opt-in because inputs can be large and may contain sensitive data.
29
+ *
30
+ * - `true` — capture all inputs as-is
31
+ * - `{ maxLength, transform }` — capture with truncation or custom transform
32
+ * @default false
33
+ */
34
+ toolInputs?: boolean | ToolInputsOptions;
35
+ }
36
+ /**
37
+ * Per-step token usage breakdown for multi-step agent runs.
38
+ */
39
+ interface AIStepUsage {
40
+ model: string;
41
+ inputTokens: number;
42
+ outputTokens: number;
43
+ toolCalls?: string[];
44
+ }
45
+ /**
46
+ * Shape of the `ai` field written to the wide event.
47
+ */
48
+ interface AIEventData {
49
+ calls: number;
50
+ model: string;
51
+ models?: string[];
52
+ provider: string;
53
+ inputTokens: number;
54
+ outputTokens: number;
55
+ totalTokens: number;
56
+ cacheReadTokens?: number;
57
+ cacheWriteTokens?: number;
58
+ reasoningTokens?: number;
59
+ finishReason?: string;
60
+ toolCalls?: string[] | Array<{
61
+ name: string;
62
+ input: unknown;
63
+ }>;
64
+ responseId?: string;
65
+ steps?: number;
66
+ stepsUsage?: AIStepUsage[];
67
+ msToFirstChunk?: number;
68
+ msToFinish?: number;
69
+ tokensPerSecond?: number;
70
+ error?: string;
71
+ }
72
+ interface AILogger {
73
+ /**
74
+ * Wrap a language model with evlog middleware.
75
+ * All `generateText`, `streamText`, `generateObject`, and `streamObject` calls
76
+ * using the wrapped model are captured automatically into the wide event.
77
+ *
78
+ * Accepts a `LanguageModelV3` object or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).
79
+ * Strings are resolved via the AI SDK gateway.
80
+ *
81
+ * Also works with pre-wrapped models (e.g. from supermemory, guardrails):
82
+ * `ai.wrap(withSupermemory(base, orgId))` composes correctly.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const ai = createAILogger(log)
87
+ * const model = ai.wrap('anthropic/claude-sonnet-4.6')
88
+ *
89
+ * // Also accepts a model object
90
+ * const model = ai.wrap(anthropic('claude-sonnet-4.6'))
91
+ * ```
92
+ */
93
+ wrap: (model: LanguageModelV3 | GatewayModelId) => LanguageModelV3;
94
+ /**
95
+ * Manually capture token usage from an `embed()` or `embedMany()` result.
96
+ * Embedding models use a different type than language models, so they
97
+ * cannot be wrapped with middleware.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const { embedding, usage } = await embed({ model: embeddingModel, value: query })
102
+ * ai.captureEmbed({ usage })
103
+ * ```
104
+ */
105
+ captureEmbed: (result: {
106
+ usage: {
107
+ tokens: number;
108
+ };
109
+ }) => void;
110
+ }
111
+ /**
112
+ * Create the evlog AI middleware that captures AI SDK data into a wide event.
113
+ *
114
+ * Use this when you need explicit middleware composition with other wrappers
115
+ * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.
116
+ *
117
+ * Note: `captureEmbed` is not available with the raw middleware — use
118
+ * `createAILogger` if you need embedding capture.
119
+ *
120
+ * @example Nuxt API route with supermemory
121
+ * ```ts
122
+ * import { createAIMiddleware } from 'evlog/ai'
123
+ * import { wrapLanguageModel } from 'ai'
124
+ *
125
+ * export default defineEventHandler(async (event) => {
126
+ * const log = useLogger(event)
127
+ *
128
+ * const model = wrapLanguageModel({
129
+ * model: withSupermemory(base, orgId),
130
+ * middleware: [createAIMiddleware(log, { toolInputs: true })],
131
+ * })
132
+ * })
133
+ * ```
134
+ */
135
+ declare function createAIMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelV3Middleware;
136
+ /**
137
+ * Create an AI logger that captures AI SDK data into the wide event.
138
+ *
139
+ * Uses model middleware (`wrapLanguageModel`) to transparently intercept
140
+ * all LLM calls. `onFinish` and `onStepFinish` remain free for user code.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { createAILogger } from 'evlog/ai'
145
+ *
146
+ * const log = useLogger(event)
147
+ * const ai = createAILogger(log)
148
+ * const model = ai.wrap('anthropic/claude-sonnet-4.6')
149
+ *
150
+ * const result = streamText({
151
+ * model,
152
+ * messages,
153
+ * onFinish: ({ text }) => saveConversation(text),
154
+ * })
155
+ * ```
156
+ *
157
+ * @example Capture tool call inputs
158
+ * ```ts
159
+ * const ai = createAILogger(log, { toolInputs: true })
160
+ * ```
161
+ */
162
+ declare function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger;
163
+ //#endregion
164
+ export { AIEventData, AILogger, AILoggerOptions, AIStepUsage, ToolInputsOptions, createAILogger, createAIMiddleware };
165
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ai/index.ts"],"mappings":";;;;;;;AAQA;UAAiB,iBAAA;;;;;EAKf,SAAA;EAM6B;;;AAM/B;;EANE,SAAA,IAAa,KAAA,WAAgB,QAAA;AAAA;;AAqB/B;;UAfiB,eAAA;EAeW;;;;;;;AAU5B;EAhBE,UAAA,aAAuB,iBAAA;AAAA;;;;UAMR,WAAA;EACf,KAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;AAAA;;;;UAMe,WAAA;EACf,KAAA;EACA,KAAA;EACA,MAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;EACA,YAAA;EACA,SAAA,cAAuB,KAAA;IAAQ,IAAA;IAAc,KAAA;EAAA;EAC7C,UAAA;EACA,KAAA;EACA,UAAA,GAAa,WAAA;EACb,cAAA;EACA,UAAA;EACA,eAAA;EACA,KAAA;AAAA;AAAA,UAGe,QAAA;EAqBR;;;;;;;;AA8ET;;;;;;;;;;;;EA9EE,IAAA,GAAO,KAAA,EAAO,eAAA,GAAkB,cAAA,KAAmB,eAAA;EA8EuD;;AA8B5G;;;;;;;;;EA/FE,YAAA,GAAe,MAAA;IAAU,KAAA;MAAS,MAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEpB,kBAAA,CAAmB,GAAA,EAAK,aAAA,EAAe,OAAA,GAAU,eAAA,GAAkB,yBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8BnE,cAAA,CAAe,GAAA,EAAK,aAAA,EAAe,OAAA,GAAU,eAAA,GAAkB,QAAA"}
@@ -0,0 +1,340 @@
1
+ import { gateway, wrapLanguageModel } from "ai";
2
+ //#region src/ai/index.ts
3
+ function addUsage(acc, usage) {
4
+ acc.inputTokens += usage.inputTokens.total ?? 0;
5
+ acc.outputTokens += usage.outputTokens.total ?? 0;
6
+ acc.cacheReadTokens += usage.inputTokens.cacheRead ?? 0;
7
+ acc.cacheWriteTokens += usage.inputTokens.cacheWrite ?? 0;
8
+ acc.reasoningTokens += usage.outputTokens.reasoning ?? 0;
9
+ }
10
+ /**
11
+ * When using `gateway('google/gemini-3-flash')`, the model object has
12
+ * `provider: 'gateway'` and `modelId: 'google/gemini-3-flash'`.
13
+ * This extracts the real provider and model name from the modelId.
14
+ */
15
+ function resolveProviderAndModel(provider, modelId) {
16
+ if (provider !== "gateway" || !modelId.includes("/")) return {
17
+ provider,
18
+ model: modelId
19
+ };
20
+ const slashIndex = modelId.indexOf("/");
21
+ return {
22
+ provider: modelId.slice(0, slashIndex),
23
+ model: modelId.slice(slashIndex + 1)
24
+ };
25
+ }
26
+ /**
27
+ * Create the evlog AI middleware that captures AI SDK data into a wide event.
28
+ *
29
+ * Use this when you need explicit middleware composition with other wrappers
30
+ * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.
31
+ *
32
+ * Note: `captureEmbed` is not available with the raw middleware — use
33
+ * `createAILogger` if you need embedding capture.
34
+ *
35
+ * @example Nuxt API route with supermemory
36
+ * ```ts
37
+ * import { createAIMiddleware } from 'evlog/ai'
38
+ * import { wrapLanguageModel } from 'ai'
39
+ *
40
+ * export default defineEventHandler(async (event) => {
41
+ * const log = useLogger(event)
42
+ *
43
+ * const model = wrapLanguageModel({
44
+ * model: withSupermemory(base, orgId),
45
+ * middleware: [createAIMiddleware(log, { toolInputs: true })],
46
+ * })
47
+ * })
48
+ * ```
49
+ */
50
+ function createAIMiddleware(log, options) {
51
+ return buildMiddleware(log, options);
52
+ }
53
+ /**
54
+ * Create an AI logger that captures AI SDK data into the wide event.
55
+ *
56
+ * Uses model middleware (`wrapLanguageModel`) to transparently intercept
57
+ * all LLM calls. `onFinish` and `onStepFinish` remain free for user code.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * import { createAILogger } from 'evlog/ai'
62
+ *
63
+ * const log = useLogger(event)
64
+ * const ai = createAILogger(log)
65
+ * const model = ai.wrap('anthropic/claude-sonnet-4.6')
66
+ *
67
+ * const result = streamText({
68
+ * model,
69
+ * messages,
70
+ * onFinish: ({ text }) => saveConversation(text),
71
+ * })
72
+ * ```
73
+ *
74
+ * @example Capture tool call inputs
75
+ * ```ts
76
+ * const ai = createAILogger(log, { toolInputs: true })
77
+ * ```
78
+ */
79
+ function createAILogger(log, options) {
80
+ const state = createAccumulatorState(options);
81
+ const middleware = buildMiddlewareFromState(log, state);
82
+ return {
83
+ wrap: (model) => {
84
+ return wrapLanguageModel({
85
+ model: typeof model === "string" ? gateway(model) : model,
86
+ middleware
87
+ });
88
+ },
89
+ captureEmbed: (result) => {
90
+ state.calls++;
91
+ state.usage.inputTokens += result.usage.tokens;
92
+ flushState(log, state);
93
+ }
94
+ };
95
+ }
96
+ function resolveToolInputs(raw) {
97
+ if (!raw) return {
98
+ enabled: false,
99
+ options: void 0
100
+ };
101
+ if (raw === true) return {
102
+ enabled: true,
103
+ options: void 0
104
+ };
105
+ return {
106
+ enabled: true,
107
+ options: raw
108
+ };
109
+ }
110
+ function processToolInput(input, toolName, options) {
111
+ let value = input;
112
+ if (options?.transform) value = options.transform(value, toolName);
113
+ if (options?.maxLength) {
114
+ const str = typeof value === "string" ? value : JSON.stringify(value);
115
+ if (str.length > options.maxLength) return `${str.slice(0, options.maxLength)}…`;
116
+ }
117
+ return value;
118
+ }
119
+ function createAccumulatorState(options) {
120
+ const { enabled, options: captureOpts } = resolveToolInputs(options?.toolInputs);
121
+ return {
122
+ calls: 0,
123
+ steps: 0,
124
+ usage: {
125
+ inputTokens: 0,
126
+ outputTokens: 0,
127
+ cacheReadTokens: 0,
128
+ cacheWriteTokens: 0,
129
+ reasoningTokens: 0
130
+ },
131
+ models: [],
132
+ lastProvider: void 0,
133
+ allToolCalls: [],
134
+ allToolCallInputs: [],
135
+ stepsUsage: [],
136
+ lastFinishReason: void 0,
137
+ lastMsToFirstChunk: void 0,
138
+ lastMsToFinish: void 0,
139
+ lastError: void 0,
140
+ lastResponseId: void 0,
141
+ toolInputs: enabled,
142
+ toolInputsOptions: captureOpts
143
+ };
144
+ }
145
+ function flushState(log, state) {
146
+ const uniqueModels = [...new Set(state.models)];
147
+ const lastModel = state.models[state.models.length - 1];
148
+ const data = {
149
+ calls: state.calls,
150
+ inputTokens: state.usage.inputTokens,
151
+ outputTokens: state.usage.outputTokens,
152
+ totalTokens: state.usage.inputTokens + state.usage.outputTokens
153
+ };
154
+ if (lastModel) data.model = lastModel;
155
+ if (state.lastProvider) data.provider = state.lastProvider;
156
+ if (uniqueModels.length > 1) data.models = uniqueModels;
157
+ if (state.usage.cacheReadTokens > 0) data.cacheReadTokens = state.usage.cacheReadTokens;
158
+ if (state.usage.cacheWriteTokens > 0) data.cacheWriteTokens = state.usage.cacheWriteTokens;
159
+ if (state.usage.reasoningTokens > 0) data.reasoningTokens = state.usage.reasoningTokens;
160
+ if (state.lastFinishReason) data.finishReason = state.lastFinishReason;
161
+ if (state.toolInputs && state.allToolCallInputs.length > 0) data.toolCalls = [...state.allToolCallInputs];
162
+ else if (state.allToolCalls.length > 0) data.toolCalls = [...state.allToolCalls];
163
+ if (state.lastResponseId) data.responseId = state.lastResponseId;
164
+ if (state.steps > 1) {
165
+ data.steps = state.steps;
166
+ data.stepsUsage = [...state.stepsUsage];
167
+ }
168
+ if (state.lastMsToFirstChunk !== void 0) data.msToFirstChunk = state.lastMsToFirstChunk;
169
+ if (state.lastMsToFinish !== void 0) {
170
+ data.msToFinish = state.lastMsToFinish;
171
+ if (state.usage.outputTokens > 0 && state.lastMsToFinish > 0) data.tokensPerSecond = Math.round(state.usage.outputTokens / state.lastMsToFinish * 1e3);
172
+ }
173
+ if (state.lastError) data.error = state.lastError;
174
+ log.set({ ai: data });
175
+ }
176
+ function recordModel(state, provider, modelId, responseModelId) {
177
+ const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId);
178
+ state.models.push(resolved.model);
179
+ state.lastProvider = resolved.provider;
180
+ }
181
+ function safeParseJSON(input) {
182
+ try {
183
+ return JSON.parse(input);
184
+ } catch {
185
+ return input;
186
+ }
187
+ }
188
+ function recordError(log, state, model, error) {
189
+ state.calls++;
190
+ state.steps++;
191
+ recordModel(state, model.provider, model.modelId);
192
+ state.lastFinishReason = "error";
193
+ state.lastError = error instanceof Error ? error.message : String(error);
194
+ const resolved = resolveProviderAndModel(model.provider, model.modelId);
195
+ state.stepsUsage.push({
196
+ model: resolved.model,
197
+ inputTokens: 0,
198
+ outputTokens: 0
199
+ });
200
+ flushState(log, state);
201
+ }
202
+ function buildMiddleware(log, options) {
203
+ return buildMiddlewareFromState(log, createAccumulatorState(options));
204
+ }
205
+ function buildMiddlewareFromState(log, state) {
206
+ return {
207
+ specificationVersion: "v3",
208
+ wrapGenerate: async ({ doGenerate, model }) => {
209
+ try {
210
+ const result = await doGenerate();
211
+ state.calls++;
212
+ state.steps++;
213
+ addUsage(state.usage, result.usage);
214
+ recordModel(state, model.provider, model.modelId, result.response?.modelId);
215
+ state.lastFinishReason = result.finishReason.unified;
216
+ if (result.response?.id) state.lastResponseId = result.response.id;
217
+ const stepToolCalls = [];
218
+ for (const item of result.content) if (item.type === "tool-call") {
219
+ state.allToolCalls.push(item.toolName);
220
+ stepToolCalls.push(item.toolName);
221
+ if (state.toolInputs) {
222
+ const raw = typeof item.input === "string" ? safeParseJSON(item.input) : item.input;
223
+ state.allToolCallInputs.push({
224
+ name: item.toolName,
225
+ input: processToolInput(raw, item.toolName, state.toolInputsOptions)
226
+ });
227
+ }
228
+ }
229
+ const resolvedModel = resolveProviderAndModel(model.provider, result.response?.modelId ?? model.modelId);
230
+ state.stepsUsage.push({
231
+ model: resolvedModel.model,
232
+ inputTokens: result.usage.inputTokens.total ?? 0,
233
+ outputTokens: result.usage.outputTokens.total ?? 0,
234
+ ...stepToolCalls.length > 0 ? { toolCalls: stepToolCalls } : {}
235
+ });
236
+ flushState(log, state);
237
+ return result;
238
+ } catch (error) {
239
+ recordError(log, state, model, error);
240
+ throw error;
241
+ }
242
+ },
243
+ wrapStream: async ({ doStream, model }) => {
244
+ const streamStart = Date.now();
245
+ let firstChunkTime;
246
+ let streamUsage;
247
+ let streamFinishReason;
248
+ let streamModelId;
249
+ let streamResponseId;
250
+ const streamToolCalls = [];
251
+ const streamToolInputBuffers = /* @__PURE__ */ new Map();
252
+ let streamError;
253
+ let doStreamResult;
254
+ try {
255
+ doStreamResult = await doStream();
256
+ } catch (error) {
257
+ recordError(log, state, model, error);
258
+ throw error;
259
+ }
260
+ const { stream, ...rest } = doStreamResult;
261
+ const transformStream = new TransformStream({
262
+ transform(chunk, controller) {
263
+ if (!firstChunkTime && chunk.type === "text-delta") firstChunkTime = Date.now();
264
+ if (chunk.type === "tool-input-start") {
265
+ streamToolCalls.push(chunk.toolName);
266
+ if (state.toolInputs) streamToolInputBuffers.set(chunk.id, {
267
+ name: chunk.toolName,
268
+ chunks: []
269
+ });
270
+ }
271
+ if (chunk.type === "tool-input-delta" && state.toolInputs) {
272
+ const buffer = streamToolInputBuffers.get(chunk.id);
273
+ if (buffer) buffer.chunks.push(chunk.delta);
274
+ }
275
+ if (chunk.type === "tool-input-end" && state.toolInputs) {
276
+ const buffer = streamToolInputBuffers.get(chunk.id);
277
+ if (buffer) {
278
+ const raw = safeParseJSON(buffer.chunks.join(""));
279
+ state.allToolCallInputs.push({
280
+ name: buffer.name,
281
+ input: processToolInput(raw, buffer.name, state.toolInputsOptions)
282
+ });
283
+ streamToolInputBuffers.delete(chunk.id);
284
+ }
285
+ }
286
+ if (chunk.type === "finish") {
287
+ streamUsage = {
288
+ inputTokens: chunk.usage.inputTokens.total ?? 0,
289
+ outputTokens: chunk.usage.outputTokens.total ?? 0,
290
+ cacheReadTokens: chunk.usage.inputTokens.cacheRead ?? 0,
291
+ cacheWriteTokens: chunk.usage.inputTokens.cacheWrite ?? 0,
292
+ reasoningTokens: chunk.usage.outputTokens.reasoning ?? 0
293
+ };
294
+ streamFinishReason = chunk.finishReason.unified;
295
+ }
296
+ if (chunk.type === "response-metadata") {
297
+ if (chunk.modelId) streamModelId = chunk.modelId;
298
+ if (chunk.id) streamResponseId = chunk.id;
299
+ }
300
+ if (chunk.type === "error") streamError = chunk.error instanceof Error ? chunk.error.message : String(chunk.error);
301
+ controller.enqueue(chunk);
302
+ },
303
+ flush() {
304
+ state.calls++;
305
+ state.steps++;
306
+ if (streamUsage) {
307
+ state.usage.inputTokens += streamUsage.inputTokens;
308
+ state.usage.outputTokens += streamUsage.outputTokens;
309
+ state.usage.cacheReadTokens += streamUsage.cacheReadTokens;
310
+ state.usage.cacheWriteTokens += streamUsage.cacheWriteTokens;
311
+ state.usage.reasoningTokens += streamUsage.reasoningTokens;
312
+ }
313
+ recordModel(state, model.provider, model.modelId, streamModelId);
314
+ state.lastFinishReason = streamFinishReason;
315
+ state.allToolCalls.push(...streamToolCalls);
316
+ if (streamResponseId) state.lastResponseId = streamResponseId;
317
+ if (firstChunkTime) state.lastMsToFirstChunk = firstChunkTime - streamStart;
318
+ state.lastMsToFinish = Date.now() - streamStart;
319
+ if (streamError) state.lastError = streamError;
320
+ const resolvedModel = resolveProviderAndModel(model.provider, streamModelId ?? model.modelId);
321
+ state.stepsUsage.push({
322
+ model: resolvedModel.model,
323
+ inputTokens: streamUsage?.inputTokens ?? 0,
324
+ outputTokens: streamUsage?.outputTokens ?? 0,
325
+ ...streamToolCalls.length > 0 ? { toolCalls: [...streamToolCalls] } : {}
326
+ });
327
+ flushState(log, state);
328
+ }
329
+ });
330
+ return {
331
+ stream: stream.pipeThrough(transformStream),
332
+ ...rest
333
+ };
334
+ }
335
+ };
336
+ }
337
+ //#endregion
338
+ export { createAILogger, createAIMiddleware };
339
+
340
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/ai/index.ts"],"sourcesContent":["import { gateway, wrapLanguageModel } from 'ai'\nimport type { GatewayModelId } from 'ai'\nimport type { LanguageModelV3, LanguageModelV3Middleware, LanguageModelV3StreamPart } from '@ai-sdk/provider'\nimport type { RequestLogger } from '../types'\n\n/**\n * Fine-grained control over tool call input capture.\n */\nexport interface ToolInputsOptions {\n /**\n * Max character length for the stringified input JSON.\n * Inputs exceeding this limit are truncated with a `…` suffix.\n */\n maxLength?: number\n /**\n * Custom transform applied to each captured input before storing.\n * Receives the parsed input and tool name; return value is stored.\n * Runs before `maxLength` truncation.\n */\n transform?: (input: unknown, toolName: string) => unknown\n}\n\n/**\n * Options for `createAILogger` and `createAIMiddleware`.\n */\nexport interface AILoggerOptions {\n /**\n * When enabled, `toolCalls` contains `{ name, input }` objects instead of plain tool name strings.\n * Opt-in because inputs can be large and may contain sensitive data.\n *\n * - `true` — capture all inputs as-is\n * - `{ maxLength, transform }` — capture with truncation or custom transform\n * @default false\n */\n toolInputs?: boolean | ToolInputsOptions\n}\n\n/**\n * Per-step token usage breakdown for multi-step agent runs.\n */\nexport interface AIStepUsage {\n model: string\n inputTokens: number\n outputTokens: number\n toolCalls?: string[]\n}\n\n/**\n * Shape of the `ai` field written to the wide event.\n */\nexport interface AIEventData {\n calls: number\n model: string\n models?: string[]\n provider: string\n inputTokens: number\n outputTokens: number\n totalTokens: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n reasoningTokens?: number\n finishReason?: string\n toolCalls?: string[] | Array<{ name: string, input: unknown }>\n responseId?: string\n steps?: number\n stepsUsage?: AIStepUsage[]\n msToFirstChunk?: number\n msToFinish?: number\n tokensPerSecond?: number\n error?: string\n}\n\nexport interface AILogger {\n /**\n * Wrap a language model with evlog middleware.\n * All `generateText`, `streamText`, `generateObject`, and `streamObject` calls\n * using the wrapped model are captured automatically into the wide event.\n *\n * Accepts a `LanguageModelV3` object or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).\n * Strings are resolved via the AI SDK gateway.\n *\n * Also works with pre-wrapped models (e.g. from supermemory, guardrails):\n * `ai.wrap(withSupermemory(base, orgId))` composes correctly.\n *\n * @example\n * ```ts\n * const ai = createAILogger(log)\n * const model = ai.wrap('anthropic/claude-sonnet-4.6')\n *\n * // Also accepts a model object\n * const model = ai.wrap(anthropic('claude-sonnet-4.6'))\n * ```\n */\n wrap: (model: LanguageModelV3 | GatewayModelId) => LanguageModelV3\n\n /**\n * Manually capture token usage from an `embed()` or `embedMany()` result.\n * Embedding models use a different type than language models, so they\n * cannot be wrapped with middleware.\n *\n * @example\n * ```ts\n * const { embedding, usage } = await embed({ model: embeddingModel, value: query })\n * ai.captureEmbed({ usage })\n * ```\n */\n captureEmbed: (result: { usage: { tokens: number } }) => void\n}\n\ninterface UsageAccumulator {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheWriteTokens: number\n reasoningTokens: number\n}\n\nfunction addUsage(\n acc: UsageAccumulator,\n usage: {\n inputTokens: { total: number | undefined, cacheRead?: number | undefined, cacheWrite?: number | undefined }\n outputTokens: { total: number | undefined, reasoning?: number | undefined }\n },\n): void {\n acc.inputTokens += usage.inputTokens.total ?? 0\n acc.outputTokens += usage.outputTokens.total ?? 0\n acc.cacheReadTokens += usage.inputTokens.cacheRead ?? 0\n acc.cacheWriteTokens += usage.inputTokens.cacheWrite ?? 0\n acc.reasoningTokens += usage.outputTokens.reasoning ?? 0\n}\n\n/**\n * When using `gateway('google/gemini-3-flash')`, the model object has\n * `provider: 'gateway'` and `modelId: 'google/gemini-3-flash'`.\n * This extracts the real provider and model name from the modelId.\n */\nfunction resolveProviderAndModel(provider: string, modelId: string): { provider: string, model: string } {\n if (provider !== 'gateway' || !modelId.includes('/')) {\n return { provider, model: modelId }\n }\n const slashIndex = modelId.indexOf('/')\n return {\n provider: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n }\n}\n\n/**\n * Create the evlog AI middleware that captures AI SDK data into a wide event.\n *\n * Use this when you need explicit middleware composition with other wrappers\n * (e.g. supermemory, guardrails). For most cases, use `createAILogger` instead.\n *\n * Note: `captureEmbed` is not available with the raw middleware — use\n * `createAILogger` if you need embedding capture.\n *\n * @example Nuxt API route with supermemory\n * ```ts\n * import { createAIMiddleware } from 'evlog/ai'\n * import { wrapLanguageModel } from 'ai'\n *\n * export default defineEventHandler(async (event) => {\n * const log = useLogger(event)\n *\n * const model = wrapLanguageModel({\n * model: withSupermemory(base, orgId),\n * middleware: [createAIMiddleware(log, { toolInputs: true })],\n * })\n * })\n * ```\n */\nexport function createAIMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelV3Middleware {\n return buildMiddleware(log, options)\n}\n\n/**\n * Create an AI logger that captures AI SDK data into the wide event.\n *\n * Uses model middleware (`wrapLanguageModel`) to transparently intercept\n * all LLM calls. `onFinish` and `onStepFinish` remain free for user code.\n *\n * @example\n * ```ts\n * import { createAILogger } from 'evlog/ai'\n *\n * const log = useLogger(event)\n * const ai = createAILogger(log)\n * const model = ai.wrap('anthropic/claude-sonnet-4.6')\n *\n * const result = streamText({\n * model,\n * messages,\n * onFinish: ({ text }) => saveConversation(text),\n * })\n * ```\n *\n * @example Capture tool call inputs\n * ```ts\n * const ai = createAILogger(log, { toolInputs: true })\n * ```\n */\nexport function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger {\n const state = createAccumulatorState(options)\n const middleware = buildMiddlewareFromState(log, state)\n\n return {\n wrap: (model: LanguageModelV3 | GatewayModelId) => {\n const resolved = typeof model === 'string' ? gateway(model) : model\n return wrapLanguageModel({ model: resolved, middleware })\n },\n\n captureEmbed: (result: { usage: { tokens: number } }) => {\n state.calls++\n state.usage.inputTokens += result.usage.tokens\n flushState(log, state)\n },\n }\n}\n\ninterface AccumulatorState {\n calls: number\n steps: number\n usage: UsageAccumulator\n models: string[]\n lastProvider: string | undefined\n allToolCalls: string[]\n allToolCallInputs: Array<{ name: string, input: unknown }>\n stepsUsage: AIStepUsage[]\n lastFinishReason: string | undefined\n lastMsToFirstChunk: number | undefined\n lastMsToFinish: number | undefined\n lastError: string | undefined\n lastResponseId: string | undefined\n toolInputs: boolean\n toolInputsOptions: ToolInputsOptions | undefined\n}\n\nfunction resolveToolInputs(raw?: boolean | ToolInputsOptions): { enabled: boolean, options: ToolInputsOptions | undefined } {\n if (!raw) return { enabled: false, options: undefined }\n if (raw === true) return { enabled: true, options: undefined }\n return { enabled: true, options: raw }\n}\n\nfunction processToolInput(input: unknown, toolName: string, options: ToolInputsOptions | undefined): unknown {\n let value = input\n if (options?.transform) {\n value = options.transform(value, toolName)\n }\n if (options?.maxLength) {\n const str = typeof value === 'string' ? value : JSON.stringify(value)\n if (str.length > options.maxLength) {\n return `${str.slice(0, options.maxLength)}…`\n }\n }\n return value\n}\n\nfunction createAccumulatorState(options?: AILoggerOptions): AccumulatorState {\n const { enabled, options: captureOpts } = resolveToolInputs(options?.toolInputs)\n return {\n calls: 0,\n steps: 0,\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n reasoningTokens: 0,\n },\n models: [],\n lastProvider: undefined,\n allToolCalls: [],\n allToolCallInputs: [],\n stepsUsage: [],\n lastFinishReason: undefined,\n lastMsToFirstChunk: undefined,\n lastMsToFinish: undefined,\n lastError: undefined,\n lastResponseId: undefined,\n toolInputs: enabled,\n toolInputsOptions: captureOpts,\n }\n}\n\nfunction flushState(log: RequestLogger, state: AccumulatorState): void {\n const uniqueModels = [...new Set(state.models)]\n const lastModel = state.models[state.models.length - 1]\n\n const data: Partial<AIEventData> & { calls: number, inputTokens: number, outputTokens: number, totalTokens: number } = {\n calls: state.calls,\n inputTokens: state.usage.inputTokens,\n outputTokens: state.usage.outputTokens,\n totalTokens: state.usage.inputTokens + state.usage.outputTokens,\n }\n\n if (lastModel) data.model = lastModel\n if (state.lastProvider) data.provider = state.lastProvider\n if (uniqueModels.length > 1) data.models = uniqueModels\n if (state.usage.cacheReadTokens > 0) data.cacheReadTokens = state.usage.cacheReadTokens\n if (state.usage.cacheWriteTokens > 0) data.cacheWriteTokens = state.usage.cacheWriteTokens\n if (state.usage.reasoningTokens > 0) data.reasoningTokens = state.usage.reasoningTokens\n if (state.lastFinishReason) data.finishReason = state.lastFinishReason\n if (state.toolInputs && state.allToolCallInputs.length > 0) {\n data.toolCalls = [...state.allToolCallInputs]\n } else if (state.allToolCalls.length > 0) {\n data.toolCalls = [...state.allToolCalls]\n }\n if (state.lastResponseId) data.responseId = state.lastResponseId\n if (state.steps > 1) {\n data.steps = state.steps\n data.stepsUsage = [...state.stepsUsage]\n }\n if (state.lastMsToFirstChunk !== undefined) data.msToFirstChunk = state.lastMsToFirstChunk\n if (state.lastMsToFinish !== undefined) {\n data.msToFinish = state.lastMsToFinish\n if (state.usage.outputTokens > 0 && state.lastMsToFinish > 0) {\n data.tokensPerSecond = Math.round((state.usage.outputTokens / state.lastMsToFinish) * 1000)\n }\n }\n if (state.lastError) data.error = state.lastError\n\n log.set({ ai: data } as Record<string, unknown>)\n}\n\nfunction recordModel(state: AccumulatorState, provider: string, modelId: string, responseModelId?: string): void {\n const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId)\n state.models.push(resolved.model)\n state.lastProvider = resolved.provider\n}\n\nfunction safeParseJSON(input: string): unknown {\n try {\n return JSON.parse(input)\n } catch {\n return input\n }\n}\n\nfunction recordError(log: RequestLogger, state: AccumulatorState, model: { provider: string, modelId: string }, error: unknown): void {\n state.calls++\n state.steps++\n recordModel(state, model.provider, model.modelId)\n state.lastFinishReason = 'error'\n state.lastError = error instanceof Error ? error.message : String(error)\n\n const resolved = resolveProviderAndModel(model.provider, model.modelId)\n state.stepsUsage.push({\n model: resolved.model,\n inputTokens: 0,\n outputTokens: 0,\n })\n\n flushState(log, state)\n}\n\nfunction buildMiddleware(log: RequestLogger, options?: AILoggerOptions): LanguageModelV3Middleware {\n const state = createAccumulatorState(options)\n return buildMiddlewareFromState(log, state)\n}\n\nfunction buildMiddlewareFromState(log: RequestLogger, state: AccumulatorState): LanguageModelV3Middleware {\n return {\n specificationVersion: 'v3',\n wrapGenerate: async ({ doGenerate, model }) => {\n try {\n const result = await doGenerate()\n\n state.calls++\n state.steps++\n addUsage(state.usage, result.usage)\n recordModel(state, model.provider, model.modelId, result.response?.modelId)\n state.lastFinishReason = result.finishReason.unified\n\n if (result.response?.id) {\n state.lastResponseId = result.response.id\n }\n\n const stepToolCalls: string[] = []\n for (const item of result.content) {\n if (item.type === 'tool-call') {\n state.allToolCalls.push(item.toolName)\n stepToolCalls.push(item.toolName)\n if (state.toolInputs) {\n const raw = typeof item.input === 'string' ? safeParseJSON(item.input) : item.input\n state.allToolCallInputs.push({\n name: item.toolName,\n input: processToolInput(raw, item.toolName, state.toolInputsOptions),\n })\n }\n }\n }\n\n const resolvedModel = resolveProviderAndModel(model.provider, result.response?.modelId ?? model.modelId)\n state.stepsUsage.push({\n model: resolvedModel.model,\n inputTokens: result.usage.inputTokens.total ?? 0,\n outputTokens: result.usage.outputTokens.total ?? 0,\n ...(stepToolCalls.length > 0 ? { toolCalls: stepToolCalls } : {}),\n })\n\n flushState(log, state)\n return result\n } catch (error) {\n recordError(log, state, model, error)\n throw error\n }\n },\n\n wrapStream: async ({ doStream, model }) => {\n const streamStart = Date.now()\n let firstChunkTime: number | undefined\n\n let streamUsage: UsageAccumulator | undefined\n let streamFinishReason: string | undefined\n let streamModelId: string | undefined\n let streamResponseId: string | undefined\n const streamToolCalls: string[] = []\n const streamToolInputBuffers = new Map<string, { name: string, chunks: string[] }>()\n let streamError: string | undefined\n\n let doStreamResult: Awaited<ReturnType<typeof doStream>>\n try {\n doStreamResult = await doStream()\n } catch (error) {\n recordError(log, state, model, error)\n throw error\n }\n\n const { stream, ...rest } = doStreamResult\n\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (!firstChunkTime && chunk.type === 'text-delta') {\n firstChunkTime = Date.now()\n }\n\n if (chunk.type === 'tool-input-start') {\n streamToolCalls.push(chunk.toolName)\n if (state.toolInputs) {\n streamToolInputBuffers.set(chunk.id, { name: chunk.toolName, chunks: [] })\n }\n }\n\n if (chunk.type === 'tool-input-delta' && state.toolInputs) {\n const buffer = streamToolInputBuffers.get(chunk.id)\n if (buffer) {\n buffer.chunks.push(chunk.delta)\n }\n }\n\n if (chunk.type === 'tool-input-end' && state.toolInputs) {\n const buffer = streamToolInputBuffers.get(chunk.id)\n if (buffer) {\n const raw = safeParseJSON(buffer.chunks.join(''))\n state.allToolCallInputs.push({\n name: buffer.name,\n input: processToolInput(raw, buffer.name, state.toolInputsOptions),\n })\n streamToolInputBuffers.delete(chunk.id)\n }\n }\n\n if (chunk.type === 'finish') {\n streamUsage = {\n inputTokens: chunk.usage.inputTokens.total ?? 0,\n outputTokens: chunk.usage.outputTokens.total ?? 0,\n cacheReadTokens: chunk.usage.inputTokens.cacheRead ?? 0,\n cacheWriteTokens: chunk.usage.inputTokens.cacheWrite ?? 0,\n reasoningTokens: chunk.usage.outputTokens.reasoning ?? 0,\n }\n streamFinishReason = chunk.finishReason.unified\n }\n\n if (chunk.type === 'response-metadata') {\n if (chunk.modelId) streamModelId = chunk.modelId\n if (chunk.id) streamResponseId = chunk.id\n }\n\n if (chunk.type === 'error') {\n streamError = chunk.error instanceof Error ? chunk.error.message : String(chunk.error)\n }\n\n controller.enqueue(chunk)\n },\n\n flush() {\n state.calls++\n state.steps++\n\n if (streamUsage) {\n state.usage.inputTokens += streamUsage.inputTokens\n state.usage.outputTokens += streamUsage.outputTokens\n state.usage.cacheReadTokens += streamUsage.cacheReadTokens\n state.usage.cacheWriteTokens += streamUsage.cacheWriteTokens\n state.usage.reasoningTokens += streamUsage.reasoningTokens\n }\n\n recordModel(state, model.provider, model.modelId, streamModelId)\n state.lastFinishReason = streamFinishReason\n\n state.allToolCalls.push(...streamToolCalls)\n\n if (streamResponseId) {\n state.lastResponseId = streamResponseId\n }\n\n if (firstChunkTime) {\n state.lastMsToFirstChunk = firstChunkTime - streamStart\n }\n state.lastMsToFinish = Date.now() - streamStart\n\n if (streamError) state.lastError = streamError\n\n const resolvedModel = resolveProviderAndModel(model.provider, streamModelId ?? model.modelId)\n state.stepsUsage.push({\n model: resolvedModel.model,\n inputTokens: streamUsage?.inputTokens ?? 0,\n outputTokens: streamUsage?.outputTokens ?? 0,\n ...(streamToolCalls.length > 0 ? { toolCalls: [...streamToolCalls] } : {}),\n })\n\n flushState(log, state)\n },\n })\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { r as DrainContext } from "./types-US-1hJZl.mjs";
1
+ import { r as DrainContext } from "./types-BpsDbwHU.mjs";
2
2
  import { DrainPipelineOptions, PipelineDrainFn } from "./pipeline.mjs";
3
3
 
4
4
  //#region src/browser.d.ts
@@ -0,0 +1,2 @@
1
+ import { clearIdentity, initLog, log as _clientLog, setIdentity } from "./runtime/client/log.mjs";
2
+ export { clearIdentity, initLog, _clientLog as log, setIdentity };
@@ -0,0 +1,2 @@
1
+ import { clearIdentity, initLog, log as _clientLog, setIdentity } from "./runtime/client/log.mjs";
2
+ export { clearIdentity, initLog, _clientLog as log, setIdentity };