From fedb54c9d8ec91a94de572352e05f7fa256295aa Mon Sep 17 00:00:00 2001 From: Roman Sirokov Date: Mon, 23 Mar 2026 21:19:05 +0100 Subject: [PATCH 1/4] feat(evlog): allow to configure request credentials in browser drain --- apps/docs/content/4.adapters/11.browser.md | 3 ++- packages/evlog/src/browser.ts | 6 ++++-- packages/evlog/test/browser.test.ts | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/docs/content/4.adapters/11.browser.md b/apps/docs/content/4.adapters/11.browser.md index f985d57a..ecce318b 100644 --- a/apps/docs/content/4.adapters/11.browser.md +++ b/apps/docs/content/4.adapters/11.browser.md @@ -90,6 +90,7 @@ const drain = pipeline(transport) | `headers` | - | Custom headers sent with each `fetch` request (e.g. `Authorization`, `X-API-Key`) | | `timeout` | `5000` | Request timeout in milliseconds | | `useBeacon` | `true` | Use `sendBeacon` when the page is hidden | +| `credentials` | `'same-origin'` | Fetch credentials mode (`'omit'`, `'same-origin'`, `'include'`). Set to `'include'` for cross-origin endpoints | ### `BrowserLogDrainOptions` @@ -123,7 +124,7 @@ const drain = createBrowserLogDrain({ ``` ::callout{icon="i-lucide-shield-alert" color="warning"} -`headers` are applied to `fetch` requests only. The `sendBeacon` API does not support custom headers, so when the page is hidden and `sendBeacon` is used, headers are not sent. If your endpoint requires authentication, consider validating via a session cookie (`credentials: 'same-origin'` is set by default) or disable `sendBeacon` with `useBeacon: false`. +`headers` are applied to `fetch` requests only. The `sendBeacon` API does not support custom headers, so when the page is hidden and `sendBeacon` is used, headers are not sent. If your endpoint requires authentication, consider validating via a session cookie (set `credentials: 'include'` for cross-origin endpoints, defaults to `'same-origin'`) or disable `sendBeacon` with `useBeacon: false`. :: ## Server Endpoint diff --git a/packages/evlog/src/browser.ts b/packages/evlog/src/browser.ts index 64c949a4..2a88450c 100644 --- a/packages/evlog/src/browser.ts +++ b/packages/evlog/src/browser.ts @@ -11,6 +11,8 @@ export interface BrowserDrainConfig { timeout?: number /** Use sendBeacon when the page is hidden. @default true */ useBeacon?: boolean + /** Fetch credentials mode. @default 'same-origin' */ + credentials?: RequestCredentials } export interface BrowserLogDrainOptions { @@ -39,7 +41,7 @@ export interface BrowserLogDrainOptions { * ``` */ export function createBrowserDrain(config: BrowserDrainConfig): (batch: DrainContext[]) => Promise { - const { endpoint, headers: customHeaders, timeout = 5000, useBeacon = true } = config + const { endpoint, headers: customHeaders, timeout = 5000, useBeacon = true, credentials = 'same-origin' } = config return async (batch: DrainContext[]): Promise => { if (batch.length === 0) return @@ -70,7 +72,7 @@ export function createBrowserDrain(config: BrowserDrainConfig): (batch: DrainCon body, signal: controller.signal, keepalive: true, - credentials: 'same-origin', + credentials, }) if (!response.ok) { diff --git a/packages/evlog/test/browser.test.ts b/packages/evlog/test/browser.test.ts index f4032233..368ac80b 100644 --- a/packages/evlog/test/browser.test.ts +++ b/packages/evlog/test/browser.test.ts @@ -58,6 +58,15 @@ describe('createBrowserDrain', () => { }) }) + it('uses custom credentials mode', async () => { + const drain = createBrowserDrain({ endpoint: '/api/logs', credentials: 'include' }) + + await drain([createTestContext(1)]) + + const [, options] = vi.mocked(fetch).mock.calls[0]! + expect(options?.credentials).toBe('include') + }) + it('skips empty batches', async () => { const drain = createBrowserDrain({ endpoint: '/api/logs' }) From 84926cea7a163594fec83c2685ae3b3e453b9161 Mon Sep 17 00:00:00 2001 From: Roman Sirokov Date: Mon, 23 Mar 2026 21:25:44 +0100 Subject: [PATCH 2/4] feat(evlog): allow to configure request credentials in client transport --- .../content/3.core-concepts/6.client-logging.md | 3 ++- .../content/3.core-concepts/7.configuration.md | 1 + packages/evlog/src/runtime/client/log.ts | 4 +++- packages/evlog/src/types.ts | 6 ++++++ packages/evlog/test/client-console.test.ts | 14 ++++++++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/3.core-concepts/6.client-logging.md b/apps/docs/content/3.core-concepts/6.client-logging.md index 309fb898..87939b4c 100644 --- a/apps/docs/content/3.core-concepts/6.client-logging.md +++ b/apps/docs/content/3.core-concepts/6.client-logging.md @@ -128,7 +128,8 @@ initLog({ service: 'web', transport: { enabled: true, - endpoint: '/api/_evlog/ingest', + endpoint: '/api/_evlog/ingest', // default + credentials: 'same-origin', // default }, }) ``` diff --git a/apps/docs/content/3.core-concepts/7.configuration.md b/apps/docs/content/3.core-concepts/7.configuration.md index da182add..b58942d0 100644 --- a/apps/docs/content/3.core-concepts/7.configuration.md +++ b/apps/docs/content/3.core-concepts/7.configuration.md @@ -147,6 +147,7 @@ The Nuxt module accepts all global options and middleware options in `nuxt.confi | `console` | `boolean` | `true` | Enable/disable browser console output (client-side only) | | `transport.enabled` | `boolean` | `false` | Send client logs to the server via API endpoint | | `transport.endpoint` | `string` | `'/api/_evlog/ingest'` | Custom transport endpoint | +| `transport.credentials` | `RequestCredentials` | `'same-origin'` | Fetch credentials mode (`'include'` for cross-origin endpoints) | See the full [Nuxt configuration](/frameworks/nuxt#configuration). diff --git a/packages/evlog/src/runtime/client/log.ts b/packages/evlog/src/runtime/client/log.ts index d4034ee3..8aa1186e 100644 --- a/packages/evlog/src/runtime/client/log.ts +++ b/packages/evlog/src/runtime/client/log.ts @@ -9,6 +9,7 @@ let clientPretty = true let clientService = 'client' let transportEnabled = false let transportEndpoint = '/api/_evlog/ingest' +let transportCredentials: RequestCredentials = 'same-origin' let identityContext: Record = {} export function setIdentity(identity: Record): void { @@ -27,6 +28,7 @@ export function initLog(options: { enabled?: boolean, console?: boolean, pretty? clientService = options.service ?? 'client' transportEnabled = options.transport?.enabled ?? false transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest' + transportCredentials = options.transport?.credentials ?? 'same-origin' } async function sendToServer(event: Record): Promise { @@ -38,7 +40,7 @@ async function sendToServer(event: Record): Promise { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(event), keepalive: true, - credentials: 'same-origin', + credentials: transportCredentials, }) } catch { // Silently fail - don't break the app diff --git a/packages/evlog/src/types.ts b/packages/evlog/src/types.ts index 26093da9..f7b2e153 100644 --- a/packages/evlog/src/types.ts +++ b/packages/evlog/src/types.ts @@ -73,6 +73,12 @@ export interface TransportConfig { * @default '/api/_evlog/ingest' */ endpoint?: string + + /** + * Fetch credentials mode + * @default 'same-origin' + */ + credentials?: RequestCredentials } /** diff --git a/packages/evlog/test/client-console.test.ts b/packages/evlog/test/client-console.test.ts index e3e739d7..87e07f66 100644 --- a/packages/evlog/test/client-console.test.ts +++ b/packages/evlog/test/client-console.test.ts @@ -54,6 +54,20 @@ describe('client console option', () => { expect(fetchSpy).toHaveBeenCalledTimes(1) }) + it('uses custom credentials mode for transport', () => { + initLog({ + enabled: true, + console: false, + pretty: false, + transport: { enabled: true, endpoint: '/api/_evlog/ingest', credentials: 'include' }, + }) + + log.info({ action: 'test' }) + + const [, options] = fetchSpy.mock.calls[0]! + expect(options?.credentials).toBe('include') + }) + it('suppresses pretty console output when console is false', () => { initLog({ enabled: true, console: false, pretty: true }) From 6c860e1110e3d04ba8645ef0904a83c00b359a09 Mon Sep 17 00:00:00 2001 From: Hugo Richard Date: Tue, 24 Mar 2026 09:25:26 +0000 Subject: [PATCH 3/4] up --- apps/docs/content/3.core-concepts/6.client-logging.md | 5 ++--- packages/evlog/src/nuxt/module.ts | 7 +++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/docs/content/3.core-concepts/6.client-logging.md b/apps/docs/content/3.core-concepts/6.client-logging.md index 87939b4c..e0644b79 100644 --- a/apps/docs/content/3.core-concepts/6.client-logging.md +++ b/apps/docs/content/3.core-concepts/6.client-logging.md @@ -128,8 +128,7 @@ initLog({ service: 'web', transport: { enabled: true, - endpoint: '/api/_evlog/ingest', // default - credentials: 'same-origin', // default + endpoint: '/api/_evlog/ingest', }, }) ``` @@ -151,7 +150,7 @@ export default defineNuxtPlugin(() => { service: 'web', transport: { enabled: true, - endpoint: '/api/_evlog/ingest', // default + endpoint: '/api/_evlog/ingest', }, }) }) diff --git a/packages/evlog/src/nuxt/module.ts b/packages/evlog/src/nuxt/module.ts index 47e25f8a..0d2555ad 100644 --- a/packages/evlog/src/nuxt/module.ts +++ b/packages/evlog/src/nuxt/module.ts @@ -136,8 +136,9 @@ export interface ModuleOptions { * @example * ```ts * transport: { - * enabled: true, // Send logs to server API - * endpoint: '/api/_evlog/ingest' // Custom endpoint + * enabled: true, // send client logs to server via API endpoint + * endpoint: '/api/_evlog/ingest', // default endpoint (or custom endpoint) + * credentials: 'include', // optional: cross-origin ingest * } * ``` */ @@ -264,6 +265,7 @@ export default defineNuxtModule({ const transportEnabled = options.transport?.enabled ?? false const transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest' + const transportCredentials = options.transport?.credentials ?? 'same-origin' // Register custom error handler for proper EvlogError serialization // Only set if not already configured to avoid overwriting user's custom handler @@ -280,6 +282,7 @@ export default defineNuxtModule({ transport: { enabled: transportEnabled, endpoint: transportEndpoint, + credentials: transportCredentials, }, } From c8c83cbd4cd18bfd416cd64b8a85d255fc9a97e0 Mon Sep 17 00:00:00 2001 From: Hugo Richard Date: Tue, 24 Mar 2026 09:26:35 +0000 Subject: [PATCH 4/4] add changeset --- .changeset/transport-credentials.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/transport-credentials.md diff --git a/.changeset/transport-credentials.md b/.changeset/transport-credentials.md new file mode 100644 index 00000000..41a2b68d --- /dev/null +++ b/.changeset/transport-credentials.md @@ -0,0 +1,5 @@ +--- +"evlog": minor +--- + +Add configurable `credentials` (`RequestCredentials`, default `same-origin`) for the client log transport and browser drain `fetch` calls. The Nuxt module forwards `transport.credentials` into `runtimeConfig.public.evlog` so client `initLog()` receives it.