@beignet/core
Version:
Core framework primitives for Beignet
256 lines • 8.51 kB
JavaScript
/**
* @beignet/core/tracing
*
* W3C trace context primitives used to correlate Beignet activity across
* requests, use cases, providers, and instrumentation sinks.
*
* This module is dependency-free so client bundles that import app context
* types stay clean.
*/
const TRACEPARENT_PATTERN = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
const MAX_TRACESTATE_LENGTH = 512;
const MAX_TRACESTATE_MEMBERS = 32;
const TRACESTATE_SIMPLE_KEY_PATTERN = /^[a-z][a-z0-9_*/-]{0,255}$/;
const TRACESTATE_VENDOR_KEY_PATTERN = /^[a-z0-9][a-z0-9_*/-]{0,240}@[a-z][a-z0-9_*/-]{0,13}$/;
const TRACESTATE_VALUE_PATTERN = /^[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]$/;
/** Current version of Beignet's durable trace carrier. */
export const TRACE_CARRIER_VERSION = 1;
function isObject(value) {
return typeof value === "object" && value !== null;
}
/** Return whether a value implements `TracingPort`. */
export function isTracingPort(value) {
return (isObject(value) &&
"current" in value &&
"startActiveSpan" in value &&
typeof value.current === "function" &&
typeof value.startActiveSpan === "function");
}
/** Resolve a tracing port from a direct port, ports object, or app context. */
export function resolveTracingPort(target) {
if (!target)
return undefined;
if (isTracingPort(target))
return target;
if (!isObject(target))
return undefined;
const direct = "tracing" in target ? target.tracing : undefined;
if (isTracingPort(direct))
return direct;
const ports = "ports" in target ? target.ports : undefined;
if (!ports || ports === target)
return undefined;
return resolveTracingPort(ports);
}
function validTracestate(value) {
if (typeof value !== "string")
return undefined;
const normalized = value.trim();
if (normalized.length === 0 || normalized.length > MAX_TRACESTATE_LENGTH) {
return undefined;
}
const members = normalized.split(",");
if (members.length > MAX_TRACESTATE_MEMBERS)
return undefined;
const keys = new Set();
for (const rawMember of members) {
const member = rawMember.trim();
const separator = member.indexOf("=");
if (separator <= 0)
return undefined;
const key = member.slice(0, separator).trim();
const memberValue = member.slice(separator + 1).trimStart();
if ((!TRACESTATE_SIMPLE_KEY_PATTERN.test(key) &&
!TRACESTATE_VENDOR_KEY_PATTERN.test(key)) ||
!TRACESTATE_VALUE_PATTERN.test(memberValue) ||
keys.has(key)) {
return undefined;
}
keys.add(key);
}
return normalized;
}
/**
* Parse an untrusted durable trace carrier.
*
* Invalid carriers return `undefined`; trace metadata must never prevent
* message delivery.
*/
export function parseTraceCarrier(value) {
if (!isObject(value) || value.version !== TRACE_CARRIER_VERSION) {
return undefined;
}
const parsed = parseTraceparent(typeof value.traceparent === "string" ? value.traceparent : undefined);
if (!parsed)
return undefined;
if (value.tracestate !== undefined) {
const tracestate = validTracestate(value.tracestate);
if (!tracestate)
return undefined;
return {
version: TRACE_CARRIER_VERSION,
traceparent: parsed.traceparent,
tracestate,
};
}
return {
version: TRACE_CARRIER_VERSION,
traceparent: parsed.traceparent,
};
}
/**
* Capture the current trace context from a tracing port, ports object, app
* context, or explicit trace context. Returns `undefined` when no valid
* context is available.
*/
export function captureTraceCarrier(target) {
let context;
try {
context = resolveTraceContextInput(target);
}
catch {
// Context-like inputs may be proxy-backed and reject unknown properties.
}
try {
context = resolveTracingPort(target)?.current() ?? context;
}
catch {
// Tracing is best-effort and must not block the owning operation.
}
try {
if (!context?.traceparent)
return undefined;
return parseTraceCarrier({
version: TRACE_CARRIER_VERSION,
traceparent: context.traceparent,
...(context.tracestate ? { tracestate: context.tracestate } : {}),
});
}
catch {
// Trace contexts are app-provided and remain best-effort inputs.
return undefined;
}
}
/** Resolve trace fields from a trace context or context-like object. */
export function resolveTraceContextInput(target) {
if (!isObject(target))
return undefined;
const context = target;
if (context.traceId ||
context.spanId ||
context.parentSpanId ||
context.traceparent ||
context.tracestate) {
return {
...(context.traceId ? { traceId: context.traceId } : {}),
...(context.spanId ? { spanId: context.spanId } : {}),
...(context.parentSpanId ? { parentSpanId: context.parentSpanId } : {}),
...(context.traceparent ? { traceparent: context.traceparent } : {}),
...(context.tracestate ? { tracestate: context.tracestate } : {}),
};
}
const traceContext = "trace" in target ? target.trace : undefined;
if (!traceContext || traceContext === target)
return undefined;
return resolveTraceContextInput(traceContext);
}
/** Run a callback inside a tracing span when an app tracing port is installed. */
export function runWithTracing(target, operation, run) {
const tracing = resolveTracingPort(target);
if (!tracing)
return run();
return tracing.startActiveSpan(operation, (span) => run(span));
}
function createHexId(length) {
const bytes = new Uint8Array(length / 2);
if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
crypto.getRandomValues(bytes);
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
}
let value = "";
while (value.length < length) {
value += Math.floor(Math.random() * 16).toString(16);
}
return value.slice(0, length);
}
function isNonZeroHex(value) {
return !/^0+$/.test(value);
}
/**
* Create a non-zero W3C trace ID.
*/
export function createTraceId() {
let traceId = createHexId(32);
while (!isNonZeroHex(traceId)) {
traceId = createHexId(32);
}
return traceId;
}
/**
* Create a non-zero W3C span ID.
*/
export function createSpanId() {
let spanId = createHexId(16);
while (!isNonZeroHex(spanId)) {
spanId = createHexId(16);
}
return spanId;
}
/**
* Create a W3C traceparent header value.
*/
export function createTraceparent(args) {
return `00-${args.traceId}-${args.spanId}-${args.traceFlags ?? "01"}`;
}
/**
* Parse and validate a W3C traceparent header value.
*/
export function parseTraceparent(value) {
if (!value)
return undefined;
const normalized = value.trim().toLowerCase();
const match = TRACEPARENT_PATTERN.exec(normalized);
if (!match)
return undefined;
const [, traceId, spanId, traceFlags] = match;
if (!isNonZeroHex(traceId) || !isNonZeroHex(spanId))
return undefined;
return {
traceId,
spanId,
traceFlags,
traceparent: normalized,
};
}
/**
* Create trace context from explicit IDs or an existing traceparent.
*/
export function createTraceContext(input = {}) {
const parsed = parseTraceparent(input.traceparent);
const traceId = input.traceId ?? parsed?.traceId ?? createTraceId();
const parentSpanId = input.parentSpanId ?? parsed?.spanId;
const spanId = input.spanId ?? createSpanId();
return {
traceId,
spanId,
...(parentSpanId ? { parentSpanId } : {}),
traceparent: createTraceparent({
traceId,
spanId,
traceFlags: parsed?.traceFlags,
}),
...(input.tracestate ? { tracestate: input.tracestate } : {}),
};
}
/**
* Create child trace context from a parent context.
*/
export function createChildTraceContext(parent) {
return createTraceContext({
traceId: parent.traceId,
parentSpanId: parent.spanId,
traceparent: parent.traceparent,
tracestate: parent.tracestate,
});
}
//# sourceMappingURL=index.js.map