evlog 2.4.0 → 2.5.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/dist/{_http-DVDwNag0.mjs → _http-DHpGetLZ.mjs} +2 -6
- package/dist/{_http-DVDwNag0.mjs.map → _http-DHpGetLZ.mjs.map} +1 -1
- package/dist/{_severity-78FkT5MD.mjs → _severity-CLNgC2HU.mjs} +2 -2
- package/dist/{_severity-78FkT5MD.mjs.map → _severity-CLNgC2HU.mjs.map} +1 -1
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.d.mts.map +1 -1
- package/dist/adapters/axiom.mjs +2 -3
- package/dist/adapters/axiom.mjs.map +1 -1
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.d.mts.map +1 -1
- package/dist/adapters/better-stack.mjs +2 -3
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.d.mts.map +1 -1
- package/dist/adapters/otlp.mjs +14 -11
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.d.mts.map +1 -1
- package/dist/adapters/posthog.mjs +2 -3
- package/dist/adapters/posthog.mjs.map +1 -1
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.d.mts.map +1 -1
- package/dist/adapters/sentry.mjs +3 -4
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/browser.mjs +1 -2
- package/dist/browser.mjs.map +1 -1
- package/dist/{dist-By0jiJRA.mjs → dist-Dalk68oO.mjs} +3 -4
- package/dist/{dist-By0jiJRA.mjs.map → dist-Dalk68oO.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.mjs +2 -3
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/enrichers.mjs +1 -1
- package/dist/enrichers.mjs.map +1 -1
- package/dist/error-Bu4fv0NO.d.mts +65 -0
- package/dist/error-Bu4fv0NO.d.mts.map +1 -0
- package/dist/error.d.mts +2 -65
- package/dist/error.mjs +1 -2
- package/dist/error.mjs.map +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.mjs +3 -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 +3 -4
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/{headers-CXOd5EyZ.mjs → headers-C95MOQ4w.mjs} +4 -6
- package/dist/{headers-CXOd5EyZ.mjs.map → headers-C95MOQ4w.mjs.map} +1 -1
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +2 -3
- package/dist/hono/index.mjs.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1 -2
- package/dist/logger-BNxG5Kyd.d.mts +59 -0
- package/dist/logger-BNxG5Kyd.d.mts.map +1 -0
- package/dist/logger.d.mts +2 -59
- package/dist/logger.mjs +1 -2
- package/dist/logger.mjs.map +1 -1
- package/dist/{middleware-BoVCgsfQ.d.mts → middleware-gCuB0w7F.d.mts} +2 -2
- package/dist/{middleware-BoVCgsfQ.d.mts.map → middleware-gCuB0w7F.d.mts.map} +1 -1
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +3 -4
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +1 -1
- package/dist/next/client.mjs +2 -4
- package/dist/next/client.mjs.map +1 -1
- package/dist/next/index.d.mts +4 -4
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +2 -6
- package/dist/next/index.mjs.map +1 -1
- package/dist/nitro/errorHandler.mjs +2 -3
- package/dist/nitro/errorHandler.mjs.map +1 -1
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/module.mjs +1 -2
- package/dist/nitro/module.mjs.map +1 -1
- package/dist/nitro/plugin.mjs +3 -4
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +3 -4
- package/dist/nitro/v3/errorHandler.mjs.map +1 -1
- package/dist/nitro/v3/index.mjs +1 -2
- package/dist/nitro/v3/middleware.mjs +1 -2
- package/dist/nitro/v3/middleware.mjs.map +1 -1
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/module.mjs +1 -2
- package/dist/nitro/v3/module.mjs.map +1 -1
- package/dist/nitro/v3/plugin.mjs +4 -5
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/nitro/v3/useLogger.mjs +1 -1
- package/dist/nitro/v3/useLogger.mjs.map +1 -1
- package/dist/{nitro-Da8tEfJ3.mjs → nitro-B3vZrLGI.mjs} +2 -2
- package/dist/{nitro-Da8tEfJ3.mjs.map → nitro-B3vZrLGI.mjs.map} +1 -1
- package/dist/{nitro-BRisWfGy.d.mts → nitro-wZ05H2wx.d.mts} +2 -2
- package/dist/{nitro-BRisWfGy.d.mts.map → nitro-wZ05H2wx.d.mts.map} +1 -1
- package/dist/nuxt/module.d.mts +1 -1
- package/dist/nuxt/module.mjs +2 -5
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/parseError-BeVEVQBh.d.mts +7 -0
- package/dist/parseError-BeVEVQBh.d.mts.map +1 -0
- package/dist/pipeline.mjs +1 -1
- package/dist/{routes-BNbrnm14.mjs → routes-DdmLpEnG.mjs} +2 -3
- package/dist/{routes-BNbrnm14.mjs.map → routes-DdmLpEnG.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/client/log.mjs +1 -2
- package/dist/runtime/client/log.mjs.map +1 -1
- package/dist/runtime/client/plugin.mjs +1 -2
- package/dist/runtime/client/plugin.mjs.map +1 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +1 -2
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs.map +1 -1
- package/dist/runtime/server/useLogger.d.mts +2 -39
- package/dist/runtime/server/useLogger.mjs +1 -1
- package/dist/runtime/server/useLogger.mjs.map +1 -1
- package/dist/runtime/utils/parseError.d.mts +3 -7
- package/dist/runtime/utils/parseError.mjs +1 -1
- package/dist/{storage-Dd3PHiMh.mjs → storage-CRSAAEiG.mjs} +18 -3
- package/dist/storage-CRSAAEiG.mjs.map +1 -0
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +4 -5
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/types-CRj5tGVA.d.mts +496 -0
- package/dist/types-CRj5tGVA.d.mts.map +1 -0
- package/dist/types.d.mts +2 -496
- package/dist/types.mjs +1 -1
- package/dist/useLogger-w7MZjqW_.d.mts +39 -0
- package/dist/useLogger-w7MZjqW_.d.mts.map +1 -0
- package/dist/utils.d.mts +1 -1
- package/dist/utils.mjs +1 -1
- package/dist/workers.d.mts +1 -1
- package/dist/workers.mjs +1 -2
- package/dist/workers.mjs.map +1 -1
- package/package.json +55 -34
- package/dist/error.d.mts.map +0 -1
- package/dist/logger.d.mts.map +0 -1
- package/dist/runtime/server/useLogger.d.mts.map +0 -1
- package/dist/runtime/utils/parseError.d.mts.map +0 -1
- package/dist/storage-Dd3PHiMh.mjs.map +0 -1
- package/dist/types.d.mts.map +0 -1
package/dist/nitro/module.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useLogger } from "../runtime/server/useLogger.mjs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
4
|
//#region src/nitro/module.ts
|
|
6
5
|
const _dir = dirname(fileURLToPath(import.meta.url));
|
|
7
6
|
function evlog(options) {
|
|
@@ -18,7 +17,7 @@ function evlog(options) {
|
|
|
18
17
|
}
|
|
19
18
|
};
|
|
20
19
|
}
|
|
21
|
-
|
|
22
20
|
//#endregion
|
|
23
21
|
export { evlog as default, useLogger };
|
|
22
|
+
|
|
24
23
|
//# sourceMappingURL=module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":[],"sources":["../../src/nitro/module.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Nitro } from 'nitropack'\nimport type { NitroModuleOptions } from '../nitro'\n\nexport type { NitroModuleOptions }\n\nconst _dir = dirname(fileURLToPath(import.meta.url))\n\nexport default function evlog(options?: NitroModuleOptions) {\n return {\n name: 'evlog',\n setup(nitro: Nitro) {\n // Push the plugin (no extension — Nitro's bundler resolves it)\n nitro.options.plugins = nitro.options.plugins || []\n nitro.options.plugins.push(resolve(_dir, 'plugin'))\n\n // Set error handler only if not already configured by user\n if (!nitro.options.errorHandler) {\n nitro.options.errorHandler = resolve(_dir, 'errorHandler')\n }\n\n // explicitly tell nitro to bundle evlog's files to correctly resolve nitro dependencies\n // in nitro v2 we can only disable externals globally\n\n nitro.options.noExternals = true\n\n // Inject config into runtimeConfig — works in production where the\n // plugin is bundled through Nitro's builder and the virtual\n // runtime-config module resolves correctly.\n nitro.options.runtimeConfig = nitro.options.runtimeConfig || {}\n nitro.options.runtimeConfig.evlog = options || {}\n\n // In dev mode, Nitro loads plugins externally (not bundled), so the\n // virtual runtime-config module is unreachable and useRuntimeConfig()\n // returns a stub without our values. process.env is inherited by the\n // Worker Threads that run the dev server, making it a reliable bridge.\n // The plugin reads: useRuntimeConfig().evlog ?? process.env.__EVLOG_CONFIG\n process.env.__EVLOG_CONFIG = JSON.stringify(options || {})\n },\n }\n}\n\nexport { useLogger } from '../runtime/server/useLogger'\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.mjs","names":[],"sources":["../../src/nitro/module.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Nitro } from 'nitropack'\nimport type { NitroModuleOptions } from '../nitro'\n\nexport type { NitroModuleOptions }\n\nconst _dir = dirname(fileURLToPath(import.meta.url))\n\nexport default function evlog(options?: NitroModuleOptions) {\n return {\n name: 'evlog',\n setup(nitro: Nitro) {\n // Push the plugin (no extension — Nitro's bundler resolves it)\n nitro.options.plugins = nitro.options.plugins || []\n nitro.options.plugins.push(resolve(_dir, 'plugin'))\n\n // Set error handler only if not already configured by user\n if (!nitro.options.errorHandler) {\n nitro.options.errorHandler = resolve(_dir, 'errorHandler')\n }\n\n // explicitly tell nitro to bundle evlog's files to correctly resolve nitro dependencies\n // in nitro v2 we can only disable externals globally\n\n nitro.options.noExternals = true\n\n // Inject config into runtimeConfig — works in production where the\n // plugin is bundled through Nitro's builder and the virtual\n // runtime-config module resolves correctly.\n nitro.options.runtimeConfig = nitro.options.runtimeConfig || {}\n nitro.options.runtimeConfig.evlog = options || {}\n\n // In dev mode, Nitro loads plugins externally (not bundled), so the\n // virtual runtime-config module is unreachable and useRuntimeConfig()\n // returns a stub without our values. process.env is inherited by the\n // Worker Threads that run the dev server, making it a reliable bridge.\n // The plugin reads: useRuntimeConfig().evlog ?? process.env.__EVLOG_CONFIG\n process.env.__EVLOG_CONFIG = JSON.stringify(options || {})\n },\n }\n}\n\nexport { useLogger } from '../runtime/server/useLogger'\n"],"mappings":";;;;AAOA,MAAM,OAAO,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAEpD,SAAwB,MAAM,SAA8B;AAC1D,QAAO;EACL,MAAM;EACN,MAAM,OAAc;AAElB,SAAM,QAAQ,UAAU,MAAM,QAAQ,WAAW,EAAE;AACnD,SAAM,QAAQ,QAAQ,KAAK,QAAQ,MAAM,SAAS,CAAC;AAGnD,OAAI,CAAC,MAAM,QAAQ,aACjB,OAAM,QAAQ,eAAe,QAAQ,MAAM,eAAe;AAM5D,SAAM,QAAQ,cAAc;AAK5B,SAAM,QAAQ,gBAAgB,MAAM,QAAQ,iBAAiB,EAAE;AAC/D,SAAM,QAAQ,cAAc,QAAQ,WAAW,EAAE;AAOjD,WAAQ,IAAI,iBAAiB,KAAK,UAAU,WAAW,EAAE,CAAC;;EAE7D"}
|
package/dist/nitro/plugin.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { filterSafeHeaders } from "../utils.mjs";
|
|
2
2
|
import { createRequestLogger, initLogger, isEnabled } from "../logger.mjs";
|
|
3
|
-
import { n as shouldLog, t as getServiceForPath } from "../routes-
|
|
4
|
-
import { t as extractErrorStatus } from "../nitro-
|
|
3
|
+
import { n as shouldLog, t as getServiceForPath } from "../routes-DdmLpEnG.mjs";
|
|
4
|
+
import { t as extractErrorStatus } from "../nitro-B3vZrLGI.mjs";
|
|
5
5
|
import { defineNitroPlugin } from "nitropack/runtime/internal/plugin";
|
|
6
6
|
import { getHeaders } from "h3";
|
|
7
|
-
|
|
8
7
|
//#region src/nitro/plugin.ts
|
|
9
8
|
function getSafeHeaders(event) {
|
|
10
9
|
return filterSafeHeaders(getHeaders(event));
|
|
@@ -139,7 +138,7 @@ var plugin_default = defineNitroPlugin(async (nitroApp) => {
|
|
|
139
138
|
}
|
|
140
139
|
});
|
|
141
140
|
});
|
|
142
|
-
|
|
143
141
|
//#endregion
|
|
144
142
|
export { plugin_default as default };
|
|
143
|
+
|
|
145
144
|
//# sourceMappingURL=plugin.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../src/nitro/plugin.ts"],"sourcesContent":["import type { NitroApp } from 'nitropack/types'\n// Import from specific subpaths to avoid the barrel 'nitropack/runtime' which\n// re-exports from internal/app.mjs — that file imports #nitro-internal-virtual/*\n// modules that only exist inside rollup builds and crash when loaded externally\n// (nitropack dev loads plugins outside the bundle via Worker threads).\nimport { defineNitroPlugin } from 'nitropack/runtime/internal/plugin'\nimport { getHeaders } from 'h3'\nimport { createRequestLogger, initLogger, isEnabled } from '../logger'\nimport { shouldLog, getServiceForPath, extractErrorStatus } from '../nitro'\nimport type { EvlogConfig } from '../nitro'\nimport type { EnrichContext, RequestLogger, ServerEvent, TailSamplingContext, WideEvent } from '../types'\nimport { filterSafeHeaders } from '../utils'\n\nfunction getSafeHeaders(event: ServerEvent): Record<string, string> {\n const allHeaders = getHeaders(event as Parameters<typeof getHeaders>[0])\n return filterSafeHeaders(allHeaders)\n}\n\nfunction getSafeResponseHeaders(event: ServerEvent): Record<string, string> | undefined {\n const headers: Record<string, string> = {}\n const nodeRes = event.node?.res as { getHeaders?: () => Record<string, unknown> } | undefined\n\n if (nodeRes?.getHeaders) {\n for (const [key, value] of Object.entries(nodeRes.getHeaders())) {\n if (value === undefined) continue\n headers[key] = Array.isArray(value) ? value.join(', ') : String(value)\n }\n }\n\n if (event.response?.headers) {\n event.response.headers.forEach((value, key) => {\n headers[key] = value\n })\n }\n\n if (Object.keys(headers).length === 0) return undefined\n return filterSafeHeaders(headers)\n}\n\nfunction getResponseStatus(event: ServerEvent): number {\n // Node.js style\n if (event.node?.res?.statusCode) {\n return event.node.res.statusCode\n }\n\n // Web Standard\n if (event.response?.status) {\n return event.response.status\n }\n\n // Context-based\n if (typeof event.context.status === 'number') {\n return event.context.status\n }\n\n return 200\n}\n\nfunction buildHookContext(event: ServerEvent): Omit<EnrichContext, 'event'> {\n const responseHeaders = getSafeResponseHeaders(event)\n return {\n request: { method: event.method, path: event.path },\n headers: getSafeHeaders(event),\n response: {\n status: getResponseStatus(event),\n headers: responseHeaders,\n },\n }\n}\n\nasync function callEnrichAndDrain(\n nitroApp: NitroApp,\n emittedEvent: WideEvent | null,\n event: ServerEvent,\n): Promise<void> {\n if (!emittedEvent) return\n\n const hookContext = buildHookContext(event)\n\n try {\n await nitroApp.hooks.callHook('evlog:enrich', { event: emittedEvent, ...hookContext })\n } catch (err) {\n console.error('[evlog] enrich failed:', err)\n }\n\n const drainPromise = nitroApp.hooks.callHook('evlog:drain', {\n event: emittedEvent,\n request: hookContext.request,\n headers: hookContext.headers,\n }).catch((err) => {\n console.error('[evlog] drain failed:', err)\n })\n\n // Use waitUntil if available (Cloudflare Workers, Vercel Edge, etc.)\n // This keeps the runtime alive for background work without blocking the response\n const waitUntilCtx = event.context.cloudflare?.context ?? event.context\n if (typeof waitUntilCtx?.waitUntil === 'function') {\n waitUntilCtx.waitUntil(drainPromise)\n } else {\n // Fallback: await drain to prevent lost logs in serverless environments\n // (e.g. Vercel Fluid Compute). On the normal path this runs from\n // afterResponse (response already sent); on the error path it may run\n // before the error response is finalized.\n await drainPromise\n }\n}\n\nexport default defineNitroPlugin(async (nitroApp) => {\n // Config resolution: process.env bridge first (always set by the module),\n // then lazy useRuntimeConfig() for production builds where env may not persist.\n let evlogConfig: EvlogConfig | undefined\n if (process.env.__EVLOG_CONFIG) {\n evlogConfig = JSON.parse(process.env.__EVLOG_CONFIG)\n } else {\n try {\n // nitropack/runtime/internal/config imports virtual modules —\n // only works inside rollup-bundled output (production builds).\n const { useRuntimeConfig } = await import('nitropack/runtime/internal/config')\n evlogConfig = (useRuntimeConfig() as Record<string, any>).evlog\n } catch {\n // Expected in dev mode — virtual modules unavailable outside rollup\n }\n }\n\n initLogger({\n enabled: evlogConfig?.enabled,\n env: evlogConfig?.env,\n pretty: evlogConfig?.pretty,\n sampling: evlogConfig?.sampling,\n })\n\n if (!isEnabled()) return\n\n nitroApp.hooks.hook('request', (event) => {\n const e = event as ServerEvent\n\n // Skip logging for routes not matching include/exclude patterns\n if (!shouldLog(e.path, evlogConfig?.include, evlogConfig?.exclude)) {\n return\n }\n\n // Store start time for duration calculation in tail sampling\n e.context._evlogStartTime = Date.now()\n\n let requestIdOverride: string | undefined = undefined\n if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') {\n const cfRay = getSafeHeaders(e)?.['cf-ray']\n if (cfRay) requestIdOverride = cfRay\n }\n\n const requestLog = createRequestLogger({\n method: e.method,\n path: e.path,\n requestId: requestIdOverride || e.context.requestId || crypto.randomUUID(),\n })\n\n // Apply route-based service configuration if a matching route is found\n const routeService = getServiceForPath(e.path, evlogConfig?.routes)\n if (routeService) {\n requestLog.set({ service: routeService })\n }\n\n e.context.log = requestLog\n })\n\n nitroApp.hooks.hook('error', async (error, { event }) => {\n const e = event as ServerEvent | undefined\n if (!e) return\n\n const requestLog = e.context.log as RequestLogger | undefined\n if (requestLog) {\n requestLog.error(error as Error)\n\n const errorStatus = extractErrorStatus(error)\n requestLog.set({ status: errorStatus })\n\n // Build tail sampling context\n const startTime = e.context._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const tailCtx: TailSamplingContext = {\n status: errorStatus,\n duration: durationMs,\n path: e.path,\n method: e.method,\n context: requestLog.getContext(),\n shouldKeep: false,\n }\n\n // Call evlog:emit:keep hook\n await nitroApp.hooks.callHook('evlog:emit:keep', tailCtx)\n\n e.context._evlogEmitted = true\n\n const emittedEvent = requestLog.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(nitroApp, emittedEvent, e)\n }\n })\n\n nitroApp.hooks.hook('afterResponse', async (event) => {\n const e = event as ServerEvent\n // Skip if already emitted by error hook\n if (e.context._evlogEmitted) return\n\n const requestLog = e.context.log as RequestLogger | undefined\n if (requestLog) {\n const status = getResponseStatus(e)\n requestLog.set({ status })\n\n const startTime = e.context._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const tailCtx: TailSamplingContext = {\n status,\n duration: durationMs,\n path: e.path,\n method: e.method,\n context: requestLog.getContext(),\n shouldKeep: false,\n }\n\n await nitroApp.hooks.callHook('evlog:emit:keep', tailCtx)\n\n const emittedEvent = requestLog.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(nitroApp, emittedEvent, e)\n }\n })\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../src/nitro/plugin.ts"],"sourcesContent":["import type { NitroApp } from 'nitropack/types'\n// Import from specific subpaths to avoid the barrel 'nitropack/runtime' which\n// re-exports from internal/app.mjs — that file imports #nitro-internal-virtual/*\n// modules that only exist inside rollup builds and crash when loaded externally\n// (nitropack dev loads plugins outside the bundle via Worker threads).\nimport { defineNitroPlugin } from 'nitropack/runtime/internal/plugin'\nimport { getHeaders } from 'h3'\nimport { createRequestLogger, initLogger, isEnabled } from '../logger'\nimport { shouldLog, getServiceForPath, extractErrorStatus } from '../nitro'\nimport type { EvlogConfig } from '../nitro'\nimport type { EnrichContext, RequestLogger, ServerEvent, TailSamplingContext, WideEvent } from '../types'\nimport { filterSafeHeaders } from '../utils'\n\nfunction getSafeHeaders(event: ServerEvent): Record<string, string> {\n const allHeaders = getHeaders(event as Parameters<typeof getHeaders>[0])\n return filterSafeHeaders(allHeaders)\n}\n\nfunction getSafeResponseHeaders(event: ServerEvent): Record<string, string> | undefined {\n const headers: Record<string, string> = {}\n const nodeRes = event.node?.res as { getHeaders?: () => Record<string, unknown> } | undefined\n\n if (nodeRes?.getHeaders) {\n for (const [key, value] of Object.entries(nodeRes.getHeaders())) {\n if (value === undefined) continue\n headers[key] = Array.isArray(value) ? value.join(', ') : String(value)\n }\n }\n\n if (event.response?.headers) {\n event.response.headers.forEach((value, key) => {\n headers[key] = value\n })\n }\n\n if (Object.keys(headers).length === 0) return undefined\n return filterSafeHeaders(headers)\n}\n\nfunction getResponseStatus(event: ServerEvent): number {\n // Node.js style\n if (event.node?.res?.statusCode) {\n return event.node.res.statusCode\n }\n\n // Web Standard\n if (event.response?.status) {\n return event.response.status\n }\n\n // Context-based\n if (typeof event.context.status === 'number') {\n return event.context.status\n }\n\n return 200\n}\n\nfunction buildHookContext(event: ServerEvent): Omit<EnrichContext, 'event'> {\n const responseHeaders = getSafeResponseHeaders(event)\n return {\n request: { method: event.method, path: event.path },\n headers: getSafeHeaders(event),\n response: {\n status: getResponseStatus(event),\n headers: responseHeaders,\n },\n }\n}\n\nasync function callEnrichAndDrain(\n nitroApp: NitroApp,\n emittedEvent: WideEvent | null,\n event: ServerEvent,\n): Promise<void> {\n if (!emittedEvent) return\n\n const hookContext = buildHookContext(event)\n\n try {\n await nitroApp.hooks.callHook('evlog:enrich', { event: emittedEvent, ...hookContext })\n } catch (err) {\n console.error('[evlog] enrich failed:', err)\n }\n\n const drainPromise = nitroApp.hooks.callHook('evlog:drain', {\n event: emittedEvent,\n request: hookContext.request,\n headers: hookContext.headers,\n }).catch((err) => {\n console.error('[evlog] drain failed:', err)\n })\n\n // Use waitUntil if available (Cloudflare Workers, Vercel Edge, etc.)\n // This keeps the runtime alive for background work without blocking the response\n const waitUntilCtx = event.context.cloudflare?.context ?? event.context\n if (typeof waitUntilCtx?.waitUntil === 'function') {\n waitUntilCtx.waitUntil(drainPromise)\n } else {\n // Fallback: await drain to prevent lost logs in serverless environments\n // (e.g. Vercel Fluid Compute). On the normal path this runs from\n // afterResponse (response already sent); on the error path it may run\n // before the error response is finalized.\n await drainPromise\n }\n}\n\nexport default defineNitroPlugin(async (nitroApp) => {\n // Config resolution: process.env bridge first (always set by the module),\n // then lazy useRuntimeConfig() for production builds where env may not persist.\n let evlogConfig: EvlogConfig | undefined\n if (process.env.__EVLOG_CONFIG) {\n evlogConfig = JSON.parse(process.env.__EVLOG_CONFIG)\n } else {\n try {\n // nitropack/runtime/internal/config imports virtual modules —\n // only works inside rollup-bundled output (production builds).\n const { useRuntimeConfig } = await import('nitropack/runtime/internal/config')\n evlogConfig = (useRuntimeConfig() as Record<string, any>).evlog\n } catch {\n // Expected in dev mode — virtual modules unavailable outside rollup\n }\n }\n\n initLogger({\n enabled: evlogConfig?.enabled,\n env: evlogConfig?.env,\n pretty: evlogConfig?.pretty,\n sampling: evlogConfig?.sampling,\n })\n\n if (!isEnabled()) return\n\n nitroApp.hooks.hook('request', (event) => {\n const e = event as ServerEvent\n\n // Skip logging for routes not matching include/exclude patterns\n if (!shouldLog(e.path, evlogConfig?.include, evlogConfig?.exclude)) {\n return\n }\n\n // Store start time for duration calculation in tail sampling\n e.context._evlogStartTime = Date.now()\n\n let requestIdOverride: string | undefined = undefined\n if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') {\n const cfRay = getSafeHeaders(e)?.['cf-ray']\n if (cfRay) requestIdOverride = cfRay\n }\n\n const requestLog = createRequestLogger({\n method: e.method,\n path: e.path,\n requestId: requestIdOverride || e.context.requestId || crypto.randomUUID(),\n })\n\n // Apply route-based service configuration if a matching route is found\n const routeService = getServiceForPath(e.path, evlogConfig?.routes)\n if (routeService) {\n requestLog.set({ service: routeService })\n }\n\n e.context.log = requestLog\n })\n\n nitroApp.hooks.hook('error', async (error, { event }) => {\n const e = event as ServerEvent | undefined\n if (!e) return\n\n const requestLog = e.context.log as RequestLogger | undefined\n if (requestLog) {\n requestLog.error(error as Error)\n\n const errorStatus = extractErrorStatus(error)\n requestLog.set({ status: errorStatus })\n\n // Build tail sampling context\n const startTime = e.context._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const tailCtx: TailSamplingContext = {\n status: errorStatus,\n duration: durationMs,\n path: e.path,\n method: e.method,\n context: requestLog.getContext(),\n shouldKeep: false,\n }\n\n // Call evlog:emit:keep hook\n await nitroApp.hooks.callHook('evlog:emit:keep', tailCtx)\n\n e.context._evlogEmitted = true\n\n const emittedEvent = requestLog.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(nitroApp, emittedEvent, e)\n }\n })\n\n nitroApp.hooks.hook('afterResponse', async (event) => {\n const e = event as ServerEvent\n // Skip if already emitted by error hook\n if (e.context._evlogEmitted) return\n\n const requestLog = e.context.log as RequestLogger | undefined\n if (requestLog) {\n const status = getResponseStatus(e)\n requestLog.set({ status })\n\n const startTime = e.context._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const tailCtx: TailSamplingContext = {\n status,\n duration: durationMs,\n path: e.path,\n method: e.method,\n context: requestLog.getContext(),\n shouldKeep: false,\n }\n\n await nitroApp.hooks.callHook('evlog:emit:keep', tailCtx)\n\n const emittedEvent = requestLog.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(nitroApp, emittedEvent, e)\n }\n })\n})\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as resolveEvlogError, r as serializeEvlogErrorResponse, t as extractErrorStatus } from "../../nitro-
|
|
2
|
-
import { t as parseURL } from "../../dist-
|
|
1
|
+
import { n as resolveEvlogError, r as serializeEvlogErrorResponse, t as extractErrorStatus } from "../../nitro-B3vZrLGI.mjs";
|
|
2
|
+
import { t as parseURL } from "../../dist-Dalk68oO.mjs";
|
|
3
3
|
import { defineErrorHandler } from "nitro";
|
|
4
|
-
|
|
5
4
|
//#region src/nitro-v3/errorHandler.ts
|
|
6
5
|
/**
|
|
7
6
|
* Custom Nitro v3 error handler that properly serializes EvlogError.
|
|
@@ -30,7 +29,7 @@ var errorHandler_default = defineErrorHandler((error, event) => {
|
|
|
30
29
|
headers: { "Content-Type": "application/json" }
|
|
31
30
|
});
|
|
32
31
|
});
|
|
33
|
-
|
|
34
32
|
//#endregion
|
|
35
33
|
export { errorHandler_default as default };
|
|
34
|
+
|
|
36
35
|
//# sourceMappingURL=errorHandler.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.mjs","names":[],"sources":["../../../src/nitro-v3/errorHandler.ts"],"sourcesContent":["import { parseURL } from 'ufo'\nimport { defineErrorHandler } from 'nitro'\nimport { resolveEvlogError, extractErrorStatus, serializeEvlogErrorResponse } from '../nitro'\n\n/**\n * Custom Nitro v3 error handler that properly serializes EvlogError.\n * This ensures that 'data' (containing 'why', 'fix', 'link') is preserved\n * in the JSON response regardless of the underlying HTTP framework.\n *\n * For non-EvlogError, returns undefined to let Nitro's default handler take over.\n *\n * Usage in nitro.config.ts:\n * ```ts\n * // errorHandler.ts\n * export { default } from 'evlog/nitro/v3/errorHandler'\n * // nitro.config.ts\n * export default defineConfig({\n * errorHandler: './errorHandler',\n * })\n * ```\n */\nexport default defineErrorHandler((error, event) => {\n const evlogError = resolveEvlogError(error)\n\n if (!evlogError) return\n\n const url = parseURL(event.req.url).pathname\n const status = extractErrorStatus(evlogError)\n\n return new Response(JSON.stringify(serializeEvlogErrorResponse(evlogError, url)), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n})\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"errorHandler.mjs","names":[],"sources":["../../../src/nitro-v3/errorHandler.ts"],"sourcesContent":["import { parseURL } from 'ufo'\nimport { defineErrorHandler } from 'nitro'\nimport { resolveEvlogError, extractErrorStatus, serializeEvlogErrorResponse } from '../nitro'\n\n/**\n * Custom Nitro v3 error handler that properly serializes EvlogError.\n * This ensures that 'data' (containing 'why', 'fix', 'link') is preserved\n * in the JSON response regardless of the underlying HTTP framework.\n *\n * For non-EvlogError, returns undefined to let Nitro's default handler take over.\n *\n * Usage in nitro.config.ts:\n * ```ts\n * // errorHandler.ts\n * export { default } from 'evlog/nitro/v3/errorHandler'\n * // nitro.config.ts\n * export default defineConfig({\n * errorHandler: './errorHandler',\n * })\n * ```\n */\nexport default defineErrorHandler((error, event) => {\n const evlogError = resolveEvlogError(error)\n\n if (!evlogError) return\n\n const url = parseURL(event.req.url).pathname\n const status = extractErrorStatus(evlogError)\n\n return new Response(JSON.stringify(serializeEvlogErrorResponse(evlogError, url)), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,IAAA,uBAAe,oBAAoB,OAAO,UAAU;CAClD,MAAM,aAAa,kBAAkB,MAAM;AAE3C,KAAI,CAAC,WAAY;CAEjB,MAAM,MAAM,SAAS,MAAM,IAAI,IAAI,CAAC;CACpC,MAAM,SAAS,mBAAmB,WAAW;AAE7C,QAAO,IAAI,SAAS,KAAK,UAAU,4BAA4B,YAAY,IAAI,CAAC,EAAE;EAChF;EACA,SAAS,EAAE,gBAAgB,oBAAoB;EAChD,CAAC;EACF"}
|
package/dist/nitro/v3/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { EvlogError } from "../../error.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/nitro-v3/middleware.ts
|
|
4
3
|
/**
|
|
5
4
|
* Server middleware handler that catches EvlogError and returns a structured JSON response.
|
|
@@ -39,7 +38,7 @@ async function evlogErrorHandler(next) {
|
|
|
39
38
|
throw error;
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
|
-
|
|
43
41
|
//#endregion
|
|
44
42
|
export { evlogErrorHandler };
|
|
43
|
+
|
|
45
44
|
//# sourceMappingURL=middleware.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.mjs","names":[],"sources":["../../../src/nitro-v3/middleware.ts"],"sourcesContent":["import type { RequestLogger } from '../types'\nimport { EvlogError } from '../error'\nimport { serializeEvlogErrorResponse } from '../nitro'\n\n/**\n * Server middleware handler that catches EvlogError and returns a structured JSON response.\n *\n * Frameworks like TanStack Start catch thrown errors before Nitro's error handler,\n * stripping the `data` field (containing `why`, `fix`, `link`). This middleware\n * intercepts EvlogErrors first, attaches them to the wide event, and throws a\n * `Response` that the framework passes through directly.\n *\n * @example TanStack Start\n * ```ts\n * import { createMiddleware } from '@tanstack/react-start'\n * import { evlogErrorHandler } from 'evlog/nitro/v3'\n *\n * const evlogMiddleware = createMiddleware().server(evlogErrorHandler)\n *\n * export const Route = createRootRoute({\n * server: { middleware: [evlogMiddleware] },\n * })\n * ```\n */\n\nexport async function evlogErrorHandler<T>(next: (...args: any[]) => Promise<T>): Promise<T> {\n try {\n return await next()\n } catch (error: unknown) {\n if (error instanceof EvlogError || (error && typeof error === 'object' && (error as Error).name === 'EvlogError')) {\n const evlogError = error as EvlogError\n\n try {\n const { useRequest } = await import('nitro/context')\n const req = useRequest()\n const log = req.context?.log as RequestLogger | undefined\n log?.error(evlogError)\n } catch {\n // ignore\n }\n\n // Throw as Response so frameworks (TanStack Start, SolidStart, etc.) pass it through\n throw new Response(JSON.stringify(evlogError.toJSON()), {\n status: evlogError.status || 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n throw error\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"middleware.mjs","names":[],"sources":["../../../src/nitro-v3/middleware.ts"],"sourcesContent":["import type { RequestLogger } from '../types'\nimport { EvlogError } from '../error'\nimport { serializeEvlogErrorResponse } from '../nitro'\n\n/**\n * Server middleware handler that catches EvlogError and returns a structured JSON response.\n *\n * Frameworks like TanStack Start catch thrown errors before Nitro's error handler,\n * stripping the `data` field (containing `why`, `fix`, `link`). This middleware\n * intercepts EvlogErrors first, attaches them to the wide event, and throws a\n * `Response` that the framework passes through directly.\n *\n * @example TanStack Start\n * ```ts\n * import { createMiddleware } from '@tanstack/react-start'\n * import { evlogErrorHandler } from 'evlog/nitro/v3'\n *\n * const evlogMiddleware = createMiddleware().server(evlogErrorHandler)\n *\n * export const Route = createRootRoute({\n * server: { middleware: [evlogMiddleware] },\n * })\n * ```\n */\n\nexport async function evlogErrorHandler<T>(next: (...args: any[]) => Promise<T>): Promise<T> {\n try {\n return await next()\n } catch (error: unknown) {\n if (error instanceof EvlogError || (error && typeof error === 'object' && (error as Error).name === 'EvlogError')) {\n const evlogError = error as EvlogError\n\n try {\n const { useRequest } = await import('nitro/context')\n const req = useRequest()\n const log = req.context?.log as RequestLogger | undefined\n log?.error(evlogError)\n } catch {\n // ignore\n }\n\n // Throw as Response so frameworks (TanStack Start, SolidStart, etc.) pass it through\n throw new Response(JSON.stringify(evlogError.toJSON()), {\n status: evlogError.status || 500,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n throw error\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA,eAAsB,kBAAqB,MAAkD;AAC3F,KAAI;AACF,SAAO,MAAM,MAAM;UACZ,OAAgB;AACvB,MAAI,iBAAiB,cAAe,SAAS,OAAO,UAAU,YAAa,MAAgB,SAAS,cAAe;GACjH,MAAM,aAAa;AAEnB,OAAI;IACF,MAAM,EAAE,eAAe,MAAM,OAAO;AAGpC,KAFY,YAAY,CACR,SAAS,MACpB,MAAM,WAAW;WAChB;AAKR,SAAM,IAAI,SAAS,KAAK,UAAU,WAAW,QAAQ,CAAC,EAAE;IACtD,QAAQ,WAAW,UAAU;IAC7B,SAAS,EAAE,gBAAgB,oBAAoB;IAChD,CAAC;;AAEJ,QAAM"}
|
package/dist/nitro/v3/module.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { dirname, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
3
|
//#region src/nitro-v3/module.ts
|
|
5
4
|
const _dir = dirname(fileURLToPath(import.meta.url));
|
|
6
5
|
function evlog(options) {
|
|
@@ -18,7 +17,7 @@ function evlog(options) {
|
|
|
18
17
|
}
|
|
19
18
|
};
|
|
20
19
|
}
|
|
21
|
-
|
|
22
20
|
//#endregion
|
|
23
21
|
export { evlog as default };
|
|
22
|
+
|
|
24
23
|
//# sourceMappingURL=module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":[],"sources":["../../../src/nitro-v3/module.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Nitro } from 'nitro/types'\nimport type { NitroModuleOptions } from '../nitro'\n\nexport type { NitroModuleOptions }\n\nconst _dir = dirname(fileURLToPath(import.meta.url))\n\nexport default function evlog(options?: NitroModuleOptions) {\n return {\n name: 'evlog',\n setup(nitro: Nitro) {\n // Push the plugin (no extension — Nitro's bundler resolves it)\n nitro.options.plugins = nitro.options.plugins || []\n nitro.options.plugins.push(resolve(_dir, 'plugin'))\n\n // explicitly tell nitro to bundle evlog's files to correctly resolve nitro dependencies\n if (!nitro.options.noExternals) {\n nitro.options.noExternals = ['evlog']\n } else if (Array.isArray(nitro.options.noExternals)) {\n nitro.options.noExternals.push('evlog')\n }\n \n\n // Set error handler only if not already configured by user\n if (!nitro.options.errorHandler) {\n nitro.options.errorHandler = resolve(_dir, 'errorHandler')\n }\n\n // Inject config into runtimeConfig — works in production where the\n // plugin is bundled through Nitro's builder and the virtual\n // runtime-config module resolves correctly.\n nitro.options.runtimeConfig = nitro.options.runtimeConfig || {}\n nitro.options.runtimeConfig.evlog = options || {}\n\n // In dev mode, Nitro loads plugins externally (not bundled), so the\n // virtual runtime-config module is unreachable and useRuntimeConfig()\n // returns a stub without our values. process.env is inherited by the\n // Worker Threads that run the dev server, making it a reliable bridge.\n // The plugin reads: useRuntimeConfig().evlog ?? process.env.__EVLOG_CONFIG\n process.env.__EVLOG_CONFIG = JSON.stringify(options || {})\n },\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.mjs","names":[],"sources":["../../../src/nitro-v3/module.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Nitro } from 'nitro/types'\nimport type { NitroModuleOptions } from '../nitro'\n\nexport type { NitroModuleOptions }\n\nconst _dir = dirname(fileURLToPath(import.meta.url))\n\nexport default function evlog(options?: NitroModuleOptions) {\n return {\n name: 'evlog',\n setup(nitro: Nitro) {\n // Push the plugin (no extension — Nitro's bundler resolves it)\n nitro.options.plugins = nitro.options.plugins || []\n nitro.options.plugins.push(resolve(_dir, 'plugin'))\n\n // explicitly tell nitro to bundle evlog's files to correctly resolve nitro dependencies\n if (!nitro.options.noExternals) {\n nitro.options.noExternals = ['evlog']\n } else if (Array.isArray(nitro.options.noExternals)) {\n nitro.options.noExternals.push('evlog')\n }\n \n\n // Set error handler only if not already configured by user\n if (!nitro.options.errorHandler) {\n nitro.options.errorHandler = resolve(_dir, 'errorHandler')\n }\n\n // Inject config into runtimeConfig — works in production where the\n // plugin is bundled through Nitro's builder and the virtual\n // runtime-config module resolves correctly.\n nitro.options.runtimeConfig = nitro.options.runtimeConfig || {}\n nitro.options.runtimeConfig.evlog = options || {}\n\n // In dev mode, Nitro loads plugins externally (not bundled), so the\n // virtual runtime-config module is unreachable and useRuntimeConfig()\n // returns a stub without our values. process.env is inherited by the\n // Worker Threads that run the dev server, making it a reliable bridge.\n // The plugin reads: useRuntimeConfig().evlog ?? process.env.__EVLOG_CONFIG\n process.env.__EVLOG_CONFIG = JSON.stringify(options || {})\n },\n }\n}\n"],"mappings":";;;AAOA,MAAM,OAAO,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAEpD,SAAwB,MAAM,SAA8B;AAC1D,QAAO;EACL,MAAM;EACN,MAAM,OAAc;AAElB,SAAM,QAAQ,UAAU,MAAM,QAAQ,WAAW,EAAE;AACnD,SAAM,QAAQ,QAAQ,KAAK,QAAQ,MAAM,SAAS,CAAC;AAGnD,OAAI,CAAC,MAAM,QAAQ,YACjB,OAAM,QAAQ,cAAc,CAAC,QAAQ;YAC5B,MAAM,QAAQ,MAAM,QAAQ,YAAY,CACjD,OAAM,QAAQ,YAAY,KAAK,QAAQ;AAKzC,OAAI,CAAC,MAAM,QAAQ,aACjB,OAAM,QAAQ,eAAe,QAAQ,MAAM,eAAe;AAM5D,SAAM,QAAQ,gBAAgB,MAAM,QAAQ,iBAAiB,EAAE;AAC/D,SAAM,QAAQ,cAAc,QAAQ,WAAW,EAAE;AAOjD,WAAQ,IAAI,iBAAiB,KAAK,UAAU,WAAW,EAAE,CAAC;;EAE7D"}
|
package/dist/nitro/v3/plugin.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { filterSafeHeaders } from "../../utils.mjs";
|
|
2
2
|
import { createRequestLogger, initLogger, isEnabled } from "../../logger.mjs";
|
|
3
|
-
import { n as shouldLog, t as getServiceForPath } from "../../routes-
|
|
4
|
-
import { t as extractErrorStatus } from "../../nitro-
|
|
5
|
-
import { t as parseURL } from "../../dist-
|
|
3
|
+
import { n as shouldLog, t as getServiceForPath } from "../../routes-DdmLpEnG.mjs";
|
|
4
|
+
import { t as extractErrorStatus } from "../../nitro-B3vZrLGI.mjs";
|
|
5
|
+
import { t as parseURL } from "../../dist-Dalk68oO.mjs";
|
|
6
6
|
import { definePlugin } from "nitro";
|
|
7
7
|
import { useRuntimeConfig } from "nitro/runtime-config";
|
|
8
|
-
|
|
9
8
|
//#region src/nitro-v3/plugin.ts
|
|
10
9
|
function getContext(event) {
|
|
11
10
|
if (!event.req.context) event.req.context = {};
|
|
@@ -156,7 +155,7 @@ var plugin_default = definePlugin((nitroApp) => {
|
|
|
156
155
|
await callEnrichAndDrain(hooks, log.emit({ _forceKeep: tailCtx.shouldKeep }), e);
|
|
157
156
|
});
|
|
158
157
|
});
|
|
159
|
-
|
|
160
158
|
//#endregion
|
|
161
159
|
export { plugin_default as default };
|
|
160
|
+
|
|
162
161
|
//# sourceMappingURL=plugin.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/nitro-v3/plugin.ts"],"sourcesContent":["import { definePlugin } from 'nitro'\nimport { useRuntimeConfig } from 'nitro/runtime-config'\nimport type { CaptureError } from 'nitro/types'\nimport type { HTTPEvent } from 'nitro/h3'\nimport { parseURL } from 'ufo'\nimport { createRequestLogger, initLogger, isEnabled } from '../logger'\nimport { shouldLog, getServiceForPath, extractErrorStatus } from '../nitro'\nimport type { EvlogConfig } from '../nitro'\nimport type { EnrichContext, RequestLogger, TailSamplingContext, WideEvent } from '../types'\nimport { filterSafeHeaders } from '../utils'\n\n// Nitro v3 doesn't fully export hook types yet\n// https://github.com/nitrojs/nitro/blob/8882bc9e1dbf2d342e73097f22a2156f70f50575/src/types/runtime/nitro.ts#L48-L53\ninterface NitroV3Hooks {\n close: () => void\n error: CaptureError\n request: (event: HTTPEvent) => void | Promise<void>\n response: (res: Response, event: HTTPEvent) => void | Promise<void>\n 'evlog:emit:keep': (ctx: TailSamplingContext) => void | Promise<void>\n 'evlog:enrich': (ctx: EnrichContext) => void | Promise<void>\n 'evlog:drain': (ctx: { event: WideEvent; request?: { method?: string; path: string; requestId?: string }; headers?: Record<string, string> }) => void | Promise<void>\n}\n\ntype Hooks = {\n hook: <T extends keyof NitroV3Hooks>(name: T, listener: NitroV3Hooks[T]) => void\n callHook: <T extends keyof NitroV3Hooks>(name: T, ...args: Parameters<NitroV3Hooks[T]>) => Promise<void>\n}\n\nfunction getContext(event: HTTPEvent): Record<string, unknown> {\n if (!event.req.context) {\n event.req.context = {}\n }\n return event.req.context\n}\n\nfunction getSafeRequestHeaders(event: HTTPEvent): Record<string, string> {\n const headers: Record<string, string> = {}\n event.req.headers.forEach((value, key) => {\n headers[key] = value\n })\n return filterSafeHeaders(headers)\n}\n\nfunction getSafeResponseHeaders(res: Response): Record<string, string> | undefined {\n const headers: Record<string, string> = {}\n res.headers.forEach((value, key) => {\n headers[key] = value\n })\n if (Object.keys(headers).length === 0) return undefined\n return filterSafeHeaders(headers)\n}\n\nfunction buildHookContext(\n event: HTTPEvent,\n res?: Response,\n): Omit<EnrichContext, 'event'> {\n const { pathname } = parseURL(event.req.url)\n const responseHeaders = res ? getSafeResponseHeaders(res) : undefined\n return {\n request: { method: event.req.method, path: pathname },\n headers: getSafeRequestHeaders(event),\n response: {\n status: res?.status ?? 200,\n headers: responseHeaders,\n },\n }\n}\n\nasync function callDrainHook(\n hooks: Hooks,\n emittedEvent: WideEvent | null,\n event: HTTPEvent,\n hookContext: Omit<EnrichContext, 'event'>,\n): Promise<void> {\n if (!emittedEvent) return\n\n let drainPromise: Promise<unknown> | undefined\n try {\n const result = hooks.callHook('evlog:drain', {\n event: emittedEvent,\n request: hookContext.request,\n headers: hookContext.headers,\n })\n drainPromise = result?.catch?.((err: unknown) => {\n console.error('[evlog] drain failed:', err)\n })\n } catch (err) {\n console.error('[evlog] drain failed:', err)\n }\n\n if (!drainPromise) return\n\n // Use waitUntil if available (srvx native — Cloudflare Workers, Vercel Edge, etc.)\n // This keeps the runtime alive for background work without blocking the response\n if (typeof event.req.waitUntil === 'function') {\n event.req.waitUntil(drainPromise)\n } else {\n // Fallback: await drain to prevent lost logs in serverless environments\n // (e.g. Vercel Fluid Compute). On the normal path this runs from the\n // response hook (response already sent); on the error path it may run\n // before the error response is finalized.\n await drainPromise\n }\n}\n\nasync function callEnrichAndDrain(\n hooks: Hooks,\n emittedEvent: WideEvent | null,\n event: HTTPEvent,\n res?: Response,\n): Promise<void> {\n if (!emittedEvent) return\n\n const hookContext = buildHookContext(event, res)\n\n try {\n await hooks.callHook('evlog:enrich', { event: emittedEvent, ...hookContext })\n } catch (err) {\n console.error('[evlog] enrich failed:', err)\n }\n\n await callDrainHook(hooks, emittedEvent, event, hookContext)\n}\n\n/**\n * Nitro v3 plugin entry point.\n *\n * Usage in Nitro v3:\n * ```ts\n * // plugins/evlog.ts\n * export { default } from 'evlog/nitro/v3'\n * ```\n */\nexport default definePlugin((nitroApp) => {\n // In production builds the plugin is bundled and useRuntimeConfig()\n // resolves the virtual module correctly. In dev mode the plugin is\n // loaded externally so useRuntimeConfig() returns a stub — fall back\n // to the env var bridge set by the module.\n const evlogConfig = (useRuntimeConfig().evlog ?? (process.env.__EVLOG_CONFIG ? JSON.parse(process.env.__EVLOG_CONFIG) : undefined)) as EvlogConfig | undefined\n\n initLogger({\n enabled: evlogConfig?.enabled,\n env: evlogConfig?.env,\n pretty: evlogConfig?.pretty,\n sampling: evlogConfig?.sampling,\n })\n\n if (!isEnabled()) return\n\n const hooks = nitroApp.hooks as unknown as Hooks\n\n hooks.hook('request', (event) => {\n const { pathname } = parseURL(event.req.url)\n\n // Skip logging for routes not matching include/exclude patterns\n if (!shouldLog(pathname, evlogConfig?.include, evlogConfig?.exclude)) {\n return\n }\n\n const ctx = getContext(event)\n\n // Store start time for duration calculation in tail sampling\n ctx._evlogStartTime = Date.now()\n\n let requestIdOverride: string | undefined = undefined\n if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') {\n const cfRay = event.req.headers.get('cf-ray')\n if (cfRay) requestIdOverride = cfRay\n }\n\n const log = createRequestLogger({\n method: event.req.method,\n path: pathname,\n requestId: requestIdOverride || ctx.requestId as string | undefined || crypto.randomUUID(),\n })\n\n // Apply route-based service configuration if a matching route is found\n const routeService = getServiceForPath(pathname, evlogConfig?.routes)\n if (routeService) {\n log.set({ service: routeService })\n }\n\n ctx.log = log\n })\n\n hooks.hook('response', async (res, event) => {\n const ctx = event.req.context\n // Skip if already emitted by error hook\n if (ctx?._evlogEmitted) return\n\n const log = ctx?.log as RequestLogger | undefined\n if (!log || !ctx) return\n\n const { status } = res\n log.set({ status })\n\n const startTime = ctx._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const { pathname } = parseURL(event.req.url)\n\n const tailCtx: TailSamplingContext = {\n status,\n duration: durationMs,\n path: pathname,\n method: event.req.method,\n context: log.getContext(),\n shouldKeep: false,\n }\n\n await hooks.callHook('evlog:emit:keep', tailCtx)\n\n const emittedEvent = log.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(hooks, emittedEvent, event, res)\n })\n\n hooks.hook('error', async (error, { event }) => {\n if (!event) return\n const e = event as HTTPEvent\n\n const ctx = e.req.context\n const log = ctx?.log as RequestLogger | undefined\n if (!log || !ctx) return\n\n // Check if error.cause is an EvlogError (thrown errors get wrapped in HTTPError by nitro)\n const actualError = (error.cause as Error)?.name === 'EvlogError' \n ? error.cause as Error \n : error as Error\n\n log.error(actualError)\n\n const errorStatus = extractErrorStatus(actualError)\n log.set({ status: errorStatus })\n\n const { pathname } = parseURL(e.req.url)\n const startTime = ctx._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const tailCtx: TailSamplingContext = {\n status: errorStatus,\n duration: durationMs,\n path: pathname,\n method: e.req.method,\n context: log.getContext(),\n shouldKeep: false,\n }\n\n await hooks.callHook('evlog:emit:keep', tailCtx)\n\n ctx._evlogEmitted = true\n\n const emittedEvent = log.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(hooks, emittedEvent, e)\n })\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/nitro-v3/plugin.ts"],"sourcesContent":["import { definePlugin } from 'nitro'\nimport { useRuntimeConfig } from 'nitro/runtime-config'\nimport type { CaptureError } from 'nitro/types'\nimport type { HTTPEvent } from 'nitro/h3'\nimport { parseURL } from 'ufo'\nimport { createRequestLogger, initLogger, isEnabled } from '../logger'\nimport { shouldLog, getServiceForPath, extractErrorStatus } from '../nitro'\nimport type { EvlogConfig } from '../nitro'\nimport type { EnrichContext, RequestLogger, TailSamplingContext, WideEvent } from '../types'\nimport { filterSafeHeaders } from '../utils'\n\n// Nitro v3 doesn't fully export hook types yet\n// https://github.com/nitrojs/nitro/blob/8882bc9e1dbf2d342e73097f22a2156f70f50575/src/types/runtime/nitro.ts#L48-L53\ninterface NitroV3Hooks {\n close: () => void\n error: CaptureError\n request: (event: HTTPEvent) => void | Promise<void>\n response: (res: Response, event: HTTPEvent) => void | Promise<void>\n 'evlog:emit:keep': (ctx: TailSamplingContext) => void | Promise<void>\n 'evlog:enrich': (ctx: EnrichContext) => void | Promise<void>\n 'evlog:drain': (ctx: { event: WideEvent; request?: { method?: string; path: string; requestId?: string }; headers?: Record<string, string> }) => void | Promise<void>\n}\n\ntype Hooks = {\n hook: <T extends keyof NitroV3Hooks>(name: T, listener: NitroV3Hooks[T]) => void\n callHook: <T extends keyof NitroV3Hooks>(name: T, ...args: Parameters<NitroV3Hooks[T]>) => Promise<void>\n}\n\nfunction getContext(event: HTTPEvent): Record<string, unknown> {\n if (!event.req.context) {\n event.req.context = {}\n }\n return event.req.context\n}\n\nfunction getSafeRequestHeaders(event: HTTPEvent): Record<string, string> {\n const headers: Record<string, string> = {}\n event.req.headers.forEach((value, key) => {\n headers[key] = value\n })\n return filterSafeHeaders(headers)\n}\n\nfunction getSafeResponseHeaders(res: Response): Record<string, string> | undefined {\n const headers: Record<string, string> = {}\n res.headers.forEach((value, key) => {\n headers[key] = value\n })\n if (Object.keys(headers).length === 0) return undefined\n return filterSafeHeaders(headers)\n}\n\nfunction buildHookContext(\n event: HTTPEvent,\n res?: Response,\n): Omit<EnrichContext, 'event'> {\n const { pathname } = parseURL(event.req.url)\n const responseHeaders = res ? getSafeResponseHeaders(res) : undefined\n return {\n request: { method: event.req.method, path: pathname },\n headers: getSafeRequestHeaders(event),\n response: {\n status: res?.status ?? 200,\n headers: responseHeaders,\n },\n }\n}\n\nasync function callDrainHook(\n hooks: Hooks,\n emittedEvent: WideEvent | null,\n event: HTTPEvent,\n hookContext: Omit<EnrichContext, 'event'>,\n): Promise<void> {\n if (!emittedEvent) return\n\n let drainPromise: Promise<unknown> | undefined\n try {\n const result = hooks.callHook('evlog:drain', {\n event: emittedEvent,\n request: hookContext.request,\n headers: hookContext.headers,\n })\n drainPromise = result?.catch?.((err: unknown) => {\n console.error('[evlog] drain failed:', err)\n })\n } catch (err) {\n console.error('[evlog] drain failed:', err)\n }\n\n if (!drainPromise) return\n\n // Use waitUntil if available (srvx native — Cloudflare Workers, Vercel Edge, etc.)\n // This keeps the runtime alive for background work without blocking the response\n if (typeof event.req.waitUntil === 'function') {\n event.req.waitUntil(drainPromise)\n } else {\n // Fallback: await drain to prevent lost logs in serverless environments\n // (e.g. Vercel Fluid Compute). On the normal path this runs from the\n // response hook (response already sent); on the error path it may run\n // before the error response is finalized.\n await drainPromise\n }\n}\n\nasync function callEnrichAndDrain(\n hooks: Hooks,\n emittedEvent: WideEvent | null,\n event: HTTPEvent,\n res?: Response,\n): Promise<void> {\n if (!emittedEvent) return\n\n const hookContext = buildHookContext(event, res)\n\n try {\n await hooks.callHook('evlog:enrich', { event: emittedEvent, ...hookContext })\n } catch (err) {\n console.error('[evlog] enrich failed:', err)\n }\n\n await callDrainHook(hooks, emittedEvent, event, hookContext)\n}\n\n/**\n * Nitro v3 plugin entry point.\n *\n * Usage in Nitro v3:\n * ```ts\n * // plugins/evlog.ts\n * export { default } from 'evlog/nitro/v3'\n * ```\n */\nexport default definePlugin((nitroApp) => {\n // In production builds the plugin is bundled and useRuntimeConfig()\n // resolves the virtual module correctly. In dev mode the plugin is\n // loaded externally so useRuntimeConfig() returns a stub — fall back\n // to the env var bridge set by the module.\n const evlogConfig = (useRuntimeConfig().evlog ?? (process.env.__EVLOG_CONFIG ? JSON.parse(process.env.__EVLOG_CONFIG) : undefined)) as EvlogConfig | undefined\n\n initLogger({\n enabled: evlogConfig?.enabled,\n env: evlogConfig?.env,\n pretty: evlogConfig?.pretty,\n sampling: evlogConfig?.sampling,\n })\n\n if (!isEnabled()) return\n\n const hooks = nitroApp.hooks as unknown as Hooks\n\n hooks.hook('request', (event) => {\n const { pathname } = parseURL(event.req.url)\n\n // Skip logging for routes not matching include/exclude patterns\n if (!shouldLog(pathname, evlogConfig?.include, evlogConfig?.exclude)) {\n return\n }\n\n const ctx = getContext(event)\n\n // Store start time for duration calculation in tail sampling\n ctx._evlogStartTime = Date.now()\n\n let requestIdOverride: string | undefined = undefined\n if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') {\n const cfRay = event.req.headers.get('cf-ray')\n if (cfRay) requestIdOverride = cfRay\n }\n\n const log = createRequestLogger({\n method: event.req.method,\n path: pathname,\n requestId: requestIdOverride || ctx.requestId as string | undefined || crypto.randomUUID(),\n })\n\n // Apply route-based service configuration if a matching route is found\n const routeService = getServiceForPath(pathname, evlogConfig?.routes)\n if (routeService) {\n log.set({ service: routeService })\n }\n\n ctx.log = log\n })\n\n hooks.hook('response', async (res, event) => {\n const ctx = event.req.context\n // Skip if already emitted by error hook\n if (ctx?._evlogEmitted) return\n\n const log = ctx?.log as RequestLogger | undefined\n if (!log || !ctx) return\n\n const { status } = res\n log.set({ status })\n\n const startTime = ctx._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const { pathname } = parseURL(event.req.url)\n\n const tailCtx: TailSamplingContext = {\n status,\n duration: durationMs,\n path: pathname,\n method: event.req.method,\n context: log.getContext(),\n shouldKeep: false,\n }\n\n await hooks.callHook('evlog:emit:keep', tailCtx)\n\n const emittedEvent = log.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(hooks, emittedEvent, event, res)\n })\n\n hooks.hook('error', async (error, { event }) => {\n if (!event) return\n const e = event as HTTPEvent\n\n const ctx = e.req.context\n const log = ctx?.log as RequestLogger | undefined\n if (!log || !ctx) return\n\n // Check if error.cause is an EvlogError (thrown errors get wrapped in HTTPError by nitro)\n const actualError = (error.cause as Error)?.name === 'EvlogError' \n ? error.cause as Error \n : error as Error\n\n log.error(actualError)\n\n const errorStatus = extractErrorStatus(actualError)\n log.set({ status: errorStatus })\n\n const { pathname } = parseURL(e.req.url)\n const startTime = ctx._evlogStartTime as number | undefined\n const durationMs = startTime ? Date.now() - startTime : undefined\n\n const tailCtx: TailSamplingContext = {\n status: errorStatus,\n duration: durationMs,\n path: pathname,\n method: e.req.method,\n context: log.getContext(),\n shouldKeep: false,\n }\n\n await hooks.callHook('evlog:emit:keep', tailCtx)\n\n ctx._evlogEmitted = true\n\n const emittedEvent = log.emit({ _forceKeep: tailCtx.shouldKeep })\n await callEnrichAndDrain(hooks, emittedEvent, e)\n })\n})\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLogger.mjs","names":[],"sources":["../../../src/nitro-v3/useLogger.ts"],"sourcesContent":["import type { HTTPEvent } from 'nitro/h3'\nimport type { RequestLogger } from '../types'\n\n/**\n * Returns the request logger attached to the given Nitro v3 HTTP event.\n *\n * @param event - The current HTTPEvent from Nitro v3.\n * @param service - Optional service name to override the default service.\n * @returns The request-scoped logger.\n * @throws Error if the logger is not initialized on the event context.\n *\n * @example\n * import { useLogger } from 'evlog/nitro/v3'\n *\n * export default defineHandler((event) => {\n * const log = useLogger(event)\n * log.set({ foo: 'bar' })\n * })\n */\nexport function useLogger<T extends object = Record<string, unknown>>(event: HTTPEvent, service?: string): RequestLogger<T> {\n const ctx = event.req.context as Record<string, unknown> | undefined\n const log = ctx?.log as RequestLogger<T> | undefined\n\n if (!log) {\n throw new Error(\n '[evlog] Logger not initialized. Make sure the evlog Nitro module is registered in nitro.config.ts. '\n + 'Example: modules: [evlog({ env: { service: \\'my-app\\' } })]',\n )\n }\n\n if (service) {\n const untyped = log as unknown as RequestLogger\n untyped.set({ service })\n }\n\n return log\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,SAAgB,UAAsD,OAAkB,SAAoC;CAE1H,MAAM,MADM,MAAM,IAAI,SACL;AAEjB,KAAI,CAAC,IACH,OAAM,IAAI,MACR,+JAED;AAGH,KAAI,
|
|
1
|
+
{"version":3,"file":"useLogger.mjs","names":[],"sources":["../../../src/nitro-v3/useLogger.ts"],"sourcesContent":["import type { HTTPEvent } from 'nitro/h3'\nimport type { RequestLogger } from '../types'\n\n/**\n * Returns the request logger attached to the given Nitro v3 HTTP event.\n *\n * @param event - The current HTTPEvent from Nitro v3.\n * @param service - Optional service name to override the default service.\n * @returns The request-scoped logger.\n * @throws Error if the logger is not initialized on the event context.\n *\n * @example\n * import { useLogger } from 'evlog/nitro/v3'\n *\n * export default defineHandler((event) => {\n * const log = useLogger(event)\n * log.set({ foo: 'bar' })\n * })\n */\nexport function useLogger<T extends object = Record<string, unknown>>(event: HTTPEvent, service?: string): RequestLogger<T> {\n const ctx = event.req.context as Record<string, unknown> | undefined\n const log = ctx?.log as RequestLogger<T> | undefined\n\n if (!log) {\n throw new Error(\n '[evlog] Logger not initialized. Make sure the evlog Nitro module is registered in nitro.config.ts. '\n + 'Example: modules: [evlog({ env: { service: \\'my-app\\' } })]',\n )\n }\n\n if (service) {\n const untyped = log as unknown as RequestLogger\n untyped.set({ service })\n }\n\n return log\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,SAAgB,UAAsD,OAAkB,SAAoC;CAE1H,MAAM,MADM,MAAM,IAAI,SACL;AAEjB,KAAI,CAAC,IACH,OAAM,IAAI,MACR,+JAED;AAGH,KAAI,QACc,KACR,IAAI,EAAE,SAAS,CAAC;AAG1B,QAAO"}
|
|
@@ -33,7 +33,7 @@ function serializeEvlogErrorResponse(error, url) {
|
|
|
33
33
|
...data !== void 0 && { data }
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
|
|
37
36
|
//#endregion
|
|
38
37
|
export { resolveEvlogError as n, serializeEvlogErrorResponse as r, extractErrorStatus as t };
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=nitro-B3vZrLGI.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nitro-
|
|
1
|
+
{"version":3,"file":"nitro-B3vZrLGI.mjs","names":[],"sources":["../src/nitro.ts"],"sourcesContent":["import type { EnvironmentContext, RouteConfig, SamplingConfig } from './types'\n\nexport { shouldLog, getServiceForPath } from './shared/routes'\n\nexport interface NitroModuleOptions {\n /**\n * Enable or disable all logging globally.\n * @default true\n */\n enabled?: boolean\n\n /**\n * Environment context overrides.\n */\n env?: Partial<EnvironmentContext>\n\n /**\n * Enable pretty printing.\n * @default true in development, false in production\n */\n pretty?: boolean\n\n /**\n * Route patterns to include in logging.\n * Supports glob patterns like '/api/**'.\n * If not set, all routes are logged.\n */\n include?: string[]\n\n /**\n * Route patterns to exclude from logging.\n * Supports glob patterns like '/_nitro/**'.\n * Exclusions take precedence over inclusions.\n */\n exclude?: string[]\n\n /**\n * Route-specific service configuration.\n */\n routes?: Record<string, RouteConfig>\n\n /**\n * Sampling configuration for filtering logs.\n */\n sampling?: SamplingConfig\n}\n\nexport interface EvlogConfig {\n enabled?: boolean\n env?: Record<string, unknown>\n pretty?: boolean\n include?: string[]\n exclude?: string[]\n routes?: Record<string, RouteConfig>\n sampling?: SamplingConfig\n}\n\n/**\n * Resolve an EvlogError from an error or its cause chain.\n * Both Nitro v2 (h3) and v3 wrap thrown errors — this unwraps them.\n */\nexport function resolveEvlogError(error: Error): Error | null {\n if (error.name === 'EvlogError') return error\n if ((error.cause as Error)?.name === 'EvlogError') return error.cause as Error\n return null\n}\n\n/**\n * Extract HTTP status from an error, checking both `status` and `statusCode`.\n */\nexport function extractErrorStatus(error: unknown): number {\n return (error as { status?: number }).status\n ?? (error as { statusCode?: number }).statusCode\n ?? 500\n}\n\n/**\n * Build a standard evlog error JSON response body.\n * Used by both v2 and v3 error handlers to ensure consistent shape.\n */\nexport function serializeEvlogErrorResponse(error: Error, url: string): Record<string, unknown> {\n const status = extractErrorStatus(error)\n const { data } = error as { data?: unknown }\n const statusMessage = (error as { statusMessage?: string }).statusMessage || error.message\n return {\n url,\n status,\n statusCode: status,\n statusText: statusMessage,\n statusMessage,\n message: error.message,\n error: true,\n ...(data !== undefined && { data }),\n }\n}\n\n"],"mappings":";;;;;AA6DA,SAAgB,kBAAkB,OAA4B;AAC5D,KAAI,MAAM,SAAS,aAAc,QAAO;AACxC,KAAK,MAAM,OAAiB,SAAS,aAAc,QAAO,MAAM;AAChE,QAAO;;;;;AAMT,SAAgB,mBAAmB,OAAwB;AACzD,QAAQ,MAA8B,UAChC,MAAkC,cACnC;;;;;;AAOP,SAAgB,4BAA4B,OAAc,KAAsC;CAC9F,MAAM,SAAS,mBAAmB,MAAM;CACxC,MAAM,EAAE,SAAS;CACjB,MAAM,gBAAiB,MAAqC,iBAAiB,MAAM;AACnF,QAAO;EACL;EACA;EACA,YAAY;EACZ,YAAY;EACZ;EACA,SAAS,MAAM;EACf,OAAO;EACP,GAAI,SAAS,KAAA,KAAa,EAAE,MAAM;EACnC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnvironmentContext, RouteConfig, SamplingConfig } from "./types.mjs";
|
|
1
|
+
import { a as EnvironmentContext, v as RouteConfig, y as SamplingConfig } from "./types-CRj5tGVA.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/nitro.d.ts
|
|
4
4
|
interface NitroModuleOptions {
|
|
@@ -39,4 +39,4 @@ interface NitroModuleOptions {
|
|
|
39
39
|
}
|
|
40
40
|
//#endregion
|
|
41
41
|
export { NitroModuleOptions as t };
|
|
42
|
-
//# sourceMappingURL=nitro-
|
|
42
|
+
//# sourceMappingURL=nitro-wZ05H2wx.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nitro-
|
|
1
|
+
{"version":3,"file":"nitro-wZ05H2wx.d.mts","names":[],"sources":["../src/nitro.ts"],"mappings":";;;UAIiB,kBAAA;EAAA;;;;EAKf,OAAA;EA8BwB;;;EAzBxB,GAAA,GAAM,OAAA,CAAQ,kBAAA;EA8BW;;;;EAxBzB,MAAA;EAAA;;;;;EAOA,OAAA;EAiBA;;;;;EAVA,OAAA;;;;EAKA,MAAA,GAAS,MAAA,SAAe,WAAA;;;;EAKxB,QAAA,GAAW,cAAA;AAAA"}
|
package/dist/nuxt/module.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EnvironmentContext, RouteConfig,
|
|
1
|
+
import { a as EnvironmentContext, v as RouteConfig, w as TransportConfig, y as SamplingConfig } from "../types-CRj5tGVA.mjs";
|
|
2
2
|
import * as _nuxt_schema0 from "@nuxt/schema";
|
|
3
3
|
|
|
4
4
|
//#region src/nuxt/module.d.ts
|
package/dist/nuxt/module.mjs
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { addImports, addPlugin, addServerHandler, addServerImports, addServerPlugin, createResolver, defineNuxtModule } from "@nuxt/kit";
|
|
2
|
-
|
|
3
2
|
//#region package.json
|
|
4
3
|
var name = "evlog";
|
|
5
|
-
var version = "2.4.0";
|
|
6
|
-
|
|
7
4
|
//#endregion
|
|
8
5
|
//#region src/nuxt/module.ts
|
|
9
6
|
var module_default = defineNuxtModule({
|
|
10
7
|
meta: {
|
|
11
8
|
name,
|
|
12
|
-
version,
|
|
9
|
+
version: "2.5.0",
|
|
13
10
|
configKey: name,
|
|
14
11
|
docs: "https://evlog.dev"
|
|
15
12
|
},
|
|
@@ -79,7 +76,7 @@ var module_default = defineNuxtModule({
|
|
|
79
76
|
]);
|
|
80
77
|
}
|
|
81
78
|
});
|
|
82
|
-
|
|
83
79
|
//#endregion
|
|
84
80
|
export { module_default as default };
|
|
81
|
+
|
|
85
82
|
//# sourceMappingURL=module.mjs.map
|
package/dist/nuxt/module.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":[],"sources":["../../package.json","../../src/nuxt/module.ts"],"sourcesContent":["","import {\n addImports,\n addPlugin,\n addServerHandler,\n addServerImports,\n addServerPlugin,\n createResolver,\n defineNuxtModule,\n} from '@nuxt/kit'\nimport type { NitroConfig } from 'nitropack'\nimport type { EnvironmentContext, RouteConfig, SamplingConfig, TransportConfig } from '../types'\nimport { name, version } from '../../package.json'\n\ninterface ModuleAxiomBaseConfig {\n /** Axiom dataset name */\n dataset: string\n /** Axiom API token */\n token: string\n /** Organization ID (required for Personal Access Tokens) */\n orgId?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n}\n\ninterface ModuleAxiomEdgeConfig {\n /**\n * Edge URL for Axiom ingest/query endpoints.\n * If no path is provided, uses /v1/ingest/{dataset}.\n * If a custom path is provided, it is used as-is (trailing slash trimmed).\n */\n edgeUrl: string\n /** Mutually exclusive with edgeUrl. */\n baseUrl?: never\n}\n\ninterface ModuleAxiomEndpointConfig {\n /** Base URL for Axiom API. Uses /v1/datasets/{dataset}/ingest. */\n baseUrl?: string\n /** Mutually exclusive with baseUrl. */\n edgeUrl?: never\n}\n\ntype ModuleAxiomConfig = ModuleAxiomBaseConfig & (ModuleAxiomEdgeConfig | ModuleAxiomEndpointConfig)\n\nexport interface ModuleOptions {\n /**\n * Enable or disable all logging globally.\n * When false, all emits, tagged logs, and request logger operations become no-ops.\n * @default true\n */\n enabled?: boolean\n\n /**\n * Environment context overrides.\n */\n env?: Partial<EnvironmentContext>\n\n /**\n * Enable or disable browser console output.\n * When false, client-side logs are suppressed in the browser DevTools console\n * but still sent to the server via transport (if enabled).\n * @default true\n */\n console?: boolean\n\n /**\n * Enable pretty printing.\n * @default true in development, false in production\n */\n pretty?: boolean\n\n /**\n * Route patterns to include in logging.\n * Supports glob patterns like '/api/**'.\n * If not set, all routes are logged.\n * @example ['/api/**', '/auth/**']\n */\n include?: string[]\n\n /**\n * Route patterns to exclude from logging.\n * Supports glob patterns like '/api/_nuxt_icon/**'.\n * Exclusions take precedence over inclusions.\n * @example ['/api/_nuxt_icon/**', '/health']\n */\n exclude?: string[]\n\n /**\n * Route-specific service configuration.\n * Allows setting different service names for different routes.\n * Patterns are matched using glob syntax.\n *\n * @example\n * ```ts\n * routes: {\n * '/api/foo/**': { service: 'service1' },\n * '/api/bar/**': { service: 'service2' }\n * }\n * ```\n */\n routes?: Record<string, RouteConfig>\n\n /**\n * Sampling configuration for filtering logs.\n * Allows configuring what percentage of logs to keep per level.\n *\n * @example\n * ```ts\n * sampling: {\n * rates: {\n * info: 10, // Keep 10% of info logs\n * warn: 50, // Keep 50% of warning logs\n * debug: 5, // Keep 5% of debug logs\n * error: 100, // Always keep errors (default)\n * }\n * }\n * ```\n */\n sampling?: SamplingConfig\n\n /**\n * Transport configuration for sending client logs to the server.\n *\n * @example\n * ```ts\n * transport: {\n * enabled: true, // Send logs to server API\n * endpoint: '/api/_evlog/ingest' // Custom endpoint\n * }\n * ```\n */\n transport?: TransportConfig\n\n /**\n * Axiom adapter configuration.\n * When configured, use `createAxiomDrain()` from `evlog/axiom` to send logs.\n *\n * @example\n * ```ts\n * axiom: {\n * dataset: 'my-app-logs',\n * token: process.env.AXIOM_TOKEN,\n * }\n * ```\n */\n axiom?: ModuleAxiomConfig\n\n /**\n * OTLP adapter configuration.\n * When configured, use `createOTLPDrain()` from `evlog/otlp` to send logs.\n *\n * @example\n * ```ts\n * otlp: {\n * endpoint: 'http://localhost:4318',\n * headers: {\n * 'Authorization': `Basic ${process.env.GRAFANA_TOKEN}`,\n * },\n * }\n * ```\n */\n otlp?: {\n /** OTLP HTTP endpoint (e.g., http://localhost:4318) */\n endpoint: string\n /** Override service name (defaults to event.service) */\n serviceName?: string\n /** Additional resource attributes */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Custom headers (e.g., for authentication) */\n headers?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n }\n\n /**\n * PostHog adapter configuration.\n * When configured, use `createPostHogDrain()` from `evlog/posthog` to send logs\n * via PostHog Logs (OTLP).\n *\n * @example\n * ```ts\n * posthog: {\n * apiKey: process.env.POSTHOG_API_KEY,\n * }\n * ```\n */\n posthog?: {\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 }\n\n /**\n * Sentry adapter configuration.\n * When configured, use `createSentryDrain()` from `evlog/sentry` to send logs.\n *\n * @example\n * ```ts\n * sentry: {\n * dsn: process.env.SENTRY_DSN,\n * }\n * ```\n */\n sentry?: {\n /** Sentry DSN */\n dsn: string\n /** Environment override (defaults to event.environment) */\n environment?: string\n /** Release version override (defaults to event.version) */\n release?: string\n /** Additional tags to attach as attributes */\n tags?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n }\n\n /**\n * How long to retain events before cleanup (used by @evlog/nuxthub).\n * Supports \"30d\" (days), \"24h\" (hours), \"60m\" (minutes).\n * @default '30d'\n */\n retention?: string\n}\n\nexport default defineNuxtModule<ModuleOptions>({\n meta: {\n name,\n version,\n configKey: name,\n docs: 'https://evlog.dev',\n },\n defaults: {},\n setup(options, nuxt) {\n const resolver = createResolver(import.meta.url)\n\n const transportEnabled = options.transport?.enabled ?? false\n const transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest'\n\n // Register custom error handler for proper EvlogError serialization\n // Only set if not already configured to avoid overwriting user's custom handler\n // @ts-expect-error nitro:config hook exists but is not in NuxtHooks type\n nuxt.hook('nitro:config', (nitroConfig: NitroConfig) => {\n nitroConfig.errorHandler = nitroConfig.errorHandler || resolver.resolve('../nitro/errorHandler')\n })\n\n nuxt.options.runtimeConfig.evlog = options\n nuxt.options.runtimeConfig.public.evlog = {\n enabled: options.enabled ?? true,\n console: options.console,\n pretty: options.pretty,\n transport: {\n enabled: transportEnabled,\n endpoint: transportEndpoint,\n },\n }\n\n if (transportEnabled) {\n addServerHandler({\n route: transportEndpoint,\n method: 'post',\n handler: resolver.resolve('../runtime/server/routes/_evlog/ingest.post'),\n })\n }\n\n addServerPlugin(resolver.resolve('../nitro/plugin'))\n\n addPlugin({\n src: resolver.resolve('../runtime/client/plugin'),\n mode: 'client',\n })\n\n addImports([\n {\n name: 'log',\n from: resolver.resolve('../runtime/client/log'),\n },\n {\n name: 'setIdentity',\n from: resolver.resolve('../runtime/client/log'),\n },\n {\n name: 'clearIdentity',\n from: resolver.resolve('../runtime/client/log'),\n },\n {\n name: 'createEvlogError',\n from: resolver.resolve('../error'),\n },\n {\n name: 'parseError',\n from: resolver.resolve('../runtime/utils/parseError'),\n },\n ])\n\n addServerImports([\n {\n name: 'useLogger',\n from: resolver.resolve('../runtime/server/useLogger'),\n },\n {\n name: 'log',\n from: resolver.resolve('../logger'),\n },\n {\n name: 'createEvlogError',\n from: resolver.resolve('../error'),\n },\n ])\n },\n})\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.mjs","names":[],"sources":["../../package.json","../../src/nuxt/module.ts"],"sourcesContent":["","import {\n addImports,\n addPlugin,\n addServerHandler,\n addServerImports,\n addServerPlugin,\n createResolver,\n defineNuxtModule,\n} from '@nuxt/kit'\nimport type { NitroConfig } from 'nitropack'\nimport type { EnvironmentContext, RouteConfig, SamplingConfig, TransportConfig } from '../types'\nimport { name, version } from '../../package.json'\n\ninterface ModuleAxiomBaseConfig {\n /** Axiom dataset name */\n dataset: string\n /** Axiom API token */\n token: string\n /** Organization ID (required for Personal Access Tokens) */\n orgId?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n}\n\ninterface ModuleAxiomEdgeConfig {\n /**\n * Edge URL for Axiom ingest/query endpoints.\n * If no path is provided, uses /v1/ingest/{dataset}.\n * If a custom path is provided, it is used as-is (trailing slash trimmed).\n */\n edgeUrl: string\n /** Mutually exclusive with edgeUrl. */\n baseUrl?: never\n}\n\ninterface ModuleAxiomEndpointConfig {\n /** Base URL for Axiom API. Uses /v1/datasets/{dataset}/ingest. */\n baseUrl?: string\n /** Mutually exclusive with baseUrl. */\n edgeUrl?: never\n}\n\ntype ModuleAxiomConfig = ModuleAxiomBaseConfig & (ModuleAxiomEdgeConfig | ModuleAxiomEndpointConfig)\n\nexport interface ModuleOptions {\n /**\n * Enable or disable all logging globally.\n * When false, all emits, tagged logs, and request logger operations become no-ops.\n * @default true\n */\n enabled?: boolean\n\n /**\n * Environment context overrides.\n */\n env?: Partial<EnvironmentContext>\n\n /**\n * Enable or disable browser console output.\n * When false, client-side logs are suppressed in the browser DevTools console\n * but still sent to the server via transport (if enabled).\n * @default true\n */\n console?: boolean\n\n /**\n * Enable pretty printing.\n * @default true in development, false in production\n */\n pretty?: boolean\n\n /**\n * Route patterns to include in logging.\n * Supports glob patterns like '/api/**'.\n * If not set, all routes are logged.\n * @example ['/api/**', '/auth/**']\n */\n include?: string[]\n\n /**\n * Route patterns to exclude from logging.\n * Supports glob patterns like '/api/_nuxt_icon/**'.\n * Exclusions take precedence over inclusions.\n * @example ['/api/_nuxt_icon/**', '/health']\n */\n exclude?: string[]\n\n /**\n * Route-specific service configuration.\n * Allows setting different service names for different routes.\n * Patterns are matched using glob syntax.\n *\n * @example\n * ```ts\n * routes: {\n * '/api/foo/**': { service: 'service1' },\n * '/api/bar/**': { service: 'service2' }\n * }\n * ```\n */\n routes?: Record<string, RouteConfig>\n\n /**\n * Sampling configuration for filtering logs.\n * Allows configuring what percentage of logs to keep per level.\n *\n * @example\n * ```ts\n * sampling: {\n * rates: {\n * info: 10, // Keep 10% of info logs\n * warn: 50, // Keep 50% of warning logs\n * debug: 5, // Keep 5% of debug logs\n * error: 100, // Always keep errors (default)\n * }\n * }\n * ```\n */\n sampling?: SamplingConfig\n\n /**\n * Transport configuration for sending client logs to the server.\n *\n * @example\n * ```ts\n * transport: {\n * enabled: true, // Send logs to server API\n * endpoint: '/api/_evlog/ingest' // Custom endpoint\n * }\n * ```\n */\n transport?: TransportConfig\n\n /**\n * Axiom adapter configuration.\n * When configured, use `createAxiomDrain()` from `evlog/axiom` to send logs.\n *\n * @example\n * ```ts\n * axiom: {\n * dataset: 'my-app-logs',\n * token: process.env.AXIOM_TOKEN,\n * }\n * ```\n */\n axiom?: ModuleAxiomConfig\n\n /**\n * OTLP adapter configuration.\n * When configured, use `createOTLPDrain()` from `evlog/otlp` to send logs.\n *\n * @example\n * ```ts\n * otlp: {\n * endpoint: 'http://localhost:4318',\n * headers: {\n * 'Authorization': `Basic ${process.env.GRAFANA_TOKEN}`,\n * },\n * }\n * ```\n */\n otlp?: {\n /** OTLP HTTP endpoint (e.g., http://localhost:4318) */\n endpoint: string\n /** Override service name (defaults to event.service) */\n serviceName?: string\n /** Additional resource attributes */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Custom headers (e.g., for authentication) */\n headers?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n }\n\n /**\n * PostHog adapter configuration.\n * When configured, use `createPostHogDrain()` from `evlog/posthog` to send logs\n * via PostHog Logs (OTLP).\n *\n * @example\n * ```ts\n * posthog: {\n * apiKey: process.env.POSTHOG_API_KEY,\n * }\n * ```\n */\n posthog?: {\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 }\n\n /**\n * Sentry adapter configuration.\n * When configured, use `createSentryDrain()` from `evlog/sentry` to send logs.\n *\n * @example\n * ```ts\n * sentry: {\n * dsn: process.env.SENTRY_DSN,\n * }\n * ```\n */\n sentry?: {\n /** Sentry DSN */\n dsn: string\n /** Environment override (defaults to event.environment) */\n environment?: string\n /** Release version override (defaults to event.version) */\n release?: string\n /** Additional tags to attach as attributes */\n tags?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n }\n\n /**\n * How long to retain events before cleanup (used by @evlog/nuxthub).\n * Supports \"30d\" (days), \"24h\" (hours), \"60m\" (minutes).\n * @default '30d'\n */\n retention?: string\n}\n\nexport default defineNuxtModule<ModuleOptions>({\n meta: {\n name,\n version,\n configKey: name,\n docs: 'https://evlog.dev',\n },\n defaults: {},\n setup(options, nuxt) {\n const resolver = createResolver(import.meta.url)\n\n const transportEnabled = options.transport?.enabled ?? false\n const transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest'\n\n // Register custom error handler for proper EvlogError serialization\n // Only set if not already configured to avoid overwriting user's custom handler\n // @ts-expect-error nitro:config hook exists but is not in NuxtHooks type\n nuxt.hook('nitro:config', (nitroConfig: NitroConfig) => {\n nitroConfig.errorHandler = nitroConfig.errorHandler || resolver.resolve('../nitro/errorHandler')\n })\n\n nuxt.options.runtimeConfig.evlog = options\n nuxt.options.runtimeConfig.public.evlog = {\n enabled: options.enabled ?? true,\n console: options.console,\n pretty: options.pretty,\n transport: {\n enabled: transportEnabled,\n endpoint: transportEndpoint,\n },\n }\n\n if (transportEnabled) {\n addServerHandler({\n route: transportEndpoint,\n method: 'post',\n handler: resolver.resolve('../runtime/server/routes/_evlog/ingest.post'),\n })\n }\n\n addServerPlugin(resolver.resolve('../nitro/plugin'))\n\n addPlugin({\n src: resolver.resolve('../runtime/client/plugin'),\n mode: 'client',\n })\n\n addImports([\n {\n name: 'log',\n from: resolver.resolve('../runtime/client/log'),\n },\n {\n name: 'setIdentity',\n from: resolver.resolve('../runtime/client/log'),\n },\n {\n name: 'clearIdentity',\n from: resolver.resolve('../runtime/client/log'),\n },\n {\n name: 'createEvlogError',\n from: resolver.resolve('../error'),\n },\n {\n name: 'parseError',\n from: resolver.resolve('../runtime/utils/parseError'),\n },\n ])\n\n addServerImports([\n {\n name: 'useLogger',\n from: resolver.resolve('../runtime/server/useLogger'),\n },\n {\n name: 'log',\n from: resolver.resolve('../logger'),\n },\n {\n name: 'createEvlogError',\n from: resolver.resolve('../error'),\n },\n ])\n },\n})\n"],"mappings":";;;;;ACmOA,IAAA,iBAAe,iBAAgC;CAC7C,MAAM;EACJ;EACA;EACA,WAAW;EACX,MAAM;EACP;CACD,UAAU,EAAE;CACZ,MAAM,SAAS,MAAM;EACnB,MAAM,WAAW,eAAe,OAAO,KAAK,IAAI;EAEhD,MAAM,mBAAmB,QAAQ,WAAW,WAAW;EACvD,MAAM,oBAAoB,QAAQ,WAAW,YAAY;AAKzD,OAAK,KAAK,iBAAiB,gBAA6B;AACtD,eAAY,eAAe,YAAY,gBAAgB,SAAS,QAAQ,wBAAwB;IAChG;AAEF,OAAK,QAAQ,cAAc,QAAQ;AACnC,OAAK,QAAQ,cAAc,OAAO,QAAQ;GACxC,SAAS,QAAQ,WAAW;GAC5B,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,WAAW;IACT,SAAS;IACT,UAAU;IACX;GACF;AAED,MAAI,iBACF,kBAAiB;GACf,OAAO;GACP,QAAQ;GACR,SAAS,SAAS,QAAQ,8CAA8C;GACzE,CAAC;AAGJ,kBAAgB,SAAS,QAAQ,kBAAkB,CAAC;AAEpD,YAAU;GACR,KAAK,SAAS,QAAQ,2BAA2B;GACjD,MAAM;GACP,CAAC;AAEF,aAAW;GACT;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,wBAAwB;IAChD;GACD;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,wBAAwB;IAChD;GACD;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,wBAAwB;IAChD;GACD;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,WAAW;IACnC;GACD;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,8BAA8B;IACtD;GACF,CAAC;AAEF,mBAAiB;GACf;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,8BAA8B;IACtD;GACD;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,YAAY;IACpC;GACD;IACE,MAAM;IACN,MAAM,SAAS,QAAQ,WAAW;IACnC;GACF,CAAC;;CAEL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseError-BeVEVQBh.d.mts","names":[],"sources":["../src/runtime/utils/parseError.ts"],"mappings":";;;iBAKgB,UAAA,CAAW,KAAA,YAAiB,WAAA"}
|
package/dist/pipeline.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { matchesPattern } from "./utils.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/shared/routes.ts
|
|
4
3
|
function shouldLog(path, include, exclude) {
|
|
5
4
|
if (exclude && exclude.length > 0) {
|
|
@@ -33,7 +32,7 @@ function getServiceForPath(path, routes) {
|
|
|
33
32
|
if (!routes) return void 0;
|
|
34
33
|
for (const [pattern, config] of Object.entries(routes)) if (matchesPattern(path, pattern)) return config.service;
|
|
35
34
|
}
|
|
36
|
-
|
|
37
35
|
//#endregion
|
|
38
36
|
export { shouldLog as n, getServiceForPath as t };
|
|
39
|
-
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=routes-DdmLpEnG.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes-
|
|
1
|
+
{"version":3,"file":"routes-DdmLpEnG.mjs","names":[],"sources":["../src/shared/routes.ts"],"sourcesContent":["import type { RouteConfig } from '../types'\nimport { matchesPattern } from '../utils'\n\nexport function shouldLog(path: string, include?: string[], exclude?: string[]): boolean {\n // Check exclusions first (they take precedence)\n if (exclude && exclude.length > 0) {\n if (exclude.some(pattern => matchesPattern(path, pattern))) {\n return false\n }\n }\n\n // If no include patterns, log everything (that wasn't excluded)\n if (!include || include.length === 0) {\n return true\n }\n\n // Log only if path matches at least one include pattern\n return include.some(pattern => matchesPattern(path, pattern))\n}\n\n/**\n * Find the service name for a given path based on route patterns.\n *\n * When multiple patterns match the same path, the first matching pattern wins\n * based on object iteration order. To ensure predictable behavior, order your\n * route patterns from most specific to most general.\n *\n * @param path - The request path to match\n * @param routes - Route configuration mapping patterns to service names\n * @returns The service name for the matching route, or undefined if no match\n *\n * @example\n * ```ts\n * // Good: specific patterns first, general patterns last\n * routes: {\n * '/api/auth/admin/**': { service: 'admin-service' },\n * '/api/auth/**': { service: 'auth-service' },\n * '/api/**': { service: 'api-service' },\n * }\n * ```\n */\nexport function getServiceForPath(path: string, routes?: Record<string, RouteConfig>): string | undefined {\n if (!routes) return undefined\n\n for (const [pattern, config] of Object.entries(routes)) {\n if (matchesPattern(path, pattern)) {\n return config.service\n }\n }\n\n return undefined\n}\n"],"mappings":";;AAGA,SAAgB,UAAU,MAAc,SAAoB,SAA6B;AAEvF,KAAI,WAAW,QAAQ,SAAS;MAC1B,QAAQ,MAAK,YAAW,eAAe,MAAM,QAAQ,CAAC,CACxD,QAAO;;AAKX,KAAI,CAAC,WAAW,QAAQ,WAAW,EACjC,QAAO;AAIT,QAAO,QAAQ,MAAK,YAAW,eAAe,MAAM,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;AAwB/D,SAAgB,kBAAkB,MAAc,QAA0D;AACxG,KAAI,CAAC,OAAQ,QAAO,KAAA;AAEpB,MAAK,MAAM,CAAC,SAAS,WAAW,OAAO,QAAQ,OAAO,CACpD,KAAI,eAAe,MAAM,QAAQ,CAC/B,QAAO,OAAO"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getConsoleMethod } from "../../utils.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/runtime/client/log.ts
|
|
4
3
|
const isClient = typeof window !== "undefined";
|
|
5
4
|
let clientEnabled = true;
|
|
@@ -90,7 +89,7 @@ const _clientLog = {
|
|
|
90
89
|
warn: createLogMethod("warn"),
|
|
91
90
|
debug: createLogMethod("debug")
|
|
92
91
|
};
|
|
93
|
-
|
|
94
92
|
//#endregion
|
|
95
93
|
export { clearIdentity, initLog, _clientLog as log, setIdentity };
|
|
94
|
+
|
|
96
95
|
//# sourceMappingURL=log.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.mjs","names":[],"sources":["../../../src/runtime/client/log.ts"],"sourcesContent":["import type { Log, LogLevel, TransportConfig } from '../../types'\nimport { getConsoleMethod } from '../../utils'\n\nconst isClient = typeof window !== 'undefined'\n\nlet clientEnabled = true\nlet clientConsole = true\nlet clientPretty = true\nlet clientService = 'client'\nlet transportEnabled = false\nlet transportEndpoint = '/api/_evlog/ingest'\nlet identityContext: Record<string, unknown> = {}\n\nexport function setIdentity(identity: Record<string, unknown>): void {\n identityContext = { ...identity }\n}\n\nexport function clearIdentity(): void {\n identityContext = {}\n}\n\nconst LEVEL_COLORS: Record<string, string> = {\n error: 'color: #ef4444; font-weight: bold',\n warn: 'color: #f59e0b; font-weight: bold',\n info: 'color: #06b6d4; font-weight: bold',\n debug: 'color: #6b7280; font-weight: bold',\n}\n\nexport function initLog(options: { enabled?: boolean, console?: boolean, pretty?: boolean, service?: string, transport?: TransportConfig } = {}): void {\n clientEnabled = typeof options.enabled === 'boolean' ? options.enabled : true\n clientConsole = typeof options.console === 'boolean' ? options.console : true\n clientPretty = typeof options.pretty === 'boolean' ? options.pretty : true\n clientService = options.service ?? 'client'\n transportEnabled = options.transport?.enabled ?? false\n transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest'\n}\n\nasync function sendToServer(event: Record<string, unknown>): Promise<void> {\n if (!transportEnabled) return\n\n try {\n await fetch(transportEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(event),\n keepalive: true,\n credentials: 'same-origin',\n })\n } catch {\n // Silently fail - don't break the app\n }\n}\n\nfunction emitLog(level: LogLevel, event: Record<string, unknown>): void {\n if (!clientEnabled) return\n\n const formatted = {\n timestamp: new Date().toISOString(),\n level,\n service: clientService,\n ...identityContext,\n ...event,\n }\n\n if (clientConsole) {\n const method = getConsoleMethod(level)\n if (clientPretty) {\n const { level: lvl, service, ...rest } = formatted\n console[method](`%c[${service}]%c ${lvl}`, LEVEL_COLORS[lvl] || '', 'color: inherit', rest)\n } else {\n console[method](JSON.stringify(formatted))\n }\n }\n\n sendToServer(formatted)\n}\n\nfunction emitTaggedLog(level: LogLevel, tag: string, message: string): void {\n if (!clientEnabled) return\n if (clientPretty) {\n if (clientConsole) {\n console[getConsoleMethod(level)](`%c[${tag}]%c ${message}`, LEVEL_COLORS[level] || '', 'color: inherit')\n }\n sendToServer({\n timestamp: new Date().toISOString(),\n level,\n service: clientService,\n ...identityContext,\n tag,\n message,\n })\n } else {\n emitLog(level, { tag, message })\n }\n}\n\nfunction createLogMethod(level: LogLevel) {\n return function logMethod(tagOrEvent: string | Record<string, unknown>, message?: string): void {\n if (!(import.meta.client ?? isClient)) {\n return\n }\n\n if (typeof tagOrEvent === 'string' && message !== undefined) {\n emitTaggedLog(level, tagOrEvent, message)\n } else if (typeof tagOrEvent === 'object') {\n emitLog(level, tagOrEvent)\n } else {\n emitTaggedLog(level, 'log', String(tagOrEvent))\n }\n }\n}\n\nconst _clientLog: Log = {\n info: createLogMethod('info'),\n error: createLogMethod('error'),\n warn: createLogMethod('warn'),\n debug: createLogMethod('debug'),\n}\n\nexport { _clientLog as log }\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"log.mjs","names":[],"sources":["../../../src/runtime/client/log.ts"],"sourcesContent":["import type { Log, LogLevel, TransportConfig } from '../../types'\nimport { getConsoleMethod } from '../../utils'\n\nconst isClient = typeof window !== 'undefined'\n\nlet clientEnabled = true\nlet clientConsole = true\nlet clientPretty = true\nlet clientService = 'client'\nlet transportEnabled = false\nlet transportEndpoint = '/api/_evlog/ingest'\nlet identityContext: Record<string, unknown> = {}\n\nexport function setIdentity(identity: Record<string, unknown>): void {\n identityContext = { ...identity }\n}\n\nexport function clearIdentity(): void {\n identityContext = {}\n}\n\nconst LEVEL_COLORS: Record<string, string> = {\n error: 'color: #ef4444; font-weight: bold',\n warn: 'color: #f59e0b; font-weight: bold',\n info: 'color: #06b6d4; font-weight: bold',\n debug: 'color: #6b7280; font-weight: bold',\n}\n\nexport function initLog(options: { enabled?: boolean, console?: boolean, pretty?: boolean, service?: string, transport?: TransportConfig } = {}): void {\n clientEnabled = typeof options.enabled === 'boolean' ? options.enabled : true\n clientConsole = typeof options.console === 'boolean' ? options.console : true\n clientPretty = typeof options.pretty === 'boolean' ? options.pretty : true\n clientService = options.service ?? 'client'\n transportEnabled = options.transport?.enabled ?? false\n transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest'\n}\n\nasync function sendToServer(event: Record<string, unknown>): Promise<void> {\n if (!transportEnabled) return\n\n try {\n await fetch(transportEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(event),\n keepalive: true,\n credentials: 'same-origin',\n })\n } catch {\n // Silently fail - don't break the app\n }\n}\n\nfunction emitLog(level: LogLevel, event: Record<string, unknown>): void {\n if (!clientEnabled) return\n\n const formatted = {\n timestamp: new Date().toISOString(),\n level,\n service: clientService,\n ...identityContext,\n ...event,\n }\n\n if (clientConsole) {\n const method = getConsoleMethod(level)\n if (clientPretty) {\n const { level: lvl, service, ...rest } = formatted\n console[method](`%c[${service}]%c ${lvl}`, LEVEL_COLORS[lvl] || '', 'color: inherit', rest)\n } else {\n console[method](JSON.stringify(formatted))\n }\n }\n\n sendToServer(formatted)\n}\n\nfunction emitTaggedLog(level: LogLevel, tag: string, message: string): void {\n if (!clientEnabled) return\n if (clientPretty) {\n if (clientConsole) {\n console[getConsoleMethod(level)](`%c[${tag}]%c ${message}`, LEVEL_COLORS[level] || '', 'color: inherit')\n }\n sendToServer({\n timestamp: new Date().toISOString(),\n level,\n service: clientService,\n ...identityContext,\n tag,\n message,\n })\n } else {\n emitLog(level, { tag, message })\n }\n}\n\nfunction createLogMethod(level: LogLevel) {\n return function logMethod(tagOrEvent: string | Record<string, unknown>, message?: string): void {\n if (!(import.meta.client ?? isClient)) {\n return\n }\n\n if (typeof tagOrEvent === 'string' && message !== undefined) {\n emitTaggedLog(level, tagOrEvent, message)\n } else if (typeof tagOrEvent === 'object') {\n emitLog(level, tagOrEvent)\n } else {\n emitTaggedLog(level, 'log', String(tagOrEvent))\n }\n }\n}\n\nconst _clientLog: Log = {\n info: createLogMethod('info'),\n error: createLogMethod('error'),\n warn: createLogMethod('warn'),\n debug: createLogMethod('debug'),\n}\n\nexport { _clientLog as log }\n"],"mappings":";;AAGA,MAAM,WAAW,OAAO,WAAW;AAEnC,IAAI,gBAAgB;AACpB,IAAI,gBAAgB;AACpB,IAAI,eAAe;AACnB,IAAI,gBAAgB;AACpB,IAAI,mBAAmB;AACvB,IAAI,oBAAoB;AACxB,IAAI,kBAA2C,EAAE;AAEjD,SAAgB,YAAY,UAAyC;AACnE,mBAAkB,EAAE,GAAG,UAAU;;AAGnC,SAAgB,gBAAsB;AACpC,mBAAkB,EAAE;;AAGtB,MAAM,eAAuC;CAC3C,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAED,SAAgB,QAAQ,UAAqH,EAAE,EAAQ;AACrJ,iBAAgB,OAAO,QAAQ,YAAY,YAAY,QAAQ,UAAU;AACzE,iBAAgB,OAAO,QAAQ,YAAY,YAAY,QAAQ,UAAU;AACzE,gBAAe,OAAO,QAAQ,WAAW,YAAY,QAAQ,SAAS;AACtE,iBAAgB,QAAQ,WAAW;AACnC,oBAAmB,QAAQ,WAAW,WAAW;AACjD,qBAAoB,QAAQ,WAAW,YAAY;;AAGrD,eAAe,aAAa,OAA+C;AACzE,KAAI,CAAC,iBAAkB;AAEvB,KAAI;AACF,QAAM,MAAM,mBAAmB;GAC7B,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU,MAAM;GAC3B,WAAW;GACX,aAAa;GACd,CAAC;SACI;;AAKV,SAAS,QAAQ,OAAiB,OAAsC;AACtE,KAAI,CAAC,cAAe;CAEpB,MAAM,YAAY;EAChB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;EACA,SAAS;EACT,GAAG;EACH,GAAG;EACJ;AAED,KAAI,eAAe;EACjB,MAAM,SAAS,iBAAiB,MAAM;AACtC,MAAI,cAAc;GAChB,MAAM,EAAE,OAAO,KAAK,SAAS,GAAG,SAAS;AACzC,WAAQ,QAAQ,MAAM,QAAQ,MAAM,OAAO,aAAa,QAAQ,IAAI,kBAAkB,KAAK;QAE3F,SAAQ,QAAQ,KAAK,UAAU,UAAU,CAAC;;AAI9C,cAAa,UAAU;;AAGzB,SAAS,cAAc,OAAiB,KAAa,SAAuB;AAC1E,KAAI,CAAC,cAAe;AACpB,KAAI,cAAc;AAChB,MAAI,cACF,SAAQ,iBAAiB,MAAM,EAAE,MAAM,IAAI,MAAM,WAAW,aAAa,UAAU,IAAI,iBAAiB;AAE1G,eAAa;GACX,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACA,SAAS;GACT,GAAG;GACH;GACA;GACD,CAAC;OAEF,SAAQ,OAAO;EAAE;EAAK;EAAS,CAAC;;AAIpC,SAAS,gBAAgB,OAAiB;AACxC,QAAO,SAAS,UAAU,YAA8C,SAAwB;AAC9F,MAAI,EAAE,OAAO,KAAK,UAAU,UAC1B;AAGF,MAAI,OAAO,eAAe,YAAY,YAAY,KAAA,EAChD,eAAc,OAAO,YAAY,QAAQ;WAChC,OAAO,eAAe,SAC/B,SAAQ,OAAO,WAAW;MAE1B,eAAc,OAAO,OAAO,OAAO,WAAW,CAAC;;;AAKrD,MAAM,aAAkB;CACtB,MAAM,gBAAgB,OAAO;CAC7B,OAAO,gBAAgB,QAAQ;CAC/B,MAAM,gBAAgB,OAAO;CAC7B,OAAO,gBAAgB,QAAQ;CAChC"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { initLog } from "./log.mjs";
|
|
2
2
|
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
3
|
-
|
|
4
3
|
//#region src/runtime/client/plugin.ts
|
|
5
4
|
var plugin_default = defineNuxtPlugin(() => {
|
|
6
5
|
const evlogConfig = useRuntimeConfig().public?.evlog;
|
|
@@ -12,7 +11,7 @@ var plugin_default = defineNuxtPlugin(() => {
|
|
|
12
11
|
transport: evlogConfig?.transport
|
|
13
12
|
});
|
|
14
13
|
});
|
|
15
|
-
|
|
16
14
|
//#endregion
|
|
17
15
|
export { plugin_default as default };
|
|
16
|
+
|
|
18
17
|
//# sourceMappingURL=plugin.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/runtime/client/plugin.ts"],"sourcesContent":["import type { TransportConfig } from '../../types'\nimport { initLog } from './log'\nimport { defineNuxtPlugin, useRuntimeConfig } from '#app'\n\ninterface EvlogPublicConfig {\n enabled?: boolean\n console?: boolean\n pretty?: boolean\n transport?: TransportConfig\n}\n\nexport default defineNuxtPlugin(() => {\n const config = useRuntimeConfig()\n const evlogConfig = config.public?.evlog as EvlogPublicConfig | undefined\n\n initLog({\n enabled: evlogConfig?.enabled,\n console: evlogConfig?.console,\n pretty: evlogConfig?.pretty ?? import.meta.dev,\n service: 'client',\n transport: evlogConfig?.transport,\n })\n})\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../../../src/runtime/client/plugin.ts"],"sourcesContent":["import type { TransportConfig } from '../../types'\nimport { initLog } from './log'\nimport { defineNuxtPlugin, useRuntimeConfig } from '#app'\n\ninterface EvlogPublicConfig {\n enabled?: boolean\n console?: boolean\n pretty?: boolean\n transport?: TransportConfig\n}\n\nexport default defineNuxtPlugin(() => {\n const config = useRuntimeConfig()\n const evlogConfig = config.public?.evlog as EvlogPublicConfig | undefined\n\n initLog({\n enabled: evlogConfig?.enabled,\n console: evlogConfig?.console,\n pretty: evlogConfig?.pretty ?? import.meta.dev,\n service: 'client',\n transport: evlogConfig?.transport,\n })\n})\n"],"mappings":";;;AAWA,IAAA,iBAAe,uBAAuB;CAEpC,MAAM,cADS,kBAAkB,CACN,QAAQ;AAEnC,SAAQ;EACN,SAAS,aAAa;EACtB,SAAS,aAAa;EACtB,QAAQ,aAAa,UAAU,OAAO,KAAK;EAC3C,SAAS;EACT,WAAW,aAAa;EACzB,CAAC;EACF"}
|