/** * RequestContext * * Carries per-request state through the entire call chain using * Node's AsyncLocalStorage. No manual threading needed — any * async function called during request processing can access * the current context. * * ┌─────────────────────────────────────────────────────────────┐ * │ HTTP request arrives at users service │ * │ │ * │ RequestContext created: │ * │ correlationId: "req_abc123" │ * │ traceId: "trace_xyz789" (from ForgeProxy) │ * │ spanId: "span_001" │ * │ auth: { userId, tenantId, role } │ * │ service: "users" │ * │ method: "createUser" │ * │ startTime: 1707500000000 │ * │ deadline: 1707500005000 │ * │ baggage: Map { custom key-value pairs } │ * │ │ * │ this.notifications.userCreated(user) │ * │ → Proxy reads current RequestContext │ * │ → Propagates correlationId + traceId + auth via IPC │ * │ → Notifications service creates child span │ * │ → Same correlationId, new spanId │ * │ │ * │ All log lines include correlationId automatically │ * │ All metrics include tenantId automatically │ * │ Prometheus histograms track per-tenant latency │ * └─────────────────────────────────────────────────────────────┘ * * Usage in service code (automatic — engineers don't touch this): * * // The framework does this: * RequestContext.run(ctx, async () => { * await service.createUser(data); * }); * * // Any code inside that async tree can do: * const ctx = RequestContext.current(); * ctx.correlationId // always available * ctx.auth // if authenticated * ctx.tenantId // shortcut * ctx.addBaggage('paymentId', 'pay_123'); */ interface AuthInfo { userId?: string; tenantId?: string; projectId?: string; project_id?: string; role?: string; [key: string]: unknown; } interface RequestContextOptions { correlationId?: string | null; traceId?: string; parentSpanId?: string; auth?: AuthInfo | null; service?: string | null; method?: string | null; deadline?: number | null; baggage?: Iterable<[string, string]> | null; } interface PropagationHeaders { "x-correlation-id"?: string; "x-request-id"?: string; "x-trace-id"?: string; "x-parent-span-id"?: string; "x-forge-auth"?: string; "x-forge-deadline"?: string; "x-forge-baggage"?: string; "x-forge-project"?: string; traceparent?: string; [key: string]: string | undefined; } interface IPCPropagationData { __forge_correlation_id?: string; __forge_trace_id?: string; __forge_parent_span_id?: string; __forge_auth?: unknown; __forge_deadline?: unknown; __forge_baggage?: unknown; __forge_project?: unknown; [key: string]: unknown; } type PropagationData = PropagationHeaders | IPCPropagationData; interface MetricLabels { tenant_id?: string; project_id?: string; service?: string; method?: string; } interface LogFields { correlationId: string; tenantId?: string; userId?: string; traceId?: string; } interface IPCPayload { __forge_correlation_id: string; __forge_trace_id: string; __forge_parent_span_id: string; __forge_auth: AuthInfo | null; __forge_deadline: number | null; __forge_baggage: [string, string][] | null; __forge_project: string | null; } export interface Span { traceId: string; spanId: string; parentSpanId: string | null; operationName: string; service: string | null; startTimeUnixNano: number; endTimeUnixNano: number; durationMs: number; status: "ok" | "error"; attributes: Record; } export type TraceExporterFn = (spans: Span[]) => void | Promise; /** Format spans as OTLP-compatible JSON (ResourceSpans structure) */ export declare function formatOTLP(spans: Span[]): object; export declare class RequestContext { correlationId: string; traceId: string; spanId: string; parentSpanId: string | null; auth: AuthInfo | null; service: string | null; method: string | null; startTime: number; startEpoch: number; deadline: number | null; baggage: Map; spans: Span[]; _projectId?: string; /** Configurable trace exporter — set via RequestContext.setTraceExporter() */ private static _traceExporter; constructor(opts?: RequestContextOptions); /** Shortcut for auth fields */ get userId(): string | null; get tenantId(): string | null; get projectId(): string | null; get role(): string | null; /** Elapsed time since this context was created */ get elapsed(): number; /** Remaining time before deadline */ get remaining(): number; get isExpired(): boolean; /** Add custom baggage (propagated to downstream services) */ addBaggage(key: string, value: unknown): void; /** Serialize for propagation over IPC/HTTP */ toHeaders(): Record; /** Serialize for IPC (colocated + cross-process) */ toIPC(): IPCPayload; /** Sanitize an auth object — must be a plain object, strip dangerous keys */ static _sanitizeAuth(auth: unknown): AuthInfo | null; /** Validate an absolute deadline value — must be a finite positive epoch ms, capped at 5 min */ static _sanitizeDeadline(value: unknown): number | null; /** Validate baggage — max 32 entries and max 4KB serialized size */ static _sanitizeBaggage(baggage: unknown): [string, string][] | null; /** Validate a relative deadline (remaining ms) — must be finite, non-negative, max 5 min */ static _sanitizeRelativeDeadline(value: unknown): number | null; /** Reconstruct from IPC message or HTTP headers */ static fromPropagation(data: PropagationData): RequestContext; /** Create a child context for a downstream call */ child(service: string, method: string): RequestContext; /** Run a function within a request context */ static run(ctx: RequestContext, fn: () => T): T; /** Get the current request context (or null if none) */ static current(): RequestContext | null; /** Labels for metrics (always includes tenant if available) */ toMetricLabels(): MetricLabels; /** Labels for structured logging */ toLogFields(): LogFields; /** * Record a completed span on this request context. * Spans are accumulated and flushed when end() is called or flushSpans() is invoked. */ recordSpan(operationName: string, startTimeMs: number, endTimeMs: number, opts?: { status?: "ok" | "error"; attributes?: Record; spanId?: string; parentSpanId?: string | null; }): Span; /** * Start a span and return a finish function. * Usage: const finish = ctx.startSpan('doWork'); ... finish(); */ startSpan(operationName: string, attributes?: Record): (status?: "ok" | "error", extraAttrs?: Record) => Span; /** * Flush all accumulated spans to the configured trace exporter. * Clears the spans array after flushing. Returns the flushed spans. */ flushSpans(): Promise; /** * End this request context: record the root span and flush all spans. * Call this when the request is complete. */ end(status?: "ok" | "error"): Promise; /** Set the global trace exporter function */ static setTraceExporter(fn: TraceExporterFn | null): void; /** Get the current trace exporter */ static getTraceExporter(): TraceExporterFn | null; /** Built-in console exporter for development */ static consoleExporter: TraceExporterFn; static generateId(): string; static generateTraceId(): string; static generateSpanId(): string; } export {}; //# sourceMappingURL=RequestContext.d.ts.map