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(options: SpanOptions, callback: (span: TelemetrySpan) => T | Promise): Promise; } 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; } export type TelemetryParentDefinition = | { kind: "any" } | { kind: "root_or_external" } | { kind: "spans"; spans: readonly string[] }; export interface TelemetrySpanDefinition { description: string; parents: TelemetryParentDefinition; startAttributes: Record; endAttributes: Record; events?: Record; status: { default: "ok"; errorWhen: string }; } export interface TelemetrySchemaDefinition { version: number; spans: Record; } /** Typed identity helper for serializable telemetry schema data. */ export function defineTelemetrySchema(schema: T): T { return schema; } type AttributeDefinitionValue = 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, > = { [Name in keyof Definitions]-?: Definitions[Name]["required"] extends true ? Name : never; }[keyof Definitions]; type OptionalAttributeNames< Definitions extends Record, > = Exclude>; export type InferRequiredAndOptionalAttributes< Definitions extends Record, > = keyof Definitions extends never ? Record : { [Name in RequiredAttributeNames]: AttributeDefinitionValue; } & { [Name in OptionalAttributeNames]?: AttributeDefinitionValue; }; export type InferStartAttributes> = InferRequiredAndOptionalAttributes; export type InferOptionalAttributes> = keyof Definitions extends never ? Record : { [Name in keyof Definitions]?: AttributeDefinitionValue }; export type ExactTelemetryAttributes = Actual & Record, never>; export type InferEventAttributes> = InferRequiredAndOptionalAttributes; export type TelemetrySchemaSpanName = keyof Schema["spans"] & string; type SchemaSpan< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, > = Schema["spans"][Name]; export type TelemetrySchemaSpanStartAttributes< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, > = SchemaSpan["startAttributes"] extends infer Definitions extends Record< string, TelemetryStartAttributeDefinition > ? InferStartAttributes : never; export type TelemetrySchemaSpanEndAttributes< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, > = SchemaSpan["endAttributes"] extends infer Definitions extends Record< string, TelemetryAttributeDefinition > ? InferOptionalAttributes : never; type SchemaSpanEvents< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, > = SchemaSpan extends { events: infer Events extends Record } ? Events : Record; export type TelemetrySchemaSpanEventName< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, > = keyof SchemaSpanEvents & string; type SchemaSpanEvent< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, EventName extends TelemetrySchemaSpanEventName, > = SchemaSpanEvents extends infer Events ? EventName extends keyof Events ? Events[EventName] : never : never; export type TelemetrySchemaSpanEventAttributes< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, EventName extends TelemetrySchemaSpanEventName, > = SchemaSpanEvent extends { attributes: infer Definitions extends Record; } ? InferEventAttributes : never; type SchemaSpanEventAttributeDefinitions< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, EventName extends TelemetrySchemaSpanEventName, > = SchemaSpanEvent extends { attributes: infer Definitions extends Record; } ? Definitions : Record; type EventArguments< Definitions extends Record, Attributes extends InferEventAttributes, > = [RequiredAttributeNames] extends [never] ? [attributes?: ExactTelemetryAttributes, Attributes>] : [attributes: ExactTelemetryAttributes, Attributes>]; export type SchemaTelemetrySpan< Schema extends TelemetrySchemaDefinition, Name extends TelemetrySchemaSpanName, > = Omit & { addEvent< EventName extends TelemetrySchemaSpanEventName, const Attributes extends InferEventAttributes< SchemaSpanEventAttributeDefinitions > = InferEventAttributes>, >( name: EventName, ...args: EventArguments, Attributes> ): void; setAttributes>( attributes: ExactTelemetryAttributes, Attributes>, ): void; }; export type TelemetrySchemaSpanUnion = { [Name in TelemetrySchemaSpanName]: { name: Name; startAttributes: TelemetrySchemaSpanStartAttributes; endAttributes: TelemetrySchemaSpanEndAttributes; events: { [EventName in TelemetrySchemaSpanEventName]: TelemetrySchemaSpanEventAttributes< Schema, Name, EventName >; }; }; }[TelemetrySchemaSpanName]; type TelemetrySchemaTuple = readonly [TelemetrySchemaDefinition, ...TelemetrySchemaDefinition[]]; type SpanNameInSchema = Schema extends TelemetrySchemaDefinition ? TelemetrySchemaSpanName : never; type SpanNameInSchemas = SpanNameInSchema; type SpanStartAttributesInSchema< Schema extends TelemetrySchemaDefinition, Name extends string, > = Schema extends TelemetrySchemaDefinition ? Name extends TelemetrySchemaSpanName ? TelemetrySchemaSpanStartAttributes : never : never; type SpanInSchema< Schema extends TelemetrySchemaDefinition, Name extends string, > = Schema extends TelemetrySchemaDefinition ? Name extends TelemetrySchemaSpanName ? SchemaTelemetrySpan : 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, Seen> | DuplicateTelemetrySpanNames> : never; type UniqueTelemetrySchemas = [DuplicateTelemetrySpanNames] extends [ never, ] ? unknown : { readonly "duplicate telemetry span names": DuplicateTelemetrySpanNames }; type UnionToIntersection = (Union extends unknown ? (value: Union) => void : never) extends ( value: infer Intersection, ) => void ? Intersection : never; type TypedSpanStarterForName> = < const Attributes extends SpanStartAttributesInSchema, Result, >( name: Name, attributes: ExactTelemetryAttributes, Attributes>, callback: ( span: SpanInSchema, startChildSpan: TypedSpanStarter, ) => Result | Promise, ) => Promise; /** A per-span overload set bound to one explicit parent context and one or more schemas. */ export type TypedSpanStarter = UnionToIntersection< { [Name in SpanNameInSchemas]: TypedSpanStarterForName; }[SpanNameInSchemas] >; function bindTypedSpanStarter( telemetryContext: TelemetryContext, ): TypedSpanStarter { const startSpan = ( name: SpanNameInSchemas, attributes: SpanAttributes, callback: ( span: SpanInSchema>, startChildSpan: TypedSpanStarter, ) => unknown, ): Promise => telemetryContext.startSpan({ name, attributes }, (span) => callback( span as SpanInSchema>, bindTypedSpanStarter(span), ), ); return startSpan as TypedSpanStarter; } /** * 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( telemetryContext: TelemetryContext, _schemas: Schemas & UniqueTelemetrySchemas, ): TypedSpanStarter { return bindTypedSpanStarter(telemetryContext); } export type { RecordedTelemetryEvent, RecordedTelemetrySpan } from "./memory.ts"; export { InMemoryTelemetryContext } from "./memory.ts";