chore(vendor): import pi 0.85.1

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
2026-09-14 10:48:47 -04:00
parent 5ebc7018c3
commit 4def357d09
1669 changed files with 397627 additions and 0 deletions

View File

@@ -0,0 +1,357 @@
export type AttributeValue = string | number | boolean | readonly string[] | readonly number[] | readonly boolean[];
export interface SpanAttributes {
[name: string]: AttributeValue | undefined;
}
export interface SpanOptions {
name: string;
attributes?: SpanAttributes;
}
export type SpanStatus = { status: "ok" } | { status: "error"; error?: { name: string; message: string } };
export interface TelemetryContext {
startSpan<T>(options: SpanOptions, callback: (span: TelemetrySpan) => T | Promise<T>): Promise<T>;
}
export interface TelemetrySpan extends TelemetryContext {
addEvent(name: string, attributes?: SpanAttributes): void;
setAttributes(attributes: SpanAttributes): void;
setStatus(status: SpanStatus): void;
}
export { NOOP_TELEMETRY_CONTEXT } from "./noop.ts";
export type TelemetryAttributeType = "string" | "number" | "boolean" | "string[]" | "number[]" | "boolean[]";
export interface TelemetryAttributeMetadata {
description: string;
sensitive?: boolean;
cardinality?: "low" | "high";
}
export type TelemetryAttributeDefinition = TelemetryAttributeMetadata &
(
| { type: "string"; values?: readonly string[]; examples?: readonly string[] }
| { type: "number"; values?: readonly number[]; examples?: readonly number[] }
| { type: "boolean"; values?: readonly boolean[]; examples?: readonly boolean[] }
| { type: "string[]"; elementValues?: readonly string[]; examples?: readonly (readonly string[])[] }
| { type: "number[]"; elementValues?: readonly number[]; examples?: readonly (readonly number[])[] }
| { type: "boolean[]"; elementValues?: readonly boolean[]; examples?: readonly (readonly boolean[])[] }
);
export type TelemetryStartAttributeDefinition = TelemetryAttributeDefinition & { required: boolean };
export type TelemetryEventAttributeDefinition = TelemetryAttributeDefinition & { required: boolean };
export interface TelemetryEventDefinition {
description: string;
attributes: Record<string, TelemetryEventAttributeDefinition>;
}
export type TelemetryParentDefinition =
| { kind: "any" }
| { kind: "root_or_external" }
| { kind: "spans"; spans: readonly string[] };
export interface TelemetrySpanDefinition {
description: string;
parents: TelemetryParentDefinition;
startAttributes: Record<string, TelemetryStartAttributeDefinition>;
endAttributes: Record<string, TelemetryAttributeDefinition>;
events?: Record<string, TelemetryEventDefinition>;
status: { default: "ok"; errorWhen: string };
}
export interface TelemetrySchemaDefinition {
version: number;
spans: Record<string, TelemetrySpanDefinition>;
}
/** Typed identity helper for serializable telemetry schema data. */
export function defineTelemetrySchema<const T extends TelemetrySchemaDefinition>(schema: T): T {
return schema;
}
type AttributeDefinitionValue<Definition extends TelemetryAttributeDefinition> = Definition extends {
type: "string";
values: readonly (infer Value extends string)[];
}
? Value
: Definition extends { type: "string" }
? string
: Definition extends { type: "number"; values: readonly (infer Value extends number)[] }
? Value
: Definition extends { type: "number" }
? number
: Definition extends { type: "boolean"; values: readonly (infer Value extends boolean)[] }
? Value
: Definition extends { type: "boolean" }
? boolean
: Definition extends {
type: "string[]";
elementValues: readonly (infer Value extends string)[];
}
? readonly Value[]
: Definition extends { type: "string[]" }
? readonly string[]
: Definition extends {
type: "number[]";
elementValues: readonly (infer Value extends number)[];
}
? readonly Value[]
: Definition extends { type: "number[]" }
? readonly number[]
: Definition extends {
type: "boolean[]";
elementValues: readonly (infer Value extends boolean)[];
}
? readonly Value[]
: readonly boolean[];
type RequiredAttributeNames<
Definitions extends Record<string, TelemetryStartAttributeDefinition | TelemetryEventAttributeDefinition>,
> = {
[Name in keyof Definitions]-?: Definitions[Name]["required"] extends true ? Name : never;
}[keyof Definitions];
type OptionalAttributeNames<
Definitions extends Record<string, TelemetryStartAttributeDefinition | TelemetryEventAttributeDefinition>,
> = Exclude<keyof Definitions, RequiredAttributeNames<Definitions>>;
export type InferRequiredAndOptionalAttributes<
Definitions extends Record<string, TelemetryStartAttributeDefinition | TelemetryEventAttributeDefinition>,
> = keyof Definitions extends never
? Record<string, never>
: {
[Name in RequiredAttributeNames<Definitions>]: AttributeDefinitionValue<Definitions[Name]>;
} & {
[Name in OptionalAttributeNames<Definitions>]?: AttributeDefinitionValue<Definitions[Name]>;
};
export type InferStartAttributes<Definitions extends Record<string, TelemetryStartAttributeDefinition>> =
InferRequiredAndOptionalAttributes<Definitions>;
export type InferOptionalAttributes<Definitions extends Record<string, TelemetryAttributeDefinition>> =
keyof Definitions extends never
? Record<string, never>
: { [Name in keyof Definitions]?: AttributeDefinitionValue<Definitions[Name]> };
export type ExactTelemetryAttributes<Expected, Actual extends Expected> = Actual &
Record<Exclude<keyof Actual, keyof Expected>, never>;
export type InferEventAttributes<Definitions extends Record<string, TelemetryEventAttributeDefinition>> =
InferRequiredAndOptionalAttributes<Definitions>;
export type TelemetrySchemaSpanName<Schema extends TelemetrySchemaDefinition> = keyof Schema["spans"] & string;
type SchemaSpan<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
> = Schema["spans"][Name];
export type TelemetrySchemaSpanStartAttributes<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
> = SchemaSpan<Schema, Name>["startAttributes"] extends infer Definitions extends Record<
string,
TelemetryStartAttributeDefinition
>
? InferStartAttributes<Definitions>
: never;
export type TelemetrySchemaSpanEndAttributes<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
> = SchemaSpan<Schema, Name>["endAttributes"] extends infer Definitions extends Record<
string,
TelemetryAttributeDefinition
>
? InferOptionalAttributes<Definitions>
: never;
type SchemaSpanEvents<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
> = SchemaSpan<Schema, Name> extends { events: infer Events extends Record<string, TelemetryEventDefinition> }
? Events
: Record<never, never>;
export type TelemetrySchemaSpanEventName<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
> = keyof SchemaSpanEvents<Schema, Name> & string;
type SchemaSpanEvent<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
EventName extends TelemetrySchemaSpanEventName<Schema, Name>,
> = SchemaSpanEvents<Schema, Name> extends infer Events
? EventName extends keyof Events
? Events[EventName]
: never
: never;
export type TelemetrySchemaSpanEventAttributes<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
EventName extends TelemetrySchemaSpanEventName<Schema, Name>,
> = SchemaSpanEvent<Schema, Name, EventName> extends {
attributes: infer Definitions extends Record<string, TelemetryEventAttributeDefinition>;
}
? InferEventAttributes<Definitions>
: never;
type SchemaSpanEventAttributeDefinitions<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
EventName extends TelemetrySchemaSpanEventName<Schema, Name>,
> = SchemaSpanEvent<Schema, Name, EventName> extends {
attributes: infer Definitions extends Record<string, TelemetryEventAttributeDefinition>;
}
? Definitions
: Record<never, never>;
type EventArguments<
Definitions extends Record<string, TelemetryEventAttributeDefinition>,
Attributes extends InferEventAttributes<Definitions>,
> = [RequiredAttributeNames<Definitions>] extends [never]
? [attributes?: ExactTelemetryAttributes<InferEventAttributes<Definitions>, Attributes>]
: [attributes: ExactTelemetryAttributes<InferEventAttributes<Definitions>, Attributes>];
export type SchemaTelemetrySpan<
Schema extends TelemetrySchemaDefinition,
Name extends TelemetrySchemaSpanName<Schema>,
> = Omit<TelemetrySpan, "addEvent" | "setAttributes"> & {
addEvent<
EventName extends TelemetrySchemaSpanEventName<Schema, Name>,
const Attributes extends InferEventAttributes<
SchemaSpanEventAttributeDefinitions<Schema, Name, EventName>
> = InferEventAttributes<SchemaSpanEventAttributeDefinitions<Schema, Name, EventName>>,
>(
name: EventName,
...args: EventArguments<SchemaSpanEventAttributeDefinitions<Schema, Name, EventName>, Attributes>
): void;
setAttributes<const Attributes extends TelemetrySchemaSpanEndAttributes<Schema, Name>>(
attributes: ExactTelemetryAttributes<TelemetrySchemaSpanEndAttributes<Schema, Name>, Attributes>,
): void;
};
export type TelemetrySchemaSpanUnion<Schema extends TelemetrySchemaDefinition> = {
[Name in TelemetrySchemaSpanName<Schema>]: {
name: Name;
startAttributes: TelemetrySchemaSpanStartAttributes<Schema, Name>;
endAttributes: TelemetrySchemaSpanEndAttributes<Schema, Name>;
events: {
[EventName in TelemetrySchemaSpanEventName<Schema, Name>]: TelemetrySchemaSpanEventAttributes<
Schema,
Name,
EventName
>;
};
};
}[TelemetrySchemaSpanName<Schema>];
type TelemetrySchemaTuple = readonly [TelemetrySchemaDefinition, ...TelemetrySchemaDefinition[]];
type SpanNameInSchema<Schema extends TelemetrySchemaDefinition> = Schema extends TelemetrySchemaDefinition
? TelemetrySchemaSpanName<Schema>
: never;
type SpanNameInSchemas<Schemas extends TelemetrySchemaTuple> = SpanNameInSchema<Schemas[number]>;
type SpanStartAttributesInSchema<
Schema extends TelemetrySchemaDefinition,
Name extends string,
> = Schema extends TelemetrySchemaDefinition
? Name extends TelemetrySchemaSpanName<Schema>
? TelemetrySchemaSpanStartAttributes<Schema, Name>
: never
: never;
type SpanInSchema<
Schema extends TelemetrySchemaDefinition,
Name extends string,
> = Schema extends TelemetrySchemaDefinition
? Name extends TelemetrySchemaSpanName<Schema>
? SchemaTelemetrySpan<Schema, Name>
: never
: never;
type DuplicateTelemetrySpanNames<
Schemas extends readonly TelemetrySchemaDefinition[],
Seen extends string = never,
> = Schemas extends readonly [
infer Schema extends TelemetrySchemaDefinition,
...infer Rest extends readonly TelemetrySchemaDefinition[],
]
?
| Extract<TelemetrySchemaSpanName<Schema>, Seen>
| DuplicateTelemetrySpanNames<Rest, Seen | TelemetrySchemaSpanName<Schema>>
: never;
type UniqueTelemetrySchemas<Schemas extends TelemetrySchemaTuple> = [DuplicateTelemetrySpanNames<Schemas>] extends [
never,
]
? unknown
: { readonly "duplicate telemetry span names": DuplicateTelemetrySpanNames<Schemas> };
type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (
value: infer Intersection,
) => void
? Intersection
: never;
type TypedSpanStarterForName<Schemas extends TelemetrySchemaTuple, Name extends SpanNameInSchemas<Schemas>> = <
const Attributes extends SpanStartAttributesInSchema<Schemas[number], Name>,
Result,
>(
name: Name,
attributes: ExactTelemetryAttributes<SpanStartAttributesInSchema<Schemas[number], Name>, Attributes>,
callback: (
span: SpanInSchema<Schemas[number], Name>,
startChildSpan: TypedSpanStarter<Schemas>,
) => Result | Promise<Result>,
) => Promise<Result>;
/** A per-span overload set bound to one explicit parent context and one or more schemas. */
export type TypedSpanStarter<Schemas extends TelemetrySchemaTuple> = UnionToIntersection<
{
[Name in SpanNameInSchemas<Schemas>]: TypedSpanStarterForName<Schemas, Name>;
}[SpanNameInSchemas<Schemas>]
>;
function bindTypedSpanStarter<Schemas extends TelemetrySchemaTuple>(
telemetryContext: TelemetryContext,
): TypedSpanStarter<Schemas> {
const startSpan = (
name: SpanNameInSchemas<Schemas>,
attributes: SpanAttributes,
callback: (
span: SpanInSchema<Schemas[number], SpanNameInSchemas<Schemas>>,
startChildSpan: TypedSpanStarter<Schemas>,
) => unknown,
): Promise<unknown> =>
telemetryContext.startSpan({ name, attributes }, (span) =>
callback(
span as SpanInSchema<Schemas[number], SpanNameInSchemas<Schemas>>,
bindTypedSpanStarter<Schemas>(span),
),
);
return startSpan as TypedSpanStarter<Schemas>;
}
/**
* Bind an explicit parent context to the combined span vocabulary of one or more schemas.
* Schema values are used only for type inference; no runtime schema validation is performed.
*/
export function createTypedSpanStarter<const Schemas extends TelemetrySchemaTuple>(
telemetryContext: TelemetryContext,
_schemas: Schemas & UniqueTelemetrySchemas<Schemas>,
): TypedSpanStarter<Schemas> {
return bindTypedSpanStarter<Schemas>(telemetryContext);
}
export type { RecordedTelemetryEvent, RecordedTelemetrySpan } from "./memory.ts";
export { InMemoryTelemetryContext } from "./memory.ts";

View File

@@ -0,0 +1,219 @@
import type {
AttributeValue,
SpanAttributes,
SpanOptions,
SpanStatus,
TelemetryContext,
TelemetrySpan,
} from "./index.ts";
import { NOOP_TELEMETRY_CONTEXT } from "./noop.ts";
export interface RecordedTelemetryEvent {
readonly name: string;
readonly attributes: Readonly<SpanAttributes>;
}
export interface RecordedTelemetrySpan {
readonly id: number;
readonly parentId: number | null;
readonly name: string;
readonly attributes: Readonly<SpanAttributes>;
readonly events: readonly RecordedTelemetryEvent[];
readonly status: SpanStatus;
readonly settled: boolean;
readonly endSequence?: number;
}
interface MutableRecordedTelemetryEvent {
name: string;
attributes: SpanAttributes;
}
interface MutableRecordedTelemetrySpan {
id: number;
parentId: number | null;
name: string;
attributes: SpanAttributes;
events: MutableRecordedTelemetryEvent[];
status: SpanStatus;
explicitStatus: boolean;
settled: boolean;
endSequence?: number;
}
interface InMemoryTelemetryState {
spans: MutableRecordedTelemetrySpan[];
nextSpanId: number;
nextEndSequence: number;
}
function copyAttributeValue(value: AttributeValue): AttributeValue {
return Array.isArray(value) ? ([...value] as AttributeValue) : value;
}
function copyAttributes(attributes?: SpanAttributes): SpanAttributes {
const copy: SpanAttributes = {};
if (!attributes) return copy;
for (const [name, value] of Object.entries(attributes)) {
if (value !== undefined) copy[name] = copyAttributeValue(value);
}
return copy;
}
function mergeAttributes(current: SpanAttributes, attributes: SpanAttributes): SpanAttributes {
const merged = copyAttributes(current);
for (const [name, value] of Object.entries(attributes)) {
if (value !== undefined) merged[name] = copyAttributeValue(value);
}
return merged;
}
function copyStatus(status: SpanStatus): SpanStatus {
if (status.status === "ok") return { status: "ok" };
return status.error
? { status: "error", error: { name: status.error.name, message: status.error.message } }
: { status: "error" };
}
function automaticErrorStatus(error: unknown): SpanStatus {
try {
if (error instanceof Error) {
return { status: "error", error: { name: error.name, message: error.message } };
}
} catch {
// Error inspection is passive. Fall through to an error status without details.
}
return { status: "error" };
}
function settleSpan(
state: InMemoryTelemetryState,
span: MutableRecordedTelemetrySpan,
failed: boolean,
error?: unknown,
): void {
if (span.settled) return;
if (failed && !span.explicitStatus) span.status = automaticErrorStatus(error);
span.settled = true;
span.endSequence = state.nextEndSequence++;
}
function createSpan(
state: InMemoryTelemetryState,
parent: MutableRecordedTelemetrySpan | undefined,
options: SpanOptions,
): MutableRecordedTelemetrySpan {
const name = options.name;
const attributes = copyAttributes(options.attributes);
return {
id: state.nextSpanId++,
parentId: parent?.id ?? null,
name,
attributes,
events: [],
status: { status: "ok" },
explicitStatus: false,
settled: false,
};
}
function startInMemorySpan<T>(
state: InMemoryTelemetryState,
parent: MutableRecordedTelemetrySpan | undefined,
options: SpanOptions,
callback: (span: TelemetrySpan) => T | Promise<T>,
): Promise<T> {
if (parent?.settled) return NOOP_TELEMETRY_CONTEXT.startSpan(options, callback);
let recordedSpan: MutableRecordedTelemetrySpan;
try {
recordedSpan = createSpan(state, parent, options);
state.spans.push(recordedSpan);
} catch {
return NOOP_TELEMETRY_CONTEXT.startSpan(options, callback);
}
const span: TelemetrySpan = {
startSpan: <Result>(
childOptions: SpanOptions,
childCallback: (child: TelemetrySpan) => Result | Promise<Result>,
) => startInMemorySpan(state, recordedSpan, childOptions, childCallback),
addEvent(name, attributes) {
if (recordedSpan.settled) return;
try {
recordedSpan.events.push({ name, attributes: copyAttributes(attributes) });
} catch {
// Recording is passive. Ignore malformed or unreadable telemetry payloads.
}
},
setAttributes(attributes) {
if (recordedSpan.settled) return;
try {
recordedSpan.attributes = mergeAttributes(recordedSpan.attributes, attributes);
} catch {
// Recording is passive. Ignore malformed or unreadable telemetry payloads.
}
},
setStatus(status) {
if (recordedSpan.settled) return;
try {
recordedSpan.status = copyStatus(status);
recordedSpan.explicitStatus = true;
} catch {
// Recording is passive. Ignore malformed or unreadable telemetry payloads.
}
},
};
let result: T | Promise<T>;
try {
result = callback(span);
} catch (error) {
settleSpan(state, recordedSpan, true, error);
return Promise.reject(error);
}
return Promise.resolve(result).then(
(value) => {
settleSpan(state, recordedSpan, false);
return value;
},
(error: unknown) => {
settleSpan(state, recordedSpan, true, error);
throw error;
},
);
}
/**
* Backend-neutral reference implementation that records spans in process memory.
* Create a fresh instance to isolate tests or independent recording scopes.
*/
export class InMemoryTelemetryContext implements TelemetryContext {
private readonly state: InMemoryTelemetryState = {
spans: [],
nextSpanId: 1,
nextEndSequence: 1,
};
startSpan<T>(options: SpanOptions, callback: (span: TelemetrySpan) => T | Promise<T>): Promise<T> {
return startInMemorySpan(this.state, undefined, options, callback);
}
/** Returns detached snapshots in span-start order. */
getSpans(): readonly RecordedTelemetrySpan[] {
return this.state.spans.map((span) => ({
id: span.id,
parentId: span.parentId,
name: span.name,
attributes: copyAttributes(span.attributes),
events: span.events.map((event) => ({
name: event.name,
attributes: copyAttributes(event.attributes),
})),
status: copyStatus(span.status),
settled: span.settled,
...(span.endSequence === undefined ? {} : { endSequence: span.endSequence }),
}));
}
}

View File

@@ -0,0 +1,20 @@
import type { SpanOptions, TelemetryContext, TelemetrySpan } from "./index.ts";
function startNoopSpan<T>(_options: SpanOptions, callback: (span: TelemetrySpan) => T | Promise<T>): Promise<T> {
try {
return Promise.resolve(callback(noopTelemetrySpan));
} catch (error) {
return Promise.reject(error);
}
}
const noopTelemetrySpan: TelemetrySpan = {
startSpan: startNoopSpan,
addEvent: () => {},
setAttributes: () => {},
setStatus: () => {},
};
Object.freeze(noopTelemetrySpan);
/** Shared telemetry context used when an application does not provide one. */
export const NOOP_TELEMETRY_CONTEXT: TelemetryContext = noopTelemetrySpan;

View File

@@ -0,0 +1,315 @@
import { deepStrictEqual, doesNotThrow, fail, ok, strictEqual } from "node:assert/strict";
import type { SpanAttributes, SpanOptions, SpanStatus, TelemetrySpan } from "../index.ts";
import type { RecordedTelemetrySpan } from "../memory.ts";
import type {
TelemetryAdapterConformanceCase,
TelemetryAdapterFixture,
TelemetryAdapterFixtureFactory,
} from "./types.ts";
type ConformanceTest = (fixture: TelemetryAdapterFixture) => Promise<void>;
function createCase(
factory: TelemetryAdapterFixtureFactory,
group: string,
name: string,
test: ConformanceTest,
): TelemetryAdapterConformanceCase {
return {
group,
name,
async run() {
await using fixture = await factory();
await test(fixture);
},
};
}
function findSpan(spans: readonly RecordedTelemetrySpan[], name: string): RecordedTelemetrySpan {
const span = spans.find((candidate) => candidate.name === name);
ok(span, `Expected recorded span ${name}`);
return span;
}
async function rejectsWithSameValue(operation: Promise<unknown>, expected: unknown): Promise<void> {
try {
await operation;
fail("Expected operation to reject");
} catch (error) {
strictEqual(error, expected);
}
}
function unreadable<T extends object>(value: T): T {
return new Proxy(value, {
get: () => {
throw new Error("read");
},
getOwnPropertyDescriptor: () => {
throw new Error("inspect");
},
getPrototypeOf: () => {
throw new Error("prototype");
},
ownKeys: () => {
throw new Error("enumerate");
},
});
}
/** Creates runner-independent cases for the callback telemetry adapter contract. */
export function createTelemetryAdapterConformance(
factory: TelemetryAdapterFixtureFactory,
): readonly TelemetryAdapterConformanceCase[] {
return [
createCase(
factory,
"callback lifecycle",
"admits once synchronously and preserves the result",
async (fixture) => {
let admitted = false;
let calls = 0;
const expected = { value: 42 };
const result = fixture.context.startSpan({ name: "success" }, () => {
admitted = true;
calls++;
return expected;
});
strictEqual(admitted, true);
strictEqual(calls, 1);
strictEqual(await result, expected);
deepStrictEqual(findSpan(await fixture.getSpans(), "success").status, { status: "ok" });
strictEqual(findSpan(await fixture.getSpans(), "success").settled, true);
},
),
createCase(
factory,
"callback lifecycle",
"preserves synchronous and asynchronous rejection values",
async (fixture) => {
const syncError = new Error("sync");
await rejectsWithSameValue(
fixture.context.startSpan({ name: "sync-error" }, () => {
throw syncError;
}),
syncError,
);
const asyncError = { kind: "async" };
await rejectsWithSameValue(
fixture.context.startSpan({ name: "async-error" }, async () => {
throw asyncError;
}),
asyncError,
);
await rejectsWithSameValue(
fixture.context.startSpan({ name: "undefined-error" }, () => Promise.reject(undefined)),
undefined,
);
const unreadableError = unreadable({ kind: "unreadable" });
await rejectsWithSameValue(
fixture.context.startSpan({ name: "unreadable-error" }, () => {
throw unreadableError;
}),
unreadableError,
);
const asyncUnreadableError = unreadable({ kind: "async-unreadable" });
await rejectsWithSameValue(
fixture.context.startSpan({ name: "async-unreadable-error" }, () =>
Promise.reject(asyncUnreadableError),
),
asyncUnreadableError,
);
const spans = await fixture.getSpans();
for (const name of [
"sync-error",
"async-error",
"undefined-error",
"unreadable-error",
"async-unreadable-error",
]) {
strictEqual(findSpan(spans, name).status.status, "error");
}
},
),
createCase(factory, "status", "uses last explicit status without automatic overwrite", async (fixture) => {
await fixture.context.startSpan({ name: "last-status" }, (span) => {
span.setStatus({ status: "error", error: { name: "Expected", message: "first" } });
span.setStatus({ status: "ok" });
});
const thrown = new Error("after explicit status");
await rejectsWithSameValue(
fixture.context.startSpan({ name: "explicit-before-throw" }, (span) => {
span.setStatus({ status: "ok" });
throw thrown;
}),
thrown,
);
const rejected = new Error("after async explicit status");
await rejectsWithSameValue(
fixture.context.startSpan({ name: "explicit-before-rejection" }, (span) => {
span.setStatus({ status: "error", error: { name: "Expected", message: "async failure" } });
return Promise.reject(rejected);
}),
rejected,
);
await fixture.context.startSpan({ name: "expected-failure" }, (span) => {
span.setStatus({ status: "error", error: { name: "Expected", message: "returned failure" } });
return { ok: false };
});
const spans = await fixture.getSpans();
deepStrictEqual(findSpan(spans, "last-status").status, { status: "ok" });
deepStrictEqual(findSpan(spans, "explicit-before-throw").status, { status: "ok" });
deepStrictEqual(findSpan(spans, "explicit-before-rejection").status, {
status: "error",
error: { name: "Expected", message: "async failure" },
});
deepStrictEqual(findSpan(spans, "expected-failure").status, {
status: "error",
error: { name: "Expected", message: "returned failure" },
});
}),
createCase(factory, "recording", "merges attributes and records ordered events", async (fixture) => {
await fixture.context.startSpan(
{
name: "recording",
attributes: { start: "value", overwrite: "start", ignored: undefined },
},
(span) => {
span.setAttributes({ count: 1, overwrite: "middle" });
span.setAttributes({ count: undefined, overwrite: "end" });
span.addEvent("first", { index: 1, ignored: undefined });
span.addEvent("second", { index: 2 });
},
);
const span = findSpan(await fixture.getSpans(), "recording");
deepStrictEqual(span.attributes, { start: "value", overwrite: "end", count: 1 });
deepStrictEqual(span.events, [
{ name: "first", attributes: { index: 1 } },
{ name: "second", attributes: { index: 2 } },
]);
}),
createCase(factory, "recording", "ignores failed attribute calls atomically", async (fixture) => {
await fixture.context.startSpan({ name: "atomic-attributes", attributes: { retained: "value" } }, (span) => {
const attributes: SpanAttributes = {
partial: "must not survive",
unreadable: unreadable(["value"]),
};
doesNotThrow(() => span.setAttributes(attributes));
});
deepStrictEqual(findSpan(await fixture.getSpans(), "atomic-attributes").attributes, {
retained: "value",
});
}),
createCase(factory, "recording", "makes calls after settlement inert", async (fixture) => {
let settledSpan: TelemetrySpan | undefined;
await fixture.context.startSpan({ name: "settled", attributes: { value: "initial" } }, (span) => {
settledSpan = span;
});
const capturedSpan = settledSpan;
if (!capturedSpan) throw new Error("Expected callback span");
doesNotThrow(() => capturedSpan.setAttributes({ value: "late" }));
doesNotThrow(() => capturedSpan.addEvent("late", { value: true }));
doesNotThrow(() => capturedSpan.setStatus({ status: "error" }));
let childAdmitted = false;
const childResult = capturedSpan.startSpan({ name: "late-child" }, () => {
childAdmitted = true;
return 7;
});
strictEqual(childAdmitted, true);
strictEqual(await childResult, 7);
const spans = await fixture.getSpans();
strictEqual(spans.length, 1);
deepStrictEqual(spans[0]?.attributes, { value: "initial" });
deepStrictEqual(spans[0]?.events, []);
deepStrictEqual(spans[0]?.status, { status: "ok" });
}),
createCase(factory, "parentage", "records nested and concurrent child relationships", async (fixture) => {
let releaseFirst: (() => void) | undefined;
const firstGate = new Promise<void>((resolve) => {
releaseFirst = resolve;
});
await fixture.context.startSpan({ name: "parent" }, async (parent) => {
const first = parent.startSpan({ name: "first-child" }, async () => {
await firstGate;
});
const second = parent.startSpan({ name: "second-child" }, () => "done");
strictEqual(await second, "done");
releaseFirst?.();
await first;
});
const spans = await fixture.getSpans();
const parent = findSpan(spans, "parent");
const first = findSpan(spans, "first-child");
const second = findSpan(spans, "second-child");
strictEqual(parent.parentId, null);
strictEqual(first.parentId, parent.id);
strictEqual(second.parentId, parent.id);
ok(second.endSequence !== undefined && first.endSequence !== undefined && parent.endSequence !== undefined);
ok(second.endSequence < first.endSequence);
ok(first.endSequence < parent.endSequence);
}),
createCase(factory, "passivity", "suppresses unreadable telemetry payload failures", async (fixture) => {
let calls = 0;
const options = unreadable<SpanOptions>({ name: "unreadable-options", attributes: { secret: "value" } });
const result = fixture.context.startSpan(options, () => {
calls++;
return 9;
});
strictEqual(calls, 1);
strictEqual(await result, 9);
deepStrictEqual(await fixture.getSpans(), []);
await fixture.context.startSpan({ name: "unreadable-recording" }, (span) => {
const attributes = unreadable<SpanAttributes>({ secret: "value" });
const status = unreadable<SpanStatus>({ status: "ok" });
doesNotThrow(() => span.setAttributes(attributes));
doesNotThrow(() => span.addEvent("unreadable-event", attributes));
doesNotThrow(() => span.setStatus(status));
});
const recorded = await fixture.getSpans();
strictEqual(recorded.length, 1);
deepStrictEqual(recorded[0]?.attributes, {});
deepStrictEqual(recorded[0]?.events, []);
deepStrictEqual(recorded[0]?.status, { status: "ok" });
}),
createCase(factory, "passivity", "ignores failed status calls atomically", async (fixture) => {
const rejection = new Error("rejected after unreadable status");
await rejectsWithSameValue(
fixture.context.startSpan({ name: "unreadable-status" }, (span) => {
const status = unreadable<SpanStatus>({ status: "ok" });
doesNotThrow(() => span.setStatus(status));
return Promise.reject(rejection);
}),
rejection,
);
strictEqual(findSpan(await fixture.getSpans(), "unreadable-status").status.status, "error");
}),
];
}

View File

@@ -0,0 +1,6 @@
export { createTelemetryAdapterConformance } from "./conformance.ts";
export type {
TelemetryAdapterConformanceCase,
TelemetryAdapterFixture,
TelemetryAdapterFixtureFactory,
} from "./types.ts";

View File

@@ -0,0 +1,18 @@
import type { TelemetryContext } from "../index.ts";
import type { RecordedTelemetrySpan } from "../memory.ts";
/** A fresh adapter instance and normalized snapshot reader owned by one conformance case. */
export interface TelemetryAdapterFixture extends AsyncDisposable {
readonly context: TelemetryContext;
getSpans(): Promise<readonly RecordedTelemetrySpan[]>;
}
/** Creates an isolated adapter fixture for one conformance case. */
export type TelemetryAdapterFixtureFactory = () => Promise<TelemetryAdapterFixture>;
/** A runner-independent conformance case that can be registered with any test framework. */
export interface TelemetryAdapterConformanceCase {
readonly group: string;
readonly name: string;
run(): Promise<void>;
}