evlog 2.16.0 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.mjs +4 -2
- package/dist/adapters/axiom.mjs.map +1 -1
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.mjs +4 -2
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/datadog.d.mts +1 -1
- package/dist/adapters/datadog.mjs +4 -2
- package/dist/adapters/datadog.mjs.map +1 -1
- package/dist/adapters/fs.d.mts +64 -2
- package/dist/adapters/fs.d.mts.map +1 -1
- package/dist/adapters/fs.mjs +222 -3
- package/dist/adapters/fs.mjs.map +1 -1
- package/dist/adapters/hyperdx.d.mts +1 -1
- package/dist/adapters/hyperdx.mjs +1 -1
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.mjs +6 -4
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.mjs +4 -2
- package/dist/adapters/posthog.mjs.map +1 -1
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.mjs +5 -3
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/{audit-X1uUukm3.d.mts → audit-CC8nfazi.d.mts} +56 -5
- package/dist/{audit-X1uUukm3.d.mts.map → audit-CC8nfazi.d.mts.map} +1 -1
- package/dist/better-auth/index.d.mts +14 -7
- package/dist/better-auth/index.d.mts.map +1 -1
- package/dist/better-auth/index.mjs +11 -1
- package/dist/better-auth/index.mjs.map +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/{define-CuXOqecD.d.mts → define-MSdhzmXn.d.mts} +3 -3
- package/dist/{define-CuXOqecD.d.mts.map → define-MSdhzmXn.d.mts.map} +1 -1
- package/dist/{dist-BIlS38vi.mjs → dist-H3GIh-KK.mjs} +1 -1
- package/dist/{dist-BIlS38vi.mjs.map → dist-H3GIh-KK.mjs.map} +1 -1
- package/dist/{drain-ByWUeOQC.mjs → drain-X7_5szSI.mjs} +6 -49
- package/dist/drain-X7_5szSI.mjs.map +1 -0
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.mjs +2 -2
- package/dist/{enricher-DYTr9I16.d.mts → enricher-DxgML6IC.d.mts} +2 -2
- package/dist/{enricher-DYTr9I16.d.mts.map → enricher-DxgML6IC.d.mts.map} +1 -1
- package/dist/{enricher-Dy06T17G.mjs → enricher-N0erZS87.mjs} +2 -2
- package/dist/{enricher-Dy06T17G.mjs.map → enricher-N0erZS87.mjs.map} +1 -1
- package/dist/enrichers.d.mts +2 -2
- package/dist/enrichers.mjs +1 -1
- package/dist/{error-Cpc7RVz6.d.mts → error-CpbbtyXL.d.mts} +2 -2
- package/dist/{error-Cpc7RVz6.d.mts.map → error-CpbbtyXL.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-prnQ3kES.d.mts → errors-DySW1F9_.d.mts} +2 -2
- package/dist/{errors-prnQ3kES.d.mts.map → errors-DySW1F9_.d.mts.map} +1 -1
- package/dist/{event-DcHmEm3O.mjs → event-1BMl7o0k.mjs} +1 -1
- package/dist/{event-DcHmEm3O.mjs.map → event-1BMl7o0k.mjs.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.d.mts.map +1 -1
- package/dist/express/index.mjs +5 -6
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.mjs +2 -2
- package/dist/{fork-DPN8aL8O.mjs → fork-8u_zFOJq.mjs} +2 -2
- package/dist/{fork-DPN8aL8O.mjs.map → fork-8u_zFOJq.mjs.map} +1 -1
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +1 -1
- package/dist/http-6umVAKDW.mjs +82 -0
- package/dist/http-6umVAKDW.mjs.map +1 -0
- package/dist/http.d.mts +1 -1
- package/dist/http.mjs +1 -0
- package/dist/http.mjs.map +1 -1
- package/dist/index-o1_z4phv.d.mts +213 -0
- package/dist/index-o1_z4phv.d.mts.map +1 -0
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +209 -1
- package/dist/index.mjs.map +1 -0
- package/dist/{integration-DSZPbI9N.mjs → integration-DTZtjSqh.mjs} +2 -2
- package/dist/{integration-DSZPbI9N.mjs.map → integration-DTZtjSqh.mjs.map} +1 -1
- package/dist/{logger-U8lgdc9x.d.mts → logger-DntcxxHg.d.mts} +2 -2
- package/dist/{logger-U8lgdc9x.d.mts.map → logger-DntcxxHg.d.mts.map} +1 -1
- package/dist/logger.d.mts +1 -1
- package/dist/{middleware-CAQHJRN1.d.mts → middleware-U-lIAzHg.d.mts} +2 -2
- package/dist/{middleware-CAQHJRN1.d.mts.map → middleware-U-lIAzHg.d.mts.map} +1 -1
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.d.mts.map +1 -1
- package/dist/nestjs/index.mjs +4 -5
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +1 -1
- package/dist/next/index.d.mts +4 -4
- package/dist/next/index.mjs +2 -2
- package/dist/next/instrumentation.d.mts +1 -1
- package/dist/next/stream.d.mts +29 -0
- package/dist/next/stream.d.mts.map +1 -0
- package/dist/next/stream.mjs +78 -0
- package/dist/next/stream.mjs.map +1 -0
- package/dist/nitro/errorHandler.mjs +1 -1
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.mjs +11 -2
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +2 -2
- package/dist/nitro/v3/index.d.mts +2 -2
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.mjs +3 -3
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-DavLelNz.mjs → nitro-DErMq_Zj.mjs} +1 -1
- package/dist/{nitro-DavLelNz.mjs.map → nitro-DErMq_Zj.mjs.map} +1 -1
- package/dist/{nitro-C6Bd682U.d.mts → nitro-oZre8ab3.d.mts} +2 -2
- package/dist/{nitro-C6Bd682U.d.mts.map → nitro-oZre8ab3.d.mts.map} +1 -1
- package/dist/{nitroConfigBridge-aZ1e5upQ.mjs → nitroConfigBridge-DKk7eOn-.mjs} +1 -1
- package/dist/{nitroConfigBridge-aZ1e5upQ.mjs.map → nitroConfigBridge-DKk7eOn-.mjs.map} +1 -1
- package/dist/nodeResponse-BkkionWl.mjs +42 -0
- package/dist/nodeResponse-BkkionWl.mjs.map +1 -0
- package/dist/nuxt/module.d.mts +28 -1
- package/dist/nuxt/module.d.mts.map +1 -1
- package/dist/nuxt/module.mjs +11 -4
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/package-v_MmOZeA.mjs +7 -0
- package/dist/package-v_MmOZeA.mjs.map +1 -0
- package/dist/{parseError-B-dKF6Fd.d.mts → parseError-yVZ58wIK.d.mts} +2 -2
- package/dist/parseError-yVZ58wIK.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +2 -2
- package/dist/react-router/index.mjs +2 -2
- package/dist/{routes-B48wm7Pb.mjs → routes-CnIgYWf8.mjs} +1 -1
- package/dist/{routes-B48wm7Pb.mjs.map → routes-CnIgYWf8.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/server/routes/_evlog/stream-info.get.d.mts +18 -0
- package/dist/runtime/server/routes/_evlog/stream-info.get.d.mts.map +1 -0
- package/dist/runtime/server/routes/_evlog/stream-info.get.mjs +28 -0
- package/dist/runtime/server/routes/_evlog/stream-info.get.mjs.map +1 -0
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/{severity-BYWZ96Sb.mjs → severity-R5Egq3qz.mjs} +1 -1
- package/dist/{severity-BYWZ96Sb.mjs.map → severity-R5Egq3qz.mjs.map} +1 -1
- package/dist/{storage-BT-3fT1-.mjs → storage-Dwinmg8P.mjs} +1 -1
- package/dist/{storage-BT-3fT1-.mjs.map → storage-Dwinmg8P.mjs.map} +1 -1
- package/dist/stream.d.mts +185 -0
- package/dist/stream.d.mts.map +1 -0
- package/dist/stream.mjs +374 -0
- package/dist/stream.mjs.map +1 -0
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +3 -3
- package/dist/toolkit.d.mts +42 -7
- package/dist/toolkit.d.mts.map +1 -1
- package/dist/toolkit.mjs +10 -9
- package/dist/types.d.mts +2 -2
- package/dist/{useLogger-CoNgTjp5.d.mts → useLogger-BsPL4AQm.d.mts} +2 -2
- package/dist/{useLogger-CoNgTjp5.d.mts.map → useLogger-BsPL4AQm.d.mts.map} +1 -1
- package/dist/{utils-Db4qhBWn.d.mts → utils-DLCeShxL.d.mts} +2 -2
- package/dist/{utils-Db4qhBWn.d.mts.map → utils-DLCeShxL.d.mts.map} +1 -1
- package/dist/utils.d.mts +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/workers.d.mts +1 -1
- package/package.json +17 -1
- package/dist/drain-ByWUeOQC.mjs.map +0 -1
- package/dist/parseError-B-dKF6Fd.d.mts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-
|
|
1
|
+
{"version":3,"file":"storage-Dwinmg8P.mjs","names":[],"sources":["../src/shared/storage.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport type { RequestLogger } from '../types'\n\n/**\n * Create a request-scoped `AsyncLocalStorage` and matching `useLogger`\n * accessor. Every framework that exposes `useLogger()` (Express, Fastify,\n * NestJS, SvelteKit) calls this once at module level.\n *\n * @param contextHint - Appended to the error message when `useLogger()` is\n * called outside of a request, e.g. `\"middleware context. Make sure\n * app.use(evlog()) is registered before your routes.\"`.\n */\nexport function createLoggerStorage(contextHint: string) {\n const storage = new AsyncLocalStorage<RequestLogger>()\n\n function useLogger<T extends object = Record<string, unknown>>(): RequestLogger<T> {\n const logger = storage.getStore()\n if (!logger) {\n throw new Error(\n `[evlog] useLogger() was called outside of an evlog ${contextHint}`,\n )\n }\n return logger as RequestLogger<T>\n }\n\n return { storage, useLogger }\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,oBAAoB,aAAqB;CACvD,MAAM,UAAU,IAAI,mBAAkC;CAEtD,SAAS,YAA0E;EACjF,MAAM,SAAS,QAAQ,UAAU;AACjC,MAAI,CAAC,OACH,OAAM,IAAI,MACR,sDAAsD,cACvD;AAEH,SAAO;;AAGT,QAAO;EAAE;EAAS;EAAW"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { I as DrainContext, ct as WideEvent } from "./audit-CC8nfazi.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/stream.d.ts
|
|
4
|
+
/** Configuration accepted by {@link createStreamDrain}. */
|
|
5
|
+
interface StreamDrainOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Number of recent events kept in the ring buffer and replayed to new
|
|
8
|
+
* subscribers via {@link StreamDrain.recent}. Set to `0` to disable
|
|
9
|
+
* replay.
|
|
10
|
+
* @default 500
|
|
11
|
+
*/
|
|
12
|
+
buffer?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Optional predicate run on each drained event — return `false` to skip
|
|
15
|
+
* the event entirely (it is neither buffered nor delivered).
|
|
16
|
+
*/
|
|
17
|
+
filter?: (event: WideEvent) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Per-subscriber queue size for the {@link StreamDrain.events} async
|
|
20
|
+
* iterator. When the consumer falls behind by more than this many events,
|
|
21
|
+
* the oldest queued events are dropped — the drain is never blocked.
|
|
22
|
+
* @default 1000
|
|
23
|
+
*/
|
|
24
|
+
perSubscriberQueue?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Live drain that exposes its events to in-process subscribers. */
|
|
27
|
+
interface StreamDrain {
|
|
28
|
+
/**
|
|
29
|
+
* Drain callback. Pass to `nitroApp.hooks.hook('evlog:drain', stream.drain)`
|
|
30
|
+
* or to a plugin via `drainPlugin('stream', stream.drain)`.
|
|
31
|
+
*/
|
|
32
|
+
drain: (ctx: DrainContext | DrainContext[]) => Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Register a synchronous listener. Errors thrown by the listener are
|
|
35
|
+
* caught and logged — they never affect other subscribers or the drain.
|
|
36
|
+
* @returns Unsubscribe function.
|
|
37
|
+
*/
|
|
38
|
+
subscribe: (listener: (event: WideEvent) => void) => () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Async iterator over live events. Each call returns a fresh iterator —
|
|
41
|
+
* past events from the ring buffer are NOT replayed. Combine with
|
|
42
|
+
* {@link StreamDrain.recent} to seed history.
|
|
43
|
+
*
|
|
44
|
+
* Calling `return()` (e.g. via `break`) cleanly unsubscribes.
|
|
45
|
+
*/
|
|
46
|
+
events: () => AsyncIterableIterator<WideEvent>;
|
|
47
|
+
/**
|
|
48
|
+
* Snapshot of buffered events (oldest first, most recent last). Useful
|
|
49
|
+
* to seed a new connection / UI panel before switching to live updates.
|
|
50
|
+
*/
|
|
51
|
+
recent: () => readonly WideEvent[];
|
|
52
|
+
/** Number of currently active subscribers (sync + async iterators). */
|
|
53
|
+
readonly subscriberCount: number;
|
|
54
|
+
/** Number of events dropped because the buffer was disabled or wrapped. */
|
|
55
|
+
readonly droppedCount: number;
|
|
56
|
+
/**
|
|
57
|
+
* Disconnect all subscribers and end any pending async iterators. The
|
|
58
|
+
* stream itself remains usable — new subscriptions still work.
|
|
59
|
+
*/
|
|
60
|
+
close: () => void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create an in-process {@link StreamDrain}. Multiple stream drains can
|
|
64
|
+
* coexist in the same process — they are fully independent.
|
|
65
|
+
*/
|
|
66
|
+
declare function createStreamDrain(options?: StreamDrainOptions): StreamDrain;
|
|
67
|
+
/**
|
|
68
|
+
* Lazily create / return the process-wide default {@link StreamDrain}.
|
|
69
|
+
*
|
|
70
|
+
* Used by built-in framework integrations (Nuxt SSE bridge, devtools panel)
|
|
71
|
+
* so they share a single buffer. Custom code can subscribe to this stream
|
|
72
|
+
* to observe everything draining through evlog without registering a new
|
|
73
|
+
* drain.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { getDefaultStream } from 'evlog/stream'
|
|
78
|
+
*
|
|
79
|
+
* getDefaultStream().subscribe((event) => console.log(event.action))
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare function getDefaultStream(options?: StreamDrainOptions): StreamDrain;
|
|
83
|
+
/**
|
|
84
|
+
* Replace or clear the default stream. Pass `null` to reset (mostly useful
|
|
85
|
+
* in tests).
|
|
86
|
+
*/
|
|
87
|
+
declare function setDefaultStream(stream: StreamDrain | null): void;
|
|
88
|
+
/** Configuration accepted by {@link startStreamServer}. */
|
|
89
|
+
interface StreamServerOptions {
|
|
90
|
+
/**
|
|
91
|
+
* TCP port to listen on. `0` (default) lets the OS pick an ephemeral port —
|
|
92
|
+
* the actual port is exposed on the returned `port` / `url` and printed in
|
|
93
|
+
* the startup banner.
|
|
94
|
+
* @default 0
|
|
95
|
+
*/
|
|
96
|
+
port?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Listen address. Default `127.0.0.1` — local-only, never exposed to the
|
|
99
|
+
* LAN. Override to `0.0.0.0` only if you understand the security
|
|
100
|
+
* implications.
|
|
101
|
+
* @default '127.0.0.1'
|
|
102
|
+
*/
|
|
103
|
+
host?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Bearer token. When set, the server requires `Authorization: Bearer
|
|
106
|
+
* <token>` on every request and 401s otherwise. When unset, only
|
|
107
|
+
* connections from `127.0.0.1` (and equivalent local hosts) are
|
|
108
|
+
* accepted — same-origin policy applies on non-local hosts.
|
|
109
|
+
*/
|
|
110
|
+
token?: string;
|
|
111
|
+
/**
|
|
112
|
+
* Heartbeat interval (ms) sent as `event: ping` to keep the connection
|
|
113
|
+
* alive through proxies and detect disconnects.
|
|
114
|
+
* @default 15000
|
|
115
|
+
*/
|
|
116
|
+
heartbeatMs?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Ring buffer size kept on the underlying default stream — replayed for
|
|
119
|
+
* late-joining clients via `?since=<iso>`.
|
|
120
|
+
* @default 500
|
|
121
|
+
*/
|
|
122
|
+
buffer?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Print `[evlog] Stream → http://...` at startup.
|
|
125
|
+
* @default true
|
|
126
|
+
*/
|
|
127
|
+
banner?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Directory where `stream.url` is written so external tools can discover
|
|
130
|
+
* the server. Set to `false` to disable.
|
|
131
|
+
* @default '.evlog'
|
|
132
|
+
*/
|
|
133
|
+
urlFileDir?: string | false;
|
|
134
|
+
}
|
|
135
|
+
/** Return value of {@link startStreamServer}. */
|
|
136
|
+
interface StreamServer {
|
|
137
|
+
/** Listening URL, e.g. `http://127.0.0.1:51203`. */
|
|
138
|
+
readonly url: string;
|
|
139
|
+
/** Resolved TCP port (useful when `port: 0`). */
|
|
140
|
+
readonly port: number;
|
|
141
|
+
/**
|
|
142
|
+
* Drain function — register on `evlog:drain` (Nitro / Nuxt), pass to
|
|
143
|
+
* `initLogger({ drain })` (Next / standalone), or call directly.
|
|
144
|
+
*
|
|
145
|
+
* Equivalent to the underlying default stream's drain — events flow
|
|
146
|
+
* through and reach every connected SSE client.
|
|
147
|
+
*/
|
|
148
|
+
readonly drain: (ctx: DrainContext | DrainContext[]) => Promise<void>;
|
|
149
|
+
/** The underlying in-process {@link StreamDrain}. */
|
|
150
|
+
readonly stream: StreamDrain;
|
|
151
|
+
/** Stop the server, remove the URL file, and unsubscribe all clients. */
|
|
152
|
+
close: () => Promise<void>;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Start a local HTTP server that exposes the default in-process stream over
|
|
156
|
+
* Server-Sent Events. Framework-agnostic: works in any Node / Bun process
|
|
157
|
+
* (Nuxt, Next.js, Hono, Express, Fastify, raw scripts).
|
|
158
|
+
*
|
|
159
|
+
* The server binds to `127.0.0.1` on an ephemeral port by default — local
|
|
160
|
+
* tools and browser tabs from the same machine can connect, but the LAN
|
|
161
|
+
* cannot reach it.
|
|
162
|
+
*
|
|
163
|
+
* Idempotent: subsequent calls return the same instance until `close()` is
|
|
164
|
+
* called.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* import { startStreamServer } from 'evlog/stream'
|
|
169
|
+
*
|
|
170
|
+
* const server = await startStreamServer()
|
|
171
|
+
* console.log(server.url) // → http://127.0.0.1:51203
|
|
172
|
+
*
|
|
173
|
+
* // Hook into your drain pipeline (Nitro example):
|
|
174
|
+
* nitroApp.hooks.hook('evlog:drain', server.drain)
|
|
175
|
+
*
|
|
176
|
+
* // Or for a standalone script with initLogger:
|
|
177
|
+
* initLogger({ drain: server.drain })
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
declare function startStreamServer(options?: StreamServerOptions): Promise<StreamServer>;
|
|
181
|
+
/** @internal Used by tests to reset state between specs. */
|
|
182
|
+
declare function resetStreamServerForTests(): void;
|
|
183
|
+
//#endregion
|
|
184
|
+
export { StreamDrain, StreamDrainOptions, StreamServer, StreamServerOptions, createStreamDrain, getDefaultStream, resetStreamServerForTests, setDefaultStream, startStreamServer };
|
|
185
|
+
//# sourceMappingURL=stream.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.mts","names":[],"sources":["../src/stream.ts"],"mappings":";;;;UA0BiB,kBAAA;EA+CiB;;;;;;EAxChC,MAAA;EA2BA;;;;EAtBA,MAAA,IAAU,KAAA,EAAO,SAAA;EA8BH;;;;;;EAvBd,kBAAA;AAAA;;UAIe,WAAA;EAgDgB;;;;EA3C/B,KAAA,GAAQ,GAAA,EAAK,YAAA,GAAe,YAAA,OAAmB,OAAA;EA2CoB;;;AAsJrE;;EA3LE,SAAA,GAAY,QAAA,GAAW,KAAA,EAAO,SAAA;EA2L2C;;;;;;AAS3E;EA5LE,MAAA,QAAc,qBAAA,CAAsB,SAAA;;;;AAkMtC;EA7LE,MAAA,iBAAuB,SAAA;;WAEd,eAAA;EAkMT;EAAA,SAhMS,YAAA;EA8MT;;;;EAzMA,KAAA;AAAA;;AAoOF;;;iBArNgB,iBAAA,CAAkB,OAAA,GAAS,kBAAA,GAA0B,WAAA;;;;;;;;;;;;;;;;iBAsJrD,gBAAA,CAAiB,OAAA,GAAU,kBAAA,GAAqB,WAAA;;;;AA6HhE;iBApHgB,gBAAA,CAAiB,MAAA,EAAQ,WAAA;;UAMxB,mBAAA;EA8GmE;;;;;;EAvGlF,IAAA;EAuGkF;;;AAyMpF;;;EAzSE,IAAA;EAySuC;;;;;;EAlSvC,KAAA;;;;;;EAMA,WAAA;;;;;;EAMA,MAAA;;;;;EAKA,MAAA;;;;;;EAMA,UAAA;AAAA;;UAIe,YAAA;;WAEN,GAAA;;WAEA,IAAA;;;;;;;;WAQA,KAAA,GAAQ,GAAA,EAAK,YAAA,GAAe,YAAA,OAAmB,OAAA;;WAE/C,MAAA,EAAQ,WAAA;;EAEjB,KAAA,QAAa,OAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CO,iBAAA,CAAkB,OAAA,GAAS,mBAAA,GAA2B,OAAA,CAAQ,YAAA;;iBAyMpE,yBAAA,CAAA"}
|
package/dist/stream.mjs
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { n as EVLOG_VERSION } from "./http-6umVAKDW.mjs";
|
|
2
|
+
//#region src/stream.ts
|
|
3
|
+
const DEFAULT_BUFFER = 500;
|
|
4
|
+
const DEFAULT_QUEUE = 1e3;
|
|
5
|
+
/**
|
|
6
|
+
* Create an in-process {@link StreamDrain}. Multiple stream drains can
|
|
7
|
+
* coexist in the same process — they are fully independent.
|
|
8
|
+
*/
|
|
9
|
+
function createStreamDrain(options = {}) {
|
|
10
|
+
const bufferSize = Math.max(0, Math.floor(options.buffer ?? DEFAULT_BUFFER));
|
|
11
|
+
const queueLimit = Math.max(1, Math.floor(options.perSubscriberQueue ?? DEFAULT_QUEUE));
|
|
12
|
+
const { filter } = options;
|
|
13
|
+
const buffer = [];
|
|
14
|
+
const syncListeners = /* @__PURE__ */ new Set();
|
|
15
|
+
const asyncSubscribers = /* @__PURE__ */ new Set();
|
|
16
|
+
let dropped = 0;
|
|
17
|
+
function publish(event) {
|
|
18
|
+
if (filter && !filter(event)) return;
|
|
19
|
+
if (bufferSize > 0) {
|
|
20
|
+
buffer.push(event);
|
|
21
|
+
while (buffer.length > bufferSize) {
|
|
22
|
+
buffer.shift();
|
|
23
|
+
dropped++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
for (const listener of syncListeners) try {
|
|
27
|
+
listener(event);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error("[evlog/stream] subscriber threw:", err);
|
|
30
|
+
}
|
|
31
|
+
for (const sub of asyncSubscribers) sub.push(event);
|
|
32
|
+
}
|
|
33
|
+
const drain = (ctx) => {
|
|
34
|
+
const contexts = Array.isArray(ctx) ? ctx : [ctx];
|
|
35
|
+
for (const c of contexts) if (c?.event) publish(c.event);
|
|
36
|
+
return Promise.resolve();
|
|
37
|
+
};
|
|
38
|
+
const subscribe = (listener) => {
|
|
39
|
+
syncListeners.add(listener);
|
|
40
|
+
return () => {
|
|
41
|
+
syncListeners.delete(listener);
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
function createAsyncIterator() {
|
|
45
|
+
const queue = [];
|
|
46
|
+
let pendingResolve = null;
|
|
47
|
+
let closed = false;
|
|
48
|
+
const subscriber = {
|
|
49
|
+
push(event) {
|
|
50
|
+
if (closed) return;
|
|
51
|
+
if (pendingResolve) {
|
|
52
|
+
const resolve = pendingResolve;
|
|
53
|
+
pendingResolve = null;
|
|
54
|
+
resolve({
|
|
55
|
+
value: event,
|
|
56
|
+
done: false
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
queue.push(event);
|
|
61
|
+
while (queue.length > queueLimit) {
|
|
62
|
+
queue.shift();
|
|
63
|
+
dropped++;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
end() {
|
|
67
|
+
if (closed) return;
|
|
68
|
+
closed = true;
|
|
69
|
+
if (pendingResolve) {
|
|
70
|
+
const resolve = pendingResolve;
|
|
71
|
+
pendingResolve = null;
|
|
72
|
+
resolve({
|
|
73
|
+
value: void 0,
|
|
74
|
+
done: true
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
asyncSubscribers.add(subscriber);
|
|
80
|
+
const iterator = {
|
|
81
|
+
[Symbol.asyncIterator]() {
|
|
82
|
+
return iterator;
|
|
83
|
+
},
|
|
84
|
+
next() {
|
|
85
|
+
if (queue.length > 0) return Promise.resolve({
|
|
86
|
+
value: queue.shift(),
|
|
87
|
+
done: false
|
|
88
|
+
});
|
|
89
|
+
if (closed) return Promise.resolve({
|
|
90
|
+
value: void 0,
|
|
91
|
+
done: true
|
|
92
|
+
});
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
pendingResolve = resolve;
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
return() {
|
|
98
|
+
closed = true;
|
|
99
|
+
asyncSubscribers.delete(subscriber);
|
|
100
|
+
if (pendingResolve) {
|
|
101
|
+
const resolve = pendingResolve;
|
|
102
|
+
pendingResolve = null;
|
|
103
|
+
resolve({
|
|
104
|
+
value: void 0,
|
|
105
|
+
done: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return Promise.resolve({
|
|
109
|
+
value: void 0,
|
|
110
|
+
done: true
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
return iterator;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
drain,
|
|
118
|
+
subscribe,
|
|
119
|
+
events: createAsyncIterator,
|
|
120
|
+
recent: () => buffer.slice(),
|
|
121
|
+
get subscriberCount() {
|
|
122
|
+
return syncListeners.size + asyncSubscribers.size;
|
|
123
|
+
},
|
|
124
|
+
get droppedCount() {
|
|
125
|
+
return dropped;
|
|
126
|
+
},
|
|
127
|
+
close() {
|
|
128
|
+
syncListeners.clear();
|
|
129
|
+
for (const sub of asyncSubscribers) sub.end();
|
|
130
|
+
asyncSubscribers.clear();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
let defaultStream = null;
|
|
135
|
+
/**
|
|
136
|
+
* Lazily create / return the process-wide default {@link StreamDrain}.
|
|
137
|
+
*
|
|
138
|
+
* Used by built-in framework integrations (Nuxt SSE bridge, devtools panel)
|
|
139
|
+
* so they share a single buffer. Custom code can subscribe to this stream
|
|
140
|
+
* to observe everything draining through evlog without registering a new
|
|
141
|
+
* drain.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* import { getDefaultStream } from 'evlog/stream'
|
|
146
|
+
*
|
|
147
|
+
* getDefaultStream().subscribe((event) => console.log(event.action))
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
function getDefaultStream(options) {
|
|
151
|
+
if (!defaultStream) defaultStream = createStreamDrain(options);
|
|
152
|
+
return defaultStream;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Replace or clear the default stream. Pass `null` to reset (mostly useful
|
|
156
|
+
* in tests).
|
|
157
|
+
*/
|
|
158
|
+
function setDefaultStream(stream) {
|
|
159
|
+
defaultStream?.close();
|
|
160
|
+
defaultStream = stream;
|
|
161
|
+
}
|
|
162
|
+
const STREAM_DEFAULTS = {
|
|
163
|
+
port: 0,
|
|
164
|
+
host: "127.0.0.1",
|
|
165
|
+
heartbeatMs: 15e3,
|
|
166
|
+
buffer: 500,
|
|
167
|
+
banner: true,
|
|
168
|
+
urlFileDir: ".evlog"
|
|
169
|
+
};
|
|
170
|
+
let activeStreamServer = null;
|
|
171
|
+
function isLocalHost(host) {
|
|
172
|
+
if (!host) return false;
|
|
173
|
+
const lower = host.toLowerCase();
|
|
174
|
+
return lower.startsWith("localhost") || lower.startsWith("127.0.0.1") || lower.startsWith("[::1]");
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Start a local HTTP server that exposes the default in-process stream over
|
|
178
|
+
* Server-Sent Events. Framework-agnostic: works in any Node / Bun process
|
|
179
|
+
* (Nuxt, Next.js, Hono, Express, Fastify, raw scripts).
|
|
180
|
+
*
|
|
181
|
+
* The server binds to `127.0.0.1` on an ephemeral port by default — local
|
|
182
|
+
* tools and browser tabs from the same machine can connect, but the LAN
|
|
183
|
+
* cannot reach it.
|
|
184
|
+
*
|
|
185
|
+
* Idempotent: subsequent calls return the same instance until `close()` is
|
|
186
|
+
* called.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* import { startStreamServer } from 'evlog/stream'
|
|
191
|
+
*
|
|
192
|
+
* const server = await startStreamServer()
|
|
193
|
+
* console.log(server.url) // → http://127.0.0.1:51203
|
|
194
|
+
*
|
|
195
|
+
* // Hook into your drain pipeline (Nitro example):
|
|
196
|
+
* nitroApp.hooks.hook('evlog:drain', server.drain)
|
|
197
|
+
*
|
|
198
|
+
* // Or for a standalone script with initLogger:
|
|
199
|
+
* initLogger({ drain: server.drain })
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
async function startStreamServer(options = {}) {
|
|
203
|
+
if (activeStreamServer) return activeStreamServer;
|
|
204
|
+
const opts = {
|
|
205
|
+
...STREAM_DEFAULTS,
|
|
206
|
+
...options
|
|
207
|
+
};
|
|
208
|
+
const stream = getDefaultStream({ buffer: opts.buffer });
|
|
209
|
+
const { createServer } = await import("node:http");
|
|
210
|
+
const { writeFile, unlink, mkdir } = await import("node:fs/promises");
|
|
211
|
+
const { unlinkSync } = await import("node:fs");
|
|
212
|
+
const { join } = await import("node:path");
|
|
213
|
+
function envelopeFrame(type, data) {
|
|
214
|
+
return `data: ${JSON.stringify({
|
|
215
|
+
evlog: "1",
|
|
216
|
+
type,
|
|
217
|
+
data
|
|
218
|
+
})}\n\n`;
|
|
219
|
+
}
|
|
220
|
+
function writeCors(res) {
|
|
221
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
222
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
223
|
+
res.setHeader("Access-Control-Allow-Headers", "Authorization, Accept");
|
|
224
|
+
}
|
|
225
|
+
const server = createServer((req, res) => {
|
|
226
|
+
if (req.method === "OPTIONS") {
|
|
227
|
+
writeCors(res);
|
|
228
|
+
res.writeHead(204);
|
|
229
|
+
res.end();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (req.method !== "GET") {
|
|
233
|
+
res.writeHead(405);
|
|
234
|
+
res.end("method not allowed");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (opts.token) {
|
|
238
|
+
if (req.headers.authorization !== `Bearer ${opts.token}`) {
|
|
239
|
+
writeCors(res);
|
|
240
|
+
res.writeHead(401);
|
|
241
|
+
res.end("unauthorized");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
const origin = typeof req.headers.origin === "string" ? req.headers.origin : null;
|
|
246
|
+
if (origin) {
|
|
247
|
+
let originHost = null;
|
|
248
|
+
try {
|
|
249
|
+
originHost = new URL(origin).host;
|
|
250
|
+
} catch {
|
|
251
|
+
originHost = null;
|
|
252
|
+
}
|
|
253
|
+
if (!isLocalHost(originHost ?? void 0)) {
|
|
254
|
+
writeCors(res);
|
|
255
|
+
res.writeHead(403);
|
|
256
|
+
res.end("forbidden");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
262
|
+
if (url.pathname === "/info") {
|
|
263
|
+
writeCors(res);
|
|
264
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
265
|
+
res.end(JSON.stringify({
|
|
266
|
+
evlogVersion: EVLOG_VERSION,
|
|
267
|
+
bufferSize: opts.buffer,
|
|
268
|
+
heartbeatMs: opts.heartbeatMs
|
|
269
|
+
}));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
writeCors(res);
|
|
273
|
+
res.writeHead(200, {
|
|
274
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
275
|
+
"Cache-Control": "no-cache, no-transform",
|
|
276
|
+
"Connection": "keep-alive",
|
|
277
|
+
"X-Accel-Buffering": "no",
|
|
278
|
+
"X-Evlog-Version": EVLOG_VERSION
|
|
279
|
+
});
|
|
280
|
+
res.write(envelopeFrame("hello", {
|
|
281
|
+
evlogVersion: EVLOG_VERSION,
|
|
282
|
+
bufferSize: opts.buffer,
|
|
283
|
+
heartbeatMs: opts.heartbeatMs
|
|
284
|
+
}));
|
|
285
|
+
const since = url.searchParams.get("since");
|
|
286
|
+
const sinceMs = since ? Date.parse(since) : NaN;
|
|
287
|
+
if (Number.isFinite(sinceMs)) for (const past of stream.recent()) {
|
|
288
|
+
const ts = typeof past.timestamp === "string" ? Date.parse(past.timestamp) : NaN;
|
|
289
|
+
if (Number.isFinite(ts) && ts >= sinceMs) res.write(envelopeFrame("replay", past));
|
|
290
|
+
}
|
|
291
|
+
let closed = false;
|
|
292
|
+
const unsubscribe = stream.subscribe((event) => {
|
|
293
|
+
if (closed) return;
|
|
294
|
+
res.write(envelopeFrame("event", event));
|
|
295
|
+
});
|
|
296
|
+
const heartbeat = setInterval(() => {
|
|
297
|
+
if (closed) return;
|
|
298
|
+
res.write(`event: ping\ndata: ${JSON.stringify({
|
|
299
|
+
evlog: "1",
|
|
300
|
+
type: "ping",
|
|
301
|
+
data: { t: Date.now() }
|
|
302
|
+
})}\n\n`);
|
|
303
|
+
}, opts.heartbeatMs);
|
|
304
|
+
const cleanup = () => {
|
|
305
|
+
if (closed) return;
|
|
306
|
+
closed = true;
|
|
307
|
+
clearInterval(heartbeat);
|
|
308
|
+
unsubscribe();
|
|
309
|
+
try {
|
|
310
|
+
res.end();
|
|
311
|
+
} catch {}
|
|
312
|
+
};
|
|
313
|
+
req.on("close", cleanup);
|
|
314
|
+
req.on("error", cleanup);
|
|
315
|
+
res.on("close", cleanup);
|
|
316
|
+
res.on("error", cleanup);
|
|
317
|
+
});
|
|
318
|
+
const port = await new Promise((resolve, reject) => {
|
|
319
|
+
server.once("error", reject);
|
|
320
|
+
server.listen(opts.port, opts.host, () => {
|
|
321
|
+
const addr = server.address();
|
|
322
|
+
if (addr && typeof addr === "object") resolve(addr.port);
|
|
323
|
+
else reject(/* @__PURE__ */ new Error("[evlog/stream] failed to determine listening port"));
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
const url = `http://${opts.host}:${port}`;
|
|
327
|
+
let urlFile;
|
|
328
|
+
if (opts.urlFileDir !== false) try {
|
|
329
|
+
await mkdir(opts.urlFileDir, { recursive: true });
|
|
330
|
+
urlFile = join(opts.urlFileDir, "stream.url");
|
|
331
|
+
await writeFile(urlFile, url, "utf-8");
|
|
332
|
+
} catch (err) {
|
|
333
|
+
console.warn("[evlog/stream] failed to write stream.url:", err);
|
|
334
|
+
}
|
|
335
|
+
if (opts.banner) console.info(`\n [evlog] Stream → ${url}\n`);
|
|
336
|
+
const exitHandler = () => {
|
|
337
|
+
if (urlFile) try {
|
|
338
|
+
unlinkSync(urlFile);
|
|
339
|
+
} catch {}
|
|
340
|
+
server.close();
|
|
341
|
+
};
|
|
342
|
+
process.once("SIGINT", exitHandler);
|
|
343
|
+
process.once("SIGTERM", exitHandler);
|
|
344
|
+
process.once("exit", exitHandler);
|
|
345
|
+
const close = async () => {
|
|
346
|
+
process.off("SIGINT", exitHandler);
|
|
347
|
+
process.off("SIGTERM", exitHandler);
|
|
348
|
+
process.off("exit", exitHandler);
|
|
349
|
+
if (urlFile) try {
|
|
350
|
+
await unlink(urlFile);
|
|
351
|
+
} catch {}
|
|
352
|
+
await new Promise((resolve, reject) => {
|
|
353
|
+
server.close((err) => err ? reject(err) : resolve());
|
|
354
|
+
});
|
|
355
|
+
if (activeStreamServer === result) activeStreamServer = null;
|
|
356
|
+
};
|
|
357
|
+
const result = {
|
|
358
|
+
url,
|
|
359
|
+
port,
|
|
360
|
+
drain: (ctx) => stream.drain(ctx),
|
|
361
|
+
stream,
|
|
362
|
+
close
|
|
363
|
+
};
|
|
364
|
+
activeStreamServer = result;
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
/** @internal Used by tests to reset state between specs. */
|
|
368
|
+
function resetStreamServerForTests() {
|
|
369
|
+
activeStreamServer = null;
|
|
370
|
+
}
|
|
371
|
+
//#endregion
|
|
372
|
+
export { createStreamDrain, getDefaultStream, resetStreamServerForTests, setDefaultStream, startStreamServer };
|
|
373
|
+
|
|
374
|
+
//# sourceMappingURL=stream.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.mjs","names":[],"sources":["../src/stream.ts"],"sourcesContent":["/**\n * In-process event stream — the foundation for any local consumer (devtools,\n * dashboards, CLIs, SSE/WebSocket bridges) to observe events flowing through\n * the drain pipeline without re-implementing one.\n *\n * @example\n * ```ts\n * import { createStreamDrain } from 'evlog/stream'\n *\n * const stream = createStreamDrain({ buffer: 200 })\n * nitroApp.hooks.hook('evlog:drain', stream.drain)\n *\n * stream.subscribe((event) => {\n * if (event.level === 'error') notifyOps(event)\n * })\n *\n * for await (const event of stream.events()) {\n * console.log(event.timestamp, event.action ?? event.message)\n * }\n * ```\n */\n\nimport type { DrainContext, WideEvent } from './types'\nimport { EVLOG_VERSION } from './shared/http'\n\n/** Configuration accepted by {@link createStreamDrain}. */\nexport interface StreamDrainOptions {\n /**\n * Number of recent events kept in the ring buffer and replayed to new\n * subscribers via {@link StreamDrain.recent}. Set to `0` to disable\n * replay.\n * @default 500\n */\n buffer?: number\n /**\n * Optional predicate run on each drained event — return `false` to skip\n * the event entirely (it is neither buffered nor delivered).\n */\n filter?: (event: WideEvent) => boolean\n /**\n * Per-subscriber queue size for the {@link StreamDrain.events} async\n * iterator. When the consumer falls behind by more than this many events,\n * the oldest queued events are dropped — the drain is never blocked.\n * @default 1000\n */\n perSubscriberQueue?: number\n}\n\n/** Live drain that exposes its events to in-process subscribers. */\nexport interface StreamDrain {\n /**\n * Drain callback. Pass to `nitroApp.hooks.hook('evlog:drain', stream.drain)`\n * or to a plugin via `drainPlugin('stream', stream.drain)`.\n */\n drain: (ctx: DrainContext | DrainContext[]) => Promise<void>\n /**\n * Register a synchronous listener. Errors thrown by the listener are\n * caught and logged — they never affect other subscribers or the drain.\n * @returns Unsubscribe function.\n */\n subscribe: (listener: (event: WideEvent) => void) => () => void\n /**\n * Async iterator over live events. Each call returns a fresh iterator —\n * past events from the ring buffer are NOT replayed. Combine with\n * {@link StreamDrain.recent} to seed history.\n *\n * Calling `return()` (e.g. via `break`) cleanly unsubscribes.\n */\n events: () => AsyncIterableIterator<WideEvent>\n /**\n * Snapshot of buffered events (oldest first, most recent last). Useful\n * to seed a new connection / UI panel before switching to live updates.\n */\n recent: () => readonly WideEvent[]\n /** Number of currently active subscribers (sync + async iterators). */\n readonly subscriberCount: number\n /** Number of events dropped because the buffer was disabled or wrapped. */\n readonly droppedCount: number\n /**\n * Disconnect all subscribers and end any pending async iterators. The\n * stream itself remains usable — new subscriptions still work.\n */\n close: () => void\n}\n\nconst DEFAULT_BUFFER = 500\nconst DEFAULT_QUEUE = 1000\n\ninterface AsyncSubscriber {\n push: (event: WideEvent) => void\n end: () => void\n}\n\n/**\n * Create an in-process {@link StreamDrain}. Multiple stream drains can\n * coexist in the same process — they are fully independent.\n */\nexport function createStreamDrain(options: StreamDrainOptions = {}): StreamDrain {\n const bufferSize = Math.max(0, Math.floor(options.buffer ?? DEFAULT_BUFFER))\n const queueLimit = Math.max(1, Math.floor(options.perSubscriberQueue ?? DEFAULT_QUEUE))\n const { filter } = options\n\n const buffer: WideEvent[] = []\n const syncListeners = new Set<(event: WideEvent) => void>()\n const asyncSubscribers = new Set<AsyncSubscriber>()\n let dropped = 0\n\n function publish(event: WideEvent): void {\n if (filter && !filter(event)) return\n\n if (bufferSize > 0) {\n buffer.push(event)\n while (buffer.length > bufferSize) {\n buffer.shift()\n dropped++\n }\n }\n\n for (const listener of syncListeners) {\n try {\n listener(event)\n } catch (err) {\n console.error('[evlog/stream] subscriber threw:', err)\n }\n }\n\n for (const sub of asyncSubscribers) {\n sub.push(event)\n }\n }\n\n const drain: StreamDrain['drain'] = (ctx) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx]\n for (const c of contexts) {\n if (c?.event) publish(c.event)\n }\n return Promise.resolve()\n }\n\n const subscribe: StreamDrain['subscribe'] = (listener) => {\n syncListeners.add(listener)\n return () => {\n syncListeners.delete(listener)\n }\n }\n\n function createAsyncIterator(): AsyncIterableIterator<WideEvent> {\n const queue: WideEvent[] = []\n let pendingResolve: ((result: IteratorResult<WideEvent>) => void) | null = null\n let closed = false\n\n const subscriber: AsyncSubscriber = {\n push(event) {\n if (closed) return\n if (pendingResolve) {\n const resolve = pendingResolve\n pendingResolve = null\n resolve({ value: event, done: false })\n return\n }\n queue.push(event)\n while (queue.length > queueLimit) {\n queue.shift()\n dropped++\n }\n },\n end() {\n if (closed) return\n closed = true\n if (pendingResolve) {\n const resolve = pendingResolve\n pendingResolve = null\n resolve({ value: undefined, done: true })\n }\n },\n }\n\n asyncSubscribers.add(subscriber)\n\n const iterator: AsyncIterableIterator<WideEvent> = {\n [Symbol.asyncIterator]() {\n return iterator\n },\n next(): Promise<IteratorResult<WideEvent>> {\n if (queue.length > 0) {\n return Promise.resolve({ value: queue.shift()!, done: false })\n }\n if (closed) {\n return Promise.resolve({ value: undefined, done: true })\n }\n return new Promise((resolve) => {\n pendingResolve = resolve\n })\n },\n return(): Promise<IteratorResult<WideEvent>> {\n closed = true\n asyncSubscribers.delete(subscriber)\n if (pendingResolve) {\n const resolve = pendingResolve\n pendingResolve = null\n resolve({ value: undefined, done: true })\n }\n return Promise.resolve({ value: undefined, done: true })\n },\n }\n\n return iterator\n }\n\n return {\n drain,\n subscribe,\n events: createAsyncIterator,\n recent: () => buffer.slice(),\n get subscriberCount() {\n return syncListeners.size + asyncSubscribers.size\n },\n get droppedCount() {\n return dropped\n },\n close() {\n syncListeners.clear()\n for (const sub of asyncSubscribers) {\n sub.end()\n }\n asyncSubscribers.clear()\n },\n }\n}\n\nlet defaultStream: StreamDrain | null = null\n\n/**\n * Lazily create / return the process-wide default {@link StreamDrain}.\n *\n * Used by built-in framework integrations (Nuxt SSE bridge, devtools panel)\n * so they share a single buffer. Custom code can subscribe to this stream\n * to observe everything draining through evlog without registering a new\n * drain.\n *\n * @example\n * ```ts\n * import { getDefaultStream } from 'evlog/stream'\n *\n * getDefaultStream().subscribe((event) => console.log(event.action))\n * ```\n */\nexport function getDefaultStream(options?: StreamDrainOptions): StreamDrain {\n if (!defaultStream) defaultStream = createStreamDrain(options)\n return defaultStream\n}\n\n/**\n * Replace or clear the default stream. Pass `null` to reset (mostly useful\n * in tests).\n */\nexport function setDefaultStream(stream: StreamDrain | null): void {\n defaultStream?.close()\n defaultStream = stream\n}\n\n/** Configuration accepted by {@link startStreamServer}. */\nexport interface StreamServerOptions {\n /**\n * TCP port to listen on. `0` (default) lets the OS pick an ephemeral port —\n * the actual port is exposed on the returned `port` / `url` and printed in\n * the startup banner.\n * @default 0\n */\n port?: number\n /**\n * Listen address. Default `127.0.0.1` — local-only, never exposed to the\n * LAN. Override to `0.0.0.0` only if you understand the security\n * implications.\n * @default '127.0.0.1'\n */\n host?: string\n /**\n * Bearer token. When set, the server requires `Authorization: Bearer\n * <token>` on every request and 401s otherwise. When unset, only\n * connections from `127.0.0.1` (and equivalent local hosts) are\n * accepted — same-origin policy applies on non-local hosts.\n */\n token?: string\n /**\n * Heartbeat interval (ms) sent as `event: ping` to keep the connection\n * alive through proxies and detect disconnects.\n * @default 15000\n */\n heartbeatMs?: number\n /**\n * Ring buffer size kept on the underlying default stream — replayed for\n * late-joining clients via `?since=<iso>`.\n * @default 500\n */\n buffer?: number\n /**\n * Print `[evlog] Stream → http://...` at startup.\n * @default true\n */\n banner?: boolean\n /**\n * Directory where `stream.url` is written so external tools can discover\n * the server. Set to `false` to disable.\n * @default '.evlog'\n */\n urlFileDir?: string | false\n}\n\n/** Return value of {@link startStreamServer}. */\nexport interface StreamServer {\n /** Listening URL, e.g. `http://127.0.0.1:51203`. */\n readonly url: string\n /** Resolved TCP port (useful when `port: 0`). */\n readonly port: number\n /**\n * Drain function — register on `evlog:drain` (Nitro / Nuxt), pass to\n * `initLogger({ drain })` (Next / standalone), or call directly.\n *\n * Equivalent to the underlying default stream's drain — events flow\n * through and reach every connected SSE client.\n */\n readonly drain: (ctx: DrainContext | DrainContext[]) => Promise<void>\n /** The underlying in-process {@link StreamDrain}. */\n readonly stream: StreamDrain\n /** Stop the server, remove the URL file, and unsubscribe all clients. */\n close: () => Promise<void>\n}\n\nconst STREAM_DEFAULTS = {\n port: 0,\n host: '127.0.0.1',\n heartbeatMs: 15_000,\n buffer: 500,\n banner: true,\n urlFileDir: '.evlog' as string | false,\n}\n\nlet activeStreamServer: StreamServer | null = null\n\nfunction isLocalHost(host: string | undefined): boolean {\n if (!host) return false\n const lower = host.toLowerCase()\n return lower.startsWith('localhost') || lower.startsWith('127.0.0.1') || lower.startsWith('[::1]')\n}\n\n/**\n * Start a local HTTP server that exposes the default in-process stream over\n * Server-Sent Events. Framework-agnostic: works in any Node / Bun process\n * (Nuxt, Next.js, Hono, Express, Fastify, raw scripts).\n *\n * The server binds to `127.0.0.1` on an ephemeral port by default — local\n * tools and browser tabs from the same machine can connect, but the LAN\n * cannot reach it.\n *\n * Idempotent: subsequent calls return the same instance until `close()` is\n * called.\n *\n * @example\n * ```ts\n * import { startStreamServer } from 'evlog/stream'\n *\n * const server = await startStreamServer()\n * console.log(server.url) // → http://127.0.0.1:51203\n *\n * // Hook into your drain pipeline (Nitro example):\n * nitroApp.hooks.hook('evlog:drain', server.drain)\n *\n * // Or for a standalone script with initLogger:\n * initLogger({ drain: server.drain })\n * ```\n */\nexport async function startStreamServer(options: StreamServerOptions = {}): Promise<StreamServer> {\n if (activeStreamServer) return activeStreamServer\n\n const opts = { ...STREAM_DEFAULTS, ...options }\n const stream = getDefaultStream({ buffer: opts.buffer })\n\n const { createServer } = await import('node:http')\n const { writeFile, unlink, mkdir } = await import('node:fs/promises')\n const { unlinkSync } = await import('node:fs')\n const { join } = await import('node:path')\n\n function envelopeFrame(type: 'event' | 'replay' | 'hello', data: unknown): string {\n return `data: ${JSON.stringify({ evlog: '1', type, data })}\\n\\n`\n }\n\n function writeCors(res: import('node:http').ServerResponse): void {\n res.setHeader('Access-Control-Allow-Origin', '*')\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Authorization, Accept')\n }\n\n const server = createServer((req, res) => {\n if (req.method === 'OPTIONS') {\n writeCors(res)\n res.writeHead(204)\n res.end()\n return\n }\n\n if (req.method !== 'GET') {\n res.writeHead(405)\n res.end('method not allowed')\n return\n }\n\n if (opts.token) {\n if (req.headers.authorization !== `Bearer ${opts.token}`) {\n writeCors(res)\n res.writeHead(401)\n res.end('unauthorized')\n return\n }\n } else {\n const origin = typeof req.headers.origin === 'string' ? req.headers.origin : null\n if (origin) {\n let originHost: string | null = null\n try {\n originHost = new URL(origin).host\n } catch {\n originHost = null\n }\n if (!isLocalHost(originHost ?? undefined)) {\n writeCors(res)\n res.writeHead(403)\n res.end('forbidden')\n return\n }\n }\n }\n\n const url = new URL(req.url ?? '/', 'http://localhost')\n\n if (url.pathname === '/info') {\n writeCors(res)\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({\n evlogVersion: EVLOG_VERSION,\n bufferSize: opts.buffer,\n heartbeatMs: opts.heartbeatMs,\n }))\n return\n }\n\n writeCors(res)\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-cache, no-transform',\n 'Connection': 'keep-alive',\n 'X-Accel-Buffering': 'no',\n 'X-Evlog-Version': EVLOG_VERSION,\n })\n\n res.write(envelopeFrame('hello', {\n evlogVersion: EVLOG_VERSION,\n bufferSize: opts.buffer,\n heartbeatMs: opts.heartbeatMs,\n }))\n\n const since = url.searchParams.get('since')\n const sinceMs = since ? Date.parse(since) : Number.NaN\n if (Number.isFinite(sinceMs)) {\n for (const past of stream.recent()) {\n const ts = typeof past.timestamp === 'string' ? Date.parse(past.timestamp) : Number.NaN\n if (Number.isFinite(ts) && ts >= sinceMs) {\n res.write(envelopeFrame('replay', past))\n }\n }\n }\n\n let closed = false\n const unsubscribe = stream.subscribe((event) => {\n if (closed) return\n res.write(envelopeFrame('event', event))\n })\n\n const heartbeat = setInterval(() => {\n if (closed) return\n res.write(`event: ping\\ndata: ${JSON.stringify({ evlog: '1', type: 'ping', data: { t: Date.now() } })}\\n\\n`)\n }, opts.heartbeatMs)\n\n const cleanup = () => {\n if (closed) return\n closed = true\n clearInterval(heartbeat)\n unsubscribe()\n try {\n res.end()\n } catch {\n // noop\n }\n }\n\n req.on('close', cleanup)\n req.on('error', cleanup)\n res.on('close', cleanup)\n res.on('error', cleanup)\n })\n\n const port: number = await new Promise((resolve, reject) => {\n server.once('error', reject)\n server.listen(opts.port, opts.host, () => {\n const addr = server.address()\n if (addr && typeof addr === 'object') {\n resolve(addr.port)\n } else {\n reject(new Error('[evlog/stream] failed to determine listening port'))\n }\n })\n })\n\n const url = `http://${opts.host}:${port}`\n\n let urlFile: string | undefined\n if (opts.urlFileDir !== false) {\n try {\n await mkdir(opts.urlFileDir, { recursive: true })\n urlFile = join(opts.urlFileDir, 'stream.url')\n await writeFile(urlFile, url, 'utf-8')\n } catch (err) {\n console.warn('[evlog/stream] failed to write stream.url:', err)\n }\n }\n\n if (opts.banner) {\n console.info(`\\n [evlog] Stream → ${url}\\n`)\n }\n\n const exitHandler = () => {\n if (urlFile) {\n try {\n unlinkSync(urlFile)\n } catch {\n // noop\n }\n }\n server.close()\n }\n process.once('SIGINT', exitHandler)\n process.once('SIGTERM', exitHandler)\n process.once('exit', exitHandler)\n\n const close = async (): Promise<void> => {\n process.off('SIGINT', exitHandler)\n process.off('SIGTERM', exitHandler)\n process.off('exit', exitHandler)\n if (urlFile) {\n try {\n await unlink(urlFile)\n } catch {\n // noop\n }\n }\n await new Promise<void>((resolve, reject) => {\n server.close(err => err ? reject(err) : resolve())\n })\n if (activeStreamServer === result) activeStreamServer = null\n }\n\n const result: StreamServer = {\n url,\n port,\n drain: ctx => stream.drain(ctx),\n stream,\n close,\n }\n\n activeStreamServer = result\n return result\n}\n\n/** @internal Used by tests to reset state between specs. */\nexport function resetStreamServerForTests(): void {\n activeStreamServer = null\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as BaseEvlogOptions } from "../middleware-
|
|
1
|
+
import { $ as RequestLogger } from "../audit-CC8nfazi.mjs";
|
|
2
|
+
import { t as BaseEvlogOptions } from "../middleware-U-lIAzHg.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/sveltekit/index.d.ts
|
|
5
5
|
declare const useLogger: <T extends object = Record<string, unknown>>() => RequestLogger<T>;
|
package/dist/sveltekit/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as extractSafeHeaders } from "../headers-CU-QqnYg.mjs";
|
|
2
2
|
import { EvlogError } from "../error.mjs";
|
|
3
3
|
import { t as extractErrorStatus } from "../errors-BQgyQ9xe.mjs";
|
|
4
|
-
import { n as serializeEvlogErrorResponse, t as resolveEvlogError } from "../nitro-
|
|
5
|
-
import { r as createMiddlewareLogger, t as attachForkToLogger } from "../fork-
|
|
6
|
-
import { t as createLoggerStorage } from "../storage-
|
|
4
|
+
import { n as serializeEvlogErrorResponse, t as resolveEvlogError } from "../nitro-DErMq_Zj.mjs";
|
|
5
|
+
import { r as createMiddlewareLogger, t as attachForkToLogger } from "../fork-8u_zFOJq.mjs";
|
|
6
|
+
import { t as createLoggerStorage } from "../storage-Dwinmg8P.mjs";
|
|
7
7
|
//#region src/sveltekit/index.ts
|
|
8
8
|
const { storage, useLogger } = createLoggerStorage("handle context. Make sure evlog() handle is added to your hooks.server.ts.");
|
|
9
9
|
/**
|