@tallyrow/safesignal 1.3.0 → 1.4.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 +329 -20
- package/dist/capture.cjs +77 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +49 -0
- package/dist/capture.d.ts +49 -0
- package/dist/capture.mjs +75 -0
- package/dist/capture.mjs.map +1 -0
- package/dist/dev-console.cjs +90 -0
- package/dist/dev-console.cjs.map +1 -0
- package/dist/dev-console.d.cts +67 -0
- package/dist/dev-console.d.ts +67 -0
- package/dist/dev-console.mjs +88 -0
- package/dist/dev-console.mjs.map +1 -0
- package/dist/framework-react.cjs +92 -0
- package/dist/framework-react.cjs.map +1 -0
- package/dist/framework-react.d.cts +97 -0
- package/dist/framework-react.d.ts +97 -0
- package/dist/framework-react.mjs +87 -0
- package/dist/framework-react.mjs.map +1 -0
- package/dist/framework-vue.cjs +88 -0
- package/dist/framework-vue.cjs.map +1 -0
- package/dist/framework-vue.d.cts +101 -0
- package/dist/framework-vue.d.ts +101 -0
- package/dist/framework-vue.mjs +82 -0
- package/dist/framework-vue.mjs.map +1 -0
- package/dist/index.cjs +180 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +180 -40
- package/dist/index.mjs.map +1 -1
- package/dist/stacks.cjs +81 -0
- package/dist/stacks.cjs.map +1 -0
- package/dist/stacks.d.cts +55 -0
- package/dist/stacks.d.ts +55 -0
- package/dist/stacks.mjs +77 -0
- package/dist/stacks.mjs.map +1 -0
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/transport-beacon.cjs.map +1 -1
- package/dist/transport-beacon.d.cts +1 -1
- package/dist/transport-beacon.d.ts +1 -1
- package/dist/transport-beacon.mjs.map +1 -1
- package/dist/transport-otlp.cjs.map +1 -1
- package/dist/transport-otlp.d.cts +1 -1
- package/dist/transport-otlp.d.ts +1 -1
- package/dist/transport-otlp.mjs.map +1 -1
- package/dist/{types-BiRyHi1e.d.cts → types-CZtSjgq5.d.cts} +53 -1
- package/dist/{types-BiRyHi1e.d.ts → types-CZtSjgq5.d.ts} +53 -1
- package/package.json +53 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport-beacon/batcher.ts","../src/transport-beacon/delivery.ts","../src/transport-beacon/endpoint-validation.ts","../src/transport-beacon/errors.ts","../src/transport-beacon/lifecycle.ts","../src/transport-beacon/beacon-transport.ts"],"names":["state"],"mappings":"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","file":"transport-beacon.mjs","sourcesContent":["/**\n * Batcher state machine for opt-in beacon-transport batching.\n *\n * T027 replaces T024's stub with the working state machine. T028\n * wires this batcher into `createBeaconTransport`'s send path.\n *\n * Behaviour (data-model.md § BatcherOptions + contracts/batching.md):\n *\n * - `push(event)` appends to the in-memory buffer.\n * - If this is the first event in an empty batch AND\n * `maxBatchAgeMs` is set, arm a one-shot timer.\n * - If the buffer reaches `maxBatchSize`, flush synchronously\n * at the end of `push`.\n * - `flush()` drains any pending buffer through the consumer's\n * `flush` callback. Empty buffer → no-op.\n * - `shutdown()` cancels the timer and nulls the flush callback\n * reference, so any timer/microtask queued before shutdown\n * becomes a no-op when it fires.\n *\n * Buffer-clear ordering (B-5c): the buffer is cleared BEFORE the\n * consumer's flush callback is invoked. A re-entrant `push()`\n * during the callback sees an empty buffer and starts a fresh\n * batch — no double-flush, no re-push.\n *\n * Throw safety (B-5d): if the consumer's flush callback throws,\n * the throw is swallowed. The events that were drained into the\n * callback are gone — the batcher does NOT re-push them. The\n * batcher itself never throws from `push`/`flush`/`shutdown`.\n *\n * Boundary discipline (TB-11): the only `src/` import is\n * `import type` from `'../api/types.js'`. The module is import-pure\n * — no listeners at module scope, no timers at module scope.\n */\n\nimport type { LogEvent } from '../api/types.js';\n\nexport interface BatcherOptions {\n /** Maximum events per batch. Positive integer in [1, 1000]. */\n maxBatchSize: number;\n /**\n * Optional age trigger. When set, a one-shot timer fires `maxBatchAgeMs`\n * ms after the first event enters an empty batch, flushing whatever is\n * pending. Cancelled on any other flush trigger.\n */\n maxBatchAgeMs?: number;\n /**\n * Callback invoked with the drained event array on every flush trigger\n * (size-threshold, age-timer, manual `flush()`). Throws are swallowed\n * — the events are considered lost from the batcher's perspective.\n */\n flush: (events: LogEvent[]) => void;\n}\n\nexport interface Batcher {\n /** Push an event onto the buffer. May trigger an immediate flush. */\n push(event: LogEvent): void;\n /** Drain whatever is pending. No-op when the buffer is empty. */\n flush(): void;\n /** Cancel the age timer and inhibit further flush callbacks. */\n shutdown(): void;\n}\n\nexport function createBatcher(opts: BatcherOptions): Batcher {\n const buffer: LogEvent[] = [];\n let maxAgeTimer: ReturnType<typeof setTimeout> | null = null;\n let flushCallback: ((events: LogEvent[]) => void) | null = opts.flush;\n\n const clearTimer = (): void => {\n if (maxAgeTimer !== null) {\n clearTimeout(maxAgeTimer);\n maxAgeTimer = null;\n }\n };\n\n const armTimer = (): void => {\n if (opts.maxBatchAgeMs === undefined) return;\n maxAgeTimer = setTimeout(() => {\n maxAgeTimer = null;\n doFlush();\n }, opts.maxBatchAgeMs);\n };\n\n const doFlush = (): void => {\n if (buffer.length === 0) return;\n // Post-shutdown: discard the buffered events silently. The caller\n // is responsible for draining via `flush()` before `shutdown()`\n // if it wants the data delivered.\n if (flushCallback === null) {\n buffer.length = 0;\n clearTimer();\n return;\n }\n // B-5c: copy + clear BEFORE the user callback runs, so a\n // re-entrant push during the callback starts a fresh batch.\n const events = buffer.slice();\n buffer.length = 0;\n clearTimer();\n try {\n flushCallback(events);\n } catch {\n // B-5d: swallow. The events are gone — they were copied out and\n // the consumer's callback chose to throw. Re-pushing into the\n // buffer would conflict with the \"single-flush-attempt\" model\n // in contracts/batching.md B-5.\n }\n };\n\n return {\n push(event: LogEvent): void {\n // Post-shutdown sends are silently dropped. The transport-level\n // shutdownComplete flag (T028) handles the same guarantee at\n // the createBeaconTransport boundary; this is defense in depth.\n if (flushCallback === null) return;\n buffer.push(event);\n // B-8a: arm timer when first event enters an empty batch.\n if (buffer.length === 1 && opts.maxBatchAgeMs !== undefined) {\n armTimer();\n }\n // B-5b: size threshold reached → flush synchronously at the end\n // of this push call.\n if (buffer.length >= opts.maxBatchSize) {\n doFlush();\n }\n },\n flush(): void {\n doFlush();\n },\n shutdown(): void {\n clearTimer();\n // Discard any buffered events. The caller (T028's beacon-\n // transport.ts shutdown handler) calls flush() before shutdown()\n // if it wants the pending batch drained.\n buffer.length = 0;\n flushCallback = null;\n },\n };\n}\n","/**\n * Low-level delivery primitives for the beacon transport.\n *\n * The functions here are deliberately small and composable. The\n * `beacon-transport.ts` factory (T016) wires them into the per-event\n * delivery policy defined in `contracts/delivery.md` D-3..D-7:\n *\n * 1. Compute `payload = JSON.stringify(event)`.\n * 2. If `getPayloadByteLength(payload) > BEACON_SIZE_LIMIT_BYTES`:\n * drop the event and fire `oversized_event` (F-2).\n * 3. `tryBeacon(endpoint, payload)`. On `true`, done.\n * 4. `await tryFetchKeepalive(endpoint, payload)`. On `true`, done.\n * On `false`, drop with `transport_send_failed` (F-4). On reject,\n * drop with `transport_send_failed` carrying `.cause` (F-7).\n *\n * Boundary discipline (TB-11): zero imports from `src/internal/**`,\n * `src/runtime/**`, `src/pipeline/**`, `src/config/**`, `src/context/**`,\n * or `src/transport/**`. Zero vendor-SDK imports.\n *\n * Specs: `specs/002-beacon-transport/contracts/delivery.md` D-2..D-7;\n * `specs/002-beacon-transport/research.md` §1, §2.\n */\n\n/**\n * The effective per-call `navigator.sendBeacon` size budget. ~64 KiB\n * per origin in modern browsers (research §1). Bodies whose serialized\n * byte length exceeds this constant short-circuit to `oversized_event`\n * without invoking either primitive — the fetch keepalive fallback\n * shares the same budget (research §2) so attempting it would waste a\n * network call on a guaranteed-failure payload.\n */\nexport const BEACON_SIZE_LIMIT_BYTES = 65536;\n\n/**\n * Return the UTF-8 byte length of `payload`. Uses `TextEncoder` because\n * `payload.length` is UTF-16 code-unit count, not byte count — and\n * `sendBeacon`'s budget is measured in bytes.\n *\n * `TextEncoder` is baseline-available in every modern browser this\n * package targets; it is the only ambient API the transport touches at\n * the delivery layer.\n */\nexport function getPayloadByteLength(payload: string): number {\n return new TextEncoder().encode(payload).length;\n}\n\n/**\n * Attempt delivery via `navigator.sendBeacon(endpoint, blob)` where\n * `blob` is `new Blob([payload], { type: 'application/json' })`. The\n * `Blob` form is required so the browser sends the body as\n * `application/json` and so the request is unambiguously body-only\n * (T-S1..T-S5).\n *\n * Returns:\n * - `true` — the browser accepted the payload onto the beacon queue.\n * - `false` — the browser refused (size limit / queue full / etc.),\n * `navigator.sendBeacon` is unavailable, OR a synchronous\n * throw was caught internally. Either way the caller\n * should fall through to `tryFetchKeepalive`.\n *\n * Never throws. Never returns a Promise.\n */\nexport function tryBeacon(endpoint: string, payload: string): boolean {\n const nav = (globalThis as { navigator?: Navigator }).navigator;\n if (nav === undefined || typeof nav.sendBeacon !== 'function') {\n return false;\n }\n try {\n const blob = new Blob([payload], { type: 'application/json' });\n return nav.sendBeacon(endpoint, blob);\n } catch {\n // Some legacy environments throw on oversized sendBeacon payloads\n // instead of returning false. Treat as \"refused\" so the caller\n // falls through; the same caller's size pre-check already short-\n // circuited the oversized case for modern runtimes.\n return false;\n }\n}\n\n/**\n * Attempt delivery via `fetch(endpoint, { method: 'POST', body: payload,\n * keepalive: true, headers: { 'content-type': 'application/json' },\n * credentials: 'same-origin' })`.\n *\n * Resolves:\n * - `true` — `fetch` resolved with `response.ok` (status in\n * `[200, 299]`).\n * - `false` — `fetch` is unavailable in the runtime, OR `fetch`\n * resolved with a non-2xx status.\n *\n * Rejects:\n * - With the underlying `fetch` rejection reason if the Promise\n * rejected (network error, browser-enforced budget overflow, etc.).\n * The caller wraps this in a `BeaconError(transport_send_failed)`\n * carrying `.cause` per F-7.\n *\n * The `credentials: 'same-origin'` choice keeps cookies from leaking\n * cross-origin by default (Principle IV). Consumers who need credentialed\n * delivery to a same-origin endpoint inherit the correct behaviour\n * automatically; cross-origin endpoints get no cookies, which is the\n * safer default.\n */\nexport async function tryFetchKeepalive(\n endpoint: string,\n payload: string,\n): Promise<boolean> {\n const fetchFn = (globalThis as { fetch?: typeof fetch }).fetch;\n if (typeof fetchFn !== 'function') {\n return false;\n }\n const response = await fetchFn(endpoint, {\n method: 'POST',\n body: payload,\n headers: { 'content-type': 'application/json' },\n keepalive: true,\n credentials: 'same-origin',\n });\n return response.ok;\n}\n","/**\n * Construction-time endpoint validation for `createBeaconTransport`.\n *\n * Locked behaviour:\n * - HTTPS endpoints always pass.\n * - HTTP endpoints pass IFF `allowInsecureLoopback === true` AND the\n * parsed URL's hostname is in `{ localhost, 127.0.0.1, [::1] }`.\n * - Every other case throws a typed error at construction time, before\n * any logger derives the runtime and before any listener is\n * attached (FR-016).\n *\n * The function is pure and side-effect-free: it parses the endpoint via\n * `new URL(...)` and inspects the result. It MUST NOT read ambient state\n * (no `process.env`, no `window.location`, no build-define plugin), and\n * MUST NOT read `allowInsecureLoopback` from anywhere except the\n * argument the caller passed (FR-016 clarification).\n *\n * Specs: `specs/002-beacon-transport/contracts/failure-modes.md` F-1;\n * `specs/002-beacon-transport/contracts/transport-beacon-public-api.md`\n * TB-5; `specs/002-beacon-transport/spec.md` FR-016.\n */\n\n/**\n * The exact set of hostnames permitted under\n * `allowInsecureLoopback: true`. WHATWG URL normalises `http://[::1]`\n * and `http://[0:0:0:0:0:0:0:1]` to hostname `[::1]` (with brackets),\n * so the allowlist matches the canonical form.\n */\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n]);\n\n/**\n * Validate a consumer-supplied `endpoint` string. Returns the parsed\n * `URL` on success. Throws on every form of violation.\n *\n * The thrown error's `.message` always names (a) the violated\n * constraint and (b) the offending endpoint string, so the consumer\n * can act on the diagnostic without parsing the error's stack.\n */\nexport function validateEndpoint(\n endpoint: unknown,\n allowInsecureLoopback: boolean,\n): URL {\n if (typeof endpoint !== 'string') {\n throw new TypeError(\n `beacon transport: endpoint must be a string, got ${typeName(endpoint)}`,\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(endpoint);\n } catch {\n throw new TypeError(\n `beacon transport: invalid endpoint URL: '${endpoint}'`,\n );\n }\n\n if (parsed.protocol === 'https:') {\n return parsed;\n }\n\n if (parsed.protocol === 'http:') {\n if (!allowInsecureLoopback) {\n throw new Error(\n `beacon transport refuses non-HTTPS endpoint '${endpoint}'`,\n );\n }\n if (!LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error(\n `beacon transport: allowInsecureLoopback permits only ` +\n `localhost / 127.0.0.1 / [::1]; got '${parsed.hostname}' ` +\n `in '${endpoint}'`,\n );\n }\n return parsed;\n }\n\n throw new Error(\n `beacon transport refuses non-HTTPS endpoint '${endpoint}' ` +\n `(scheme '${parsed.protocol}' is not permitted)`,\n );\n}\n\nfunction typeName(value: unknown): string {\n if (value === null) return 'null';\n return typeof value;\n}\n","/**\n * Subpath-owned diagnostic error class.\n *\n * Drop notices fired by the beacon transport are `BeaconError` instances\n * (a subclass of `Error`) owned by this subpath. They are NOT\n * `PackageError` instances and do NOT depend on the core's\n * `src/internal/errors/internal-errors.ts` module — preserving the\n * boundary in TB-11 (no runtime imports from `src/internal/**`).\n *\n * The class shape (`.code`, `.transportName`, optional `.cause`) is\n * **by-convention compatible** with `PackageError` so a consumer's\n * diagnostics handler reading `err.code` and `err.transportName` cannot\n * tell the difference between a notice emitted by `SafeTransport`\n * (a `PackageError`) and one emitted by the beacon transport\n * (a `BeaconError`).\n *\n * This module is INTERNAL to the subpath — `src/transport-beacon/index.ts`\n * does NOT re-export `BeaconError` or `BeaconErrorCode`. The\n * `onInternalError` hook receives the instance typed as `Error` per the\n * public callback signature.\n *\n * Specs: `specs/002-beacon-transport/data-model.md` § BeaconError;\n * `specs/002-beacon-transport/contracts/failure-modes.md` F-1..F-10.\n */\n\n/**\n * Documented `BeaconError.code` values. Internal to the subpath; the\n * public consumer surface for these strings is the\n * `BeaconTransportOptions.onInternalError` callback, where the value\n * arrives as `err.code` on an `Error`-shaped argument.\n */\nexport type BeaconErrorCode =\n | 'oversized_event'\n | 'beacon_batch_drop'\n | 'beacon_unavailable'\n | 'transport_send_failed'\n | 'transport_shutdown_failed';\n\n/**\n * Subclass of `Error` carrying a discriminating `.code`, the originating\n * transport's `.transportName`, and an optional `.cause` chain to the\n * underlying failure (a rejected `fetch` Promise, a thrown\n * `JSON.stringify` error, etc.).\n *\n * Construction-time invariants:\n * - `.code` is read-only (set via `Object.defineProperty` on the class\n * side by `readonly` + plain assignment).\n * - `.transportName` is read-only.\n * - `.cause` is read-only when provided (set via `Object.defineProperty`\n * with `writable: false`, `enumerable: true`). When `cause` is\n * `undefined`, the property is left unset — matching the ES2022\n * `Error.cause` convention where omitted causes are absent rather\n * than `undefined`.\n * - `.name` is `'BeaconError'`.\n */\nexport class BeaconError extends Error {\n readonly code: BeaconErrorCode;\n readonly transportName: string;\n // ES2022 standard Error.cause — declared as readonly so this\n // typechecks regardless of the current `lib` setting in tsconfig and\n // so the type system reflects the runtime non-writable contract.\n declare readonly cause?: unknown;\n\n constructor(\n code: BeaconErrorCode,\n transportName: string,\n message: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = 'BeaconError';\n this.code = code;\n this.transportName = transportName;\n if (cause !== undefined) {\n Object.defineProperty(this, 'cause', {\n value: cause,\n enumerable: true,\n writable: false,\n configurable: false,\n });\n }\n }\n}\n\n/**\n * Type guard for `BeaconError` instances. Distinguishes errors created\n * by this subpath from arbitrary errors that flow through the\n * `onInternalError` callback.\n */\nexport function isBeaconError(value: unknown): value is BeaconError {\n return value instanceof BeaconError;\n}\n","/**\n * Lazy lifecycle helper for the beacon transport's `pagehide` handler.\n *\n * Per FR-008 and contract D-10, the transport MUST NOT attach any\n * global listener at construction time. The `pagehide` listener\n * attaches on the first `send()` call that proceeds past the payload\n * size check, and detaches on `shutdown()`. This module supplies the\n * install/uninstall primitives; the caller (the transport factory)\n * owns the `installed` flag that gates against double-install.\n *\n * Why a separate module: keeps `beacon-transport.ts` focused on the\n * delivery policy, and makes the `globalThis.addEventListener`\n * boundary easy to spy on in tests.\n *\n * Boundary discipline (TB-11): zero imports from anywhere in `src/`,\n * zero vendor-SDK imports. The module is import-pure — no side effects\n * at module-evaluation time.\n *\n * Specs: `specs/002-beacon-transport/contracts/delivery.md` D-10;\n * `specs/002-beacon-transport/research.md` §3.\n */\n\n/**\n * Install a `pagehide` listener on the global event target if one is\n * available, returning an `uninstall()` function that removes exactly\n * that listener.\n *\n * If `globalThis.addEventListener` is not a function (vanishingly rare\n * outside of bare Node-like runtimes), this function is a no-op and\n * returns a no-op uninstaller. The caller's `installed` flag is the\n * authoritative single-install guard — this module does NOT track\n * installation state itself.\n *\n * Uninstall is idempotent at the DOM level: calling `removeEventListener`\n * a second time after the handler is already detached is a no-op\n * specified by the DOM standard.\n */\nexport function installPagehideHandler(handler: () => void): () => void {\n const target = globalThis as {\n addEventListener?: typeof globalThis.addEventListener;\n removeEventListener?: typeof globalThis.removeEventListener;\n };\n if (typeof target.addEventListener !== 'function') {\n return noopUninstall;\n }\n target.addEventListener('pagehide', handler);\n return (): void => {\n if (typeof target.removeEventListener === 'function') {\n target.removeEventListener('pagehide', handler);\n }\n };\n}\n\nfunction noopUninstall(): void {\n // intentionally empty\n}\n","/**\n * Default-mode `createBeaconTransport` factory.\n *\n * Composes the primitives from T004 (BeaconError), T005\n * (validateEndpoint), T006 (delivery primitives), and T007\n * (installPagehideHandler) into the `Transport`-shaped factory the\n * `./transport-beacon` subpath exports.\n *\n * Per-event delivery policy (D-3..D-7, F-2..F-7):\n *\n * send(event)\n * ├── if shutdownComplete: no-op\n * ├── payload = JSON.stringify(event) // F-4 cause if throws\n * ├── if size > 64 KiB → oversized_event drop // F-2 (D-3)\n * ├── lazy install pagehide listener // FR-008 / D-10\n * ├── if !sendBeacon AND !fetch → beacon_unavailable drop // F-3 (D-7)\n * ├── tryBeacon(endpoint, payload) // D-4\n * │ └── true ────────────────────────────── delivered\n * └── tryFetchKeepalive(endpoint, payload) // D-5, D-6\n * ├── 2xx ───────────────────────────────── delivered\n * ├── non-2xx → transport_send_failed // F-4\n * └── reject → transport_send_failed // F-4, F-7 (with .cause)\n *\n * Every notice is rate-limited per `state.notified[code]` — one\n * notice per failure class per transport instance per session (F-8).\n *\n * The factory NEVER throws from `send()`, `flush()`, or `shutdown()`.\n * Construction-time errors (invalid options, non-HTTPS endpoint) are\n * the only throws — and they happen at the consumer's call site,\n * outside the emit hot path.\n *\n * Boundary discipline (TB-11): the only `src/` import in this file\n * is `import type` from `'../api/types.js'`; the other imports are\n * intra-subpath.\n *\n * Specs: `specs/002-beacon-transport/contracts/delivery.md` D-1..D-12;\n * `specs/002-beacon-transport/contracts/failure-modes.md` F-1..F-10;\n * `specs/002-beacon-transport/data-model.md` § BeaconTransportState.\n */\n\nimport type { LogEvent, Transport } from '../api/types.js';\n\nimport { type Batcher, createBatcher } from './batcher.js';\nimport {\n BEACON_SIZE_LIMIT_BYTES,\n getPayloadByteLength,\n tryBeacon,\n tryFetchKeepalive,\n} from './delivery.js';\nimport { validateEndpoint } from './endpoint-validation.js';\nimport { BeaconError, type BeaconErrorCode } from './errors.js';\nimport { installPagehideHandler } from './lifecycle.js';\n\n// ---------------------------------------------------------------------------\n// Public options shape (data-model.md § BeaconTransportOptions)\n// ---------------------------------------------------------------------------\n\nexport interface BeaconTransportOptions {\n endpoint: string;\n batching?: {\n maxBatchSize: number;\n maxBatchAgeMs?: number;\n };\n allowInsecureLoopback?: boolean;\n name?: string;\n onInternalError?: (err: Error) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal per-instance state (data-model.md § BeaconTransportState)\n// ---------------------------------------------------------------------------\n\ninterface BeaconTransportState {\n readonly endpoint: string;\n readonly name: string;\n readonly onInternalError: (err: Error) => void;\n readonly batching: BeaconTransportOptions['batching'] | undefined;\n /** Batcher instance when batching is enabled; `null` in default mode. */\n batcher: Batcher | null;\n pagehideInstalled: boolean;\n pagehideUninstall: (() => void) | null;\n shutdownComplete: boolean;\n notified: {\n oversized_event: boolean;\n beacon_unavailable: boolean;\n transport_send_failed: boolean;\n beacon_batch_drop: boolean;\n transport_shutdown_failed: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Construction-time validation (F-1, TB-6)\n// ---------------------------------------------------------------------------\n\nfunction validateOptions(options: BeaconTransportOptions): void {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('beacon transport: options must be a non-null object');\n }\n\n if (options.batching !== undefined) {\n if (typeof options.batching !== 'object' || options.batching === null) {\n throw new TypeError(\n 'beacon transport: options.batching must be an object',\n );\n }\n const { maxBatchSize, maxBatchAgeMs } = options.batching;\n if (\n !Number.isInteger(maxBatchSize) ||\n maxBatchSize < 1 ||\n maxBatchSize > 1000\n ) {\n throw new RangeError(\n `beacon transport: batching.maxBatchSize must be an integer in [1, 1000], got ${String(maxBatchSize)}`,\n );\n }\n if (maxBatchAgeMs !== undefined) {\n if (!Number.isFinite(maxBatchAgeMs) || maxBatchAgeMs < 0) {\n throw new RangeError(\n `beacon transport: batching.maxBatchAgeMs must be a non-negative finite number, got ${String(maxBatchAgeMs)}`,\n );\n }\n }\n }\n\n if (\n options.allowInsecureLoopback !== undefined &&\n typeof options.allowInsecureLoopback !== 'boolean'\n ) {\n throw new TypeError(\n `beacon transport: allowInsecureLoopback must be a boolean, got ${typeof options.allowInsecureLoopback}`,\n );\n }\n\n if (\n options.name !== undefined &&\n (typeof options.name !== 'string' || options.name.length === 0)\n ) {\n throw new TypeError('beacon transport: name must be a non-empty string');\n }\n\n if (\n options.onInternalError !== undefined &&\n typeof options.onInternalError !== 'function'\n ) {\n throw new TypeError('beacon transport: onInternalError must be a function');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Notice routing (F-2..F-7, F-8 rate-limit)\n// ---------------------------------------------------------------------------\n\nfunction notify(\n state: BeaconTransportState,\n code: BeaconErrorCode,\n message: string,\n cause?: unknown,\n): void {\n if (state.notified[code]) return;\n state.notified[code] = true;\n const err = new BeaconError(code, state.name, message, cause);\n try {\n state.onInternalError(err);\n } catch {\n // Consumer's onInternalError threw. Swallow — we cannot re-enter the\n // same callback in response to a failed notification (FR-003).\n }\n}\n\nfunction notifyOversized(\n state: BeaconTransportState,\n event: LogEvent,\n bytes: number,\n): void {\n if (state.notified.oversized_event) return;\n state.notified.oversized_event = true;\n // Truncate the event message to 256 chars (F-2 notice integrity: never\n // include attrs/error/context — only the message, bounded).\n const messagePreview =\n event.message.length > 256 ? event.message.slice(0, 256) : event.message;\n const err = new BeaconError(\n 'oversized_event',\n state.name,\n `beacon transport '${state.name}' dropped oversized event: bytes=${bytes}, message=${messagePreview}`,\n );\n try {\n state.onInternalError(err);\n } catch {\n // swallow per FR-003\n }\n}\n\n/**\n * Fire a `beacon_batch_drop` notice for a failed batch flush. The\n * notice payload is **structural only** (B-11): droppedCount + a short\n * reason summary + the transport name. No event content (no message,\n * no attrs, no error, no context) — including such content would risk\n * leaking the same payload whose size or shape was the problem in the\n * first place.\n */\nfunction notifyBatchDrop(\n state: BeaconTransportState,\n droppedCount: number,\n reason: string,\n cause?: unknown,\n): void {\n if (state.notified.beacon_batch_drop) return;\n state.notified.beacon_batch_drop = true;\n const err = new BeaconError(\n 'beacon_batch_drop',\n state.name,\n `beacon transport '${state.name}' dropped batch: droppedCount=${droppedCount}, reason=${reason}`,\n cause,\n );\n try {\n state.onInternalError(err);\n } catch {\n // swallow per FR-003\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a body-only HTTPS beacon transport conforming to the\n * `Transport` contract from `@your-org/frontend-logging-sdk`. Pass the\n * result directly to `configureLogging({ transports: [...] })`.\n *\n * Construction throws synchronously on every form of invalid input\n * (non-string endpoint, malformed URL, non-HTTPS endpoint without\n * `allowInsecureLoopback`, batching fields out of range, etc.) — see\n * `contracts/failure-modes.md` F-1.\n *\n * Once constructed, `send()` / `flush()` / `shutdown()` NEVER throw\n * to the caller. Every drop routes through `options.onInternalError`.\n */\nexport function createBeaconTransport(\n options: BeaconTransportOptions,\n): Transport {\n validateOptions(options);\n\n const allowInsecureLoopback = options.allowInsecureLoopback ?? false;\n const parsedEndpoint = validateEndpoint(\n options.endpoint,\n allowInsecureLoopback,\n );\n // We keep the raw endpoint string (not parsedEndpoint.toString()) so the\n // wire matches exactly what the consumer supplied — important for ingestion\n // endpoints that are path-sensitive.\n void parsedEndpoint;\n\n const state: BeaconTransportState = {\n endpoint: options.endpoint,\n name: options.name ?? 'beacon',\n onInternalError: options.onInternalError ?? noopOnInternalError,\n batching: options.batching,\n batcher: null, // assigned below when batching is enabled\n pagehideInstalled: false,\n pagehideUninstall: null,\n shutdownComplete: false,\n notified: {\n oversized_event: false,\n beacon_unavailable: false,\n transport_send_failed: false,\n beacon_batch_drop: false,\n transport_shutdown_failed: false,\n },\n };\n\n // Batching is opt-in. When enabled, the batcher owns the in-memory\n // buffer; `send()` pushes per-event-sized payloads onto it and the\n // batcher invokes `dispatchBatch` on every flush trigger\n // (size-threshold, age-timer, manual `flush()`, `shutdown()`,\n // pagehide). When disabled, `send()` dispatches the event\n // immediately via `dispatchOne`.\n if (options.batching !== undefined) {\n const batcherOptions: Parameters<typeof createBatcher>[0] = {\n maxBatchSize: options.batching.maxBatchSize,\n flush: (events) => {\n dispatchBatch(state, events);\n },\n };\n if (options.batching.maxBatchAgeMs !== undefined) {\n batcherOptions.maxBatchAgeMs = options.batching.maxBatchAgeMs;\n }\n state.batcher = createBatcher(batcherOptions);\n }\n\n const pagehideHandler = (): void => {\n // Default mode: no buffered state to flush. Batching mode: drain\n // the pending batch via the batcher's flush API, which routes\n // through `dispatchBatch` for the same envelope encoding +\n // primitive dispatch as a size/age-triggered flush (B-9).\n if (state.batcher !== null) {\n state.batcher.flush();\n }\n };\n\n function ensureLazyInstall(): void {\n if (state.pagehideInstalled) return;\n state.pagehideInstalled = true;\n state.pagehideUninstall = installPagehideHandler(pagehideHandler);\n }\n\n function send(event: LogEvent): void {\n if (state.shutdownComplete) return;\n\n let payload: string;\n try {\n payload = JSON.stringify(event);\n } catch (cause) {\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: JSON.stringify threw on the event`,\n cause,\n );\n return;\n }\n\n const bytes = getPayloadByteLength(payload);\n if (bytes > BEACON_SIZE_LIMIT_BYTES) {\n // B-7 / F-2: oversized single event is ejected pre-push with one\n // oversized_event notice (rate-limited per session). In batching\n // mode this guarantees the envelope is constructed from\n // bounded-size events only; the per-envelope size check (B-6)\n // still catches over-aggressive maxBatchSize settings.\n notifyOversized(state, event, bytes);\n return;\n }\n\n ensureLazyInstall();\n\n if (state.batcher !== null) {\n // Batching mode: buffer the event. The batcher decides when to\n // flush (size threshold, optional age timer, pagehide, shutdown).\n state.batcher.push(event);\n return;\n }\n\n dispatchOne(state, payload);\n }\n\n function flush(): Promise<void> {\n // Default mode: no buffer to drain.\n // Batching mode: trigger an immediate flush of the current batch.\n if (state.batcher !== null) {\n state.batcher.flush();\n }\n return Promise.resolve();\n }\n\n function shutdown(): Promise<void> {\n if (state.shutdownComplete) return Promise.resolve();\n state.shutdownComplete = true;\n\n // Best-effort drain of the pending batch BEFORE detaching the\n // listener (B-10). The batcher's flush callback fires either a\n // successful delivery or a `beacon_batch_drop` notice. After\n // `batcher.shutdown()`, the batcher discards any further events\n // and a queued timer callback becomes a no-op.\n if (state.batcher !== null) {\n state.batcher.flush();\n state.batcher.shutdown();\n }\n\n if (state.pagehideUninstall !== null) {\n try {\n state.pagehideUninstall();\n } catch (cause) {\n notify(\n state,\n 'transport_shutdown_failed',\n `beacon transport '${state.name}' shutdown: listener removal threw`,\n cause,\n );\n }\n state.pagehideUninstall = null;\n state.pagehideInstalled = false;\n }\n return Promise.resolve();\n }\n\n // -------------------------------------------------------------------------\n // Dispatch helpers (closure-scoped — read state via `state` reference)\n // -------------------------------------------------------------------------\n\n function dispatchOne(state: BeaconTransportState, payload: string): void {\n const { hasSendBeacon, hasFetch } = primitiveAvailability();\n\n if (!hasSendBeacon && !hasFetch) {\n notify(\n state,\n 'beacon_unavailable',\n `beacon transport '${state.name}' has no usable delivery primitive (sendBeacon and fetch both unavailable)`,\n );\n return;\n }\n\n if (hasSendBeacon && tryBeacon(state.endpoint, payload)) {\n return; // delivered\n }\n\n if (hasFetch) {\n tryFetchKeepalive(state.endpoint, payload).then(\n (ok) => {\n if (!ok) {\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: fetch fallback resolved with non-2xx`,\n );\n }\n },\n (cause: unknown) => {\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: fetch fallback rejected`,\n cause,\n );\n },\n );\n return;\n }\n\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: sendBeacon returned false and fetch fallback is unavailable`,\n );\n }\n\n function dispatchBatch(\n state: BeaconTransportState,\n events: LogEvent[],\n ): void {\n if (events.length === 0) return;\n\n // Encode envelope. Failure here is unexpected (the events came\n // straight from the pipeline) — handle defensively as a batch\n // drop with the original cause attached.\n let envelope: string;\n try {\n envelope = JSON.stringify({ events });\n } catch (cause) {\n notifyBatchDrop(\n state,\n events.length,\n 'JSON.stringify threw on the envelope',\n cause,\n );\n return;\n }\n\n const bytes = getPayloadByteLength(envelope);\n if (bytes > BEACON_SIZE_LIMIT_BYTES) {\n // B-6: oversized envelope (consumer's maxBatchSize was too\n // aggressive for the average event size). No primitive call —\n // both sendBeacon and fetch-keepalive share the same per-origin\n // ~64 KiB budget so attempting them would waste a network call.\n notifyBatchDrop(\n state,\n events.length,\n `envelope size ${bytes} bytes exceeds ${BEACON_SIZE_LIMIT_BYTES} bytes`,\n );\n return;\n }\n\n const { hasSendBeacon, hasFetch } = primitiveAvailability();\n\n if (!hasSendBeacon && !hasFetch) {\n notify(\n state,\n 'beacon_unavailable',\n `beacon transport '${state.name}' has no usable delivery primitive (sendBeacon and fetch both unavailable)`,\n );\n return;\n }\n\n if (hasSendBeacon && tryBeacon(state.endpoint, envelope)) {\n return; // delivered\n }\n\n if (hasFetch) {\n tryFetchKeepalive(state.endpoint, envelope).then(\n (ok) => {\n if (!ok) {\n notifyBatchDrop(\n state,\n events.length,\n `fetch fallback resolved with non-2xx`,\n );\n }\n },\n (cause: unknown) => {\n notifyBatchDrop(\n state,\n events.length,\n `fetch fallback rejected`,\n cause,\n );\n },\n );\n return;\n }\n\n notifyBatchDrop(\n state,\n events.length,\n `sendBeacon returned false and fetch fallback is unavailable`,\n );\n }\n\n function primitiveAvailability(): {\n hasSendBeacon: boolean;\n hasFetch: boolean;\n } {\n const nav = (globalThis as { navigator?: Navigator }).navigator;\n const hasSendBeacon =\n nav !== undefined && typeof nav.sendBeacon === 'function';\n const hasFetch =\n typeof (globalThis as { fetch?: typeof fetch }).fetch === 'function';\n return { hasSendBeacon, hasFetch };\n }\n\n return {\n name: state.name,\n send,\n flush,\n shutdown,\n };\n}\n\nfunction noopOnInternalError(_err: Error): void {\n // intentional default — see BeaconTransportOptions.onInternalError docs.\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/transport-beacon/batcher.ts","../src/transport-beacon/delivery.ts","../src/transport-beacon/endpoint-validation.ts","../src/transport-beacon/errors.ts","../src/transport-beacon/lifecycle.ts","../src/transport-beacon/beacon-transport.ts"],"names":["state"],"mappings":"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","file":"transport-beacon.mjs","sourcesContent":["/**\n * Batcher state machine for opt-in beacon-transport batching.\n *\n * T027 replaces T024's stub with the working state machine. T028\n * wires this batcher into `createBeaconTransport`'s send path.\n *\n * Behaviour (data-model.md § BatcherOptions + contracts/batching.md):\n *\n * - `push(event)` appends to the in-memory buffer.\n * - If this is the first event in an empty batch AND\n * `maxBatchAgeMs` is set, arm a one-shot timer.\n * - If the buffer reaches `maxBatchSize`, flush synchronously\n * at the end of `push`.\n * - `flush()` drains any pending buffer through the consumer's\n * `flush` callback. Empty buffer → no-op.\n * - `shutdown()` cancels the timer and nulls the flush callback\n * reference, so any timer/microtask queued before shutdown\n * becomes a no-op when it fires.\n *\n * Buffer-clear ordering (B-5c): the buffer is cleared BEFORE the\n * consumer's flush callback is invoked. A re-entrant `push()`\n * during the callback sees an empty buffer and starts a fresh\n * batch — no double-flush, no re-push.\n *\n * Throw safety (B-5d): if the consumer's flush callback throws,\n * the throw is swallowed. The events that were drained into the\n * callback are gone — the batcher does NOT re-push them. The\n * batcher itself never throws from `push`/`flush`/`shutdown`.\n *\n * Boundary discipline (TB-11): the only `src/` import is\n * `import type` from `'../api/types.js'`. The module is import-pure\n * — no listeners at module scope, no timers at module scope.\n */\n\nimport type { LogEvent } from '../api/types.js';\n\nexport interface BatcherOptions {\n /** Maximum events per batch. Positive integer in [1, 1000]. */\n maxBatchSize: number;\n /**\n * Optional age trigger. When set, a one-shot timer fires `maxBatchAgeMs`\n * ms after the first event enters an empty batch, flushing whatever is\n * pending. Cancelled on any other flush trigger.\n */\n maxBatchAgeMs?: number;\n /**\n * Callback invoked with the drained event array on every flush trigger\n * (size-threshold, age-timer, manual `flush()`). Throws are swallowed\n * — the events are considered lost from the batcher's perspective.\n */\n flush: (events: LogEvent[]) => void;\n}\n\nexport interface Batcher {\n /** Push an event onto the buffer. May trigger an immediate flush. */\n push(event: LogEvent): void;\n /** Drain whatever is pending. No-op when the buffer is empty. */\n flush(): void;\n /** Cancel the age timer and inhibit further flush callbacks. */\n shutdown(): void;\n}\n\nexport function createBatcher(opts: BatcherOptions): Batcher {\n const buffer: LogEvent[] = [];\n let maxAgeTimer: ReturnType<typeof setTimeout> | null = null;\n let flushCallback: ((events: LogEvent[]) => void) | null = opts.flush;\n\n const clearTimer = (): void => {\n if (maxAgeTimer !== null) {\n clearTimeout(maxAgeTimer);\n maxAgeTimer = null;\n }\n };\n\n const armTimer = (): void => {\n if (opts.maxBatchAgeMs === undefined) return;\n maxAgeTimer = setTimeout(() => {\n maxAgeTimer = null;\n doFlush();\n }, opts.maxBatchAgeMs);\n };\n\n const doFlush = (): void => {\n if (buffer.length === 0) return;\n // Post-shutdown: discard the buffered events silently. The caller\n // is responsible for draining via `flush()` before `shutdown()`\n // if it wants the data delivered.\n if (flushCallback === null) {\n buffer.length = 0;\n clearTimer();\n return;\n }\n // B-5c: copy + clear BEFORE the user callback runs, so a\n // re-entrant push during the callback starts a fresh batch.\n const events = buffer.slice();\n buffer.length = 0;\n clearTimer();\n try {\n flushCallback(events);\n } catch {\n // B-5d: swallow. The events are gone — they were copied out and\n // the consumer's callback chose to throw. Re-pushing into the\n // buffer would conflict with the \"single-flush-attempt\" model\n // in contracts/batching.md B-5.\n }\n };\n\n return {\n push(event: LogEvent): void {\n // Post-shutdown sends are silently dropped. The transport-level\n // shutdownComplete flag (T028) handles the same guarantee at\n // the createBeaconTransport boundary; this is defense in depth.\n if (flushCallback === null) return;\n buffer.push(event);\n // B-8a: arm timer when first event enters an empty batch.\n if (buffer.length === 1 && opts.maxBatchAgeMs !== undefined) {\n armTimer();\n }\n // B-5b: size threshold reached → flush synchronously at the end\n // of this push call.\n if (buffer.length >= opts.maxBatchSize) {\n doFlush();\n }\n },\n flush(): void {\n doFlush();\n },\n shutdown(): void {\n clearTimer();\n // Discard any buffered events. The caller (T028's beacon-\n // transport.ts shutdown handler) calls flush() before shutdown()\n // if it wants the pending batch drained.\n buffer.length = 0;\n flushCallback = null;\n },\n };\n}\n","/**\n * Low-level delivery primitives for the beacon transport.\n *\n * The functions here are deliberately small and composable. The\n * `beacon-transport.ts` factory (T016) wires them into the per-event\n * delivery policy defined in `contracts/delivery.md` D-3..D-7:\n *\n * 1. Compute `payload = JSON.stringify(event)`.\n * 2. If `getPayloadByteLength(payload) > BEACON_SIZE_LIMIT_BYTES`:\n * drop the event and fire `oversized_event` (F-2).\n * 3. `tryBeacon(endpoint, payload)`. On `true`, done.\n * 4. `await tryFetchKeepalive(endpoint, payload)`. On `true`, done.\n * On `false`, drop with `transport_send_failed` (F-4). On reject,\n * drop with `transport_send_failed` carrying `.cause` (F-7).\n *\n * Boundary discipline (TB-11): zero imports from `src/internal/**`,\n * `src/runtime/**`, `src/pipeline/**`, `src/config/**`, `src/context/**`,\n * or `src/transport/**`. Zero vendor-SDK imports.\n *\n * Specs: `specs/002-beacon-transport/contracts/delivery.md` D-2..D-7;\n * `specs/002-beacon-transport/research.md` §1, §2.\n */\n\n/**\n * The effective per-call `navigator.sendBeacon` size budget. ~64 KiB\n * per origin in modern browsers (research §1). Bodies whose serialized\n * byte length exceeds this constant short-circuit to `oversized_event`\n * without invoking either primitive — the fetch keepalive fallback\n * shares the same budget (research §2) so attempting it would waste a\n * network call on a guaranteed-failure payload.\n */\nexport const BEACON_SIZE_LIMIT_BYTES = 65536;\n\n/**\n * Return the UTF-8 byte length of `payload`. Uses `TextEncoder` because\n * `payload.length` is UTF-16 code-unit count, not byte count — and\n * `sendBeacon`'s budget is measured in bytes.\n *\n * `TextEncoder` is baseline-available in every modern browser this\n * package targets; it is the only ambient API the transport touches at\n * the delivery layer.\n */\nexport function getPayloadByteLength(payload: string): number {\n return new TextEncoder().encode(payload).length;\n}\n\n/**\n * Attempt delivery via `navigator.sendBeacon(endpoint, blob)` where\n * `blob` is `new Blob([payload], { type: 'application/json' })`. The\n * `Blob` form is required so the browser sends the body as\n * `application/json` and so the request is unambiguously body-only\n * (T-S1..T-S5).\n *\n * Returns:\n * - `true` — the browser accepted the payload onto the beacon queue.\n * - `false` — the browser refused (size limit / queue full / etc.),\n * `navigator.sendBeacon` is unavailable, OR a synchronous\n * throw was caught internally. Either way the caller\n * should fall through to `tryFetchKeepalive`.\n *\n * Never throws. Never returns a Promise.\n */\nexport function tryBeacon(endpoint: string, payload: string): boolean {\n const nav = (globalThis as { navigator?: Navigator }).navigator;\n if (nav === undefined || typeof nav.sendBeacon !== 'function') {\n return false;\n }\n try {\n const blob = new Blob([payload], { type: 'application/json' });\n return nav.sendBeacon(endpoint, blob);\n } catch {\n // Some legacy environments throw on oversized sendBeacon payloads\n // instead of returning false. Treat as \"refused\" so the caller\n // falls through; the same caller's size pre-check already short-\n // circuited the oversized case for modern runtimes.\n return false;\n }\n}\n\n/**\n * Attempt delivery via `fetch(endpoint, { method: 'POST', body: payload,\n * keepalive: true, headers: { 'content-type': 'application/json' },\n * credentials: 'same-origin' })`.\n *\n * Resolves:\n * - `true` — `fetch` resolved with `response.ok` (status in\n * `[200, 299]`).\n * - `false` — `fetch` is unavailable in the runtime, OR `fetch`\n * resolved with a non-2xx status.\n *\n * Rejects:\n * - With the underlying `fetch` rejection reason if the Promise\n * rejected (network error, browser-enforced budget overflow, etc.).\n * The caller wraps this in a `BeaconError(transport_send_failed)`\n * carrying `.cause` per F-7.\n *\n * The `credentials: 'same-origin'` choice keeps cookies from leaking\n * cross-origin by default (Principle V). Consumers who need credentialed\n * delivery to a same-origin endpoint inherit the correct behaviour\n * automatically; cross-origin endpoints get no cookies, which is the\n * safer default.\n */\nexport async function tryFetchKeepalive(\n endpoint: string,\n payload: string,\n): Promise<boolean> {\n const fetchFn = (globalThis as { fetch?: typeof fetch }).fetch;\n if (typeof fetchFn !== 'function') {\n return false;\n }\n const response = await fetchFn(endpoint, {\n method: 'POST',\n body: payload,\n headers: { 'content-type': 'application/json' },\n keepalive: true,\n credentials: 'same-origin',\n });\n return response.ok;\n}\n","/**\n * Construction-time endpoint validation for `createBeaconTransport`.\n *\n * Locked behaviour:\n * - HTTPS endpoints always pass.\n * - HTTP endpoints pass IFF `allowInsecureLoopback === true` AND the\n * parsed URL's hostname is in `{ localhost, 127.0.0.1, [::1] }`.\n * - Every other case throws a typed error at construction time, before\n * any logger derives the runtime and before any listener is\n * attached (FR-016).\n *\n * The function is pure and side-effect-free: it parses the endpoint via\n * `new URL(...)` and inspects the result. It MUST NOT read ambient state\n * (no `process.env`, no `window.location`, no build-define plugin), and\n * MUST NOT read `allowInsecureLoopback` from anywhere except the\n * argument the caller passed (FR-016 clarification).\n *\n * Specs: `specs/002-beacon-transport/contracts/failure-modes.md` F-1;\n * `specs/002-beacon-transport/contracts/transport-beacon-public-api.md`\n * TB-5; `specs/002-beacon-transport/spec.md` FR-016.\n */\n\n/**\n * The exact set of hostnames permitted under\n * `allowInsecureLoopback: true`. WHATWG URL normalises `http://[::1]`\n * and `http://[0:0:0:0:0:0:0:1]` to hostname `[::1]` (with brackets),\n * so the allowlist matches the canonical form.\n */\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n]);\n\n/**\n * Validate a consumer-supplied `endpoint` string. Returns the parsed\n * `URL` on success. Throws on every form of violation.\n *\n * The thrown error's `.message` always names (a) the violated\n * constraint and (b) the offending endpoint string, so the consumer\n * can act on the diagnostic without parsing the error's stack.\n */\nexport function validateEndpoint(\n endpoint: unknown,\n allowInsecureLoopback: boolean,\n): URL {\n if (typeof endpoint !== 'string') {\n throw new TypeError(\n `beacon transport: endpoint must be a string, got ${typeName(endpoint)}`,\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(endpoint);\n } catch {\n throw new TypeError(\n `beacon transport: invalid endpoint URL: '${endpoint}'`,\n );\n }\n\n if (parsed.protocol === 'https:') {\n return parsed;\n }\n\n if (parsed.protocol === 'http:') {\n if (!allowInsecureLoopback) {\n throw new Error(\n `beacon transport refuses non-HTTPS endpoint '${endpoint}'`,\n );\n }\n if (!LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error(\n `beacon transport: allowInsecureLoopback permits only ` +\n `localhost / 127.0.0.1 / [::1]; got '${parsed.hostname}' ` +\n `in '${endpoint}'`,\n );\n }\n return parsed;\n }\n\n throw new Error(\n `beacon transport refuses non-HTTPS endpoint '${endpoint}' ` +\n `(scheme '${parsed.protocol}' is not permitted)`,\n );\n}\n\nfunction typeName(value: unknown): string {\n if (value === null) return 'null';\n return typeof value;\n}\n","/**\n * Subpath-owned diagnostic error class.\n *\n * Drop notices fired by the beacon transport are `BeaconError` instances\n * (a subclass of `Error`) owned by this subpath. They are NOT\n * `PackageError` instances and do NOT depend on the core's\n * `src/internal/errors/internal-errors.ts` module — preserving the\n * boundary in TB-11 (no runtime imports from `src/internal/**`).\n *\n * The class shape (`.code`, `.transportName`, optional `.cause`) is\n * **by-convention compatible** with `PackageError` so a consumer's\n * diagnostics handler reading `err.code` and `err.transportName` cannot\n * tell the difference between a notice emitted by `SafeTransport`\n * (a `PackageError`) and one emitted by the beacon transport\n * (a `BeaconError`).\n *\n * This module is INTERNAL to the subpath — `src/transport-beacon/index.ts`\n * does NOT re-export `BeaconError` or `BeaconErrorCode`. The\n * `onInternalError` hook receives the instance typed as `Error` per the\n * public callback signature.\n *\n * Specs: `specs/002-beacon-transport/data-model.md` § BeaconError;\n * `specs/002-beacon-transport/contracts/failure-modes.md` F-1..F-10.\n */\n\n/**\n * Documented `BeaconError.code` values. Internal to the subpath; the\n * public consumer surface for these strings is the\n * `BeaconTransportOptions.onInternalError` callback, where the value\n * arrives as `err.code` on an `Error`-shaped argument.\n */\nexport type BeaconErrorCode =\n | 'oversized_event'\n | 'beacon_batch_drop'\n | 'beacon_unavailable'\n | 'transport_send_failed'\n | 'transport_shutdown_failed';\n\n/**\n * Subclass of `Error` carrying a discriminating `.code`, the originating\n * transport's `.transportName`, and an optional `.cause` chain to the\n * underlying failure (a rejected `fetch` Promise, a thrown\n * `JSON.stringify` error, etc.).\n *\n * Construction-time invariants:\n * - `.code` is read-only (set via `Object.defineProperty` on the class\n * side by `readonly` + plain assignment).\n * - `.transportName` is read-only.\n * - `.cause` is read-only when provided (set via `Object.defineProperty`\n * with `writable: false`, `enumerable: true`). When `cause` is\n * `undefined`, the property is left unset — matching the ES2022\n * `Error.cause` convention where omitted causes are absent rather\n * than `undefined`.\n * - `.name` is `'BeaconError'`.\n */\nexport class BeaconError extends Error {\n readonly code: BeaconErrorCode;\n readonly transportName: string;\n // ES2022 standard Error.cause — declared as readonly so this\n // typechecks regardless of the current `lib` setting in tsconfig and\n // so the type system reflects the runtime non-writable contract.\n declare readonly cause?: unknown;\n\n constructor(\n code: BeaconErrorCode,\n transportName: string,\n message: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = 'BeaconError';\n this.code = code;\n this.transportName = transportName;\n if (cause !== undefined) {\n Object.defineProperty(this, 'cause', {\n value: cause,\n enumerable: true,\n writable: false,\n configurable: false,\n });\n }\n }\n}\n\n/**\n * Type guard for `BeaconError` instances. Distinguishes errors created\n * by this subpath from arbitrary errors that flow through the\n * `onInternalError` callback.\n */\nexport function isBeaconError(value: unknown): value is BeaconError {\n return value instanceof BeaconError;\n}\n","/**\n * Lazy lifecycle helper for the beacon transport's `pagehide` handler.\n *\n * Per FR-008 and contract D-10, the transport MUST NOT attach any\n * global listener at construction time. The `pagehide` listener\n * attaches on the first `send()` call that proceeds past the payload\n * size check, and detaches on `shutdown()`. This module supplies the\n * install/uninstall primitives; the caller (the transport factory)\n * owns the `installed` flag that gates against double-install.\n *\n * Why a separate module: keeps `beacon-transport.ts` focused on the\n * delivery policy, and makes the `globalThis.addEventListener`\n * boundary easy to spy on in tests.\n *\n * Boundary discipline (TB-11): zero imports from anywhere in `src/`,\n * zero vendor-SDK imports. The module is import-pure — no side effects\n * at module-evaluation time.\n *\n * Specs: `specs/002-beacon-transport/contracts/delivery.md` D-10;\n * `specs/002-beacon-transport/research.md` §3.\n */\n\n/**\n * Install a `pagehide` listener on the global event target if one is\n * available, returning an `uninstall()` function that removes exactly\n * that listener.\n *\n * If `globalThis.addEventListener` is not a function (vanishingly rare\n * outside of bare Node-like runtimes), this function is a no-op and\n * returns a no-op uninstaller. The caller's `installed` flag is the\n * authoritative single-install guard — this module does NOT track\n * installation state itself.\n *\n * Uninstall is idempotent at the DOM level: calling `removeEventListener`\n * a second time after the handler is already detached is a no-op\n * specified by the DOM standard.\n */\nexport function installPagehideHandler(handler: () => void): () => void {\n const target = globalThis as {\n addEventListener?: typeof globalThis.addEventListener;\n removeEventListener?: typeof globalThis.removeEventListener;\n };\n if (typeof target.addEventListener !== 'function') {\n return noopUninstall;\n }\n target.addEventListener('pagehide', handler);\n return (): void => {\n if (typeof target.removeEventListener === 'function') {\n target.removeEventListener('pagehide', handler);\n }\n };\n}\n\nfunction noopUninstall(): void {\n // intentionally empty\n}\n","/**\n * Default-mode `createBeaconTransport` factory.\n *\n * Composes the primitives from T004 (BeaconError), T005\n * (validateEndpoint), T006 (delivery primitives), and T007\n * (installPagehideHandler) into the `Transport`-shaped factory the\n * `./transport-beacon` subpath exports.\n *\n * Per-event delivery policy (D-3..D-7, F-2..F-7):\n *\n * send(event)\n * ├── if shutdownComplete: no-op\n * ├── payload = JSON.stringify(event) // F-4 cause if throws\n * ├── if size > 64 KiB → oversized_event drop // F-2 (D-3)\n * ├── lazy install pagehide listener // FR-008 / D-10\n * ├── if !sendBeacon AND !fetch → beacon_unavailable drop // F-3 (D-7)\n * ├── tryBeacon(endpoint, payload) // D-4\n * │ └── true ────────────────────────────── delivered\n * └── tryFetchKeepalive(endpoint, payload) // D-5, D-6\n * ├── 2xx ───────────────────────────────── delivered\n * ├── non-2xx → transport_send_failed // F-4\n * └── reject → transport_send_failed // F-4, F-7 (with .cause)\n *\n * Every notice is rate-limited per `state.notified[code]` — one\n * notice per failure class per transport instance per session (F-8).\n *\n * The factory NEVER throws from `send()`, `flush()`, or `shutdown()`.\n * Construction-time errors (invalid options, non-HTTPS endpoint) are\n * the only throws — and they happen at the consumer's call site,\n * outside the emit hot path.\n *\n * Boundary discipline (TB-11): the only `src/` import in this file\n * is `import type` from `'../api/types.js'`; the other imports are\n * intra-subpath.\n *\n * Specs: `specs/002-beacon-transport/contracts/delivery.md` D-1..D-12;\n * `specs/002-beacon-transport/contracts/failure-modes.md` F-1..F-10;\n * `specs/002-beacon-transport/data-model.md` § BeaconTransportState.\n */\n\nimport type { LogEvent, Transport } from '../api/types.js';\n\nimport { type Batcher, createBatcher } from './batcher.js';\nimport {\n BEACON_SIZE_LIMIT_BYTES,\n getPayloadByteLength,\n tryBeacon,\n tryFetchKeepalive,\n} from './delivery.js';\nimport { validateEndpoint } from './endpoint-validation.js';\nimport { BeaconError, type BeaconErrorCode } from './errors.js';\nimport { installPagehideHandler } from './lifecycle.js';\n\n// ---------------------------------------------------------------------------\n// Public options shape (data-model.md § BeaconTransportOptions)\n// ---------------------------------------------------------------------------\n\nexport interface BeaconTransportOptions {\n endpoint: string;\n batching?: {\n maxBatchSize: number;\n maxBatchAgeMs?: number;\n };\n allowInsecureLoopback?: boolean;\n name?: string;\n onInternalError?: (err: Error) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal per-instance state (data-model.md § BeaconTransportState)\n// ---------------------------------------------------------------------------\n\ninterface BeaconTransportState {\n readonly endpoint: string;\n readonly name: string;\n readonly onInternalError: (err: Error) => void;\n readonly batching: BeaconTransportOptions['batching'] | undefined;\n /** Batcher instance when batching is enabled; `null` in default mode. */\n batcher: Batcher | null;\n pagehideInstalled: boolean;\n pagehideUninstall: (() => void) | null;\n shutdownComplete: boolean;\n notified: {\n oversized_event: boolean;\n beacon_unavailable: boolean;\n transport_send_failed: boolean;\n beacon_batch_drop: boolean;\n transport_shutdown_failed: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Construction-time validation (F-1, TB-6)\n// ---------------------------------------------------------------------------\n\nfunction validateOptions(options: BeaconTransportOptions): void {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('beacon transport: options must be a non-null object');\n }\n\n if (options.batching !== undefined) {\n if (typeof options.batching !== 'object' || options.batching === null) {\n throw new TypeError(\n 'beacon transport: options.batching must be an object',\n );\n }\n const { maxBatchSize, maxBatchAgeMs } = options.batching;\n if (\n !Number.isInteger(maxBatchSize) ||\n maxBatchSize < 1 ||\n maxBatchSize > 1000\n ) {\n throw new RangeError(\n `beacon transport: batching.maxBatchSize must be an integer in [1, 1000], got ${String(maxBatchSize)}`,\n );\n }\n if (maxBatchAgeMs !== undefined) {\n if (!Number.isFinite(maxBatchAgeMs) || maxBatchAgeMs < 0) {\n throw new RangeError(\n `beacon transport: batching.maxBatchAgeMs must be a non-negative finite number, got ${String(maxBatchAgeMs)}`,\n );\n }\n }\n }\n\n if (\n options.allowInsecureLoopback !== undefined &&\n typeof options.allowInsecureLoopback !== 'boolean'\n ) {\n throw new TypeError(\n `beacon transport: allowInsecureLoopback must be a boolean, got ${typeof options.allowInsecureLoopback}`,\n );\n }\n\n if (\n options.name !== undefined &&\n (typeof options.name !== 'string' || options.name.length === 0)\n ) {\n throw new TypeError('beacon transport: name must be a non-empty string');\n }\n\n if (\n options.onInternalError !== undefined &&\n typeof options.onInternalError !== 'function'\n ) {\n throw new TypeError('beacon transport: onInternalError must be a function');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Notice routing (F-2..F-7, F-8 rate-limit)\n// ---------------------------------------------------------------------------\n\nfunction notify(\n state: BeaconTransportState,\n code: BeaconErrorCode,\n message: string,\n cause?: unknown,\n): void {\n if (state.notified[code]) return;\n state.notified[code] = true;\n const err = new BeaconError(code, state.name, message, cause);\n try {\n state.onInternalError(err);\n } catch {\n // Consumer's onInternalError threw. Swallow — we cannot re-enter the\n // same callback in response to a failed notification (FR-003).\n }\n}\n\nfunction notifyOversized(\n state: BeaconTransportState,\n event: LogEvent,\n bytes: number,\n): void {\n if (state.notified.oversized_event) return;\n state.notified.oversized_event = true;\n // Truncate the event message to 256 chars (F-2 notice integrity: never\n // include attrs/error/context — only the message, bounded).\n const messagePreview =\n event.message.length > 256 ? event.message.slice(0, 256) : event.message;\n const err = new BeaconError(\n 'oversized_event',\n state.name,\n `beacon transport '${state.name}' dropped oversized event: bytes=${bytes}, message=${messagePreview}`,\n );\n try {\n state.onInternalError(err);\n } catch {\n // swallow per FR-003\n }\n}\n\n/**\n * Fire a `beacon_batch_drop` notice for a failed batch flush. The\n * notice payload is **structural only** (B-11): droppedCount + a short\n * reason summary + the transport name. No event content (no message,\n * no attrs, no error, no context) — including such content would risk\n * leaking the same payload whose size or shape was the problem in the\n * first place.\n */\nfunction notifyBatchDrop(\n state: BeaconTransportState,\n droppedCount: number,\n reason: string,\n cause?: unknown,\n): void {\n if (state.notified.beacon_batch_drop) return;\n state.notified.beacon_batch_drop = true;\n const err = new BeaconError(\n 'beacon_batch_drop',\n state.name,\n `beacon transport '${state.name}' dropped batch: droppedCount=${droppedCount}, reason=${reason}`,\n cause,\n );\n try {\n state.onInternalError(err);\n } catch {\n // swallow per FR-003\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a body-only HTTPS beacon transport conforming to the\n * `Transport` contract from `@your-org/frontend-logging-sdk`. Pass the\n * result directly to `configureLogging({ transports: [...] })`.\n *\n * Construction throws synchronously on every form of invalid input\n * (non-string endpoint, malformed URL, non-HTTPS endpoint without\n * `allowInsecureLoopback`, batching fields out of range, etc.) — see\n * `contracts/failure-modes.md` F-1.\n *\n * Once constructed, `send()` / `flush()` / `shutdown()` NEVER throw\n * to the caller. Every drop routes through `options.onInternalError`.\n */\nexport function createBeaconTransport(\n options: BeaconTransportOptions,\n): Transport {\n validateOptions(options);\n\n const allowInsecureLoopback = options.allowInsecureLoopback ?? false;\n const parsedEndpoint = validateEndpoint(\n options.endpoint,\n allowInsecureLoopback,\n );\n // We keep the raw endpoint string (not parsedEndpoint.toString()) so the\n // wire matches exactly what the consumer supplied — important for ingestion\n // endpoints that are path-sensitive.\n void parsedEndpoint;\n\n const state: BeaconTransportState = {\n endpoint: options.endpoint,\n name: options.name ?? 'beacon',\n onInternalError: options.onInternalError ?? noopOnInternalError,\n batching: options.batching,\n batcher: null, // assigned below when batching is enabled\n pagehideInstalled: false,\n pagehideUninstall: null,\n shutdownComplete: false,\n notified: {\n oversized_event: false,\n beacon_unavailable: false,\n transport_send_failed: false,\n beacon_batch_drop: false,\n transport_shutdown_failed: false,\n },\n };\n\n // Batching is opt-in. When enabled, the batcher owns the in-memory\n // buffer; `send()` pushes per-event-sized payloads onto it and the\n // batcher invokes `dispatchBatch` on every flush trigger\n // (size-threshold, age-timer, manual `flush()`, `shutdown()`,\n // pagehide). When disabled, `send()` dispatches the event\n // immediately via `dispatchOne`.\n if (options.batching !== undefined) {\n const batcherOptions: Parameters<typeof createBatcher>[0] = {\n maxBatchSize: options.batching.maxBatchSize,\n flush: (events) => {\n dispatchBatch(state, events);\n },\n };\n if (options.batching.maxBatchAgeMs !== undefined) {\n batcherOptions.maxBatchAgeMs = options.batching.maxBatchAgeMs;\n }\n state.batcher = createBatcher(batcherOptions);\n }\n\n const pagehideHandler = (): void => {\n // Default mode: no buffered state to flush. Batching mode: drain\n // the pending batch via the batcher's flush API, which routes\n // through `dispatchBatch` for the same envelope encoding +\n // primitive dispatch as a size/age-triggered flush (B-9).\n if (state.batcher !== null) {\n state.batcher.flush();\n }\n };\n\n function ensureLazyInstall(): void {\n if (state.pagehideInstalled) return;\n state.pagehideInstalled = true;\n state.pagehideUninstall = installPagehideHandler(pagehideHandler);\n }\n\n function send(event: LogEvent): void {\n if (state.shutdownComplete) return;\n\n let payload: string;\n try {\n payload = JSON.stringify(event);\n } catch (cause) {\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: JSON.stringify threw on the event`,\n cause,\n );\n return;\n }\n\n const bytes = getPayloadByteLength(payload);\n if (bytes > BEACON_SIZE_LIMIT_BYTES) {\n // B-7 / F-2: oversized single event is ejected pre-push with one\n // oversized_event notice (rate-limited per session). In batching\n // mode this guarantees the envelope is constructed from\n // bounded-size events only; the per-envelope size check (B-6)\n // still catches over-aggressive maxBatchSize settings.\n notifyOversized(state, event, bytes);\n return;\n }\n\n ensureLazyInstall();\n\n if (state.batcher !== null) {\n // Batching mode: buffer the event. The batcher decides when to\n // flush (size threshold, optional age timer, pagehide, shutdown).\n state.batcher.push(event);\n return;\n }\n\n dispatchOne(state, payload);\n }\n\n function flush(): Promise<void> {\n // Default mode: no buffer to drain.\n // Batching mode: trigger an immediate flush of the current batch.\n if (state.batcher !== null) {\n state.batcher.flush();\n }\n return Promise.resolve();\n }\n\n function shutdown(): Promise<void> {\n if (state.shutdownComplete) return Promise.resolve();\n state.shutdownComplete = true;\n\n // Best-effort drain of the pending batch BEFORE detaching the\n // listener (B-10). The batcher's flush callback fires either a\n // successful delivery or a `beacon_batch_drop` notice. After\n // `batcher.shutdown()`, the batcher discards any further events\n // and a queued timer callback becomes a no-op.\n if (state.batcher !== null) {\n state.batcher.flush();\n state.batcher.shutdown();\n }\n\n if (state.pagehideUninstall !== null) {\n try {\n state.pagehideUninstall();\n } catch (cause) {\n notify(\n state,\n 'transport_shutdown_failed',\n `beacon transport '${state.name}' shutdown: listener removal threw`,\n cause,\n );\n }\n state.pagehideUninstall = null;\n state.pagehideInstalled = false;\n }\n return Promise.resolve();\n }\n\n // -------------------------------------------------------------------------\n // Dispatch helpers (closure-scoped — read state via `state` reference)\n // -------------------------------------------------------------------------\n\n function dispatchOne(state: BeaconTransportState, payload: string): void {\n const { hasSendBeacon, hasFetch } = primitiveAvailability();\n\n if (!hasSendBeacon && !hasFetch) {\n notify(\n state,\n 'beacon_unavailable',\n `beacon transport '${state.name}' has no usable delivery primitive (sendBeacon and fetch both unavailable)`,\n );\n return;\n }\n\n if (hasSendBeacon && tryBeacon(state.endpoint, payload)) {\n return; // delivered\n }\n\n if (hasFetch) {\n tryFetchKeepalive(state.endpoint, payload).then(\n (ok) => {\n if (!ok) {\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: fetch fallback resolved with non-2xx`,\n );\n }\n },\n (cause: unknown) => {\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: fetch fallback rejected`,\n cause,\n );\n },\n );\n return;\n }\n\n notify(\n state,\n 'transport_send_failed',\n `beacon transport '${state.name}' failed: sendBeacon returned false and fetch fallback is unavailable`,\n );\n }\n\n function dispatchBatch(\n state: BeaconTransportState,\n events: LogEvent[],\n ): void {\n if (events.length === 0) return;\n\n // Encode envelope. Failure here is unexpected (the events came\n // straight from the pipeline) — handle defensively as a batch\n // drop with the original cause attached.\n let envelope: string;\n try {\n envelope = JSON.stringify({ events });\n } catch (cause) {\n notifyBatchDrop(\n state,\n events.length,\n 'JSON.stringify threw on the envelope',\n cause,\n );\n return;\n }\n\n const bytes = getPayloadByteLength(envelope);\n if (bytes > BEACON_SIZE_LIMIT_BYTES) {\n // B-6: oversized envelope (consumer's maxBatchSize was too\n // aggressive for the average event size). No primitive call —\n // both sendBeacon and fetch-keepalive share the same per-origin\n // ~64 KiB budget so attempting them would waste a network call.\n notifyBatchDrop(\n state,\n events.length,\n `envelope size ${bytes} bytes exceeds ${BEACON_SIZE_LIMIT_BYTES} bytes`,\n );\n return;\n }\n\n const { hasSendBeacon, hasFetch } = primitiveAvailability();\n\n if (!hasSendBeacon && !hasFetch) {\n notify(\n state,\n 'beacon_unavailable',\n `beacon transport '${state.name}' has no usable delivery primitive (sendBeacon and fetch both unavailable)`,\n );\n return;\n }\n\n if (hasSendBeacon && tryBeacon(state.endpoint, envelope)) {\n return; // delivered\n }\n\n if (hasFetch) {\n tryFetchKeepalive(state.endpoint, envelope).then(\n (ok) => {\n if (!ok) {\n notifyBatchDrop(\n state,\n events.length,\n `fetch fallback resolved with non-2xx`,\n );\n }\n },\n (cause: unknown) => {\n notifyBatchDrop(\n state,\n events.length,\n `fetch fallback rejected`,\n cause,\n );\n },\n );\n return;\n }\n\n notifyBatchDrop(\n state,\n events.length,\n `sendBeacon returned false and fetch fallback is unavailable`,\n );\n }\n\n function primitiveAvailability(): {\n hasSendBeacon: boolean;\n hasFetch: boolean;\n } {\n const nav = (globalThis as { navigator?: Navigator }).navigator;\n const hasSendBeacon =\n nav !== undefined && typeof nav.sendBeacon === 'function';\n const hasFetch =\n typeof (globalThis as { fetch?: typeof fetch }).fetch === 'function';\n return { hasSendBeacon, hasFetch };\n }\n\n return {\n name: state.name,\n send,\n flush,\n shutdown,\n };\n}\n\nfunction noopOnInternalError(_err: Error): void {\n // intentional default — see BeaconTransportOptions.onInternalError docs.\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport-otlp/batcher.ts","../src/transport-otlp/delivery.ts","../src/transport-otlp/endpoint-validation.ts","../src/transport-otlp/errors.ts","../src/transport-otlp/attributes.ts","../src/transport-otlp/resource.ts","../src/transport-otlp/otlp-serializer.ts","../src/transport-otlp/traceparent-header.ts","../src/transport-otlp/otlp-transport.ts"],"names":[],"mappings":"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","file":"transport-otlp.cjs","sourcesContent":["/**\n * Bounded batch buffer for the OTLP transport.\n *\n * A parallel copy of the `./transport-beacon` batcher state machine (the\n * boundary rule forbids importing across subpaths — TO-7). Behaviour\n * (research D7, data-model § Batcher):\n *\n * - `push(event)` appends to the in-memory buffer.\n * - First event in an empty batch with `maxBatchAgeMs` set arms a\n * one-shot age timer.\n * - Reaching `maxBatchSize` flushes synchronously at end of `push`.\n * - `flush()` drains the pending buffer through the consumer callback.\n * Empty buffer → no-op.\n * - `shutdown()` cancels the timer and inhibits further callbacks.\n *\n * Buffer is copied + cleared BEFORE the callback runs, so a re-entrant\n * `push` during the callback starts a fresh batch. A throwing callback is\n * swallowed (the batcher never throws from push/flush/shutdown). The hard\n * `maxBufferedEvents` cap is enforced by the transport BEFORE `push`, so\n * the batcher itself stays simple.\n */\n\nimport type { LogEvent } from '../api/types.js';\n\nexport interface BatcherOptions {\n maxBatchSize: number;\n maxBatchAgeMs?: number;\n flush: (events: LogEvent[]) => void;\n}\n\nexport interface Batcher {\n push(event: LogEvent): void;\n flush(): void;\n shutdown(): void;\n /** Current pending count — used by the transport's buffer-cap guard. */\n size(): number;\n}\n\nexport function createBatcher(opts: BatcherOptions): Batcher {\n const buffer: LogEvent[] = [];\n let maxAgeTimer: ReturnType<typeof setTimeout> | null = null;\n let flushCallback: ((events: LogEvent[]) => void) | null = opts.flush;\n\n const clearTimer = (): void => {\n if (maxAgeTimer !== null) {\n clearTimeout(maxAgeTimer);\n maxAgeTimer = null;\n }\n };\n\n const armTimer = (): void => {\n if (opts.maxBatchAgeMs === undefined) return;\n maxAgeTimer = setTimeout(() => {\n maxAgeTimer = null;\n doFlush();\n }, opts.maxBatchAgeMs);\n };\n\n const doFlush = (): void => {\n if (buffer.length === 0) return;\n if (flushCallback === null) {\n buffer.length = 0;\n clearTimer();\n return;\n }\n const events = buffer.slice();\n buffer.length = 0;\n clearTimer();\n try {\n flushCallback(events);\n } catch {\n // Swallow: the events were copied out; the consumer callback chose\n // to throw. Single-flush-attempt model (no re-push, no retry).\n }\n };\n\n return {\n push(event: LogEvent): void {\n if (flushCallback === null) return;\n buffer.push(event);\n if (buffer.length === 1 && opts.maxBatchAgeMs !== undefined) {\n armTimer();\n }\n if (buffer.length >= opts.maxBatchSize) {\n doFlush();\n }\n },\n flush(): void {\n doFlush();\n },\n shutdown(): void {\n clearTimer();\n buffer.length = 0;\n flushCallback = null;\n },\n size(): number {\n return buffer.length;\n },\n };\n}\n","/**\n * Delivery primitive for the OTLP transport: POST the OTLP/HTTP+JSON body\n * via `fetch` with `keepalive: true` (research D6, TO-2).\n *\n * `navigator.sendBeacon` is deliberately NOT used — it cannot set the\n * custom auth + `Content-Type` headers OTLP backends require (FR-004).\n * `keepalive` preserves best-effort delivery during page unload.\n *\n * `deliver(...)` NEVER throws or rejects: every outcome (2xx, 2xx with an\n * OTLP partial-success rejection, non-2xx, network reject, missing\n * `fetch`) is reduced to a `DeliveryResult` for the caller to map onto a\n * rate-limited notice. There is no retry (research D7).\n *\n * Boundary discipline (TO-7): zero `src/` imports; zero vendor imports.\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md` OP-1;\n * `contracts/transport-otlp-public-api.md` TO-2/TO-4.\n */\n\n/** Outcome of a single delivery attempt. Never an exception. */\nexport type DeliveryResult =\n | { kind: 'delivered' }\n | { kind: 'unavailable' }\n | { kind: 'send_failed'; detail: string; cause?: unknown }\n | { kind: 'partial_rejection'; rejected: number };\n\n/**\n * POST `body` to `endpoint` with `keepalive: true`, merging the caller's\n * static `headers` (e.g. auth) over the mandatory `content-type`.\n * `credentials: 'same-origin'` keeps cookies from leaking cross-origin by\n * default (Principle IV); auth travels only in the explicit headers.\n */\nexport async function deliver(\n endpoint: string,\n headers: Readonly<Record<string, string>>,\n body: string,\n): Promise<DeliveryResult> {\n const fetchFn = (globalThis as { fetch?: typeof fetch }).fetch;\n if (typeof fetchFn !== 'function') {\n return { kind: 'unavailable' };\n }\n\n let response: Response;\n try {\n response = await fetchFn(endpoint, {\n method: 'POST',\n body,\n headers: { 'content-type': 'application/json', ...headers },\n keepalive: true,\n credentials: 'same-origin',\n });\n } catch (cause) {\n return { kind: 'send_failed', detail: 'fetch rejected', cause };\n }\n\n if (!response.ok) {\n return { kind: 'send_failed', detail: `HTTP ${response.status}` };\n }\n\n // 2xx — check for an OTLP partial-success rejection in the body. A\n // missing / non-JSON / unexpected body is treated as full success.\n const rejected = await readRejectedCount(response);\n if (rejected > 0) {\n return { kind: 'partial_rejection', rejected };\n }\n return { kind: 'delivered' };\n}\n\nasync function readRejectedCount(response: Response): Promise<number> {\n try {\n if (typeof response.json !== 'function') return 0;\n const parsed = (await response.json()) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return 0;\n const partial = (parsed as { partialSuccess?: unknown }).partialSuccess;\n if (typeof partial !== 'object' || partial === null) return 0;\n const raw = (partial as { rejectedLogRecords?: unknown })\n .rejectedLogRecords;\n // OTLP encodes int64 as a string in JSON, but tolerate a number too.\n const n = typeof raw === 'string' ? Number(raw) : raw;\n return typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : 0;\n } catch {\n // A body that cannot be read/parsed does not turn a 2xx into a failure.\n return 0;\n }\n}\n","/**\n * Construction-time endpoint validation for `createOtlpTransport`.\n *\n * Locked behaviour (mirrors `./transport-beacon`, TO-5 / research D8):\n * - HTTPS endpoints always pass.\n * - HTTP endpoints pass IFF `allowInsecureLoopback === true` AND the\n * parsed URL's hostname is in `{ localhost, 127.0.0.1, [::1] }`.\n * - Every other case throws a typed error at construction time, before\n * any logger derives the runtime and before any listener is attached.\n *\n * Pure and side-effect-free: parses via `new URL(...)` and inspects the\n * result. MUST NOT read ambient state (no `process.env`, no\n * `window.location`) and MUST NOT read `allowInsecureLoopback` from\n * anywhere except the argument the caller passed.\n *\n * The consumer supplies the FULL OTLP logs URL (e.g.\n * `https://otlp.example.com/v1/logs`); the transport appends nothing.\n *\n * Specs: `specs/007-transport-otlp/contracts/transport-otlp-public-api.md`\n * TO-5; `data-model.md` § validation rules.\n */\n\n/**\n * Hostnames permitted under `allowInsecureLoopback: true`. WHATWG URL\n * normalises `http://[::1]` to hostname `[::1]` (with brackets).\n */\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n]);\n\n/**\n * Validate a consumer-supplied `endpoint`. Returns the parsed `URL` on\n * success; throws on every violation. The thrown error's `.message` names\n * the violated constraint and the offending endpoint string.\n */\nexport function validateEndpoint(\n endpoint: unknown,\n allowInsecureLoopback: boolean,\n): URL {\n if (typeof endpoint !== 'string') {\n throw new TypeError(\n `otlp transport: endpoint must be a string, got ${typeName(endpoint)}`,\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(endpoint);\n } catch {\n throw new TypeError(`otlp transport: invalid endpoint URL: '${endpoint}'`);\n }\n\n if (parsed.protocol === 'https:') {\n return parsed;\n }\n\n if (parsed.protocol === 'http:') {\n if (!allowInsecureLoopback) {\n throw new Error(\n `otlp transport refuses non-HTTPS endpoint '${endpoint}'`,\n );\n }\n if (!LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error(\n `otlp transport: allowInsecureLoopback permits only ` +\n `localhost / 127.0.0.1 / [::1]; got '${parsed.hostname}' ` +\n `in '${endpoint}'`,\n );\n }\n return parsed;\n }\n\n throw new Error(\n `otlp transport refuses non-HTTPS endpoint '${endpoint}' ` +\n `(scheme '${parsed.protocol}' is not permitted)`,\n );\n}\n\nfunction typeName(value: unknown): string {\n if (value === null) return 'null';\n return typeof value;\n}\n","/**\n * Subpath-owned diagnostic error class + rate-limited notice helper for\n * the OTLP transport.\n *\n * Drop/diagnostic notices fired by the OTLP transport are `OtlpError`\n * instances (a subclass of `Error`) owned by this subpath — by-convention\n * compatible with the core's `PackageError` shape (`.code`,\n * `.transportName`, optional `.cause`) but with NO runtime import from\n * `src/internal/**` (TO-7). This module is INTERNAL to the subpath;\n * `src/transport-otlp/index.ts` does not re-export it. The\n * `onInternalError` hook receives the instance typed as `Error`.\n *\n * **Security (FR-009 / TO-6)**: notice messages MUST NEVER include any\n * configured request-header value. The helpers here only ever build\n * messages from the failure code, the transport name, and an optional\n * non-secret detail string supplied by the caller. Callers MUST NOT pass\n * header values into `detail`.\n *\n * Specs: `specs/007-transport-otlp/data-model.md` § OtlpFailureCode;\n * `contracts/transport-otlp-public-api.md` TO-4/TO-6.\n */\n\n/**\n * Documented `OtlpError.code` values — one per failure class. Surfaced to\n * the consumer as `err.code` on the `Error`-shaped `onInternalError`\n * argument. Each is rate-limited to one notice per class per transport\n * instance per session.\n */\nexport type OtlpFailureCode =\n | 'oversized_event'\n | 'buffer_overflow'\n | 'delivery_unavailable'\n | 'send_failed'\n | 'partial_rejection'\n | 'serialize_failed'\n | 'shutdown_failed';\n\n/**\n * Subclass of `Error` carrying a discriminating `.code`, the originating\n * transport's `.transportName`, and an optional `.cause` chain. `.name`\n * is `'OtlpError'`. `.cause` follows the ES2022 convention (absent when\n * not provided rather than `undefined`).\n */\nexport class OtlpError extends Error {\n readonly code: OtlpFailureCode;\n readonly transportName: string;\n declare readonly cause?: unknown;\n\n constructor(\n code: OtlpFailureCode,\n transportName: string,\n message: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = 'OtlpError';\n this.code = code;\n this.transportName = transportName;\n if (cause !== undefined) {\n Object.defineProperty(this, 'cause', {\n value: cause,\n enumerable: true,\n writable: false,\n configurable: false,\n });\n }\n }\n}\n\n/** Type guard for `OtlpError` instances. */\nexport function isOtlpError(value: unknown): value is OtlpError {\n return value instanceof OtlpError;\n}\n\n/**\n * Minimal shape the `notifyOnce` helper needs: the transport's name, the\n * consumer error sink, and a per-code \"already notified\" ledger. The\n * concrete `OtlpTransportState` (data-model) satisfies this.\n */\nexport interface NotifyContext {\n readonly name: string;\n readonly onInternalError: (err: Error) => void;\n readonly notified: Record<OtlpFailureCode, boolean>;\n}\n\n/**\n * Emit at most ONE diagnostic notice per failure `code` per context\n * (instance) per session (FR-010). Subsequent calls with the same code are\n * silently suppressed. The `onInternalError` callback is invoked inside a\n * try/catch so a throwing consumer handler can never propagate back into\n * the transport's hot path (Principle II).\n *\n * `detail` is an optional NON-SECRET human string (e.g. an HTTP status).\n * Callers MUST NOT pass any configured header/secret value here.\n */\nexport function notifyOnce(\n ctx: NotifyContext,\n code: OtlpFailureCode,\n message: string,\n cause?: unknown,\n): void {\n if (ctx.notified[code]) return;\n ctx.notified[code] = true;\n const err = new OtlpError(\n code,\n ctx.name,\n `otlp transport '${ctx.name}': ${message}`,\n cause,\n );\n try {\n ctx.onInternalError(err);\n } catch {\n // A throwing consumer error handler must never reach the caller.\n }\n}\n\n/** Build the per-instance \"already notified\" ledger with all flags false. */\nexport function freshNotifiedLedger(): Record<OtlpFailureCode, boolean> {\n return {\n oversized_event: false,\n buffer_overflow: false,\n delivery_unavailable: false,\n send_failed: false,\n partial_rejection: false,\n serialize_failed: false,\n shutdown_failed: false,\n };\n}\n","/**\n * Pure `AttributeValue` → OTLP `AnyValue` encoder + the shared OTLP-JSON\n * value types used across the subpath.\n *\n * The OTLP/HTTP+JSON value model is hand-encoded here with ZERO runtime\n * dependencies and no `@opentelemetry/*` import (research D1, TO-7). The\n * `AttributeValue` union (string | number | boolean | null | array |\n * object) maps 1:1 onto OTLP `AnyValue` (OP-5).\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md` OP-5.\n */\n\nimport type { AttributeValue } from '../api/types.js';\n\n/**\n * OTLP `AnyValue` (JSON encoding). `null` → `{}` (an unset value) since\n * OTLP has no explicit null. A non-finite or otherwise non-integer number\n * is a `doubleValue`; an integer-safe number is an `intValue` (string, per\n * the uint64/sint64-as-string rule).\n */\nexport type AnyValue =\n | { stringValue: string }\n | { boolValue: boolean }\n | { intValue: string }\n | { doubleValue: number }\n | { arrayValue: { values: AnyValue[] } }\n | { kvlistValue: { values: KeyValue[] } }\n // biome-ignore lint/complexity/noBannedTypes: OTLP represents an unset/null value as an empty AnyValue object.\n | {};\n\n/** OTLP `KeyValue`. */\nexport interface KeyValue {\n key: string;\n value: AnyValue;\n}\n\n/** Encode a single sanitized `AttributeValue` as an OTLP `AnyValue`. */\nexport function toAnyValue(value: AttributeValue): AnyValue {\n if (value === null) {\n return {};\n }\n switch (typeof value) {\n case 'string':\n return { stringValue: value };\n case 'boolean':\n return { boolValue: value };\n case 'number':\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value };\n default:\n break;\n }\n if (Array.isArray(value)) {\n return { arrayValue: { values: value.map(toAnyValue) } };\n }\n // Remaining case: a plain `{ [key: string]: AttributeValue }` object.\n return { kvlistValue: { values: toKeyValues(value) } };\n}\n\n/**\n * Encode a record of sanitized attributes as an OTLP `KeyValue[]`,\n * optionally prefixing each key (used to namespace merged context\n * attributes under `context.` — OP-4).\n */\nexport function toKeyValues(\n record: Readonly<Record<string, AttributeValue>>,\n keyPrefix = '',\n): KeyValue[] {\n const out: KeyValue[] = [];\n for (const key of Object.keys(record)) {\n const value = record[key];\n if (value === undefined) continue;\n out.push({ key: keyPrefix + key, value: toAnyValue(value) });\n }\n return out;\n}\n","/**\n * Map SafeSignal runtime-global identity to an OTLP `Resource` (OP-2 / D3).\n *\n * The `Resource` carries only identity that is constant across a batch\n * from one configured transport — `service.name`, `service.version`,\n * `deployment.environment` (standard OTel semantic-convention attributes\n * every OTLP backend understands). The federated `module.*` identity is\n * **per-logger** (it can differ between events in the same batch via\n * `withContext`), so it is attributed per-`LogRecord` (see\n * `otlp-serializer.ts`), not on the shared Resource — preserving correct\n * origin attribution (Principle VI).\n *\n * Only present fields are emitted (no empty/`null` keys). Pure and\n * dependency-free.\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md` OP-2.\n */\n\nimport type { LogContext } from '../api/types.js';\n\nimport type { KeyValue } from './attributes.js';\n\nexport interface OtlpResource {\n attributes: KeyValue[];\n}\n\n/**\n * Build the OTLP `Resource` for a batch from a representative context\n * (the batch's first event). `service.*` / `deployment.environment` are\n * runtime-global, so any event in the batch is representative.\n */\nexport function buildResource(context: LogContext): OtlpResource {\n const attributes: KeyValue[] = [];\n\n const push = (key: string, value: string | undefined): void => {\n if (typeof value === 'string' && value.length > 0) {\n attributes.push({ key, value: { stringValue: value } });\n }\n };\n\n push('service.name', context.application?.name);\n push('service.version', context.application?.version);\n push('deployment.environment', context.environment);\n\n return { attributes };\n}\n","/**\n * Pure `LogEvent[]` → OTLP/HTTP+JSON logs payload serializer (OP-1..OP-6).\n *\n * This is the heart of the subpath. It hand-builds the OTLP logs JSON\n * shape with ZERO runtime dependencies and NO `@opentelemetry/*` import\n * (research D1, TO-7): severity numbers are literal constants matching the\n * OTel `SeverityNumber` ranges (and the in-repo\n * `src/internal/telemetry/otel/mapping.ts` table), reused conceptually but\n * never imported.\n *\n * **Encoding seam (FR-015)**: `serializeBatch(...)` builds the encoder-\n * neutral `OtlpLogsRequest` object; `encode(...)` turns it into the wire\n * body. Today the only encoder is JSON; a future protobuf encoder slots in\n * behind `encode(...)` without changing the object model or the public\n * surface.\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md`.\n */\n\nimport type { LogContext, LogEvent, LogLevel } from '../api/types.js';\n\nimport { type AnyValue, type KeyValue, toKeyValues } from './attributes.js';\nimport { buildResource } from './resource.js';\n\n/** Constant instrumentation-scope name for every emitted ScopeLogs. */\nexport const SCOPE_NAME = '@tallyrow/safesignal';\n\n/**\n * OTLP `SeverityNumber` base value per SafeSignal level (OP-3 / D2). These\n * are the canonical OTLP range bases (DEBUG 5, INFO 9, WARN 13, ERROR 17)\n * and match `LEVEL_TO_SEVERITY` in the internal OTel seam.\n */\nconst LEVEL_TO_SEVERITY_NUMBER: Readonly<Record<LogLevel, number>> = {\n debug: 5,\n info: 9,\n warn: 13,\n error: 17,\n};\n\nconst LEVEL_TO_SEVERITY_TEXT: Readonly<Record<LogLevel, string>> = {\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARN',\n error: 'ERROR',\n};\n\n// ---------------------------------------------------------------------------\n// OTLP wire shapes (JSON encoding)\n// ---------------------------------------------------------------------------\n\nexport interface OtlpLogRecord {\n timeUnixNano: string;\n observedTimeUnixNano: string;\n severityNumber: number;\n severityText: string;\n body: AnyValue;\n attributes: KeyValue[];\n /** W3C trace correlation — present only when `event.context.trace` is set. */\n traceId?: string;\n spanId?: string;\n flags?: number;\n}\n\nexport interface OtlpScopeLogs {\n scope: { name: string };\n logRecords: OtlpLogRecord[];\n}\n\nexport interface OtlpResourceLogs {\n resource: { attributes: KeyValue[] };\n scopeLogs: OtlpScopeLogs[];\n}\n\nexport interface OtlpLogsRequest {\n resourceLogs: OtlpResourceLogs[];\n}\n\n// ---------------------------------------------------------------------------\n// Mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Convert one `LogEvent` to an OTLP `LogRecord`. Never mutates the input\n * (T-S4). Per-record attributes are: event attributes, then merged context\n * attributes under a `context.` prefix, then per-logger `module.*`\n * identity, then `exception.*` when an error is present (OP-4).\n *\n * `fallbackTimeMs` is a single resolved time used when `event.timestamp`\n * is unparseable, so the mapping never throws on a bad timestamp (OP-3).\n */\nexport function toLogRecord(\n event: LogEvent,\n fallbackTimeMs: number,\n): OtlpLogRecord {\n const ms = toEpochMs(event.timestamp, fallbackTimeMs);\n const nano = String(ms * 1_000_000);\n\n const attributes: KeyValue[] = toKeyValues(event.attributes);\n\n const context = event.context;\n if (context.attributes !== undefined) {\n attributes.push(...toKeyValues(context.attributes, 'context.'));\n }\n pushModuleIdentity(attributes, context);\n pushException(attributes, event);\n\n const record: OtlpLogRecord = {\n timeUnixNano: nano,\n observedTimeUnixNano: nano,\n severityNumber: LEVEL_TO_SEVERITY_NUMBER[event.level],\n severityText: LEVEL_TO_SEVERITY_TEXT[event.level],\n body: { stringValue: event.message },\n attributes,\n };\n\n // W3C trace correlation → OTLP standard top-level fields (OP/OT contracts).\n // The structured ids are already lowercase-hex (validated upstream), so they\n // are the OTLP/JSON encoding as-is — no base64, no @opentelemetry import.\n const trace = context.trace;\n if (trace !== undefined) {\n record.traceId = trace.traceId;\n record.spanId = trace.spanId;\n if (trace.traceFlags !== undefined) {\n record.flags = trace.traceFlags;\n }\n }\n\n return record;\n}\n\n/**\n * Build the encoder-neutral OTLP logs request object for a batch. The\n * Resource is derived from the first event's runtime-global identity; if\n * the batch is empty, an empty Resource is used.\n */\nexport function serializeBatch(\n batch: ReadonlyArray<LogEvent>,\n fallbackTimeMs: number,\n): OtlpLogsRequest {\n const first = batch[0];\n const resource = buildResource(first ? first.context : ({} as LogContext));\n const logRecords = batch.map((e) => toLogRecord(e, fallbackTimeMs));\n return {\n resourceLogs: [\n {\n resource,\n scopeLogs: [{ scope: { name: SCOPE_NAME }, logRecords }],\n },\n ],\n };\n}\n\n/**\n * Encoding seam (FR-015). Turns the request object into the wire body.\n * The only encoding in this feature is JSON; protobuf is a roadmap\n * follow-up that slots in here without touching callers.\n */\nexport function encode(request: OtlpLogsRequest): string {\n return JSON.stringify(request);\n}\n\n/**\n * Convenience: build + JSON-encode a batch in one call. Pure; never\n * mutates inputs.\n */\nexport function serializeOtlpJson(\n batch: ReadonlyArray<LogEvent>,\n fallbackTimeMs: number,\n): string {\n return encode(serializeBatch(batch, fallbackTimeMs));\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction toEpochMs(iso: string, fallbackMs: number): number {\n const parsed = Date.parse(iso);\n return Number.isFinite(parsed) ? parsed : fallbackMs;\n}\n\nfunction pushModuleIdentity(out: KeyValue[], context: LogContext): void {\n const mod = context.module;\n if (mod === undefined) return;\n if (typeof mod.name === 'string' && mod.name.length > 0) {\n out.push({ key: 'module.name', value: { stringValue: mod.name } });\n }\n if (typeof mod.version === 'string' && mod.version.length > 0) {\n out.push({ key: 'module.version', value: { stringValue: mod.version } });\n }\n}\n\nfunction pushException(out: KeyValue[], event: LogEvent): void {\n const err = event.error;\n if (err === undefined) return;\n out.push({ key: 'exception.type', value: { stringValue: err.name } });\n out.push({ key: 'exception.message', value: { stringValue: err.message } });\n if (typeof err.stack === 'string' && err.stack.length > 0) {\n out.push({\n key: 'exception.stacktrace',\n value: { stringValue: err.stack },\n });\n }\n}\n","/**\n * Outbound W3C `traceparent` header injection for the `./transport-otlp`\n * delivery path (Feature 009).\n *\n * When `injectTraceparent` is enabled, a delivery request whose batch all\n * belongs to ONE valid trace gets a standard W3C `traceparent` (and, when\n * uniform, `tracestate`) request header so the ingest request is joinable to\n * its trace. SafeSignal is **carry-only**: this reads the already-normalized\n * `event.context.trace` (Feature 008 validates it once per emit, before any\n * transport sees the event) and never mints ids.\n *\n * Policy (homogeneous-only, fail-closed — contracts TI-3..TI-6, research D3/D4):\n * - Per-event key = `none` when `context.trace` is absent OR structurally\n * invalid (defensive guard for events that reach the transport without\n * passing emit-time normalization), else the full `traceparent` string\n * (so differing flags ⇒ differing keys ⇒ not homogeneous).\n * - Inject `traceparent` iff the batch is non-empty AND every key is the\n * same non-`none` value. Empty / trace-less / heterogeneous ⇒ no header.\n * - Inject `tracestate` iff `traceparent` is injected AND every event shares\n * the same defined `traceState` within `MAX_TRACESTATE_LEN`; else omit it\n * while keeping `traceparent` (optional part dropped, valid ids kept).\n * - Consumer `options.headers` win on any collision (spread last), so the\n * injected header can never overwrite/expose an auth/secret value.\n *\n * Boundary discipline (TO-7): the only import is a **type-only** import from\n * `../api/types.js`. No `../trace/`, no `@opentelemetry/*`. Pure; never throws.\n *\n * Specs: `specs/009-traceparent-injection/contracts/traceparent-injection.md`.\n */\n\nimport type { LogEvent, TraceContext } from '../api/types.js';\n\n/** Max `tracestate` length (W3C caps the header at 512 chars), mirrored here. */\nconst MAX_TRACESTATE_LEN = 512;\n\nconst TRACE_ID_RE = /^[0-9a-f]{32}$/;\nconst SPAN_ID_RE = /^[0-9a-f]{16}$/;\nconst ALL_ZERO_TRACE_ID = '0'.repeat(32);\nconst ALL_ZERO_SPAN_ID = '0'.repeat(16);\n\n/** Outcome of evaluating one delivery batch. */\nexport type BatchTraceparentDecision =\n | { inject: false }\n | { inject: true; traceparent: string; tracestate?: string };\n\ninterface ResolvedTrace {\n /** `'none'`, or the `traceparent` string (which also uniquely keys the trace). */\n readonly key: string;\n /** The `traceparent` string when valid, else `null`. */\n readonly traceparent: string | null;\n /** A bounded, non-empty `traceState`, else `null`. */\n readonly traceState: string | null;\n}\n\nconst NONE: ResolvedTrace = {\n key: 'none',\n traceparent: null,\n traceState: null,\n};\n\nfunction hasValidIds(trace: TraceContext): boolean {\n return (\n typeof trace.traceId === 'string' &&\n TRACE_ID_RE.test(trace.traceId) &&\n trace.traceId !== ALL_ZERO_TRACE_ID &&\n typeof trace.spanId === 'string' &&\n SPAN_ID_RE.test(trace.spanId) &&\n trace.spanId !== ALL_ZERO_SPAN_ID\n );\n}\n\nfunction flagsHex(traceFlags: number | undefined): string {\n const n =\n typeof traceFlags === 'number' &&\n Number.isInteger(traceFlags) &&\n traceFlags >= 0 &&\n traceFlags <= 255\n ? traceFlags\n : 0;\n return n.toString(16).padStart(2, '0');\n}\n\nfunction resolve(event: LogEvent): ResolvedTrace {\n const trace = event.context.trace;\n if (trace === undefined || !hasValidIds(trace)) {\n return NONE;\n }\n const traceparent = `00-${trace.traceId}-${trace.spanId}-${flagsHex(\n trace.traceFlags,\n )}`;\n const traceState =\n typeof trace.traceState === 'string' &&\n trace.traceState.length > 0 &&\n trace.traceState.length <= MAX_TRACESTATE_LEN\n ? trace.traceState\n : null;\n return { key: traceparent, traceparent, traceState };\n}\n\n/**\n * Decide whether a delivery batch warrants a `traceparent`/`tracestate`\n * header. Pure; never throws.\n */\nexport function decideBatchTraceparent(\n events: ReadonlyArray<LogEvent>,\n): BatchTraceparentDecision {\n if (events.length === 0) {\n return { inject: false };\n }\n const first = resolve(events[0] as LogEvent);\n if (first.key === 'none') {\n return { inject: false };\n }\n\n let tracestateUniform = true;\n for (let i = 1; i < events.length; i += 1) {\n const r = resolve(events[i] as LogEvent);\n if (r.key !== first.key) {\n return { inject: false }; // heterogeneous (or one is `none`)\n }\n if (r.traceState !== first.traceState) {\n tracestateUniform = false;\n }\n }\n\n const traceparent = first.traceparent as string;\n if (tracestateUniform && first.traceState !== null) {\n return { inject: true, traceparent, tracestate: first.traceState };\n }\n return { inject: true, traceparent };\n}\n\n/**\n * Build the per-request header map. When injection is disabled or the batch is\n * not homogeneous, returns the SAME `base` reference (no allocation,\n * byte-identical request). Otherwise returns a new map with `traceparent`\n * (and `tracestate`) UNDER the consumer `base` headers, so `base` always wins\n * on collision (TI-6). Never mutates `base`; never throws.\n */\nexport function buildRequestHeaders(\n base: Readonly<Record<string, string>>,\n events: ReadonlyArray<LogEvent>,\n enabled: boolean,\n): Readonly<Record<string, string>> {\n if (!enabled) {\n return base;\n }\n const decision = decideBatchTraceparent(events);\n if (!decision.inject) {\n return base;\n }\n const injected: Record<string, string> = {\n traceparent: decision.traceparent,\n };\n if (decision.tracestate !== undefined) {\n injected.tracestate = decision.tracestate;\n }\n return { ...injected, ...base };\n}\n","/**\n * `createOtlpTransport` — factory for the `./transport-otlp` subpath.\n *\n * Composes the subpath primitives into a `Transport` that delivers\n * fully-processed `LogEvent`s to an OTLP logs backend as OTLP/HTTP+JSON,\n * batched, fire-and-forget (no retry), fail-closed.\n *\n * Delivery policy (research D6/D7, contracts TO-2..TO-8):\n *\n * send(event)\n * ├── if shutdownComplete: no-op\n * ├── lazily install the pagehide best-effort flush (first send)\n * ├── if serialized record > maxRecordBytes → oversized_event drop\n * ├── if buffered >= maxBufferedEvents → buffer_overflow drop\n * └── batcher.push(event) // flush on size / age\n *\n * flush(batch) // batcher callback\n * ├── serialize(batch) (fail-closed: serialize_failed → drop)\n * └── deliver(endpoint, headers, body) // fetch keepalive, never throws\n * ├── delivered ─────────────────── done\n * ├── unavailable ───────────────── delivery_unavailable notice\n * ├── send_failed ───────────────── send_failed notice (+cause)\n * └── partial_rejection ─────────── partial_rejection notice\n *\n * Every notice is rate-limited to one per failure class per instance per\n * session and NEVER carries a configured header/secret value (FR-009).\n * `send`/`flush`/`shutdown` NEVER throw or reject to the caller; only\n * construction-time validation throws, at the consumer's call site.\n *\n * Boundary discipline (TO-7): the only `src/` import is a type-only import\n * from `'../api/types.js'`. No `@opentelemetry/*` and no\n * `../internal/telemetry/otel/` import — the payload is hand-serialized.\n *\n * Specs: `specs/007-transport-otlp/contracts/*`, `data-model.md`.\n */\n\nimport type { LogEvent, Transport } from '../api/types.js';\n\nimport { type Batcher, createBatcher } from './batcher.js';\nimport { type DeliveryResult, deliver } from './delivery.js';\nimport { validateEndpoint } from './endpoint-validation.js';\nimport {\n freshNotifiedLedger,\n type NotifyContext,\n notifyOnce,\n type OtlpFailureCode,\n} from './errors.js';\nimport { encode, serializeBatch, toLogRecord } from './otlp-serializer.js';\nimport { buildRequestHeaders } from './traceparent-header.js';\n\n// ---------------------------------------------------------------------------\n// Public options shape (data-model.md § OtlpTransportOptions)\n// ---------------------------------------------------------------------------\n\nexport interface OtlpTransportOptions {\n /** Full OTLP logs endpoint URL (e.g. `https://otlp.example.com/v1/logs`). */\n endpoint: string;\n /** Static request headers (e.g. auth). Sent only on the wire. */\n headers?: Record<string, string>;\n /** Batch flush triggers. */\n batching?: {\n maxBatchSize: number;\n maxBatchAgeMs?: number;\n };\n /** Hard cap on buffered events; over-cap events are dropped. Default 1000. */\n maxBufferedEvents?: number;\n /** Per-record size guard in bytes; larger records are dropped. Default 64 KiB. */\n maxRecordBytes?: number;\n /** Stable diagnostic identifier (`Transport.name`). Default `'otlp'`. */\n name?: string;\n /** Permit `http://` localhost/127.0.0.1/[::1] only. Default false. */\n allowInsecureLoopback?: boolean;\n /**\n * When `true`, set a W3C `traceparent` (and `tracestate`) request header on a\n * delivery request whose batch all shares one valid trace context. Off by\n * default; homogeneous-only and fail-closed — a mixed, trace-less, or empty\n * batch sets no header, and the event payload is unchanged either way.\n * Carry-only: built from each event's existing `context.trace`; no ids are\n * minted. See `specs/009-traceparent-injection/`.\n */\n injectTraceparent?: boolean;\n /** Receives rate-limited diagnostic notices. Never carries header values. */\n onInternalError?: (err: Error) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal per-instance state (data-model.md § OtlpTransportState)\n// ---------------------------------------------------------------------------\n\ninterface OtlpTransportState extends NotifyContext {\n readonly endpoint: string;\n readonly headers: Readonly<Record<string, string>>;\n readonly name: string;\n readonly onInternalError: (err: Error) => void;\n readonly maxBufferedEvents: number;\n readonly maxRecordBytes: number;\n readonly injectTraceparent: boolean;\n readonly notified: Record<OtlpFailureCode, boolean>;\n batcher: Batcher;\n pagehideInstalled: boolean;\n pagehideUninstall: (() => void) | null;\n shutdownComplete: boolean;\n inFlight: Set<Promise<void>>;\n /**\n * Events accepted but not yet delivered (buffered in the batcher + in\n * flight). The true memory bound in this no-retry design: incremented on\n * accept, decremented when a batch's delivery settles. Capped at\n * `maxBufferedEvents` so a slow/failing backend cannot grow memory\n * unboundedly.\n */\n pending: number;\n}\n\nconst DEFAULTS = {\n maxBatchSize: 20,\n maxBatchAgeMs: 5000,\n maxBufferedEvents: 1000,\n maxRecordBytes: 65536,\n name: 'otlp',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Construction-time validation (TO-2)\n// ---------------------------------------------------------------------------\n\nfunction validateOptions(options: OtlpTransportOptions): void {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('otlp transport: options must be a non-null object');\n }\n const { headers, batching, maxBufferedEvents, maxRecordBytes } = options;\n\n if (\n options.injectTraceparent !== undefined &&\n typeof options.injectTraceparent !== 'boolean'\n ) {\n throw new TypeError('otlp transport: injectTraceparent must be a boolean');\n }\n\n if (headers !== undefined) {\n if (typeof headers !== 'object' || headers === null) {\n throw new TypeError('otlp transport: headers must be an object');\n }\n for (const key of Object.keys(headers)) {\n if (typeof headers[key] !== 'string') {\n throw new TypeError(\n `otlp transport: header '${key}' must be a string value`,\n );\n }\n }\n }\n\n const maxBatchSize = batching?.maxBatchSize ?? DEFAULTS.maxBatchSize;\n if (!Number.isInteger(maxBatchSize) || maxBatchSize < 1) {\n throw new RangeError(\n 'otlp transport: batching.maxBatchSize must be an integer >= 1',\n );\n }\n const cap = maxBufferedEvents ?? DEFAULTS.maxBufferedEvents;\n if (!Number.isInteger(cap) || cap < maxBatchSize) {\n throw new RangeError(\n 'otlp transport: maxBufferedEvents must be an integer >= maxBatchSize',\n );\n }\n const recBytes = maxRecordBytes ?? DEFAULTS.maxRecordBytes;\n if (!Number.isInteger(recBytes) || recBytes < 1) {\n throw new RangeError(\n 'otlp transport: maxRecordBytes must be an integer >= 1',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createOtlpTransport(options: OtlpTransportOptions): Transport {\n validateOptions(options);\n const allowInsecureLoopback = options.allowInsecureLoopback ?? false;\n // Throws at the consumer call site on a bad endpoint (off the hot path).\n validateEndpoint(options.endpoint, allowInsecureLoopback);\n\n const name = options.name ?? DEFAULTS.name;\n const maxBatchSize = options.batching?.maxBatchSize ?? DEFAULTS.maxBatchSize;\n const maxBatchAgeMs =\n options.batching?.maxBatchAgeMs ?? DEFAULTS.maxBatchAgeMs;\n\n const state: OtlpTransportState = {\n endpoint: options.endpoint,\n // Copy + freeze the headers so later consumer mutation cannot change\n // what we send, and so nothing outside delivery can read them.\n headers: Object.freeze({ ...(options.headers ?? {}) }),\n name,\n onInternalError: options.onInternalError ?? (() => undefined),\n maxBufferedEvents: options.maxBufferedEvents ?? DEFAULTS.maxBufferedEvents,\n maxRecordBytes: options.maxRecordBytes ?? DEFAULTS.maxRecordBytes,\n injectTraceparent: options.injectTraceparent ?? false,\n notified: freshNotifiedLedger(),\n // Placeholder; real batcher assigned below once the flush closure exists.\n batcher: undefined as unknown as Batcher,\n pagehideInstalled: false,\n pagehideUninstall: null,\n shutdownComplete: false,\n inFlight: new Set<Promise<void>>(),\n pending: 0,\n };\n\n state.batcher = createBatcher({\n maxBatchSize,\n maxBatchAgeMs,\n flush: (events) => {\n void flushBatch(state, events);\n },\n });\n\n return {\n name,\n send(event: LogEvent): void {\n if (state.shutdownComplete) return;\n ensurePagehide(state);\n\n // Per-record size guard (oversized_event) — measure the serialized\n // OTLP LogRecord, drop if it exceeds the budget. Never throws.\n try {\n const record = toLogRecord(event, Date.now());\n if (byteLength(JSON.stringify(record)) > state.maxRecordBytes) {\n notifyOnce(\n state,\n 'oversized_event',\n `dropped an event whose serialized record exceeds ${state.maxRecordBytes} bytes`,\n );\n return;\n }\n } catch (cause) {\n notifyOnce(\n state,\n 'serialize_failed',\n 'dropped an event that failed to serialize',\n cause,\n );\n return;\n }\n\n // Hard memory cap on undelivered (buffered + in-flight) events.\n if (state.pending >= state.maxBufferedEvents) {\n notifyOnce(\n state,\n 'buffer_overflow',\n `${state.maxBufferedEvents} events undelivered; dropping event`,\n );\n return;\n }\n state.pending += 1;\n state.batcher.push(event);\n },\n async flush(): Promise<void> {\n state.batcher.flush();\n await settleInFlight(state);\n },\n async shutdown(): Promise<void> {\n if (state.shutdownComplete) {\n await settleInFlight(state);\n return;\n }\n state.shutdownComplete = true;\n try {\n state.batcher.flush();\n await settleInFlight(state);\n } catch (cause) {\n notifyOnce(state, 'shutdown_failed', 'shutdown flush failed', cause);\n } finally {\n teardownPagehide(state);\n state.batcher.shutdown();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Batch delivery (never throws)\n// ---------------------------------------------------------------------------\n\nasync function flushBatch(\n state: OtlpTransportState,\n events: LogEvent[],\n): Promise<void> {\n const count = events.length;\n let body: string;\n try {\n body = encode(serializeBatch(events, Date.now()));\n } catch (cause) {\n // Fail-closed: drop the batch, but still release its pending budget.\n state.pending = Math.max(0, state.pending - count);\n notifyOnce(\n state,\n 'serialize_failed',\n 'dropped a batch that failed to serialize',\n cause,\n );\n return;\n }\n\n // Per-request header map. Best-effort `traceparent` injection (Feature 009)\n // never blocks delivery: any failure falls back to the plain configured\n // headers. Returns the same `state.headers` reference when disabled or the\n // batch is not homogeneous, so the request stays byte-identical.\n let headers: Readonly<Record<string, string>> = state.headers;\n try {\n headers = buildRequestHeaders(\n state.headers,\n events,\n state.injectTraceparent,\n );\n } catch {\n headers = state.headers;\n }\n\n const promise = (async (): Promise<void> => {\n const result: DeliveryResult = await deliver(state.endpoint, headers, body);\n mapResult(state, result);\n })().catch(() => {\n // Defensive: deliver() is contracted not to reject, but never let an\n // unexpected rejection escape into an unhandled rejection.\n });\n\n state.inFlight.add(promise);\n void promise.finally(() => {\n state.inFlight.delete(promise);\n // Release this batch's pending budget once delivery settles.\n state.pending = Math.max(0, state.pending - count);\n });\n}\n\nfunction mapResult(state: OtlpTransportState, result: DeliveryResult): void {\n switch (result.kind) {\n case 'delivered':\n return;\n case 'unavailable':\n notifyOnce(\n state,\n 'delivery_unavailable',\n 'fetch is unavailable; dropping batch',\n );\n return;\n case 'send_failed':\n notifyOnce(\n state,\n 'send_failed',\n `delivery failed (${result.detail})`,\n result.cause,\n );\n return;\n case 'partial_rejection':\n notifyOnce(\n state,\n 'partial_rejection',\n `backend rejected ${result.rejected} record(s)`,\n );\n return;\n }\n}\n\nasync function settleInFlight(state: OtlpTransportState): Promise<void> {\n // Snapshot: new deliveries triggered during await are not awaited here\n // (flush() is a point-in-time drain), matching beacon's flush semantics.\n await Promise.all([...state.inFlight]);\n}\n\n// ---------------------------------------------------------------------------\n// Lazy pagehide best-effort flush (Principle VII — nothing at Logger create)\n// ---------------------------------------------------------------------------\n\nfunction ensurePagehide(state: OtlpTransportState): void {\n if (state.pagehideInstalled) return;\n const target = globalThis as {\n addEventListener?: typeof globalThis.addEventListener;\n removeEventListener?: typeof globalThis.removeEventListener;\n };\n state.pagehideInstalled = true;\n if (typeof target.addEventListener !== 'function') {\n state.pagehideUninstall = null;\n return;\n }\n const handler = (): void => {\n // Best-effort: drain via the keepalive fetch path. Never blocks unload.\n state.batcher.flush();\n };\n target.addEventListener('pagehide', handler);\n state.pagehideUninstall = (): void => {\n if (typeof target.removeEventListener === 'function') {\n target.removeEventListener('pagehide', handler);\n }\n };\n}\n\nfunction teardownPagehide(state: OtlpTransportState): void {\n if (state.pagehideUninstall !== null) {\n state.pagehideUninstall();\n state.pagehideUninstall = null;\n }\n state.pagehideInstalled = false;\n}\n\nfunction byteLength(s: string): number {\n return new TextEncoder().encode(s).length;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/transport-otlp/batcher.ts","../src/transport-otlp/delivery.ts","../src/transport-otlp/endpoint-validation.ts","../src/transport-otlp/errors.ts","../src/transport-otlp/attributes.ts","../src/transport-otlp/resource.ts","../src/transport-otlp/otlp-serializer.ts","../src/transport-otlp/traceparent-header.ts","../src/transport-otlp/otlp-transport.ts"],"names":[],"mappings":"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","file":"transport-otlp.cjs","sourcesContent":["/**\n * Bounded batch buffer for the OTLP transport.\n *\n * A parallel copy of the `./transport-beacon` batcher state machine (the\n * boundary rule forbids importing across subpaths — TO-7). Behaviour\n * (research D7, data-model § Batcher):\n *\n * - `push(event)` appends to the in-memory buffer.\n * - First event in an empty batch with `maxBatchAgeMs` set arms a\n * one-shot age timer.\n * - Reaching `maxBatchSize` flushes synchronously at end of `push`.\n * - `flush()` drains the pending buffer through the consumer callback.\n * Empty buffer → no-op.\n * - `shutdown()` cancels the timer and inhibits further callbacks.\n *\n * Buffer is copied + cleared BEFORE the callback runs, so a re-entrant\n * `push` during the callback starts a fresh batch. A throwing callback is\n * swallowed (the batcher never throws from push/flush/shutdown). The hard\n * `maxBufferedEvents` cap is enforced by the transport BEFORE `push`, so\n * the batcher itself stays simple.\n */\n\nimport type { LogEvent } from '../api/types.js';\n\nexport interface BatcherOptions {\n maxBatchSize: number;\n maxBatchAgeMs?: number;\n flush: (events: LogEvent[]) => void;\n}\n\nexport interface Batcher {\n push(event: LogEvent): void;\n flush(): void;\n shutdown(): void;\n /** Current pending count — used by the transport's buffer-cap guard. */\n size(): number;\n}\n\nexport function createBatcher(opts: BatcherOptions): Batcher {\n const buffer: LogEvent[] = [];\n let maxAgeTimer: ReturnType<typeof setTimeout> | null = null;\n let flushCallback: ((events: LogEvent[]) => void) | null = opts.flush;\n\n const clearTimer = (): void => {\n if (maxAgeTimer !== null) {\n clearTimeout(maxAgeTimer);\n maxAgeTimer = null;\n }\n };\n\n const armTimer = (): void => {\n if (opts.maxBatchAgeMs === undefined) return;\n maxAgeTimer = setTimeout(() => {\n maxAgeTimer = null;\n doFlush();\n }, opts.maxBatchAgeMs);\n };\n\n const doFlush = (): void => {\n if (buffer.length === 0) return;\n if (flushCallback === null) {\n buffer.length = 0;\n clearTimer();\n return;\n }\n const events = buffer.slice();\n buffer.length = 0;\n clearTimer();\n try {\n flushCallback(events);\n } catch {\n // Swallow: the events were copied out; the consumer callback chose\n // to throw. Single-flush-attempt model (no re-push, no retry).\n }\n };\n\n return {\n push(event: LogEvent): void {\n if (flushCallback === null) return;\n buffer.push(event);\n if (buffer.length === 1 && opts.maxBatchAgeMs !== undefined) {\n armTimer();\n }\n if (buffer.length >= opts.maxBatchSize) {\n doFlush();\n }\n },\n flush(): void {\n doFlush();\n },\n shutdown(): void {\n clearTimer();\n buffer.length = 0;\n flushCallback = null;\n },\n size(): number {\n return buffer.length;\n },\n };\n}\n","/**\n * Delivery primitive for the OTLP transport: POST the OTLP/HTTP+JSON body\n * via `fetch` with `keepalive: true` (research D6, TO-2).\n *\n * `navigator.sendBeacon` is deliberately NOT used — it cannot set the\n * custom auth + `Content-Type` headers OTLP backends require (FR-004).\n * `keepalive` preserves best-effort delivery during page unload.\n *\n * `deliver(...)` NEVER throws or rejects: every outcome (2xx, 2xx with an\n * OTLP partial-success rejection, non-2xx, network reject, missing\n * `fetch`) is reduced to a `DeliveryResult` for the caller to map onto a\n * rate-limited notice. There is no retry (research D7).\n *\n * Boundary discipline (TO-7): zero `src/` imports; zero vendor imports.\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md` OP-1;\n * `contracts/transport-otlp-public-api.md` TO-2/TO-4.\n */\n\n/** Outcome of a single delivery attempt. Never an exception. */\nexport type DeliveryResult =\n | { kind: 'delivered' }\n | { kind: 'unavailable' }\n | { kind: 'send_failed'; detail: string; cause?: unknown }\n | { kind: 'partial_rejection'; rejected: number };\n\n/**\n * POST `body` to `endpoint` with `keepalive: true`, merging the caller's\n * static `headers` (e.g. auth) over the mandatory `content-type`.\n * `credentials: 'same-origin'` keeps cookies from leaking cross-origin by\n * default (Principle V); auth travels only in the explicit headers.\n */\nexport async function deliver(\n endpoint: string,\n headers: Readonly<Record<string, string>>,\n body: string,\n): Promise<DeliveryResult> {\n const fetchFn = (globalThis as { fetch?: typeof fetch }).fetch;\n if (typeof fetchFn !== 'function') {\n return { kind: 'unavailable' };\n }\n\n let response: Response;\n try {\n response = await fetchFn(endpoint, {\n method: 'POST',\n body,\n headers: { 'content-type': 'application/json', ...headers },\n keepalive: true,\n credentials: 'same-origin',\n });\n } catch (cause) {\n return { kind: 'send_failed', detail: 'fetch rejected', cause };\n }\n\n if (!response.ok) {\n return { kind: 'send_failed', detail: `HTTP ${response.status}` };\n }\n\n // 2xx — check for an OTLP partial-success rejection in the body. A\n // missing / non-JSON / unexpected body is treated as full success.\n const rejected = await readRejectedCount(response);\n if (rejected > 0) {\n return { kind: 'partial_rejection', rejected };\n }\n return { kind: 'delivered' };\n}\n\nasync function readRejectedCount(response: Response): Promise<number> {\n try {\n if (typeof response.json !== 'function') return 0;\n const parsed = (await response.json()) as unknown;\n if (typeof parsed !== 'object' || parsed === null) return 0;\n const partial = (parsed as { partialSuccess?: unknown }).partialSuccess;\n if (typeof partial !== 'object' || partial === null) return 0;\n const raw = (partial as { rejectedLogRecords?: unknown })\n .rejectedLogRecords;\n // OTLP encodes int64 as a string in JSON, but tolerate a number too.\n const n = typeof raw === 'string' ? Number(raw) : raw;\n return typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : 0;\n } catch {\n // A body that cannot be read/parsed does not turn a 2xx into a failure.\n return 0;\n }\n}\n","/**\n * Construction-time endpoint validation for `createOtlpTransport`.\n *\n * Locked behaviour (mirrors `./transport-beacon`, TO-5 / research D8):\n * - HTTPS endpoints always pass.\n * - HTTP endpoints pass IFF `allowInsecureLoopback === true` AND the\n * parsed URL's hostname is in `{ localhost, 127.0.0.1, [::1] }`.\n * - Every other case throws a typed error at construction time, before\n * any logger derives the runtime and before any listener is attached.\n *\n * Pure and side-effect-free: parses via `new URL(...)` and inspects the\n * result. MUST NOT read ambient state (no `process.env`, no\n * `window.location`) and MUST NOT read `allowInsecureLoopback` from\n * anywhere except the argument the caller passed.\n *\n * The consumer supplies the FULL OTLP logs URL (e.g.\n * `https://otlp.example.com/v1/logs`); the transport appends nothing.\n *\n * Specs: `specs/007-transport-otlp/contracts/transport-otlp-public-api.md`\n * TO-5; `data-model.md` § validation rules.\n */\n\n/**\n * Hostnames permitted under `allowInsecureLoopback: true`. WHATWG URL\n * normalises `http://[::1]` to hostname `[::1]` (with brackets).\n */\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n]);\n\n/**\n * Validate a consumer-supplied `endpoint`. Returns the parsed `URL` on\n * success; throws on every violation. The thrown error's `.message` names\n * the violated constraint and the offending endpoint string.\n */\nexport function validateEndpoint(\n endpoint: unknown,\n allowInsecureLoopback: boolean,\n): URL {\n if (typeof endpoint !== 'string') {\n throw new TypeError(\n `otlp transport: endpoint must be a string, got ${typeName(endpoint)}`,\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(endpoint);\n } catch {\n throw new TypeError(`otlp transport: invalid endpoint URL: '${endpoint}'`);\n }\n\n if (parsed.protocol === 'https:') {\n return parsed;\n }\n\n if (parsed.protocol === 'http:') {\n if (!allowInsecureLoopback) {\n throw new Error(\n `otlp transport refuses non-HTTPS endpoint '${endpoint}'`,\n );\n }\n if (!LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error(\n `otlp transport: allowInsecureLoopback permits only ` +\n `localhost / 127.0.0.1 / [::1]; got '${parsed.hostname}' ` +\n `in '${endpoint}'`,\n );\n }\n return parsed;\n }\n\n throw new Error(\n `otlp transport refuses non-HTTPS endpoint '${endpoint}' ` +\n `(scheme '${parsed.protocol}' is not permitted)`,\n );\n}\n\nfunction typeName(value: unknown): string {\n if (value === null) return 'null';\n return typeof value;\n}\n","/**\n * Subpath-owned diagnostic error class + rate-limited notice helper for\n * the OTLP transport.\n *\n * Drop/diagnostic notices fired by the OTLP transport are `OtlpError`\n * instances (a subclass of `Error`) owned by this subpath — by-convention\n * compatible with the core's `PackageError` shape (`.code`,\n * `.transportName`, optional `.cause`) but with NO runtime import from\n * `src/internal/**` (TO-7). This module is INTERNAL to the subpath;\n * `src/transport-otlp/index.ts` does not re-export it. The\n * `onInternalError` hook receives the instance typed as `Error`.\n *\n * **Security (FR-009 / TO-6)**: notice messages MUST NEVER include any\n * configured request-header value. The helpers here only ever build\n * messages from the failure code, the transport name, and an optional\n * non-secret detail string supplied by the caller. Callers MUST NOT pass\n * header values into `detail`.\n *\n * Specs: `specs/007-transport-otlp/data-model.md` § OtlpFailureCode;\n * `contracts/transport-otlp-public-api.md` TO-4/TO-6.\n */\n\n/**\n * Documented `OtlpError.code` values — one per failure class. Surfaced to\n * the consumer as `err.code` on the `Error`-shaped `onInternalError`\n * argument. Each is rate-limited to one notice per class per transport\n * instance per session.\n */\nexport type OtlpFailureCode =\n | 'oversized_event'\n | 'buffer_overflow'\n | 'delivery_unavailable'\n | 'send_failed'\n | 'partial_rejection'\n | 'serialize_failed'\n | 'shutdown_failed';\n\n/**\n * Subclass of `Error` carrying a discriminating `.code`, the originating\n * transport's `.transportName`, and an optional `.cause` chain. `.name`\n * is `'OtlpError'`. `.cause` follows the ES2022 convention (absent when\n * not provided rather than `undefined`).\n */\nexport class OtlpError extends Error {\n readonly code: OtlpFailureCode;\n readonly transportName: string;\n declare readonly cause?: unknown;\n\n constructor(\n code: OtlpFailureCode,\n transportName: string,\n message: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = 'OtlpError';\n this.code = code;\n this.transportName = transportName;\n if (cause !== undefined) {\n Object.defineProperty(this, 'cause', {\n value: cause,\n enumerable: true,\n writable: false,\n configurable: false,\n });\n }\n }\n}\n\n/** Type guard for `OtlpError` instances. */\nexport function isOtlpError(value: unknown): value is OtlpError {\n return value instanceof OtlpError;\n}\n\n/**\n * Minimal shape the `notifyOnce` helper needs: the transport's name, the\n * consumer error sink, and a per-code \"already notified\" ledger. The\n * concrete `OtlpTransportState` (data-model) satisfies this.\n */\nexport interface NotifyContext {\n readonly name: string;\n readonly onInternalError: (err: Error) => void;\n readonly notified: Record<OtlpFailureCode, boolean>;\n}\n\n/**\n * Emit at most ONE diagnostic notice per failure `code` per context\n * (instance) per session (FR-010). Subsequent calls with the same code are\n * silently suppressed. The `onInternalError` callback is invoked inside a\n * try/catch so a throwing consumer handler can never propagate back into\n * the transport's hot path (Principle III).\n *\n * `detail` is an optional NON-SECRET human string (e.g. an HTTP status).\n * Callers MUST NOT pass any configured header/secret value here.\n */\nexport function notifyOnce(\n ctx: NotifyContext,\n code: OtlpFailureCode,\n message: string,\n cause?: unknown,\n): void {\n if (ctx.notified[code]) return;\n ctx.notified[code] = true;\n const err = new OtlpError(\n code,\n ctx.name,\n `otlp transport '${ctx.name}': ${message}`,\n cause,\n );\n try {\n ctx.onInternalError(err);\n } catch {\n // A throwing consumer error handler must never reach the caller.\n }\n}\n\n/** Build the per-instance \"already notified\" ledger with all flags false. */\nexport function freshNotifiedLedger(): Record<OtlpFailureCode, boolean> {\n return {\n oversized_event: false,\n buffer_overflow: false,\n delivery_unavailable: false,\n send_failed: false,\n partial_rejection: false,\n serialize_failed: false,\n shutdown_failed: false,\n };\n}\n","/**\n * Pure `AttributeValue` → OTLP `AnyValue` encoder + the shared OTLP-JSON\n * value types used across the subpath.\n *\n * The OTLP/HTTP+JSON value model is hand-encoded here with ZERO runtime\n * dependencies and no `@opentelemetry/*` import (research D1, TO-7). The\n * `AttributeValue` union (string | number | boolean | null | array |\n * object) maps 1:1 onto OTLP `AnyValue` (OP-5).\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md` OP-5.\n */\n\nimport type { AttributeValue } from '../api/types.js';\n\n/**\n * OTLP `AnyValue` (JSON encoding). `null` → `{}` (an unset value) since\n * OTLP has no explicit null. A non-finite or otherwise non-integer number\n * is a `doubleValue`; an integer-safe number is an `intValue` (string, per\n * the uint64/sint64-as-string rule).\n */\nexport type AnyValue =\n | { stringValue: string }\n | { boolValue: boolean }\n | { intValue: string }\n | { doubleValue: number }\n | { arrayValue: { values: AnyValue[] } }\n | { kvlistValue: { values: KeyValue[] } }\n // biome-ignore lint/complexity/noBannedTypes: OTLP represents an unset/null value as an empty AnyValue object.\n | {};\n\n/** OTLP `KeyValue`. */\nexport interface KeyValue {\n key: string;\n value: AnyValue;\n}\n\n/** Encode a single sanitized `AttributeValue` as an OTLP `AnyValue`. */\nexport function toAnyValue(value: AttributeValue): AnyValue {\n if (value === null) {\n return {};\n }\n switch (typeof value) {\n case 'string':\n return { stringValue: value };\n case 'boolean':\n return { boolValue: value };\n case 'number':\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value };\n default:\n break;\n }\n if (Array.isArray(value)) {\n return { arrayValue: { values: value.map(toAnyValue) } };\n }\n // Remaining case: a plain `{ [key: string]: AttributeValue }` object.\n return { kvlistValue: { values: toKeyValues(value) } };\n}\n\n/**\n * Encode a record of sanitized attributes as an OTLP `KeyValue[]`,\n * optionally prefixing each key (used to namespace merged context\n * attributes under `context.` — OP-4).\n */\nexport function toKeyValues(\n record: Readonly<Record<string, AttributeValue>>,\n keyPrefix = '',\n): KeyValue[] {\n const out: KeyValue[] = [];\n for (const key of Object.keys(record)) {\n const value = record[key];\n if (value === undefined) continue;\n out.push({ key: keyPrefix + key, value: toAnyValue(value) });\n }\n return out;\n}\n","/**\n * Map SafeSignal runtime-global identity to an OTLP `Resource` (OP-2 / D3).\n *\n * The `Resource` carries only identity that is constant across a batch\n * from one configured transport — `service.name`, `service.version`,\n * `deployment.environment` (standard OTel semantic-convention attributes\n * every OTLP backend understands). The federated `module.*` identity is\n * **per-logger** (it can differ between events in the same batch via\n * `withContext`), so it is attributed per-`LogRecord` (see\n * `otlp-serializer.ts`), not on the shared Resource — preserving correct\n * origin attribution (Principle VII).\n *\n * Only present fields are emitted (no empty/`null` keys). Pure and\n * dependency-free.\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md` OP-2.\n */\n\nimport type { LogContext } from '../api/types.js';\n\nimport type { KeyValue } from './attributes.js';\n\nexport interface OtlpResource {\n attributes: KeyValue[];\n}\n\n/**\n * Build the OTLP `Resource` for a batch from a representative context\n * (the batch's first event). `service.*` / `deployment.environment` are\n * runtime-global, so any event in the batch is representative.\n */\nexport function buildResource(context: LogContext): OtlpResource {\n const attributes: KeyValue[] = [];\n\n const push = (key: string, value: string | undefined): void => {\n if (typeof value === 'string' && value.length > 0) {\n attributes.push({ key, value: { stringValue: value } });\n }\n };\n\n push('service.name', context.application?.name);\n push('service.version', context.application?.version);\n push('deployment.environment', context.environment);\n\n return { attributes };\n}\n","/**\n * Pure `LogEvent[]` → OTLP/HTTP+JSON logs payload serializer (OP-1..OP-6).\n *\n * This is the heart of the subpath. It hand-builds the OTLP logs JSON\n * shape with ZERO runtime dependencies and NO `@opentelemetry/*` import\n * (research D1, TO-7): severity numbers are literal constants matching the\n * OTel `SeverityNumber` ranges (and the in-repo\n * `src/internal/telemetry/otel/mapping.ts` table), reused conceptually but\n * never imported.\n *\n * **Encoding seam (FR-015)**: `serializeBatch(...)` builds the encoder-\n * neutral `OtlpLogsRequest` object; `encode(...)` turns it into the wire\n * body. Today the only encoder is JSON; a future protobuf encoder slots in\n * behind `encode(...)` without changing the object model or the public\n * surface.\n *\n * Specs: `specs/007-transport-otlp/contracts/otlp-payload.md`.\n */\n\nimport type { LogContext, LogEvent, LogLevel } from '../api/types.js';\n\nimport { type AnyValue, type KeyValue, toKeyValues } from './attributes.js';\nimport { buildResource } from './resource.js';\n\n/** Constant instrumentation-scope name for every emitted ScopeLogs. */\nexport const SCOPE_NAME = '@tallyrow/safesignal';\n\n/**\n * OTLP `SeverityNumber` base value per SafeSignal level (OP-3 / D2). These\n * are the canonical OTLP range bases (DEBUG 5, INFO 9, WARN 13, ERROR 17)\n * and match `LEVEL_TO_SEVERITY` in the internal OTel seam.\n */\nconst LEVEL_TO_SEVERITY_NUMBER: Readonly<Record<LogLevel, number>> = {\n debug: 5,\n info: 9,\n warn: 13,\n error: 17,\n};\n\nconst LEVEL_TO_SEVERITY_TEXT: Readonly<Record<LogLevel, string>> = {\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARN',\n error: 'ERROR',\n};\n\n// ---------------------------------------------------------------------------\n// OTLP wire shapes (JSON encoding)\n// ---------------------------------------------------------------------------\n\nexport interface OtlpLogRecord {\n timeUnixNano: string;\n observedTimeUnixNano: string;\n severityNumber: number;\n severityText: string;\n body: AnyValue;\n attributes: KeyValue[];\n /** W3C trace correlation — present only when `event.context.trace` is set. */\n traceId?: string;\n spanId?: string;\n flags?: number;\n}\n\nexport interface OtlpScopeLogs {\n scope: { name: string };\n logRecords: OtlpLogRecord[];\n}\n\nexport interface OtlpResourceLogs {\n resource: { attributes: KeyValue[] };\n scopeLogs: OtlpScopeLogs[];\n}\n\nexport interface OtlpLogsRequest {\n resourceLogs: OtlpResourceLogs[];\n}\n\n// ---------------------------------------------------------------------------\n// Mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Convert one `LogEvent` to an OTLP `LogRecord`. Never mutates the input\n * (T-S4). Per-record attributes are: event attributes, then merged context\n * attributes under a `context.` prefix, then per-logger `module.*`\n * identity, then `exception.*` when an error is present (OP-4).\n *\n * `fallbackTimeMs` is a single resolved time used when `event.timestamp`\n * is unparseable, so the mapping never throws on a bad timestamp (OP-3).\n */\nexport function toLogRecord(\n event: LogEvent,\n fallbackTimeMs: number,\n): OtlpLogRecord {\n const ms = toEpochMs(event.timestamp, fallbackTimeMs);\n const nano = String(ms * 1_000_000);\n\n const attributes: KeyValue[] = toKeyValues(event.attributes);\n\n const context = event.context;\n if (context.attributes !== undefined) {\n attributes.push(...toKeyValues(context.attributes, 'context.'));\n }\n pushModuleIdentity(attributes, context);\n pushException(attributes, event);\n\n const record: OtlpLogRecord = {\n timeUnixNano: nano,\n observedTimeUnixNano: nano,\n severityNumber: LEVEL_TO_SEVERITY_NUMBER[event.level],\n severityText: LEVEL_TO_SEVERITY_TEXT[event.level],\n body: { stringValue: event.message },\n attributes,\n };\n\n // W3C trace correlation → OTLP standard top-level fields (OP/OT contracts).\n // The structured ids are already lowercase-hex (validated upstream), so they\n // are the OTLP/JSON encoding as-is — no base64, no @opentelemetry import.\n const trace = context.trace;\n if (trace !== undefined) {\n record.traceId = trace.traceId;\n record.spanId = trace.spanId;\n if (trace.traceFlags !== undefined) {\n record.flags = trace.traceFlags;\n }\n }\n\n return record;\n}\n\n/**\n * Build the encoder-neutral OTLP logs request object for a batch. The\n * Resource is derived from the first event's runtime-global identity; if\n * the batch is empty, an empty Resource is used.\n */\nexport function serializeBatch(\n batch: ReadonlyArray<LogEvent>,\n fallbackTimeMs: number,\n): OtlpLogsRequest {\n const first = batch[0];\n const resource = buildResource(first ? first.context : ({} as LogContext));\n const logRecords = batch.map((e) => toLogRecord(e, fallbackTimeMs));\n return {\n resourceLogs: [\n {\n resource,\n scopeLogs: [{ scope: { name: SCOPE_NAME }, logRecords }],\n },\n ],\n };\n}\n\n/**\n * Encoding seam (FR-015). Turns the request object into the wire body.\n * The only encoding in this feature is JSON; protobuf is a roadmap\n * follow-up that slots in here without touching callers.\n */\nexport function encode(request: OtlpLogsRequest): string {\n return JSON.stringify(request);\n}\n\n/**\n * Convenience: build + JSON-encode a batch in one call. Pure; never\n * mutates inputs.\n */\nexport function serializeOtlpJson(\n batch: ReadonlyArray<LogEvent>,\n fallbackTimeMs: number,\n): string {\n return encode(serializeBatch(batch, fallbackTimeMs));\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction toEpochMs(iso: string, fallbackMs: number): number {\n const parsed = Date.parse(iso);\n return Number.isFinite(parsed) ? parsed : fallbackMs;\n}\n\nfunction pushModuleIdentity(out: KeyValue[], context: LogContext): void {\n const mod = context.module;\n if (mod === undefined) return;\n if (typeof mod.name === 'string' && mod.name.length > 0) {\n out.push({ key: 'module.name', value: { stringValue: mod.name } });\n }\n if (typeof mod.version === 'string' && mod.version.length > 0) {\n out.push({ key: 'module.version', value: { stringValue: mod.version } });\n }\n}\n\nfunction pushException(out: KeyValue[], event: LogEvent): void {\n const err = event.error;\n if (err === undefined) return;\n out.push({ key: 'exception.type', value: { stringValue: err.name } });\n out.push({ key: 'exception.message', value: { stringValue: err.message } });\n if (typeof err.stack === 'string' && err.stack.length > 0) {\n out.push({\n key: 'exception.stacktrace',\n value: { stringValue: err.stack },\n });\n }\n}\n","/**\n * Outbound W3C `traceparent` header injection for the `./transport-otlp`\n * delivery path (Feature 009).\n *\n * When `injectTraceparent` is enabled, a delivery request whose batch all\n * belongs to ONE valid trace gets a standard W3C `traceparent` (and, when\n * uniform, `tracestate`) request header so the ingest request is joinable to\n * its trace. SafeSignal is **carry-only**: this reads the already-normalized\n * `event.context.trace` (Feature 008 validates it once per emit, before any\n * transport sees the event) and never mints ids.\n *\n * Policy (homogeneous-only, fail-closed — contracts TI-3..TI-6, research D3/D4):\n * - Per-event key = `none` when `context.trace` is absent OR structurally\n * invalid (defensive guard for events that reach the transport without\n * passing emit-time normalization), else the full `traceparent` string\n * (so differing flags ⇒ differing keys ⇒ not homogeneous).\n * - Inject `traceparent` iff the batch is non-empty AND every key is the\n * same non-`none` value. Empty / trace-less / heterogeneous ⇒ no header.\n * - Inject `tracestate` iff `traceparent` is injected AND every event shares\n * the same defined `traceState` within `MAX_TRACESTATE_LEN`; else omit it\n * while keeping `traceparent` (optional part dropped, valid ids kept).\n * - Consumer `options.headers` win on any collision (spread last), so the\n * injected header can never overwrite/expose an auth/secret value.\n *\n * Boundary discipline (TO-7): the only import is a **type-only** import from\n * `../api/types.js`. No `../trace/`, no `@opentelemetry/*`. Pure; never throws.\n *\n * Specs: `specs/009-traceparent-injection/contracts/traceparent-injection.md`.\n */\n\nimport type { LogEvent, TraceContext } from '../api/types.js';\n\n/** Max `tracestate` length (W3C caps the header at 512 chars), mirrored here. */\nconst MAX_TRACESTATE_LEN = 512;\n\nconst TRACE_ID_RE = /^[0-9a-f]{32}$/;\nconst SPAN_ID_RE = /^[0-9a-f]{16}$/;\nconst ALL_ZERO_TRACE_ID = '0'.repeat(32);\nconst ALL_ZERO_SPAN_ID = '0'.repeat(16);\n\n/** Outcome of evaluating one delivery batch. */\nexport type BatchTraceparentDecision =\n | { inject: false }\n | { inject: true; traceparent: string; tracestate?: string };\n\ninterface ResolvedTrace {\n /** `'none'`, or the `traceparent` string (which also uniquely keys the trace). */\n readonly key: string;\n /** The `traceparent` string when valid, else `null`. */\n readonly traceparent: string | null;\n /** A bounded, non-empty `traceState`, else `null`. */\n readonly traceState: string | null;\n}\n\nconst NONE: ResolvedTrace = {\n key: 'none',\n traceparent: null,\n traceState: null,\n};\n\nfunction hasValidIds(trace: TraceContext): boolean {\n return (\n typeof trace.traceId === 'string' &&\n TRACE_ID_RE.test(trace.traceId) &&\n trace.traceId !== ALL_ZERO_TRACE_ID &&\n typeof trace.spanId === 'string' &&\n SPAN_ID_RE.test(trace.spanId) &&\n trace.spanId !== ALL_ZERO_SPAN_ID\n );\n}\n\nfunction flagsHex(traceFlags: number | undefined): string {\n const n =\n typeof traceFlags === 'number' &&\n Number.isInteger(traceFlags) &&\n traceFlags >= 0 &&\n traceFlags <= 255\n ? traceFlags\n : 0;\n return n.toString(16).padStart(2, '0');\n}\n\nfunction resolve(event: LogEvent): ResolvedTrace {\n const trace = event.context.trace;\n if (trace === undefined || !hasValidIds(trace)) {\n return NONE;\n }\n const traceparent = `00-${trace.traceId}-${trace.spanId}-${flagsHex(\n trace.traceFlags,\n )}`;\n const traceState =\n typeof trace.traceState === 'string' &&\n trace.traceState.length > 0 &&\n trace.traceState.length <= MAX_TRACESTATE_LEN\n ? trace.traceState\n : null;\n return { key: traceparent, traceparent, traceState };\n}\n\n/**\n * Decide whether a delivery batch warrants a `traceparent`/`tracestate`\n * header. Pure; never throws.\n */\nexport function decideBatchTraceparent(\n events: ReadonlyArray<LogEvent>,\n): BatchTraceparentDecision {\n if (events.length === 0) {\n return { inject: false };\n }\n const first = resolve(events[0] as LogEvent);\n if (first.key === 'none') {\n return { inject: false };\n }\n\n let tracestateUniform = true;\n for (let i = 1; i < events.length; i += 1) {\n const r = resolve(events[i] as LogEvent);\n if (r.key !== first.key) {\n return { inject: false }; // heterogeneous (or one is `none`)\n }\n if (r.traceState !== first.traceState) {\n tracestateUniform = false;\n }\n }\n\n const traceparent = first.traceparent as string;\n if (tracestateUniform && first.traceState !== null) {\n return { inject: true, traceparent, tracestate: first.traceState };\n }\n return { inject: true, traceparent };\n}\n\n/**\n * Build the per-request header map. When injection is disabled or the batch is\n * not homogeneous, returns the SAME `base` reference (no allocation,\n * byte-identical request). Otherwise returns a new map with `traceparent`\n * (and `tracestate`) UNDER the consumer `base` headers, so `base` always wins\n * on collision (TI-6). Never mutates `base`; never throws.\n */\nexport function buildRequestHeaders(\n base: Readonly<Record<string, string>>,\n events: ReadonlyArray<LogEvent>,\n enabled: boolean,\n): Readonly<Record<string, string>> {\n if (!enabled) {\n return base;\n }\n const decision = decideBatchTraceparent(events);\n if (!decision.inject) {\n return base;\n }\n const injected: Record<string, string> = {\n traceparent: decision.traceparent,\n };\n if (decision.tracestate !== undefined) {\n injected.tracestate = decision.tracestate;\n }\n return { ...injected, ...base };\n}\n","/**\n * `createOtlpTransport` — factory for the `./transport-otlp` subpath.\n *\n * Composes the subpath primitives into a `Transport` that delivers\n * fully-processed `LogEvent`s to an OTLP logs backend as OTLP/HTTP+JSON,\n * batched, fire-and-forget (no retry), fail-closed.\n *\n * Delivery policy (research D6/D7, contracts TO-2..TO-8):\n *\n * send(event)\n * ├── if shutdownComplete: no-op\n * ├── lazily install the pagehide best-effort flush (first send)\n * ├── if serialized record > maxRecordBytes → oversized_event drop\n * ├── if buffered >= maxBufferedEvents → buffer_overflow drop\n * └── batcher.push(event) // flush on size / age\n *\n * flush(batch) // batcher callback\n * ├── serialize(batch) (fail-closed: serialize_failed → drop)\n * └── deliver(endpoint, headers, body) // fetch keepalive, never throws\n * ├── delivered ─────────────────── done\n * ├── unavailable ───────────────── delivery_unavailable notice\n * ├── send_failed ───────────────── send_failed notice (+cause)\n * └── partial_rejection ─────────── partial_rejection notice\n *\n * Every notice is rate-limited to one per failure class per instance per\n * session and NEVER carries a configured header/secret value (FR-009).\n * `send`/`flush`/`shutdown` NEVER throw or reject to the caller; only\n * construction-time validation throws, at the consumer's call site.\n *\n * Boundary discipline (TO-7): the only `src/` import is a type-only import\n * from `'../api/types.js'`. No `@opentelemetry/*` and no\n * `../internal/telemetry/otel/` import — the payload is hand-serialized.\n *\n * Specs: `specs/007-transport-otlp/contracts/*`, `data-model.md`.\n */\n\nimport type { LogEvent, Transport } from '../api/types.js';\n\nimport { type Batcher, createBatcher } from './batcher.js';\nimport { type DeliveryResult, deliver } from './delivery.js';\nimport { validateEndpoint } from './endpoint-validation.js';\nimport {\n freshNotifiedLedger,\n type NotifyContext,\n notifyOnce,\n type OtlpFailureCode,\n} from './errors.js';\nimport { encode, serializeBatch, toLogRecord } from './otlp-serializer.js';\nimport { buildRequestHeaders } from './traceparent-header.js';\n\n// ---------------------------------------------------------------------------\n// Public options shape (data-model.md § OtlpTransportOptions)\n// ---------------------------------------------------------------------------\n\nexport interface OtlpTransportOptions {\n /** Full OTLP logs endpoint URL (e.g. `https://otlp.example.com/v1/logs`). */\n endpoint: string;\n /** Static request headers (e.g. auth). Sent only on the wire. */\n headers?: Record<string, string>;\n /** Batch flush triggers. */\n batching?: {\n maxBatchSize: number;\n maxBatchAgeMs?: number;\n };\n /** Hard cap on buffered events; over-cap events are dropped. Default 1000. */\n maxBufferedEvents?: number;\n /** Per-record size guard in bytes; larger records are dropped. Default 64 KiB. */\n maxRecordBytes?: number;\n /** Stable diagnostic identifier (`Transport.name`). Default `'otlp'`. */\n name?: string;\n /** Permit `http://` localhost/127.0.0.1/[::1] only. Default false. */\n allowInsecureLoopback?: boolean;\n /**\n * When `true`, set a W3C `traceparent` (and `tracestate`) request header on a\n * delivery request whose batch all shares one valid trace context. Off by\n * default; homogeneous-only and fail-closed — a mixed, trace-less, or empty\n * batch sets no header, and the event payload is unchanged either way.\n * Carry-only: built from each event's existing `context.trace`; no ids are\n * minted. See `specs/009-traceparent-injection/`.\n */\n injectTraceparent?: boolean;\n /** Receives rate-limited diagnostic notices. Never carries header values. */\n onInternalError?: (err: Error) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal per-instance state (data-model.md § OtlpTransportState)\n// ---------------------------------------------------------------------------\n\ninterface OtlpTransportState extends NotifyContext {\n readonly endpoint: string;\n readonly headers: Readonly<Record<string, string>>;\n readonly name: string;\n readonly onInternalError: (err: Error) => void;\n readonly maxBufferedEvents: number;\n readonly maxRecordBytes: number;\n readonly injectTraceparent: boolean;\n readonly notified: Record<OtlpFailureCode, boolean>;\n batcher: Batcher;\n pagehideInstalled: boolean;\n pagehideUninstall: (() => void) | null;\n shutdownComplete: boolean;\n inFlight: Set<Promise<void>>;\n /**\n * Events accepted but not yet delivered (buffered in the batcher + in\n * flight). The true memory bound in this no-retry design: incremented on\n * accept, decremented when a batch's delivery settles. Capped at\n * `maxBufferedEvents` so a slow/failing backend cannot grow memory\n * unboundedly.\n */\n pending: number;\n}\n\nconst DEFAULTS = {\n maxBatchSize: 20,\n maxBatchAgeMs: 5000,\n maxBufferedEvents: 1000,\n maxRecordBytes: 65536,\n name: 'otlp',\n} as const;\n\n// ---------------------------------------------------------------------------\n// Construction-time validation (TO-2)\n// ---------------------------------------------------------------------------\n\nfunction validateOptions(options: OtlpTransportOptions): void {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('otlp transport: options must be a non-null object');\n }\n const { headers, batching, maxBufferedEvents, maxRecordBytes } = options;\n\n if (\n options.injectTraceparent !== undefined &&\n typeof options.injectTraceparent !== 'boolean'\n ) {\n throw new TypeError('otlp transport: injectTraceparent must be a boolean');\n }\n\n if (headers !== undefined) {\n if (typeof headers !== 'object' || headers === null) {\n throw new TypeError('otlp transport: headers must be an object');\n }\n for (const key of Object.keys(headers)) {\n if (typeof headers[key] !== 'string') {\n throw new TypeError(\n `otlp transport: header '${key}' must be a string value`,\n );\n }\n }\n }\n\n const maxBatchSize = batching?.maxBatchSize ?? DEFAULTS.maxBatchSize;\n if (!Number.isInteger(maxBatchSize) || maxBatchSize < 1) {\n throw new RangeError(\n 'otlp transport: batching.maxBatchSize must be an integer >= 1',\n );\n }\n const cap = maxBufferedEvents ?? DEFAULTS.maxBufferedEvents;\n if (!Number.isInteger(cap) || cap < maxBatchSize) {\n throw new RangeError(\n 'otlp transport: maxBufferedEvents must be an integer >= maxBatchSize',\n );\n }\n const recBytes = maxRecordBytes ?? DEFAULTS.maxRecordBytes;\n if (!Number.isInteger(recBytes) || recBytes < 1) {\n throw new RangeError(\n 'otlp transport: maxRecordBytes must be an integer >= 1',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createOtlpTransport(options: OtlpTransportOptions): Transport {\n validateOptions(options);\n const allowInsecureLoopback = options.allowInsecureLoopback ?? false;\n // Throws at the consumer call site on a bad endpoint (off the hot path).\n validateEndpoint(options.endpoint, allowInsecureLoopback);\n\n const name = options.name ?? DEFAULTS.name;\n const maxBatchSize = options.batching?.maxBatchSize ?? DEFAULTS.maxBatchSize;\n const maxBatchAgeMs =\n options.batching?.maxBatchAgeMs ?? DEFAULTS.maxBatchAgeMs;\n\n const state: OtlpTransportState = {\n endpoint: options.endpoint,\n // Copy + freeze the headers so later consumer mutation cannot change\n // what we send, and so nothing outside delivery can read them.\n headers: Object.freeze({ ...(options.headers ?? {}) }),\n name,\n onInternalError: options.onInternalError ?? (() => undefined),\n maxBufferedEvents: options.maxBufferedEvents ?? DEFAULTS.maxBufferedEvents,\n maxRecordBytes: options.maxRecordBytes ?? DEFAULTS.maxRecordBytes,\n injectTraceparent: options.injectTraceparent ?? false,\n notified: freshNotifiedLedger(),\n // Placeholder; real batcher assigned below once the flush closure exists.\n batcher: undefined as unknown as Batcher,\n pagehideInstalled: false,\n pagehideUninstall: null,\n shutdownComplete: false,\n inFlight: new Set<Promise<void>>(),\n pending: 0,\n };\n\n state.batcher = createBatcher({\n maxBatchSize,\n maxBatchAgeMs,\n flush: (events) => {\n void flushBatch(state, events);\n },\n });\n\n return {\n name,\n send(event: LogEvent): void {\n if (state.shutdownComplete) return;\n ensurePagehide(state);\n\n // Per-record size guard (oversized_event) — measure the serialized\n // OTLP LogRecord, drop if it exceeds the budget. Never throws.\n try {\n const record = toLogRecord(event, Date.now());\n if (byteLength(JSON.stringify(record)) > state.maxRecordBytes) {\n notifyOnce(\n state,\n 'oversized_event',\n `dropped an event whose serialized record exceeds ${state.maxRecordBytes} bytes`,\n );\n return;\n }\n } catch (cause) {\n notifyOnce(\n state,\n 'serialize_failed',\n 'dropped an event that failed to serialize',\n cause,\n );\n return;\n }\n\n // Hard memory cap on undelivered (buffered + in-flight) events.\n if (state.pending >= state.maxBufferedEvents) {\n notifyOnce(\n state,\n 'buffer_overflow',\n `${state.maxBufferedEvents} events undelivered; dropping event`,\n );\n return;\n }\n state.pending += 1;\n state.batcher.push(event);\n },\n async flush(): Promise<void> {\n state.batcher.flush();\n await settleInFlight(state);\n },\n async shutdown(): Promise<void> {\n if (state.shutdownComplete) {\n await settleInFlight(state);\n return;\n }\n state.shutdownComplete = true;\n try {\n state.batcher.flush();\n await settleInFlight(state);\n } catch (cause) {\n notifyOnce(state, 'shutdown_failed', 'shutdown flush failed', cause);\n } finally {\n teardownPagehide(state);\n state.batcher.shutdown();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Batch delivery (never throws)\n// ---------------------------------------------------------------------------\n\nasync function flushBatch(\n state: OtlpTransportState,\n events: LogEvent[],\n): Promise<void> {\n const count = events.length;\n let body: string;\n try {\n body = encode(serializeBatch(events, Date.now()));\n } catch (cause) {\n // Fail-closed: drop the batch, but still release its pending budget.\n state.pending = Math.max(0, state.pending - count);\n notifyOnce(\n state,\n 'serialize_failed',\n 'dropped a batch that failed to serialize',\n cause,\n );\n return;\n }\n\n // Per-request header map. Best-effort `traceparent` injection (Feature 009)\n // never blocks delivery: any failure falls back to the plain configured\n // headers. Returns the same `state.headers` reference when disabled or the\n // batch is not homogeneous, so the request stays byte-identical.\n let headers: Readonly<Record<string, string>> = state.headers;\n try {\n headers = buildRequestHeaders(\n state.headers,\n events,\n state.injectTraceparent,\n );\n } catch {\n headers = state.headers;\n }\n\n const promise = (async (): Promise<void> => {\n const result: DeliveryResult = await deliver(state.endpoint, headers, body);\n mapResult(state, result);\n })().catch(() => {\n // Defensive: deliver() is contracted not to reject, but never let an\n // unexpected rejection escape into an unhandled rejection.\n });\n\n state.inFlight.add(promise);\n void promise.finally(() => {\n state.inFlight.delete(promise);\n // Release this batch's pending budget once delivery settles.\n state.pending = Math.max(0, state.pending - count);\n });\n}\n\nfunction mapResult(state: OtlpTransportState, result: DeliveryResult): void {\n switch (result.kind) {\n case 'delivered':\n return;\n case 'unavailable':\n notifyOnce(\n state,\n 'delivery_unavailable',\n 'fetch is unavailable; dropping batch',\n );\n return;\n case 'send_failed':\n notifyOnce(\n state,\n 'send_failed',\n `delivery failed (${result.detail})`,\n result.cause,\n );\n return;\n case 'partial_rejection':\n notifyOnce(\n state,\n 'partial_rejection',\n `backend rejected ${result.rejected} record(s)`,\n );\n return;\n }\n}\n\nasync function settleInFlight(state: OtlpTransportState): Promise<void> {\n // Snapshot: new deliveries triggered during await are not awaited here\n // (flush() is a point-in-time drain), matching beacon's flush semantics.\n await Promise.all([...state.inFlight]);\n}\n\n// ---------------------------------------------------------------------------\n// Lazy pagehide best-effort flush (Principle VIII — nothing at Logger create)\n// ---------------------------------------------------------------------------\n\nfunction ensurePagehide(state: OtlpTransportState): void {\n if (state.pagehideInstalled) return;\n const target = globalThis as {\n addEventListener?: typeof globalThis.addEventListener;\n removeEventListener?: typeof globalThis.removeEventListener;\n };\n state.pagehideInstalled = true;\n if (typeof target.addEventListener !== 'function') {\n state.pagehideUninstall = null;\n return;\n }\n const handler = (): void => {\n // Best-effort: drain via the keepalive fetch path. Never blocks unload.\n state.batcher.flush();\n };\n target.addEventListener('pagehide', handler);\n state.pagehideUninstall = (): void => {\n if (typeof target.removeEventListener === 'function') {\n target.removeEventListener('pagehide', handler);\n }\n };\n}\n\nfunction teardownPagehide(state: OtlpTransportState): void {\n if (state.pagehideUninstall !== null) {\n state.pagehideUninstall();\n state.pagehideUninstall = null;\n }\n state.pagehideInstalled = false;\n}\n\nfunction byteLength(s: string): number {\n return new TextEncoder().encode(s).length;\n}\n"]}
|
package/dist/transport-otlp.d.ts
CHANGED