evlog 2.11.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/dist/{_drain-YH8ERc5l.mjs → _drain-CmCtsuF6.mjs} +1 -1
- package/dist/{_drain-YH8ERc5l.mjs.map → _drain-CmCtsuF6.mjs.map} +1 -1
- package/dist/{_http-C_2wbJw3.mjs → _http-CHSsrWDJ.mjs} +2 -2
- package/dist/{_http-C_2wbJw3.mjs.map → _http-CHSsrWDJ.mjs.map} +1 -1
- package/dist/{_severity-BZhz3f9e.mjs → _severity-CQijvfhU.mjs} +1 -1
- package/dist/{_severity-BZhz3f9e.mjs.map → _severity-CQijvfhU.mjs.map} +1 -1
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.mjs +2 -2
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.mjs +2 -2
- package/dist/adapters/datadog.d.mts +1 -1
- package/dist/adapters/datadog.mjs +2 -2
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.mjs +1 -1
- package/dist/adapters/hyperdx.d.mts +1 -1
- package/dist/adapters/hyperdx.mjs +2 -2
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.mjs +3 -3
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.mjs +2 -2
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.mjs +3 -3
- package/dist/ai/index.d.mts +144 -5
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +108 -5
- package/dist/ai/index.mjs.map +1 -1
- package/dist/better-auth/index.d.mts +220 -0
- package/dist/better-auth/index.d.mts.map +1 -0
- package/dist/better-auth/index.mjs +205 -0
- package/dist/better-auth/index.mjs.map +1 -0
- package/dist/browser.d.mts +13 -52
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.mjs +5 -81
- package/dist/browser.mjs.map +1 -1
- package/dist/client.d.mts +2 -2
- package/dist/client.mjs +2 -2
- package/dist/{dist-BFn8qsRC.mjs → dist-Do8P4zWd.mjs} +1 -1
- package/dist/{dist-BFn8qsRC.mjs.map → dist-Do8P4zWd.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +16 -4
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-plrBYLQk.d.mts → error-B9CiGK_i.d.mts} +2 -2
- package/dist/{error-plrBYLQk.d.mts.map → error-B9CiGK_i.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-gH4C9KSC.mjs → errors-BJRXUfMg.mjs} +1 -1
- package/dist/{errors-gH4C9KSC.mjs.map → errors-BJRXUfMg.mjs.map} +1 -1
- package/dist/{errors-bPoj9UZk.d.mts → errors-Dr0r4OpR.d.mts} +2 -2
- package/dist/{errors-bPoj9UZk.d.mts.map → errors-Dr0r4OpR.d.mts.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.d.mts.map +1 -1
- package/dist/express/index.mjs +8 -4
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.d.mts.map +1 -1
- package/dist/fastify/index.mjs +8 -4
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/fork-Y4z8iHti.mjs +72 -0
- package/dist/fork-Y4z8iHti.mjs.map +1 -0
- package/dist/headers-D74M0wsg.mjs +30 -0
- package/dist/headers-D74M0wsg.mjs.map +1 -0
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +2 -1
- package/dist/hono/index.mjs.map +1 -1
- package/dist/http.d.mts +65 -0
- package/dist/http.d.mts.map +1 -0
- package/dist/http.mjs +94 -0
- package/dist/http.mjs.map +1 -0
- package/dist/index.d.mts +7 -6
- package/dist/index.mjs +3 -2
- package/dist/logger-DnobymUQ.mjs +741 -0
- package/dist/logger-DnobymUQ.mjs.map +1 -0
- package/dist/{logger-CG1eop2_.d.mts → logger-Dp6nYWjH.d.mts} +6 -2
- package/dist/logger-Dp6nYWjH.d.mts.map +1 -0
- package/dist/logger.d.mts +1 -1
- package/dist/logger.mjs +1 -361
- package/dist/{headers-BSi3UHKL.mjs → middleware-BtBuosFV.mjs} +13 -32
- package/dist/middleware-BtBuosFV.mjs.map +1 -0
- package/dist/{middleware-DojmTj9Y.d.mts → middleware-FgC1OdOD.d.mts} +21 -3
- package/dist/middleware-FgC1OdOD.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.d.mts.map +1 -1
- package/dist/nestjs/index.mjs +8 -4
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +9 -3
- package/dist/next/client.d.mts.map +1 -1
- package/dist/next/client.mjs +5 -3
- package/dist/next/client.mjs.map +1 -1
- package/dist/next/index.d.mts +9 -4
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +17 -2
- package/dist/next/index.mjs.map +1 -1
- package/dist/next/instrumentation.d.mts +3 -1
- package/dist/next/instrumentation.d.mts.map +1 -1
- package/dist/next/instrumentation.mjs +2 -1
- package/dist/next/instrumentation.mjs.map +1 -1
- package/dist/nitro/errorHandler.mjs +2 -2
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.mjs +7 -4
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +3 -3
- package/dist/nitro/v3/index.d.mts +2 -2
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.mjs +8 -5
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-CfGx0wDJ.d.mts → nitro-CDHLfRdw.d.mts} +13 -2
- package/dist/nitro-CDHLfRdw.d.mts.map +1 -0
- package/dist/{nitro-Dpq5ZmcM.mjs → nitro-OmT_M4Pb.mjs} +2 -2
- package/dist/nitro-OmT_M4Pb.mjs.map +1 -0
- package/dist/{nitroConfigBridge-fidbf-Y_.mjs → nitroConfigBridge-C37lXaNm.mjs} +1 -1
- package/dist/{nitroConfigBridge-fidbf-Y_.mjs.map → nitroConfigBridge-C37lXaNm.mjs.map} +1 -1
- package/dist/nuxt/module.d.mts +26 -1
- package/dist/nuxt/module.d.mts.map +1 -1
- package/dist/nuxt/module.mjs +7 -2
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/{parseError-B_qXj8x4.d.mts → parseError-DM-lyezZ.d.mts} +2 -2
- package/dist/parseError-DM-lyezZ.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +2 -2
- package/dist/react-router/index.d.mts.map +1 -1
- package/dist/react-router/index.mjs +8 -4
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/{routes-CE3_c-iZ.mjs → routes-CGPmbzCZ.mjs} +1 -1
- package/dist/{routes-CE3_c-iZ.mjs.map → routes-CGPmbzCZ.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +7 -2
- package/dist/runtime/client/log.d.mts.map +1 -1
- package/dist/runtime/client/log.mjs +24 -6
- package/dist/runtime/client/log.mjs.map +1 -1
- package/dist/runtime/client/plugin.mjs +1 -0
- package/dist/runtime/client/plugin.mjs.map +1 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +1 -1
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/runtime/utils/parseError.mjs +1 -1
- package/dist/{source-location-B1VVgXkh.mjs → source-location-DRvDDqfq.mjs} +1 -1
- package/dist/{source-location-B1VVgXkh.mjs.map → source-location-DRvDDqfq.mjs.map} +1 -1
- package/dist/{storage-B6NPh8rV.mjs → storage-CFGTn37X.mjs} +1 -1
- package/dist/{storage-B6NPh8rV.mjs.map → storage-CFGTn37X.mjs.map} +1 -1
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.d.mts.map +1 -1
- package/dist/sveltekit/index.mjs +10 -6
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/toolkit.d.mts +41 -4
- package/dist/toolkit.d.mts.map +1 -1
- package/dist/toolkit.mjs +7 -5
- package/dist/{types-v_JkG_D7.d.mts → types-DbzDln7O.d.mts} +120 -4
- package/dist/types-DbzDln7O.d.mts.map +1 -0
- package/dist/types.d.mts +2 -2
- package/dist/{useLogger-TjKH37BO.d.mts → useLogger-N5A-d5l9.d.mts} +2 -2
- package/dist/{useLogger-TjKH37BO.d.mts.map → useLogger-N5A-d5l9.d.mts.map} +1 -1
- package/dist/utils-DnX6VMNi.d.mts +54 -0
- package/dist/utils-DnX6VMNi.d.mts.map +1 -0
- package/dist/utils.d.mts +2 -50
- package/dist/utils.mjs +13 -1
- package/dist/utils.mjs.map +1 -1
- package/dist/vite/index.d.mts +5 -1
- package/dist/vite/index.d.mts.map +1 -1
- package/dist/vite/index.mjs +3 -1
- package/dist/vite/index.mjs.map +1 -1
- package/dist/workers.d.mts +1 -1
- package/dist/workers.mjs +1 -1
- package/package.json +24 -3
- package/dist/headers-BSi3UHKL.mjs.map +0 -1
- package/dist/logger-CG1eop2_.d.mts.map +0 -1
- package/dist/logger.mjs.map +0 -1
- package/dist/middleware-DojmTj9Y.d.mts.map +0 -1
- package/dist/nitro-CfGx0wDJ.d.mts.map +0 -1
- package/dist/nitro-Dpq5ZmcM.mjs.map +0 -1
- package/dist/parseError-B_qXj8x4.d.mts.map +0 -1
- package/dist/types-v_JkG_D7.d.mts.map +0 -1
- package/dist/utils.d.mts.map +0 -1
package/dist/ai/index.mjs.map
CHANGED
|
@@ -1 +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
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/ai/index.ts"],"sourcesContent":["import { gateway, wrapLanguageModel, bindTelemetryIntegration } from 'ai'\nimport type { GatewayModelId, TelemetryIntegration, OnStartEvent, OnToolCallFinishEvent, OnFinishEvent } 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 * Pricing entry for a single model: cost per 1 million tokens in dollars.\n */\nexport interface ModelCost {\n input: number\n output: number\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 * Pricing map for estimating request cost.\n * Keys are model IDs (e.g. `'claude-sonnet-4.6'`, `'gpt-4o'`), values are\n * `{ input, output }` in dollars per 1M tokens.\n *\n * When provided, the wide event includes `ai.estimatedCost` (in dollars).\n *\n * @example\n * ```ts\n * const ai = createAILogger(log, {\n * cost: {\n * 'claude-sonnet-4.6': { input: 3, output: 15 },\n * 'gpt-4o': { input: 2.5, output: 10 },\n * },\n * })\n * ```\n */\n cost?: Record<string, ModelCost>\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 * Tool execution detail captured via `TelemetryIntegration`.\n */\nexport interface AIToolExecution {\n name: string\n durationMs: number\n success: boolean\n error?: string\n}\n\n/**\n * Embedding metadata captured via `captureEmbed`.\n */\nexport interface AIEmbeddingData {\n model?: string\n tokens: number\n dimensions?: number\n count?: number\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 tools?: AIToolExecution[]\n totalDurationMs?: number\n embedding?: AIEmbeddingData\n estimatedCost?: number\n}\n\nexport interface AILogger {\n /**\n * Wrap a language model with evlog middleware.\n * All `generateText` and `streamText` calls using the wrapped model\n * 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 * // With model info (v2)\n * ai.captureEmbed({ usage, model: 'text-embedding-3-small', dimensions: 1536 })\n *\n * // After embedMany\n * ai.captureEmbed({ usage, count: texts.length })\n * ```\n */\n captureEmbed: (result: {\n usage: { tokens: number }\n model?: string\n dimensions?: number\n count?: number\n }) => void\n\n /**\n * Internal accumulator state exposed for `createEvlogIntegration` to share.\n * @internal\n */\n _state: AccumulatorState\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 state._log = log\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: {\n usage: { tokens: number }\n model?: string\n dimensions?: number\n count?: number\n }) => {\n state.calls++\n state.usage.inputTokens += result.usage.tokens\n state.embedding = {\n tokens: (state.embedding?.tokens ?? 0) + result.usage.tokens,\n ...(result.model ? { model: result.model } : state.embedding?.model ? { model: state.embedding.model } : {}),\n ...(result.dimensions ? { dimensions: result.dimensions } : state.embedding?.dimensions ? { dimensions: state.embedding.dimensions } : {}),\n ...(result.count ? { count: (state.embedding?.count ?? 0) + result.count } : state.embedding?.count ? { count: state.embedding.count } : {}),\n }\n flushState(log, state)\n },\n\n _state: state,\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 toolExecutions: AIToolExecution[]\n generationStartTime: number | undefined\n totalDurationMs: number | undefined\n embedding: AIEmbeddingData | undefined\n costMap: Record<string, ModelCost> | undefined\n /** @internal Logger reference for integration flush */\n _log?: RequestLogger\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 toolExecutions: [],\n generationStartTime: undefined,\n totalDurationMs: undefined,\n embedding: undefined,\n costMap: options?.cost,\n }\n}\n\nfunction computeEstimatedCost(state: AccumulatorState): number | undefined {\n if (!state.costMap) return undefined\n const lastModel = state.models[state.models.length - 1]\n if (!lastModel) return undefined\n const pricing = state.costMap[lastModel]\n if (!pricing) return undefined\n const inputCost = (state.usage.inputTokens / 1_000_000) * pricing.input\n const outputCost = (state.usage.outputTokens / 1_000_000) * pricing.output\n const total = inputCost + outputCost\n return total > 0 ? Math.round(total * 1_000_000) / 1_000_000 : undefined\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 if (state.toolExecutions.length > 0) data.tools = [...state.toolExecutions]\n if (state.totalDurationMs !== undefined) data.totalDurationMs = state.totalDurationMs\n if (state.embedding) data.embedding = { ...state.embedding }\n const cost = computeEstimatedCost(state)\n if (cost !== undefined) data.estimatedCost = cost\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 state._log = log\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\n/**\n * Create an AI SDK `TelemetryIntegration` that captures tool execution\n * timing, errors, and total generation wall time into the wide event.\n *\n * Complements the middleware-based `createAILogger`: the middleware captures\n * token usage and streaming metrics at the model level, while the integration\n * captures application-level lifecycle events (tool execution, total duration).\n *\n * When passed an `AILogger`, shares its accumulator so both paths write to\n * the same `ai.*` field. Can also be used standalone with a `RequestLogger`.\n *\n * @example Combined with middleware (recommended)\n * ```ts\n * import { createAILogger, createEvlogIntegration } from 'evlog/ai'\n *\n * const log = useLogger(event)\n * const ai = createAILogger(log)\n *\n * const result = await generateText({\n * model: ai.wrap('anthropic/claude-sonnet-4.6'),\n * tools: { getWeather },\n * experimental_telemetry: {\n * isEnabled: true,\n * integrations: [createEvlogIntegration(ai)],\n * },\n * })\n * ```\n *\n * @example Standalone (no middleware wrapping)\n * ```ts\n * import { createEvlogIntegration } from 'evlog/ai'\n *\n * const integration = createEvlogIntegration(log)\n *\n * const result = await generateText({\n * model: openai('gpt-4o'),\n * experimental_telemetry: {\n * isEnabled: true,\n * integrations: [integration],\n * },\n * })\n * ```\n */\nexport function createEvlogIntegration(\n logOrAi: RequestLogger | AILogger,\n options?: AILoggerOptions,\n): TelemetryIntegration {\n let log: RequestLogger\n let state: AccumulatorState\n\n if ('_state' in logOrAi && logOrAi._state) {\n state = logOrAi._state\n log = state._log!\n } else {\n log = logOrAi as RequestLogger\n state = createAccumulatorState(options)\n state._log = log\n }\n\n class EvlogIntegration implements TelemetryIntegration {\n\n onStart(_event: OnStartEvent) {\n state.generationStartTime = Date.now()\n }\n\n onToolCallFinish(event: OnToolCallFinishEvent) {\n const execution: AIToolExecution = {\n name: (event.toolCall as { toolName: string }).toolName,\n durationMs: event.durationMs,\n success: event.success,\n }\n if (!event.success && event.error) {\n execution.error = event.error instanceof Error ? event.error.message : String(event.error)\n }\n state.toolExecutions.push(execution)\n }\n\n onFinish(_event: OnFinishEvent) {\n if (state.generationStartTime) {\n state.totalDurationMs = Date.now() - state.generationStartTime\n }\n flushState(log, state)\n }\n \n }\n\n return bindTelemetryIntegration(new EvlogIntegration())\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { _ as RequestLogger } from "../types-DbzDln7O.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/better-auth/index.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Minimal type for the Better Auth instance.
|
|
6
|
+
* Only requires `api.getSession` — compatible with any Better Auth configuration.
|
|
7
|
+
*/
|
|
8
|
+
interface BetterAuthInstance {
|
|
9
|
+
api: {
|
|
10
|
+
getSession: (opts: {
|
|
11
|
+
headers: Headers | Record<string, string | string[] | undefined>;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
user: Record<string, unknown>;
|
|
14
|
+
session: Record<string, unknown>;
|
|
15
|
+
} | null>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* User fields extracted from a Better Auth session.
|
|
20
|
+
*/
|
|
21
|
+
interface AuthUserData {
|
|
22
|
+
id: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
email?: string;
|
|
25
|
+
image?: string;
|
|
26
|
+
emailVerified?: boolean;
|
|
27
|
+
createdAt?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Session fields extracted from a Better Auth session.
|
|
31
|
+
*/
|
|
32
|
+
interface AuthSessionData {
|
|
33
|
+
id: string;
|
|
34
|
+
expiresAt?: string;
|
|
35
|
+
ipAddress?: string;
|
|
36
|
+
userAgent?: string;
|
|
37
|
+
createdAt?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Options for `identifyUser`.
|
|
41
|
+
*/
|
|
42
|
+
interface IdentifyOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Whether to mask the user email (e.g. `h***@domain.com`).
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
maskEmail?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Whether to include session metadata on the wide event.
|
|
50
|
+
* @default true
|
|
51
|
+
*/
|
|
52
|
+
session?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Whitelist of user fields to include.
|
|
55
|
+
* @default ['id', 'name', 'email', 'image', 'emailVerified', 'createdAt']
|
|
56
|
+
*/
|
|
57
|
+
fields?: string[];
|
|
58
|
+
/**
|
|
59
|
+
* Extend the wide event with additional fields derived from the session.
|
|
60
|
+
* Useful for Better Auth plugins (organizations, roles, etc.).
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* identifyUser(log, session, {
|
|
65
|
+
* extend: (session) => ({
|
|
66
|
+
* organization: session.user.activeOrganization,
|
|
67
|
+
* role: session.user.role,
|
|
68
|
+
* }),
|
|
69
|
+
* })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
extend?: (session: {
|
|
73
|
+
user: Record<string, unknown>;
|
|
74
|
+
session: Record<string, unknown>;
|
|
75
|
+
}) => Record<string, unknown> | undefined;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Options for `createAuthMiddleware`.
|
|
79
|
+
*/
|
|
80
|
+
interface AuthMiddlewareOptions extends IdentifyOptions {
|
|
81
|
+
/**
|
|
82
|
+
* Route patterns to skip session resolution (glob).
|
|
83
|
+
* @default ['/api/auth/**']
|
|
84
|
+
*/
|
|
85
|
+
exclude?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* Route patterns to apply session resolution (glob).
|
|
88
|
+
* If set, only matching routes are resolved.
|
|
89
|
+
*/
|
|
90
|
+
include?: string[];
|
|
91
|
+
/**
|
|
92
|
+
* Called after a user is successfully identified.
|
|
93
|
+
* Use to add conditional logic based on user data (e.g. force-keep logs for premium users).
|
|
94
|
+
*/
|
|
95
|
+
onIdentify?: (log: RequestLogger, session: {
|
|
96
|
+
user: Record<string, unknown>;
|
|
97
|
+
session: Record<string, unknown>;
|
|
98
|
+
}) => void | Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Called when no session is found (anonymous request).
|
|
101
|
+
*/
|
|
102
|
+
onAnonymous?: (log: RequestLogger) => void | Promise<void>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Options for `createAuthIdentifier`.
|
|
106
|
+
*/
|
|
107
|
+
type AuthIdentifierOptions = AuthMiddlewareOptions;
|
|
108
|
+
/**
|
|
109
|
+
* Mask an email address for safe logging: `hugo@example.com` -> `h***@example.com`.
|
|
110
|
+
*/
|
|
111
|
+
declare function maskEmail(email: string): string;
|
|
112
|
+
/**
|
|
113
|
+
* Identify a user on a wide event from a Better Auth session result.
|
|
114
|
+
*
|
|
115
|
+
* Sets `userId`, `user`, and optionally `session` fields on the logger.
|
|
116
|
+
* Safe by default — only extracts whitelisted fields and never logs passwords or tokens.
|
|
117
|
+
*
|
|
118
|
+
* Returns `true` if the user was identified, `false` if session data was missing.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* import { identifyUser } from 'evlog/better-auth'
|
|
123
|
+
*
|
|
124
|
+
* const session = await auth.api.getSession({ headers: event.headers })
|
|
125
|
+
* if (session) {
|
|
126
|
+
* identifyUser(log, session)
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example With email masking
|
|
131
|
+
* ```ts
|
|
132
|
+
* identifyUser(log, session, { maskEmail: true })
|
|
133
|
+
* // user.email → "h***@example.com"
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @example With extend for Better Auth plugins
|
|
137
|
+
* ```ts
|
|
138
|
+
* identifyUser(log, session, {
|
|
139
|
+
* extend: (s) => ({
|
|
140
|
+
* organization: s.user.activeOrganization,
|
|
141
|
+
* role: s.user.role,
|
|
142
|
+
* }),
|
|
143
|
+
* })
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
declare function identifyUser(log: RequestLogger, session: {
|
|
147
|
+
user: Record<string, unknown>;
|
|
148
|
+
session: Record<string, unknown>;
|
|
149
|
+
}, options?: IdentifyOptions): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Create an async function that resolves a Better Auth session from headers
|
|
152
|
+
* and identifies the user on the logger.
|
|
153
|
+
*
|
|
154
|
+
* Works with any framework — just pass the auth instance and call the returned
|
|
155
|
+
* function with a logger and headers. Supports `include`/`exclude` route patterns
|
|
156
|
+
* and lifecycle hooks (`onIdentify`, `onAnonymous`).
|
|
157
|
+
*
|
|
158
|
+
* @example Nuxt server middleware
|
|
159
|
+
* ```ts
|
|
160
|
+
* import { createAuthMiddleware } from 'evlog/better-auth'
|
|
161
|
+
*
|
|
162
|
+
* const identify = createAuthMiddleware(auth, {
|
|
163
|
+
* exclude: ['/api/auth/**', '/api/public/**'],
|
|
164
|
+
* })
|
|
165
|
+
*
|
|
166
|
+
* export default defineEventHandler(async (event) => {
|
|
167
|
+
* if (!event.context.log) return
|
|
168
|
+
* await identify(event.context.log, event.headers, event.path)
|
|
169
|
+
* })
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @example Express
|
|
173
|
+
* ```ts
|
|
174
|
+
* const identify = createAuthMiddleware(auth, { maskEmail: true })
|
|
175
|
+
*
|
|
176
|
+
* app.use(async (req, res, next) => {
|
|
177
|
+
* await identify(req.log, req.headers, req.path)
|
|
178
|
+
* next()
|
|
179
|
+
* })
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare function createAuthMiddleware(auth: BetterAuthInstance, options?: AuthMiddlewareOptions): (log: RequestLogger, headers: Headers | Record<string, string | string[] | undefined>, path?: string) => Promise<boolean>;
|
|
183
|
+
/**
|
|
184
|
+
* Create a Nitro `request` hook that auto-identifies users from Better Auth sessions.
|
|
185
|
+
*
|
|
186
|
+
* Resolves the session from request cookies on every request and sets user/session
|
|
187
|
+
* context on the evlog logger. Skips `/api/auth/**` by default to avoid resolving
|
|
188
|
+
* sessions during auth flows.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* // server/plugins/evlog-auth.ts
|
|
193
|
+
* import { createAuthIdentifier } from 'evlog/better-auth'
|
|
194
|
+
* import { auth } from '~/lib/auth'
|
|
195
|
+
*
|
|
196
|
+
* export default defineNitroPlugin((nitroApp) => {
|
|
197
|
+
* nitroApp.hooks.hook('request', createAuthIdentifier(auth))
|
|
198
|
+
* })
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* @example With options
|
|
202
|
+
* ```ts
|
|
203
|
+
* nitroApp.hooks.hook('request', createAuthIdentifier(auth, {
|
|
204
|
+
* maskEmail: true,
|
|
205
|
+
* exclude: ['/api/auth/**', '/api/public/**'],
|
|
206
|
+
* }))
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
declare function createAuthIdentifier(auth: BetterAuthInstance, options?: AuthIdentifierOptions): (event: {
|
|
210
|
+
path: string;
|
|
211
|
+
headers: Headers | {
|
|
212
|
+
get(name: string): string | null;
|
|
213
|
+
};
|
|
214
|
+
context: {
|
|
215
|
+
log?: RequestLogger;
|
|
216
|
+
};
|
|
217
|
+
}) => Promise<void>;
|
|
218
|
+
//#endregion
|
|
219
|
+
export { AuthIdentifierOptions, AuthMiddlewareOptions, AuthSessionData, AuthUserData, BetterAuthInstance, IdentifyOptions, createAuthIdentifier, createAuthMiddleware, identifyUser, maskEmail };
|
|
220
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/better-auth/index.ts"],"mappings":";;;;;AAOA;;UAAiB,kBAAA;EACf,GAAA;IACE,UAAA,GAAa,IAAA;MACX,OAAA,EAAS,OAAA,GAAU,MAAA;IAAA,MACf,OAAA;MACJ,IAAA,EAAM,MAAA;MACN,OAAA,EAAS,MAAA;IAAA;EAAA;AAAA;;;;UAQE,YAAA;EACf,EAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,aAAA;EACA,SAAA;AAAA;AANF;;;AAAA,UAYiB,eAAA;EACf,EAAA;EACA,SAAA;EACA,SAAA;EACA,SAAA;EACA,SAAA;AAAA;;;AALF;UAWiB,eAAA;;;;;EAKf,SAAA;EAZA;;;;EAiBA,OAAA;EAV8B;;;;EAe9B,MAAA;EAeiG;;;;;;;;;;;;;;EAAjG,MAAA,IAAU,OAAA;IAAW,IAAA,EAAM,MAAA;IAAyB,OAAA,EAAS,MAAA;EAAA,MAA8B,MAAA;AAAA;;;;UAM5E,qBAAA,SAA8B,eAAA;EAAA;;;;EAK7C,OAAA;EAKA;;;;EAAA,OAAA;EAKmD;;;;EAAnD,UAAA,IAAc,GAAA,EAAK,aAAA,EAAe,OAAA;IAAW,IAAA,EAAM,MAAA;IAAyB,OAAA,EAAS,MAAA;EAAA,aAAqC,OAAA;EAI7E;;;EAA7C,WAAA,IAAe,GAAA,EAAK,aAAA,YAAyB,OAAA;AAAA;;;;KAMnC,qBAAA,GAAwB,qBAAA;;;;iBASpB,SAAA,CAAU,KAAA;AAyF1B;;;;;;;;;;;;;;;;;;;;AA4EA;;;;;;;;;;;;;;AA5EA,iBAAgB,YAAA,CACd,GAAA,EAAK,aAAA,EACL,OAAA;EAAW,IAAA,EAAM,MAAA;EAAyB,OAAA,EAAS,MAAA;AAAA,GACnD,OAAA,GAAU,eAAA;;;;;;;AAqIZ;;;;;;;;;;;;;;;;;;;;;;;;;;iBA5DgB,oBAAA,CACd,IAAA,EAAM,kBAAA,EACN,OAAA,GAAU,qBAAA,IACR,GAAA,EAAK,aAAA,EAAe,OAAA,EAAS,OAAA,GAAU,MAAA,yCAA+C,IAAA,cAAkB,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyD5F,oBAAA,CACd,IAAA,EAAM,kBAAA,EACN,OAAA,GAAU,qBAAA,IACR,KAAA;EAAS,IAAA;EAAc,OAAA,EAAS,OAAA;IAAY,GAAA,CAAI,IAAA;EAAA;EAAgC,OAAA;IAAW,GAAA,GAAM,aAAA;EAAA;AAAA,MAAsB,OAAA"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { matchesPattern } from "../utils.mjs";
|
|
2
|
+
//#region src/better-auth/index.ts
|
|
3
|
+
const DEFAULT_USER_FIELDS = [
|
|
4
|
+
"id",
|
|
5
|
+
"name",
|
|
6
|
+
"email",
|
|
7
|
+
"image",
|
|
8
|
+
"emailVerified",
|
|
9
|
+
"createdAt"
|
|
10
|
+
];
|
|
11
|
+
const isDev = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
12
|
+
/**
|
|
13
|
+
* Mask an email address for safe logging: `hugo@example.com` -> `h***@example.com`.
|
|
14
|
+
*/
|
|
15
|
+
function maskEmail(email) {
|
|
16
|
+
const atIndex = email.indexOf("@");
|
|
17
|
+
if (atIndex <= 0) return "***";
|
|
18
|
+
return `${email[0]}***${email.slice(atIndex)}`;
|
|
19
|
+
}
|
|
20
|
+
function extractUserData(user, options) {
|
|
21
|
+
const fields = options?.fields ?? DEFAULT_USER_FIELDS;
|
|
22
|
+
const data = {};
|
|
23
|
+
if (user.id !== void 0 && user.id !== null) data.id = user.id;
|
|
24
|
+
for (const field of fields) {
|
|
25
|
+
if (field === "id") continue;
|
|
26
|
+
const value = user[field];
|
|
27
|
+
if (value === void 0 || value === null) continue;
|
|
28
|
+
if (field === "email" && options?.maskEmail && typeof value === "string") data[field] = maskEmail(value);
|
|
29
|
+
else if (field === "createdAt" && value instanceof Date) data[field] = value.toISOString();
|
|
30
|
+
else data[field] = value;
|
|
31
|
+
}
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
function extractSessionData(session) {
|
|
35
|
+
const data = { id: String(session.id) };
|
|
36
|
+
if (session.expiresAt) data.expiresAt = session.expiresAt instanceof Date ? session.expiresAt.toISOString() : String(session.expiresAt);
|
|
37
|
+
if (typeof session.ipAddress === "string") data.ipAddress = session.ipAddress;
|
|
38
|
+
if (typeof session.userAgent === "string") data.userAgent = session.userAgent;
|
|
39
|
+
if (session.createdAt) data.createdAt = session.createdAt instanceof Date ? session.createdAt.toISOString() : String(session.createdAt);
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Identify a user on a wide event from a Better Auth session result.
|
|
44
|
+
*
|
|
45
|
+
* Sets `userId`, `user`, and optionally `session` fields on the logger.
|
|
46
|
+
* Safe by default — only extracts whitelisted fields and never logs passwords or tokens.
|
|
47
|
+
*
|
|
48
|
+
* Returns `true` if the user was identified, `false` if session data was missing.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { identifyUser } from 'evlog/better-auth'
|
|
53
|
+
*
|
|
54
|
+
* const session = await auth.api.getSession({ headers: event.headers })
|
|
55
|
+
* if (session) {
|
|
56
|
+
* identifyUser(log, session)
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example With email masking
|
|
61
|
+
* ```ts
|
|
62
|
+
* identifyUser(log, session, { maskEmail: true })
|
|
63
|
+
* // user.email → "h***@example.com"
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @example With extend for Better Auth plugins
|
|
67
|
+
* ```ts
|
|
68
|
+
* identifyUser(log, session, {
|
|
69
|
+
* extend: (s) => ({
|
|
70
|
+
* organization: s.user.activeOrganization,
|
|
71
|
+
* role: s.user.role,
|
|
72
|
+
* }),
|
|
73
|
+
* })
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function identifyUser(log, session, options) {
|
|
77
|
+
const user = extractUserData(session.user, options);
|
|
78
|
+
if (!user.id) return false;
|
|
79
|
+
const includeSession = options?.session !== false;
|
|
80
|
+
const data = {
|
|
81
|
+
userId: user.id,
|
|
82
|
+
user
|
|
83
|
+
};
|
|
84
|
+
if (includeSession) data.session = extractSessionData(session.session);
|
|
85
|
+
if (options?.extend) {
|
|
86
|
+
const extra = options.extend(session);
|
|
87
|
+
if (extra) Object.assign(data, extra);
|
|
88
|
+
}
|
|
89
|
+
log.set(data);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function shouldResolve(path, options) {
|
|
93
|
+
const exclude = options?.exclude ?? ["/api/auth/**"];
|
|
94
|
+
for (const pattern of exclude) if (matchesPattern(path, pattern)) return false;
|
|
95
|
+
if (options?.include) {
|
|
96
|
+
for (const pattern of options.include) if (matchesPattern(path, pattern)) return true;
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create an async function that resolves a Better Auth session from headers
|
|
103
|
+
* and identifies the user on the logger.
|
|
104
|
+
*
|
|
105
|
+
* Works with any framework — just pass the auth instance and call the returned
|
|
106
|
+
* function with a logger and headers. Supports `include`/`exclude` route patterns
|
|
107
|
+
* and lifecycle hooks (`onIdentify`, `onAnonymous`).
|
|
108
|
+
*
|
|
109
|
+
* @example Nuxt server middleware
|
|
110
|
+
* ```ts
|
|
111
|
+
* import { createAuthMiddleware } from 'evlog/better-auth'
|
|
112
|
+
*
|
|
113
|
+
* const identify = createAuthMiddleware(auth, {
|
|
114
|
+
* exclude: ['/api/auth/**', '/api/public/**'],
|
|
115
|
+
* })
|
|
116
|
+
*
|
|
117
|
+
* export default defineEventHandler(async (event) => {
|
|
118
|
+
* if (!event.context.log) return
|
|
119
|
+
* await identify(event.context.log, event.headers, event.path)
|
|
120
|
+
* })
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* @example Express
|
|
124
|
+
* ```ts
|
|
125
|
+
* const identify = createAuthMiddleware(auth, { maskEmail: true })
|
|
126
|
+
*
|
|
127
|
+
* app.use(async (req, res, next) => {
|
|
128
|
+
* await identify(req.log, req.headers, req.path)
|
|
129
|
+
* next()
|
|
130
|
+
* })
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
function createAuthMiddleware(auth, options) {
|
|
134
|
+
return async (log, headers, path) => {
|
|
135
|
+
if (path && !shouldResolve(path, options)) return false;
|
|
136
|
+
const start = Date.now();
|
|
137
|
+
try {
|
|
138
|
+
const session = await auth.api.getSession({ headers });
|
|
139
|
+
const resolvedIn = Date.now() - start;
|
|
140
|
+
if (session) {
|
|
141
|
+
if (identifyUser(log, session, options)) {
|
|
142
|
+
log.set({ auth: {
|
|
143
|
+
resolvedIn,
|
|
144
|
+
identified: true
|
|
145
|
+
} });
|
|
146
|
+
if (options?.onIdentify) await options.onIdentify(log, session);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
log.set({ auth: {
|
|
151
|
+
resolvedIn,
|
|
152
|
+
identified: false
|
|
153
|
+
} });
|
|
154
|
+
if (options?.onAnonymous) await options.onAnonymous(log);
|
|
155
|
+
return false;
|
|
156
|
+
} catch (err) {
|
|
157
|
+
const resolvedIn = Date.now() - start;
|
|
158
|
+
log.set({ auth: {
|
|
159
|
+
resolvedIn,
|
|
160
|
+
identified: false,
|
|
161
|
+
error: true
|
|
162
|
+
} });
|
|
163
|
+
if (isDev) console.warn("[evlog/better-auth] Session resolution failed:", err);
|
|
164
|
+
if (options?.onAnonymous) await options.onAnonymous(log);
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create a Nitro `request` hook that auto-identifies users from Better Auth sessions.
|
|
171
|
+
*
|
|
172
|
+
* Resolves the session from request cookies on every request and sets user/session
|
|
173
|
+
* context on the evlog logger. Skips `/api/auth/**` by default to avoid resolving
|
|
174
|
+
* sessions during auth flows.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* // server/plugins/evlog-auth.ts
|
|
179
|
+
* import { createAuthIdentifier } from 'evlog/better-auth'
|
|
180
|
+
* import { auth } from '~/lib/auth'
|
|
181
|
+
*
|
|
182
|
+
* export default defineNitroPlugin((nitroApp) => {
|
|
183
|
+
* nitroApp.hooks.hook('request', createAuthIdentifier(auth))
|
|
184
|
+
* })
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* @example With options
|
|
188
|
+
* ```ts
|
|
189
|
+
* nitroApp.hooks.hook('request', createAuthIdentifier(auth, {
|
|
190
|
+
* maskEmail: true,
|
|
191
|
+
* exclude: ['/api/auth/**', '/api/public/**'],
|
|
192
|
+
* }))
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
function createAuthIdentifier(auth, options) {
|
|
196
|
+
const middleware = createAuthMiddleware(auth, options);
|
|
197
|
+
return async (event) => {
|
|
198
|
+
if (!event.context.log) return;
|
|
199
|
+
await middleware(event.context.log, event.headers, event.path);
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { createAuthIdentifier, createAuthMiddleware, identifyUser, maskEmail };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/better-auth/index.ts"],"sourcesContent":["import type { RequestLogger } from '../types'\nimport { matchesPattern } from '../utils'\n\n/**\n * Minimal type for the Better Auth instance.\n * Only requires `api.getSession` — compatible with any Better Auth configuration.\n */\nexport interface BetterAuthInstance {\n api: {\n getSession: (opts: {\n headers: Headers | Record<string, string | string[] | undefined>\n }) => Promise<{\n user: Record<string, unknown>\n session: Record<string, unknown>\n } | null>\n }\n}\n\n/**\n * User fields extracted from a Better Auth session.\n */\nexport interface AuthUserData {\n id: string\n name?: string\n email?: string\n image?: string\n emailVerified?: boolean\n createdAt?: string\n}\n\n/**\n * Session fields extracted from a Better Auth session.\n */\nexport interface AuthSessionData {\n id: string\n expiresAt?: string\n ipAddress?: string\n userAgent?: string\n createdAt?: string\n}\n\n/**\n * Options for `identifyUser`.\n */\nexport interface IdentifyOptions {\n /**\n * Whether to mask the user email (e.g. `h***@domain.com`).\n * @default false\n */\n maskEmail?: boolean\n /**\n * Whether to include session metadata on the wide event.\n * @default true\n */\n session?: boolean\n /**\n * Whitelist of user fields to include.\n * @default ['id', 'name', 'email', 'image', 'emailVerified', 'createdAt']\n */\n fields?: string[]\n /**\n * Extend the wide event with additional fields derived from the session.\n * Useful for Better Auth plugins (organizations, roles, etc.).\n *\n * @example\n * ```ts\n * identifyUser(log, session, {\n * extend: (session) => ({\n * organization: session.user.activeOrganization,\n * role: session.user.role,\n * }),\n * })\n * ```\n */\n extend?: (session: { user: Record<string, unknown>, session: Record<string, unknown> }) => Record<string, unknown> | undefined\n}\n\n/**\n * Options for `createAuthMiddleware`.\n */\nexport interface AuthMiddlewareOptions extends IdentifyOptions {\n /**\n * Route patterns to skip session resolution (glob).\n * @default ['/api/auth/**']\n */\n exclude?: string[]\n /**\n * Route patterns to apply session resolution (glob).\n * If set, only matching routes are resolved.\n */\n include?: string[]\n /**\n * Called after a user is successfully identified.\n * Use to add conditional logic based on user data (e.g. force-keep logs for premium users).\n */\n onIdentify?: (log: RequestLogger, session: { user: Record<string, unknown>, session: Record<string, unknown> }) => void | Promise<void>\n /**\n * Called when no session is found (anonymous request).\n */\n onAnonymous?: (log: RequestLogger) => void | Promise<void>\n}\n\n/**\n * Options for `createAuthIdentifier`.\n */\nexport type AuthIdentifierOptions = AuthMiddlewareOptions\n\nconst DEFAULT_USER_FIELDS = ['id', 'name', 'email', 'image', 'emailVerified', 'createdAt']\n\nconst isDev = typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production'\n\n/**\n * Mask an email address for safe logging: `hugo@example.com` -> `h***@example.com`.\n */\nexport function maskEmail(email: string): string {\n const atIndex = email.indexOf('@')\n if (atIndex <= 0) return '***'\n return `${email[0]}***${email.slice(atIndex)}`\n}\n\nfunction extractUserData(\n user: Record<string, unknown>,\n options?: IdentifyOptions,\n): AuthUserData {\n const fields = options?.fields ?? DEFAULT_USER_FIELDS\n const data: Record<string, unknown> = {}\n\n if (user.id !== undefined && user.id !== null) {\n data.id = user.id\n }\n\n for (const field of fields) {\n if (field === 'id') continue\n const value = user[field]\n if (value === undefined || value === null) continue\n\n if (field === 'email' && options?.maskEmail && typeof value === 'string') {\n data[field] = maskEmail(value)\n } else if (field === 'createdAt' && value instanceof Date) {\n data[field] = value.toISOString()\n } else {\n data[field] = value\n }\n }\n\n return data as unknown as AuthUserData\n}\n\nfunction extractSessionData(\n session: Record<string, unknown>,\n): AuthSessionData {\n const data: AuthSessionData = { id: String(session.id) }\n\n if (session.expiresAt) {\n data.expiresAt = session.expiresAt instanceof Date\n ? session.expiresAt.toISOString()\n : String(session.expiresAt)\n }\n if (typeof session.ipAddress === 'string') data.ipAddress = session.ipAddress\n if (typeof session.userAgent === 'string') data.userAgent = session.userAgent\n if (session.createdAt) {\n data.createdAt = session.createdAt instanceof Date\n ? session.createdAt.toISOString()\n : String(session.createdAt)\n }\n\n return data\n}\n\n/**\n * Identify a user on a wide event from a Better Auth session result.\n *\n * Sets `userId`, `user`, and optionally `session` fields on the logger.\n * Safe by default — only extracts whitelisted fields and never logs passwords or tokens.\n *\n * Returns `true` if the user was identified, `false` if session data was missing.\n *\n * @example\n * ```ts\n * import { identifyUser } from 'evlog/better-auth'\n *\n * const session = await auth.api.getSession({ headers: event.headers })\n * if (session) {\n * identifyUser(log, session)\n * }\n * ```\n *\n * @example With email masking\n * ```ts\n * identifyUser(log, session, { maskEmail: true })\n * // user.email → \"h***@example.com\"\n * ```\n *\n * @example With extend for Better Auth plugins\n * ```ts\n * identifyUser(log, session, {\n * extend: (s) => ({\n * organization: s.user.activeOrganization,\n * role: s.user.role,\n * }),\n * })\n * ```\n */\nexport function identifyUser(\n log: RequestLogger,\n session: { user: Record<string, unknown>, session: Record<string, unknown> },\n options?: IdentifyOptions,\n): boolean {\n const user = extractUserData(session.user, options)\n if (!user.id) return false\n\n const includeSession = options?.session !== false\n\n const data: Record<string, unknown> = {\n userId: user.id,\n user,\n }\n\n if (includeSession) {\n data.session = extractSessionData(session.session)\n }\n\n if (options?.extend) {\n const extra = options.extend(session)\n if (extra) Object.assign(data, extra)\n }\n\n log.set(data)\n return true\n}\n\nfunction shouldResolve(path: string, options?: { exclude?: string[], include?: string[] }): boolean {\n const exclude = options?.exclude ?? ['/api/auth/**']\n for (const pattern of exclude) {\n if (matchesPattern(path, pattern)) return false\n }\n\n if (options?.include) {\n for (const pattern of options.include) {\n if (matchesPattern(path, pattern)) return true\n }\n return false\n }\n\n return true\n}\n\n/**\n * Create an async function that resolves a Better Auth session from headers\n * and identifies the user on the logger.\n *\n * Works with any framework — just pass the auth instance and call the returned\n * function with a logger and headers. Supports `include`/`exclude` route patterns\n * and lifecycle hooks (`onIdentify`, `onAnonymous`).\n *\n * @example Nuxt server middleware\n * ```ts\n * import { createAuthMiddleware } from 'evlog/better-auth'\n *\n * const identify = createAuthMiddleware(auth, {\n * exclude: ['/api/auth/**', '/api/public/**'],\n * })\n *\n * export default defineEventHandler(async (event) => {\n * if (!event.context.log) return\n * await identify(event.context.log, event.headers, event.path)\n * })\n * ```\n *\n * @example Express\n * ```ts\n * const identify = createAuthMiddleware(auth, { maskEmail: true })\n *\n * app.use(async (req, res, next) => {\n * await identify(req.log, req.headers, req.path)\n * next()\n * })\n * ```\n */\nexport function createAuthMiddleware(\n auth: BetterAuthInstance,\n options?: AuthMiddlewareOptions,\n): (log: RequestLogger, headers: Headers | Record<string, string | string[] | undefined>, path?: string) => Promise<boolean> {\n return async (log, headers, path?) => {\n if (path && !shouldResolve(path, options)) return false\n\n const start = Date.now()\n try {\n const session = await auth.api.getSession({ headers })\n const resolvedIn = Date.now() - start\n\n if (session) {\n const identified = identifyUser(log, session, options)\n if (identified) {\n log.set({ auth: { resolvedIn, identified: true } } as Record<string, unknown>)\n if (options?.onIdentify) await options.onIdentify(log, session)\n return true\n }\n }\n\n log.set({ auth: { resolvedIn, identified: false } } as Record<string, unknown>)\n if (options?.onAnonymous) await options.onAnonymous(log)\n return false\n } catch (err) {\n const resolvedIn = Date.now() - start\n log.set({ auth: { resolvedIn, identified: false, error: true } } as Record<string, unknown>)\n if (isDev) console.warn('[evlog/better-auth] Session resolution failed:', err)\n if (options?.onAnonymous) await options.onAnonymous(log)\n return false\n }\n }\n}\n\n/**\n * Create a Nitro `request` hook that auto-identifies users from Better Auth sessions.\n *\n * Resolves the session from request cookies on every request and sets user/session\n * context on the evlog logger. Skips `/api/auth/**` by default to avoid resolving\n * sessions during auth flows.\n *\n * @example\n * ```ts\n * // server/plugins/evlog-auth.ts\n * import { createAuthIdentifier } from 'evlog/better-auth'\n * import { auth } from '~/lib/auth'\n *\n * export default defineNitroPlugin((nitroApp) => {\n * nitroApp.hooks.hook('request', createAuthIdentifier(auth))\n * })\n * ```\n *\n * @example With options\n * ```ts\n * nitroApp.hooks.hook('request', createAuthIdentifier(auth, {\n * maskEmail: true,\n * exclude: ['/api/auth/**', '/api/public/**'],\n * }))\n * ```\n */\nexport function createAuthIdentifier(\n auth: BetterAuthInstance,\n options?: AuthIdentifierOptions,\n): (event: { path: string, headers: Headers | { get(name: string): string | null }, context: { log?: RequestLogger } }) => Promise<void> {\n const middleware = createAuthMiddleware(auth, options)\n\n return async (event) => {\n if (!event.context.log) return\n await middleware(event.context.log, event.headers as Headers, event.path)\n }\n}\n"],"mappings":"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"}
|