@tallyrow/safesignal 1.0.1 → 1.3.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 +127 -7
- package/dist/index.cjs +81 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.mjs +81 -13
- package/dist/index.mjs.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.mjs.map +1 -1
- package/dist/transport-beacon.cjs +65 -50
- 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 +65 -50
- package/dist/transport-beacon.mjs.map +1 -1
- package/dist/transport-otlp.cjs +621 -0
- package/dist/transport-otlp.cjs.map +1 -0
- package/dist/transport-otlp.d.cts +71 -0
- package/dist/transport-otlp.d.ts +71 -0
- package/dist/transport-otlp.mjs +619 -0
- package/dist/transport-otlp.mjs.map +1 -0
- package/dist/{types-D-xVvmvX.d.cts → types-BiRyHi1e.d.cts} +20 -1
- package/dist/{types-D-xVvmvX.d.ts → types-BiRyHi1e.d.ts} +20 -1
- package/package.json +17 -4
|
@@ -0,0 +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.mjs","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"]}
|
|
@@ -50,6 +50,23 @@ interface ModuleIdentity {
|
|
|
50
50
|
name: string;
|
|
51
51
|
version?: string;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* W3C Trace Context carried (consume/propagate only — SafeSignal is not a
|
|
55
|
+
* tracer and never mints ids) on a `LogEvent.context` when supplied. Ids are
|
|
56
|
+
* lowercase-hex strings. Present on an event only after fail-closed validation
|
|
57
|
+
* (`normalizeTraceContext`): both ids are required and well-formed, else the
|
|
58
|
+
* whole `trace` is dropped. See `specs/008-trace-context/`.
|
|
59
|
+
*/
|
|
60
|
+
interface TraceContext {
|
|
61
|
+
/** 32 lowercase-hex chars, not all-zero. */
|
|
62
|
+
traceId: string;
|
|
63
|
+
/** 16 lowercase-hex chars, not all-zero. */
|
|
64
|
+
spanId: string;
|
|
65
|
+
/** W3C trace flags as an integer 0–255 (bit 0 = sampled). */
|
|
66
|
+
traceFlags?: number;
|
|
67
|
+
/** Raw W3C `tracestate`, length-bounded. */
|
|
68
|
+
traceState?: string;
|
|
69
|
+
}
|
|
53
70
|
/**
|
|
54
71
|
* Merged context attached to every emitted `LogEvent`. Merge precedence
|
|
55
72
|
* is documented in `contracts/logger-config.md` (LC-7) and
|
|
@@ -61,6 +78,8 @@ interface LogContext {
|
|
|
61
78
|
module?: ModuleIdentity;
|
|
62
79
|
environment?: string;
|
|
63
80
|
attributes?: Attributes;
|
|
81
|
+
/** Optional W3C Trace Context (additive); present only when valid. */
|
|
82
|
+
trace?: TraceContext;
|
|
64
83
|
}
|
|
65
84
|
/**
|
|
66
85
|
* Captured error information populated by `Logger.error(msg, attrs, err)`.
|
|
@@ -224,4 +243,4 @@ interface Logger {
|
|
|
224
243
|
withContext(context: Partial<LogContext>): Logger;
|
|
225
244
|
}
|
|
226
245
|
|
|
227
|
-
export type { AppIdentity as A, CreateLoggerOptions as C, ErrorInfo as E, LevelMap as L, ModuleIdentity as M, RedactionRule as R, SanitizerLimits as S,
|
|
246
|
+
export type { AppIdentity as A, CreateLoggerOptions as C, ErrorInfo as E, LevelMap as L, ModuleIdentity as M, RedactionRule as R, SanitizerLimits as S, TraceContext as T, AttributeValue as a, Attributes as b, LogContext as c, LogEvent as d, LogLevel as e, Logger as f, LoggerConfig as g, Redactor as h, ScrubUrlOptions as i, Transport as j, TransportFactory as k };
|
|
@@ -50,6 +50,23 @@ interface ModuleIdentity {
|
|
|
50
50
|
name: string;
|
|
51
51
|
version?: string;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* W3C Trace Context carried (consume/propagate only — SafeSignal is not a
|
|
55
|
+
* tracer and never mints ids) on a `LogEvent.context` when supplied. Ids are
|
|
56
|
+
* lowercase-hex strings. Present on an event only after fail-closed validation
|
|
57
|
+
* (`normalizeTraceContext`): both ids are required and well-formed, else the
|
|
58
|
+
* whole `trace` is dropped. See `specs/008-trace-context/`.
|
|
59
|
+
*/
|
|
60
|
+
interface TraceContext {
|
|
61
|
+
/** 32 lowercase-hex chars, not all-zero. */
|
|
62
|
+
traceId: string;
|
|
63
|
+
/** 16 lowercase-hex chars, not all-zero. */
|
|
64
|
+
spanId: string;
|
|
65
|
+
/** W3C trace flags as an integer 0–255 (bit 0 = sampled). */
|
|
66
|
+
traceFlags?: number;
|
|
67
|
+
/** Raw W3C `tracestate`, length-bounded. */
|
|
68
|
+
traceState?: string;
|
|
69
|
+
}
|
|
53
70
|
/**
|
|
54
71
|
* Merged context attached to every emitted `LogEvent`. Merge precedence
|
|
55
72
|
* is documented in `contracts/logger-config.md` (LC-7) and
|
|
@@ -61,6 +78,8 @@ interface LogContext {
|
|
|
61
78
|
module?: ModuleIdentity;
|
|
62
79
|
environment?: string;
|
|
63
80
|
attributes?: Attributes;
|
|
81
|
+
/** Optional W3C Trace Context (additive); present only when valid. */
|
|
82
|
+
trace?: TraceContext;
|
|
64
83
|
}
|
|
65
84
|
/**
|
|
66
85
|
* Captured error information populated by `Logger.error(msg, attrs, err)`.
|
|
@@ -224,4 +243,4 @@ interface Logger {
|
|
|
224
243
|
withContext(context: Partial<LogContext>): Logger;
|
|
225
244
|
}
|
|
226
245
|
|
|
227
|
-
export type { AppIdentity as A, CreateLoggerOptions as C, ErrorInfo as E, LevelMap as L, ModuleIdentity as M, RedactionRule as R, SanitizerLimits as S,
|
|
246
|
+
export type { AppIdentity as A, CreateLoggerOptions as C, ErrorInfo as E, LevelMap as L, ModuleIdentity as M, RedactionRule as R, SanitizerLimits as S, TraceContext as T, AttributeValue as a, Attributes as b, LogContext as c, LogEvent as d, LogLevel as e, Logger as f, LoggerConfig as g, Redactor as h, ScrubUrlOptions as i, Transport as j, TransportFactory as k };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tallyrow/safesignal",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
],
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "git+https://
|
|
25
|
+
"url": "git+https://github.com/TallyRow/safesignal.git"
|
|
26
26
|
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/TallyRow/safesignal/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/TallyRow/safesignal#readme",
|
|
27
31
|
"type": "module",
|
|
28
32
|
"sideEffects": false,
|
|
29
33
|
"files": [
|
|
@@ -44,6 +48,11 @@
|
|
|
44
48
|
"types": "./dist/transport-beacon.d.ts",
|
|
45
49
|
"import": "./dist/transport-beacon.mjs",
|
|
46
50
|
"require": "./dist/transport-beacon.cjs"
|
|
51
|
+
},
|
|
52
|
+
"./transport-otlp": {
|
|
53
|
+
"types": "./dist/transport-otlp.d.ts",
|
|
54
|
+
"import": "./dist/transport-otlp.mjs",
|
|
55
|
+
"require": "./dist/transport-otlp.cjs"
|
|
47
56
|
}
|
|
48
57
|
},
|
|
49
58
|
"main": "./dist/index.cjs",
|
|
@@ -59,10 +68,14 @@
|
|
|
59
68
|
"test:security": "vitest run tests/security",
|
|
60
69
|
"test:integration": "vitest run tests/integration",
|
|
61
70
|
"test:unit": "vitest run tests/unit",
|
|
62
|
-
"test:watch": "vitest"
|
|
71
|
+
"test:watch": "vitest",
|
|
72
|
+
"test:coverage": "vitest run --coverage",
|
|
73
|
+
"lint": "biome lint",
|
|
74
|
+
"format": "biome format --write",
|
|
75
|
+
"format:check": "biome format"
|
|
63
76
|
},
|
|
64
|
-
"dependencies": {},
|
|
65
77
|
"devDependencies": {
|
|
78
|
+
"@biomejs/biome": "2.4.16",
|
|
66
79
|
"@opentelemetry/api": "^1.9.0",
|
|
67
80
|
"@opentelemetry/api-logs": "^0.57.0",
|
|
68
81
|
"@opentelemetry/sdk-logs": "^0.57.0",
|