/** * @absolutejs/telemetry — tiny shared OpenTelemetry substrate for the * AbsoluteJS substrate packages. * * **The problem this solves.** The deep-research audit flagged G2: each * substrate package needs OTel for the "one trace from edge router → * runtime spawn → sync mutation → queue job → secret resolve" SRE * narrative. Without coordination, every package writes its own * boilerplate (peer-dep handling, noop fallback, attribute names). * `@elysiajs/opentelemetry` covers the HTTP request lifecycle but the * substrate's internal spans are out of its scope. * * **The shape this package provides.** * * 1. **Type-replicated OTel surface.** We do NOT take a peer-dep on * `@opentelemetry/api`. Instead this module type-replicates the * shape (Tracer, Span, TracerProvider, SpanContext, SpanStatus, * SpanKind, SpanOptions). When the user has `@opentelemetry/api` * installed and passes their `TracerProvider`, it conforms to our * types structurally. When they don't, our types are still * complete and our noop tracer is used. The substrate packages * take a single `tracerProvider?: TracerProvider` option and * never need to import `@opentelemetry/api` at all. * * 2. **Zero-cost noop.** `createNoopTracer()` returns a tracer that * executes the user's callback immediately with a noop span. No * allocations beyond the callback args. * * 3. **`ABS_ATTRS` semantic conventions.** Standard attribute names * so every substrate package's spans use the same vocabulary * (`abs.tenant`, `abs.engine.id`, `abs.job.kind`). Customer SREs * can correlate across packages without reading source. * * 4. **`tracerOrNoop(provider, name)`** — the canonical entry point. * Every substrate package factory accepts `tracerProvider?` and * calls `const tracer = tracerOrNoop(options.tracerProvider, '@absolutejs/pkg-name')`. * * **Context propagation.** When `tracer.startActiveSpan(...)` runs * inside an existing OTel context (set by `@elysiajs/opentelemetry`, * a parent span in user code, etc.), the new span nests as a child * automatically — that's the OTel spec contract. So an HTTP request * span from `@elysiajs/opentelemetry` automatically contains the * substrate spans for any sync mutations or queue dispatches the * handler runs. */ import type { HandoffEvidence, HandoffSummary } from '@absolutejs/handoff'; /** Trace flags (W3C trace context). */ export type TraceFlags = number; /** W3C tracestate carrier — mirrors `@opentelemetry/api`'s `TraceState` exactly * so an OTel `SpanContext` is structurally assignable to ours. */ export type TraceState = { set(key: string, value: string): TraceState; unset(key: string): TraceState; get(key: string): string | undefined; serialize(): string; }; /** Span context — the bits that travel across boundaries. */ export type SpanContext = { traceId: string; spanId: string; traceFlags: TraceFlags; traceState?: TraceState; isRemote?: boolean; }; /** Span kind enum — values match `@opentelemetry/api`'s `SpanKind`. */ export declare const SpanKind: { readonly INTERNAL: 0; readonly SERVER: 1; readonly CLIENT: 2; readonly PRODUCER: 3; readonly CONSUMER: 4; }; export type SpanKind = (typeof SpanKind)[keyof typeof SpanKind]; /** Span status codes — values match `@opentelemetry/api`'s `SpanStatusCode`. */ export declare const SpanStatusCode: { readonly UNSET: 0; readonly OK: 1; readonly ERROR: 2; }; export type SpanStatusCode = (typeof SpanStatusCode)[keyof typeof SpanStatusCode]; export type SpanStatus = { code: SpanStatusCode; message?: string; }; export type AttributeValue = string | number | boolean | Array | Array | Array; export type Attributes = { [attributeKey: string]: AttributeValue | undefined; }; /** Span options accepted by `startActiveSpan`. */ export type SpanOptions = { kind?: SpanKind; attributes?: Attributes; links?: Array<{ context: SpanContext; attributes?: Attributes; }>; startTime?: number; root?: boolean; }; /** A span — the thing you set attributes / status / errors on. */ export type Span = { spanContext(): SpanContext; setAttribute(key: string, value: AttributeValue): Span; setAttributes(attrs: Attributes): Span; setStatus(status: SpanStatus): Span; updateName(name: string): Span; addEvent(name: string, attrs?: Attributes, time?: number): Span; recordException(exception: unknown, time?: number): void; isRecording(): boolean; end(endTime?: number): void; }; /** A tracer — what you get from `tracerProvider.getTracer(name)`. */ export type Tracer = { /** * Create a span without setting it as the active context. Returns * the Span directly — the caller manages `setStatus` / * `recordException` / `end` lifecycle. Most useful for long-running * code where the callback shape of `startActiveSpan` is awkward. * * The new span automatically links to the currently-active span as * its parent (standard OTel behavior). */ startSpan(name: string, options?: SpanOptions): Span; startActiveSpan(name: string, fn: (span: Span) => T): T; startActiveSpan(name: string, options: SpanOptions, fn: (span: Span) => T): T; }; /** A tracer provider — typically the OTel SDK's NodeTracerProvider. */ export type TracerProvider = { getTracer(name: string, version?: string): Tracer; }; /** Returns a singleton noop span. All methods are no-ops; safe to call. */ export declare const createNoopSpan: () => Span; /** Returns a tracer whose `startActiveSpan` invokes the callback with a * noop span and returns the callback's result. Zero allocations. */ export declare const createNoopTracer: () => Tracer; /** Returns a tracer provider whose `getTracer()` always returns the * noop tracer. Useful for tests + as the default substrate behavior. */ export declare const createNoopTracerProvider: () => TracerProvider; /** * Resolve a tracer from an optional provider. Returns the provider's * tracer for `name` if a provider was passed; otherwise a noop tracer. * * Every substrate package's factory should call: * * ```ts * const tracer = tracerOrNoop(options.tracerProvider, '@absolutejs/'); * ``` * * and then `tracer.startActiveSpan(...)` in hot paths. */ export declare const tracerOrNoop: (provider: TracerProvider | undefined, name: string, version?: string) => Tracer; /** * Cross-package semantic conventions. Use these EVERYWHERE rather than * inline string literals so a customer's APM query for * `abs.tenant = "acme"` resolves spans from sync + queue + runtime in * one filter. Added in 0.0.1; additive new attributes are minor bumps. */ export declare const ABS_ATTRS: { /** Tenant identifier (shard key / customer id). */ readonly tenant: "abs.tenant"; /** Shard / cluster member id (stable across processes). */ readonly shardId: "abs.shard.id"; readonly engineId: "abs.engine.id"; readonly collection: "abs.collection"; readonly mutation: "abs.mutation"; readonly mutationAttempt: "abs.mutation.attempt"; readonly subscriptionId: "abs.subscription.id"; readonly batchSize: "abs.batch.size"; readonly clusterMessageOrigin: "abs.cluster.origin"; readonly jobId: "abs.job.id"; readonly jobKind: "abs.job.kind"; readonly jobAttempt: "abs.job.attempt"; readonly jobMaxAttempts: "abs.job.max_attempts"; readonly workerId: "abs.worker.id"; readonly runtimeKey: "abs.runtime.key"; readonly runtimePid: "abs.runtime.pid"; readonly runtimePort: "abs.runtime.port"; readonly runtimeExitReason: "abs.runtime.exit_reason"; readonly runtimeReadinessMs: "abs.runtime.readiness_ms"; readonly routeShard: "abs.route.shard"; readonly routeDecision: "abs.route.decision"; readonly secretName: "abs.secret.name"; readonly secretFingerprint: "abs.secret.fingerprint"; readonly auditKind: "abs.audit.kind"; readonly handoffCorrelationId: "abs.handoff.correlation_id"; readonly handoffOperation: "abs.handoff.operation"; readonly handoffOutcome: "abs.handoff.outcome"; readonly handoffService: "abs.handoff.service"; readonly handoffSource: "abs.handoff.source"; readonly handoffAttempt: "abs.handoff.attempt"; readonly handoffContradiction: "abs.handoff.contradiction"; readonly agentRunId: "abs.agent.run.id"; readonly agentParentRunId: "abs.agent.run.parent_id"; readonly agentStatus: "abs.agent.run.status"; readonly agentDescriptorId: "abs.agent.descriptor.id"; readonly agentDescriptorVersion: "abs.agent.descriptor.version"; readonly agentDescriptorDigest: "abs.agent.descriptor.digest"; readonly agentActorTenantId: "abs.agent.actor.tenant_id"; readonly agentActorUserId: "abs.agent.actor.user_id"; readonly agentActorAgentId: "abs.agent.actor.agent_id"; readonly agentDelegationId: "abs.agent.delegation.id"; readonly agentStepId: "abs.agent.step.id"; readonly agentStepSequence: "abs.agent.step.sequence"; readonly agentStepKind: "abs.agent.step.kind"; readonly agentEffectName: "abs.agent.effect.name"; readonly agentWorkerId: "abs.agent.worker.id"; readonly agentBudgetActions: "abs.agent.budget.actions"; readonly agentBudgetCostMicros: "abs.agent.budget.cost_micros"; readonly agentBudgetInputTokens: "abs.agent.budget.input_tokens"; readonly agentBudgetOutputTokens: "abs.agent.budget.output_tokens"; readonly agentBudgetSpendMinor: "abs.agent.budget.spend_minor"; readonly agentBudgetWallTimeMs: "abs.agent.budget.wall_time_ms"; }; export type AbsAttrName = (typeof ABS_ATTRS)[keyof typeof ABS_ATTRS]; /** * Privacy-safe span attributes for one external-system observation. Message, * reference, external id, and customer data are deliberately excluded. */ export declare const handoffSpanAttributes: (evidence: HandoffEvidence) => Attributes; /** Summary attributes for reconciliation or contradiction spans. */ export declare const handoffSummarySpanAttributes: (summary: HandoffSummary) => Attributes; /** * Wrap an async fn in a span that captures success / error status and * exception details automatically. Returns the fn's resolved value or * rethrows its rejection (with the span ended either way). * * ```ts * await withSpan(tracer, 'sync.runMutation', { attributes: { [ABS_ATTRS.mutation]: name } }, async (span) => { * span.setAttribute(ABS_ATTRS.mutationAttempt, attempt); * return await actuallyRunMutation(); * }); * ``` */ export declare const withSpan: (tracer: Tracer, name: string, options: SpanOptions, fn: (span: Span) => Promise) => Promise; /** * Sync variant of {@link withSpan}. Use when the wrapped fn is * synchronous; the async version's extra microtask is wasted otherwise. */ export declare const withSpanSync: (tracer: Tracer, name: string, options: SpanOptions, fn: (span: Span) => T) => T; /** * Read the active span's `traceId` if `@opentelemetry/api` is installed * AND a span is currently active. Returns `undefined` otherwise. Used * by packages that want to attach `metadata.traceId` to non-span * artifacts (audit events, error reports, log lines) without taking a * peer-dep on `@opentelemetry/api`. * * The module specifier is built at runtime so TypeScript / bundlers * don't statically resolve `@opentelemetry/api` — it's a truly * optional dependency. When OTel isn't installed, the import resolves * to `null` and the helper returns `undefined`. * * Added in 0.0.3. * * @example * ```ts * import { readActiveTraceId } from '@absolutejs/telemetry'; * * const traceId = await readActiveTraceId(); * if (traceId !== undefined) { * auditEvent.metadata.traceId = traceId; * } * ``` */ export declare const readActiveTraceId: () => Promise; export { createFanoutSpanExporter, createMemoryTraceStore, createTraceStoreSpanExporter, projectStoredSpan, type ReadableSpanLike, type SpanExportResult, type StoredSpan, type StoredSpanEvent, type StoredSpanLink, type StoredSpanProjectionOptions, type TelemetryAttributes, type TelemetryAttributeScalar, type TelemetryAttributeValue, type TraceAnalyticsFilter, type TraceAnalyticsStore, type TraceFilter, type TraceSeriesFilter, type TraceSeriesPoint, type TraceServiceEdge, type TraceServiceSummary, type TraceStore, type TraceStoreStats, type TraceStoreSpanExporter, type TraceSummary } from './store'; //# sourceMappingURL=index.d.ts.map