evlog 2.8.0 → 2.10.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 (130) hide show
  1. package/README.md +28 -0
  2. package/dist/{_drain-C9Nr-6Wc.mjs → _drain-CJDuM0ua.mjs} +7 -2
  3. package/dist/_drain-CJDuM0ua.mjs.map +1 -0
  4. package/dist/{_http-C2UoHWgm.mjs → _http-rRIz2Q0L.mjs} +22 -19
  5. package/dist/_http-rRIz2Q0L.mjs.map +1 -0
  6. package/dist/adapters/axiom.d.mts +1 -1
  7. package/dist/adapters/axiom.mjs +4 -4
  8. package/dist/adapters/axiom.mjs.map +1 -1
  9. package/dist/adapters/better-stack.d.mts +1 -1
  10. package/dist/adapters/better-stack.mjs +4 -4
  11. package/dist/adapters/better-stack.mjs.map +1 -1
  12. package/dist/adapters/fs.d.mts +1 -1
  13. package/dist/adapters/fs.mjs +1 -1
  14. package/dist/adapters/hyperdx.d.mts +65 -0
  15. package/dist/adapters/hyperdx.d.mts.map +1 -0
  16. package/dist/adapters/hyperdx.mjs +93 -0
  17. package/dist/adapters/hyperdx.mjs.map +1 -0
  18. package/dist/adapters/otlp.d.mts +1 -1
  19. package/dist/adapters/otlp.mjs +4 -4
  20. package/dist/adapters/otlp.mjs.map +1 -1
  21. package/dist/adapters/posthog.d.mts +1 -1
  22. package/dist/adapters/posthog.mjs +6 -6
  23. package/dist/adapters/posthog.mjs.map +1 -1
  24. package/dist/adapters/sentry.d.mts +1 -1
  25. package/dist/adapters/sentry.mjs +4 -4
  26. package/dist/adapters/sentry.mjs.map +1 -1
  27. package/dist/ai/index.d.mts +82 -5
  28. package/dist/ai/index.d.mts.map +1 -1
  29. package/dist/ai/index.mjs +237 -96
  30. package/dist/ai/index.mjs.map +1 -1
  31. package/dist/browser.d.mts +3 -1
  32. package/dist/browser.d.mts.map +1 -1
  33. package/dist/browser.mjs +2 -2
  34. package/dist/browser.mjs.map +1 -1
  35. package/dist/{dist-BFn8qsRC.mjs → dist-BsWcv7B8.mjs} +1 -1
  36. package/dist/{dist-BFn8qsRC.mjs.map → dist-BsWcv7B8.mjs.map} +1 -1
  37. package/dist/elysia/index.d.mts +2 -2
  38. package/dist/elysia/index.d.mts.map +1 -1
  39. package/dist/elysia/index.mjs +7 -7
  40. package/dist/elysia/index.mjs.map +1 -1
  41. package/dist/enrichers.d.mts +1 -1
  42. package/dist/{error-BheHTFFB.d.mts → error-BJ-I4sim.d.mts} +2 -2
  43. package/dist/{error-BheHTFFB.d.mts.map → error-BJ-I4sim.d.mts.map} +1 -1
  44. package/dist/error.d.mts +1 -1
  45. package/dist/{errors-D8WVZclz.d.mts → errors-DBIBK0Bt.d.mts} +2 -2
  46. package/dist/{errors-D8WVZclz.d.mts.map → errors-DBIBK0Bt.d.mts.map} +1 -1
  47. package/dist/{errors-BQgyQ9xe.mjs → errors-gH4C9KSC.mjs} +1 -1
  48. package/dist/{errors-BQgyQ9xe.mjs.map → errors-gH4C9KSC.mjs.map} +1 -1
  49. package/dist/express/index.d.mts +2 -2
  50. package/dist/express/index.mjs +1 -1
  51. package/dist/fastify/index.d.mts +2 -2
  52. package/dist/fastify/index.mjs +1 -1
  53. package/dist/{headers-DJ_YZbxT.mjs → headers-DrdQ6uG5.mjs} +3 -3
  54. package/dist/{headers-DJ_YZbxT.mjs.map → headers-DrdQ6uG5.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-BkXYNnHP.d.mts → logger-DU3aQIUk.d.mts} +13 -3
  59. package/dist/logger-DU3aQIUk.d.mts.map +1 -0
  60. package/dist/logger.d.mts +2 -2
  61. package/dist/logger.mjs +16 -1
  62. package/dist/logger.mjs.map +1 -1
  63. package/dist/{middleware-B-4hPOVG.d.mts → middleware-BjERCCEd.d.mts} +2 -2
  64. package/dist/{middleware-B-4hPOVG.d.mts.map → middleware-BjERCCEd.d.mts.map} +1 -1
  65. package/dist/nestjs/index.d.mts +2 -2
  66. package/dist/nestjs/index.mjs +1 -1
  67. package/dist/next/client.d.mts +1 -1
  68. package/dist/next/index.d.mts +4 -4
  69. package/dist/next/index.d.mts.map +1 -1
  70. package/dist/next/index.mjs +3 -2
  71. package/dist/next/index.mjs.map +1 -1
  72. package/dist/next/instrumentation.d.mts +75 -0
  73. package/dist/next/instrumentation.d.mts.map +1 -0
  74. package/dist/next/instrumentation.mjs +108 -0
  75. package/dist/next/instrumentation.mjs.map +1 -0
  76. package/dist/nitro/errorHandler.mjs +2 -2
  77. package/dist/nitro/module.d.mts +2 -2
  78. package/dist/nitro/plugin.mjs +5 -4
  79. package/dist/nitro/plugin.mjs.map +1 -1
  80. package/dist/nitro/v3/errorHandler.mjs +3 -3
  81. package/dist/nitro/v3/middleware.d.mts +3 -1
  82. package/dist/nitro/v3/middleware.d.mts.map +1 -1
  83. package/dist/nitro/v3/middleware.mjs +2 -1
  84. package/dist/nitro/v3/middleware.mjs.map +1 -1
  85. package/dist/nitro/v3/module.d.mts +1 -1
  86. package/dist/nitro/v3/plugin.mjs +8 -7
  87. package/dist/nitro/v3/plugin.mjs.map +1 -1
  88. package/dist/nitro/v3/useLogger.d.mts +1 -1
  89. package/dist/{nitro-DCNNxY_7.d.mts → nitro-BXmkH7BD.d.mts} +2 -2
  90. package/dist/{nitro-DCNNxY_7.d.mts.map → nitro-BXmkH7BD.d.mts.map} +1 -1
  91. package/dist/{nitro-CzyGROOC.mjs → nitro-Dpq5ZmcM.mjs} +2 -2
  92. package/dist/{nitro-CzyGROOC.mjs.map → nitro-Dpq5ZmcM.mjs.map} +1 -1
  93. package/dist/nuxt/module.d.mts +4 -3
  94. package/dist/nuxt/module.d.mts.map +1 -1
  95. package/dist/nuxt/module.mjs +4 -2
  96. package/dist/nuxt/module.mjs.map +1 -1
  97. package/dist/{parseError-B08FS7EQ.d.mts → parseError-CcvBYsbl.d.mts} +2 -2
  98. package/dist/parseError-CcvBYsbl.d.mts.map +1 -0
  99. package/dist/react-router/index.d.mts +47 -0
  100. package/dist/react-router/index.d.mts.map +1 -0
  101. package/dist/react-router/index.mjs +59 -0
  102. package/dist/react-router/index.mjs.map +1 -0
  103. package/dist/{routes-CGPmbzCZ.mjs → routes-CE3_c-iZ.mjs} +1 -1
  104. package/dist/{routes-CGPmbzCZ.mjs.map → routes-CE3_c-iZ.mjs.map} +1 -1
  105. package/dist/runtime/client/log.d.mts +1 -1
  106. package/dist/runtime/client/log.d.mts.map +1 -1
  107. package/dist/runtime/client/log.mjs +3 -1
  108. package/dist/runtime/client/log.mjs.map +1 -1
  109. package/dist/runtime/server/useLogger.d.mts +1 -1
  110. package/dist/runtime/utils/parseError.d.mts +2 -2
  111. package/dist/runtime/utils/parseError.mjs +2 -1
  112. package/dist/runtime/utils/parseError.mjs.map +1 -1
  113. package/dist/sveltekit/index.d.mts +2 -2
  114. package/dist/sveltekit/index.mjs +3 -3
  115. package/dist/toolkit.d.mts +3 -3
  116. package/dist/toolkit.mjs +3 -3
  117. package/dist/{types-CBpJBj_7.d.mts → types-DbVDS9eu.d.mts} +8 -1
  118. package/dist/types-DbVDS9eu.d.mts.map +1 -0
  119. package/dist/types.d.mts +1 -1
  120. package/dist/{useLogger-DBPGEDf_.d.mts → useLogger-DY9IByDJ.d.mts} +2 -2
  121. package/dist/{useLogger-DBPGEDf_.d.mts.map → useLogger-DY9IByDJ.d.mts.map} +1 -1
  122. package/dist/utils.d.mts +1 -1
  123. package/dist/vite/index.d.mts +1 -1
  124. package/dist/workers.d.mts +1 -1
  125. package/package.json +50 -20
  126. package/dist/_drain-C9Nr-6Wc.mjs.map +0 -1
  127. package/dist/_http-C2UoHWgm.mjs.map +0 -1
  128. package/dist/logger-BkXYNnHP.d.mts.map +0 -1
  129. package/dist/parseError-B08FS7EQ.d.mts.map +0 -1
  130. package/dist/types-CBpJBj_7.d.mts.map +0 -1
@@ -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: () => {\n const config = 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: () => {\n const config = 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,OAAO,CAEZ;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,eAAe;GACb,MAAM,SAAS,qBAAoC,WAAW,gBAAgB,UAAU;AACxF,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,eAAe;GACb,MAAM,SAAS,qBAA0C,WAAW,uBAAuB,UAAU;AACrG,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 './_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,OAAO,CAEZ;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,4 +1,4 @@
1
- import { T as WideEvent, r as DrainContext } from "../types-CBpJBj_7.mjs";
1
+ import { T as WideEvent, r as DrainContext } from "../types-DbVDS9eu.mjs";
2
2
  //#region src/adapters/sentry.d.ts
3
3
  interface SentryConfig {
4
4
  /** Sentry DSN */
@@ -1,5 +1,5 @@
1
- import { n as resolveAdapterConfig, t as httpPost } from "../_http-C2UoHWgm.mjs";
2
- import { t as defineDrain } from "../_drain-C9Nr-6Wc.mjs";
1
+ import { n as resolveAdapterConfig, t as httpPost } from "../_http-rRIz2Q0L.mjs";
2
+ import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
3
3
  import { t as OTEL_SEVERITY_NUMBER } from "../_severity-BLiOKoxh.mjs";
4
4
  //#region src/adapters/sentry.ts
5
5
  const SENTRY_FIELDS = [
@@ -167,8 +167,8 @@ function buildEnvelopeBody(logs, dsn) {
167
167
  function createSentryDrain(overrides) {
168
168
  return defineDrain({
169
169
  name: "sentry",
170
- resolve: () => {
171
- const config = resolveAdapterConfig("sentry", SENTRY_FIELDS, overrides);
170
+ resolve: async () => {
171
+ const config = await resolveAdapterConfig("sentry", SENTRY_FIELDS, overrides);
172
172
  if (!config.dsn) {
173
173
  console.error("[evlog/sentry] Missing DSN. Set NUXT_SENTRY_DSN/SENTRY_DSN env var or pass to createSentryDrain()");
174
174
  return null;
@@ -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: () => {\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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"}
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":"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"}
@@ -1,8 +1,47 @@
1
- import { g as RequestLogger } from "../types-CBpJBj_7.mjs";
1
+ import { g as RequestLogger } from "../types-DbVDS9eu.mjs";
2
2
  import { GatewayModelId } from "ai";
3
- import { LanguageModelV3 } from "@ai-sdk/provider";
3
+ import { LanguageModelV3, LanguageModelV3Middleware } from "@ai-sdk/provider";
4
4
 
5
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
+ }
6
45
  /**
7
46
  * Shape of the `ai` field written to the wide event.
8
47
  */
@@ -18,8 +57,13 @@ interface AIEventData {
18
57
  cacheWriteTokens?: number;
19
58
  reasoningTokens?: number;
20
59
  finishReason?: string;
21
- toolCalls?: string[];
60
+ toolCalls?: string[] | Array<{
61
+ name: string;
62
+ input: unknown;
63
+ }>;
64
+ responseId?: string;
22
65
  steps?: number;
66
+ stepsUsage?: AIStepUsage[];
23
67
  msToFirstChunk?: number;
24
68
  msToFinish?: number;
25
69
  tokensPerSecond?: number;
@@ -34,6 +78,9 @@ interface AILogger {
34
78
  * Accepts a `LanguageModelV3` object or a model string (e.g. `'anthropic/claude-sonnet-4.6'`).
35
79
  * Strings are resolved via the AI SDK gateway.
36
80
  *
81
+ * Also works with pre-wrapped models (e.g. from supermemory, guardrails):
82
+ * `ai.wrap(withSupermemory(base, orgId))` composes correctly.
83
+ *
37
84
  * @example
38
85
  * ```ts
39
86
  * const ai = createAILogger(log)
@@ -61,6 +108,31 @@ interface AILogger {
61
108
  };
62
109
  }) => void;
63
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;
64
136
  /**
65
137
  * Create an AI logger that captures AI SDK data into the wide event.
66
138
  *
@@ -81,8 +153,13 @@ interface AILogger {
81
153
  * onFinish: ({ text }) => saveConversation(text),
82
154
  * })
83
155
  * ```
156
+ *
157
+ * @example Capture tool call inputs
158
+ * ```ts
159
+ * const ai = createAILogger(log, { toolInputs: true })
160
+ * ```
84
161
  */
85
- declare function createAILogger(log: RequestLogger): AILogger;
162
+ declare function createAILogger(log: RequestLogger, options?: AILoggerOptions): AILogger;
86
163
  //#endregion
87
- export { AIEventData, AILogger, createAILogger };
164
+ export { AIEventData, AILogger, AILoggerOptions, AIStepUsage, ToolInputsOptions, createAILogger, createAIMiddleware };
88
165
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ai/index.ts"],"mappings":";;;;;;;AAQA;UAAiB,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;EACA,KAAA;EACA,cAAA;EACA,UAAA;EACA,eAAA;EACA,KAAA;AAAA;AAAA,UAGe,QAAA;EAHV;;AAGP;;;;;;;;;;;;;;;EAkBE,IAAA,GAAO,KAAA,EAAO,eAAA,GAAkB,cAAA,KAAmB,eAAA;EAapC;;;AA8DjB;;;;;;;;EA9DE,YAAA,GAAe,MAAA;IAAU,KAAA;MAAS,MAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;iBA8DpB,cAAA,CAAe,GAAA,EAAK,aAAA,GAAgB,QAAA"}
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"}
package/dist/ai/index.mjs CHANGED
@@ -24,6 +24,33 @@ function resolveProviderAndModel(provider, modelId) {
24
24
  };
25
25
  }
26
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
+ /**
27
54
  * Create an AI logger that captures AI SDK data into the wide event.
28
55
  *
29
56
  * Uses model middleware (`wrapLanguageModel`) to transparently intercept
@@ -43,75 +70,173 @@ function resolveProviderAndModel(provider, modelId) {
43
70
  * onFinish: ({ text }) => saveConversation(text),
44
71
  * })
45
72
  * ```
73
+ *
74
+ * @example Capture tool call inputs
75
+ * ```ts
76
+ * const ai = createAILogger(log, { toolInputs: true })
77
+ * ```
46
78
  */
47
- function createAILogger(log) {
48
- let calls = 0;
49
- let steps = 0;
50
- const usage = {
51
- inputTokens: 0,
52
- outputTokens: 0,
53
- cacheReadTokens: 0,
54
- cacheWriteTokens: 0,
55
- reasoningTokens: 0
56
- };
57
- const models = [];
58
- const providers = [];
59
- const allToolCalls = [];
60
- let lastFinishReason;
61
- let lastMsToFirstChunk;
62
- let lastMsToFinish;
63
- let lastError;
64
- function flush() {
65
- const uniqueModels = [...new Set(models)];
66
- const lastModel = models[models.length - 1];
67
- const lastProvider = providers[providers.length - 1];
68
- const data = {
69
- calls,
70
- inputTokens: usage.inputTokens,
71
- outputTokens: usage.outputTokens,
72
- totalTokens: usage.inputTokens + usage.outputTokens
73
- };
74
- if (lastModel) data.model = lastModel;
75
- if (lastProvider) data.provider = lastProvider;
76
- if (uniqueModels.length > 1) data.models = uniqueModels;
77
- if (usage.cacheReadTokens > 0) data.cacheReadTokens = usage.cacheReadTokens;
78
- if (usage.cacheWriteTokens > 0) data.cacheWriteTokens = usage.cacheWriteTokens;
79
- if (usage.reasoningTokens > 0) data.reasoningTokens = usage.reasoningTokens;
80
- if (lastFinishReason) data.finishReason = lastFinishReason;
81
- if (allToolCalls.length > 0) data.toolCalls = [...allToolCalls];
82
- if (steps > 1) data.steps = steps;
83
- if (lastMsToFirstChunk !== void 0) data.msToFirstChunk = lastMsToFirstChunk;
84
- if (lastMsToFinish !== void 0) {
85
- data.msToFinish = lastMsToFinish;
86
- if (usage.outputTokens > 0 && lastMsToFinish > 0) data.tokensPerSecond = Math.round(usage.outputTokens / lastMsToFinish * 1e3);
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);
87
93
  }
88
- if (lastError) data.error = lastError;
89
- log.set({ ai: data });
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)}…`;
90
116
  }
91
- function recordModel(provider, modelId, responseModelId) {
92
- const resolved = resolveProviderAndModel(provider, responseModelId ?? modelId);
93
- models.push(resolved.model);
94
- providers.push(resolved.provider);
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;
95
186
  }
96
- const middleware = {
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",
97
208
  wrapGenerate: async ({ doGenerate, model }) => {
98
209
  try {
99
210
  const result = await doGenerate();
100
- calls++;
101
- steps++;
102
- addUsage(usage, result.usage);
103
- recordModel(model.provider, model.modelId, result.response?.modelId);
104
- lastFinishReason = result.finishReason.unified;
105
- for (const item of result.content) if (item.type === "tool-call") allToolCalls.push(item.toolName);
106
- flush();
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);
107
237
  return result;
108
238
  } catch (error) {
109
- calls++;
110
- steps++;
111
- recordModel(model.provider, model.modelId);
112
- lastFinishReason = "error";
113
- lastError = error instanceof Error ? error.message : String(error);
114
- flush();
239
+ recordError(log, state, model, error);
115
240
  throw error;
116
241
  }
117
242
  },
@@ -121,25 +246,43 @@ function createAILogger(log) {
121
246
  let streamUsage;
122
247
  let streamFinishReason;
123
248
  let streamModelId;
249
+ let streamResponseId;
124
250
  const streamToolCalls = [];
251
+ const streamToolInputBuffers = /* @__PURE__ */ new Map();
125
252
  let streamError;
126
253
  let doStreamResult;
127
254
  try {
128
255
  doStreamResult = await doStream();
129
256
  } catch (error) {
130
- calls++;
131
- steps++;
132
- recordModel(model.provider, model.modelId);
133
- lastFinishReason = "error";
134
- lastError = error instanceof Error ? error.message : String(error);
135
- flush();
257
+ recordError(log, state, model, error);
136
258
  throw error;
137
259
  }
138
260
  const { stream, ...rest } = doStreamResult;
139
261
  const transformStream = new TransformStream({
140
262
  transform(chunk, controller) {
141
263
  if (!firstChunkTime && chunk.type === "text-delta") firstChunkTime = Date.now();
142
- if (chunk.type === "tool-input-start") streamToolCalls.push(chunk.toolName);
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
+ }
143
286
  if (chunk.type === "finish") {
144
287
  streamUsage = {
145
288
  inputTokens: chunk.usage.inputTokens.total ?? 0,
@@ -150,27 +293,38 @@ function createAILogger(log) {
150
293
  };
151
294
  streamFinishReason = chunk.finishReason.unified;
152
295
  }
153
- if (chunk.type === "response-metadata" && "modelId" in chunk && chunk.modelId) streamModelId = chunk.modelId;
296
+ if (chunk.type === "response-metadata") {
297
+ if (chunk.modelId) streamModelId = chunk.modelId;
298
+ if (chunk.id) streamResponseId = chunk.id;
299
+ }
154
300
  if (chunk.type === "error") streamError = chunk.error instanceof Error ? chunk.error.message : String(chunk.error);
155
301
  controller.enqueue(chunk);
156
302
  },
157
303
  flush() {
158
- calls++;
159
- steps++;
304
+ state.calls++;
305
+ state.steps++;
160
306
  if (streamUsage) {
161
- usage.inputTokens += streamUsage.inputTokens;
162
- usage.outputTokens += streamUsage.outputTokens;
163
- usage.cacheReadTokens += streamUsage.cacheReadTokens;
164
- usage.cacheWriteTokens += streamUsage.cacheWriteTokens;
165
- usage.reasoningTokens += streamUsage.reasoningTokens;
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;
166
312
  }
167
- recordModel(model.provider, model.modelId, streamModelId);
168
- lastFinishReason = streamFinishReason;
169
- for (const name of streamToolCalls) allToolCalls.push(name);
170
- if (firstChunkTime) lastMsToFirstChunk = firstChunkTime - streamStart;
171
- lastMsToFinish = Date.now() - streamStart;
172
- if (streamError) lastError = streamError;
173
- flush();
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);
174
328
  }
175
329
  });
176
330
  return {
@@ -179,21 +333,8 @@ function createAILogger(log) {
179
333
  };
180
334
  }
181
335
  };
182
- return {
183
- wrap: (model) => {
184
- return wrapLanguageModel({
185
- model: typeof model === "string" ? gateway(model) : model,
186
- middleware
187
- });
188
- },
189
- captureEmbed: (result) => {
190
- calls++;
191
- usage.inputTokens += result.usage.tokens;
192
- flush();
193
- }
194
- };
195
336
  }
196
337
  //#endregion
197
- export { createAILogger };
338
+ export { createAILogger, createAIMiddleware };
198
339
 
199
340
  //# sourceMappingURL=index.mjs.map