@fractaal/pi-telemetry 0.86.9 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ import type { ExactTelemetryAttributes, InferRequiredAndOptionalAttributes, SpanAttributes, TelemetryContext, TelemetrySchemaDefinition, TelemetryStartAttributeDefinition } from "./index.ts";
2
+ export interface InstrumentOptions {
3
+ description?: string;
4
+ unit?: string;
5
+ }
6
+ export interface HistogramOptions extends InstrumentOptions {
7
+ /** Bucket hints; callers supply finite, strictly increasing values. */
8
+ boundaries?: readonly number[];
9
+ }
10
+ export interface TelemetryCounter {
11
+ add(value: number, attributes?: SpanAttributes): void;
12
+ }
13
+ export interface TelemetryUpDownCounter {
14
+ add(value: number, attributes?: SpanAttributes): void;
15
+ }
16
+ export interface TelemetryHistogram {
17
+ record(value: number, attributes?: SpanAttributes, context?: TelemetryContext): void;
18
+ }
19
+ /**
20
+ * Optional adapter capability with synchronous factories and recording methods.
21
+ * Typed inputs are trusted; callers supply finite measurements and nonnegative counter increments.
22
+ * Malformed input behavior is unspecified.
23
+ */
24
+ export interface TelemetryMetrics {
25
+ counter(name: string, options?: InstrumentOptions): TelemetryCounter;
26
+ upDownCounter(name: string, options?: InstrumentOptions): TelemetryUpDownCounter;
27
+ histogram(name: string, options?: HistogramOptions): TelemetryHistogram;
28
+ }
29
+ export type TelemetryMetricAttributeDefinition = TelemetryStartAttributeDefinition & {
30
+ cardinality?: "low";
31
+ };
32
+ export type TelemetryMetricDefinition = {
33
+ description: string;
34
+ unit?: string;
35
+ attributes: Record<string, TelemetryMetricAttributeDefinition>;
36
+ } & ({
37
+ kind: "counter" | "upDownCounter";
38
+ boundaries?: never;
39
+ } | {
40
+ kind: "histogram";
41
+ boundaries?: readonly number[];
42
+ });
43
+ type MetricKind = TelemetryMetricDefinition["kind"];
44
+ type MetricSchemas = readonly [TelemetrySchemaDefinition, ...TelemetrySchemaDefinition[]];
45
+ type MetricsOf<Schema extends TelemetrySchemaDefinition> = Schema extends {
46
+ metrics: infer Metrics extends Record<string, TelemetryMetricDefinition>;
47
+ } ? Metrics : Record<never, never>;
48
+ type MetricNames<Schema extends TelemetrySchemaDefinition, Kind extends MetricKind> = Schema extends TelemetrySchemaDefinition ? {
49
+ [Name in keyof MetricsOf<Schema>]: MetricsOf<Schema>[Name] extends {
50
+ kind: Kind;
51
+ } ? Name : never;
52
+ }[keyof MetricsOf<Schema>] & string : never;
53
+ type MetricByName<Schema extends TelemetrySchemaDefinition, Name extends string> = Schema extends TelemetrySchemaDefinition ? Name extends keyof MetricsOf<Schema> ? MetricsOf<Schema>[Name] : never : never;
54
+ type DuplicateMetricNames<Schemas extends readonly TelemetrySchemaDefinition[], Seen extends string = never> = Schemas extends readonly [
55
+ infer Schema extends TelemetrySchemaDefinition,
56
+ ...infer Rest extends readonly TelemetrySchemaDefinition[]
57
+ ] ? Extract<keyof MetricsOf<Schema>, Seen> | DuplicateMetricNames<Rest, Seen | (keyof MetricsOf<Schema> & string)> : never;
58
+ type MetricAttributes<Definition extends TelemetryMetricDefinition> = InferRequiredAndOptionalAttributes<Definition["attributes"]>;
59
+ type MeasurementArguments<Definition extends TelemetryMetricDefinition, Attributes extends MetricAttributes<Definition>> = {
60
+ [Name in keyof Definition["attributes"]]: Definition["attributes"][Name]["required"] extends true ? Name : never;
61
+ }[keyof Definition["attributes"]] extends never ? [attributes?: ExactTelemetryAttributes<MetricAttributes<Definition>, Attributes>] : [attributes: ExactTelemetryAttributes<MetricAttributes<Definition>, Attributes>];
62
+ export type SchemaTelemetryCounter<Definition extends TelemetryMetricDefinition> = {
63
+ add<const Attributes extends MetricAttributes<Definition> = MetricAttributes<Definition>>(value: number, ...args: MeasurementArguments<Definition, Attributes>): void;
64
+ };
65
+ export type SchemaTelemetryHistogram<Definition extends TelemetryMetricDefinition> = {
66
+ record<const Attributes extends MetricAttributes<Definition> = MetricAttributes<Definition>>(value: number, ...args: [...MeasurementArguments<Definition, Attributes>, context?: TelemetryContext]): void;
67
+ };
68
+ export interface TypedTelemetryMetrics<Schemas extends MetricSchemas> {
69
+ counter<Name extends MetricNames<Schemas[number], "counter">>(name: Name): SchemaTelemetryCounter<MetricByName<Schemas[number], Name>>;
70
+ upDownCounter<Name extends MetricNames<Schemas[number], "upDownCounter">>(name: Name): SchemaTelemetryCounter<MetricByName<Schemas[number], Name>>;
71
+ histogram<Name extends MetricNames<Schemas[number], "histogram">>(name: Name): SchemaTelemetryHistogram<MetricByName<Schemas[number], Name>>;
72
+ }
73
+ /** Bind instrument names, kinds, metadata, and exact measurement attributes to one or more schemas. */
74
+ export declare function createTypedMetrics<const Schemas extends MetricSchemas>(metrics: TelemetryMetrics, schemas: Schemas & ([DuplicateMetricNames<Schemas>] extends [never] ? unknown : {
75
+ readonly "duplicate telemetry metric names": DuplicateMetricNames<Schemas>;
76
+ })): TypedTelemetryMetrics<Schemas>;
77
+ export {};
78
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** Bind instrument names, kinds, metadata, and exact measurement attributes to one or more schemas. */
2
+ export function createTypedMetrics(metrics, schemas) {
3
+ const definitions = new Map();
4
+ for (const schema of schemas) {
5
+ for (const [name, definition] of Object.entries(schema.metrics ?? {}))
6
+ definitions.set(name, definition);
7
+ }
8
+ return {
9
+ counter: (name) => metrics.counter(name, definitions.get(name)),
10
+ upDownCounter: (name) => metrics.upDownCounter(name, definitions.get(name)),
11
+ histogram: (name) => metrics.histogram(name, definitions.get(name)),
12
+ };
13
+ }
14
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AA+HA,uGAAuG;AACvG,MAAM,UAAU,kBAAkB,CACjC,OAAyB,EACzB,OAGmF;IAEnF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAqC,CAAC;IACjE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;YAAE,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC1G,CAAC;IACD,OAAO;QACN,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvE,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnF,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;KAC9B,CAAC;AAChD,CAAC","sourcesContent":["import type {\n\tExactTelemetryAttributes,\n\tInferRequiredAndOptionalAttributes,\n\tSpanAttributes,\n\tTelemetryContext,\n\tTelemetrySchemaDefinition,\n\tTelemetryStartAttributeDefinition,\n} from \"./index.ts\";\n\nexport interface InstrumentOptions {\n\tdescription?: string;\n\tunit?: string;\n}\n\nexport interface HistogramOptions extends InstrumentOptions {\n\t/** Bucket hints; callers supply finite, strictly increasing values. */\n\tboundaries?: readonly number[];\n}\n\nexport interface TelemetryCounter {\n\tadd(value: number, attributes?: SpanAttributes): void;\n}\n\nexport interface TelemetryUpDownCounter {\n\tadd(value: number, attributes?: SpanAttributes): void;\n}\n\nexport interface TelemetryHistogram {\n\trecord(value: number, attributes?: SpanAttributes, context?: TelemetryContext): void;\n}\n\n/**\n * Optional adapter capability with synchronous factories and recording methods.\n * Typed inputs are trusted; callers supply finite measurements and nonnegative counter increments.\n * Malformed input behavior is unspecified.\n */\nexport interface TelemetryMetrics {\n\tcounter(name: string, options?: InstrumentOptions): TelemetryCounter;\n\tupDownCounter(name: string, options?: InstrumentOptions): TelemetryUpDownCounter;\n\thistogram(name: string, options?: HistogramOptions): TelemetryHistogram;\n}\n\nexport type TelemetryMetricAttributeDefinition = TelemetryStartAttributeDefinition & { cardinality?: \"low\" };\n\nexport type TelemetryMetricDefinition = {\n\tdescription: string;\n\tunit?: string;\n\tattributes: Record<string, TelemetryMetricAttributeDefinition>;\n} & ({ kind: \"counter\" | \"upDownCounter\"; boundaries?: never } | { kind: \"histogram\"; boundaries?: readonly number[] });\n\ntype MetricKind = TelemetryMetricDefinition[\"kind\"];\ntype MetricSchemas = readonly [TelemetrySchemaDefinition, ...TelemetrySchemaDefinition[]];\n\ntype MetricsOf<Schema extends TelemetrySchemaDefinition> = Schema extends {\n\tmetrics: infer Metrics extends Record<string, TelemetryMetricDefinition>;\n}\n\t? Metrics\n\t: Record<never, never>;\n\ntype MetricNames<\n\tSchema extends TelemetrySchemaDefinition,\n\tKind extends MetricKind,\n> = Schema extends TelemetrySchemaDefinition\n\t? {\n\t\t\t[Name in keyof MetricsOf<Schema>]: MetricsOf<Schema>[Name] extends { kind: Kind } ? Name : never;\n\t\t}[keyof MetricsOf<Schema>] &\n\t\t\tstring\n\t: never;\n\ntype MetricByName<\n\tSchema extends TelemetrySchemaDefinition,\n\tName extends string,\n> = Schema extends TelemetrySchemaDefinition\n\t? Name extends keyof MetricsOf<Schema>\n\t\t? MetricsOf<Schema>[Name]\n\t\t: never\n\t: never;\n\ntype DuplicateMetricNames<\n\tSchemas extends readonly TelemetrySchemaDefinition[],\n\tSeen extends string = never,\n> = Schemas extends readonly [\n\tinfer Schema extends TelemetrySchemaDefinition,\n\t...infer Rest extends readonly TelemetrySchemaDefinition[],\n]\n\t? Extract<keyof MetricsOf<Schema>, Seen> | DuplicateMetricNames<Rest, Seen | (keyof MetricsOf<Schema> & string)>\n\t: never;\n\ntype MetricAttributes<Definition extends TelemetryMetricDefinition> = InferRequiredAndOptionalAttributes<\n\tDefinition[\"attributes\"]\n>;\n\ntype MeasurementArguments<\n\tDefinition extends TelemetryMetricDefinition,\n\tAttributes extends MetricAttributes<Definition>,\n> = {\n\t[Name in keyof Definition[\"attributes\"]]: Definition[\"attributes\"][Name][\"required\"] extends true ? Name : never;\n}[keyof Definition[\"attributes\"]] extends never\n\t? [attributes?: ExactTelemetryAttributes<MetricAttributes<Definition>, Attributes>]\n\t: [attributes: ExactTelemetryAttributes<MetricAttributes<Definition>, Attributes>];\n\nexport type SchemaTelemetryCounter<Definition extends TelemetryMetricDefinition> = {\n\tadd<const Attributes extends MetricAttributes<Definition> = MetricAttributes<Definition>>(\n\t\tvalue: number,\n\t\t...args: MeasurementArguments<Definition, Attributes>\n\t): void;\n};\n\nexport type SchemaTelemetryHistogram<Definition extends TelemetryMetricDefinition> = {\n\trecord<const Attributes extends MetricAttributes<Definition> = MetricAttributes<Definition>>(\n\t\tvalue: number,\n\t\t...args: [...MeasurementArguments<Definition, Attributes>, context?: TelemetryContext]\n\t): void;\n};\n\nexport interface TypedTelemetryMetrics<Schemas extends MetricSchemas> {\n\tcounter<Name extends MetricNames<Schemas[number], \"counter\">>(\n\t\tname: Name,\n\t): SchemaTelemetryCounter<MetricByName<Schemas[number], Name>>;\n\tupDownCounter<Name extends MetricNames<Schemas[number], \"upDownCounter\">>(\n\t\tname: Name,\n\t): SchemaTelemetryCounter<MetricByName<Schemas[number], Name>>;\n\thistogram<Name extends MetricNames<Schemas[number], \"histogram\">>(\n\t\tname: Name,\n\t): SchemaTelemetryHistogram<MetricByName<Schemas[number], Name>>;\n}\n\n/** Bind instrument names, kinds, metadata, and exact measurement attributes to one or more schemas. */\nexport function createTypedMetrics<const Schemas extends MetricSchemas>(\n\tmetrics: TelemetryMetrics,\n\tschemas: Schemas &\n\t\t([DuplicateMetricNames<Schemas>] extends [never]\n\t\t\t? unknown\n\t\t\t: { readonly \"duplicate telemetry metric names\": DuplicateMetricNames<Schemas> }),\n): TypedTelemetryMetrics<Schemas> {\n\tconst definitions = new Map<string, TelemetryMetricDefinition>();\n\tfor (const schema of schemas) {\n\t\tfor (const [name, definition] of Object.entries(schema.metrics ?? {})) definitions.set(name, definition);\n\t}\n\treturn {\n\t\tcounter: (name: string) => metrics.counter(name, definitions.get(name)),\n\t\tupDownCounter: (name: string) => metrics.upDownCounter(name, definitions.get(name)),\n\t\thistogram: (name: string) => metrics.histogram(name, definitions.get(name)),\n\t} as unknown as TypedTelemetryMetrics<Schemas>;\n}\n"]}
package/dist/noop.d.ts CHANGED
@@ -1,4 +1,9 @@
1
- import type { TelemetryContext } from "./index.ts";
1
+ import type { TelemetryPropagation, TelemetrySpan } from "./index.ts";
2
+ import type { TelemetryMetrics } from "./metrics.ts";
3
+ /** Shared instruments that never inspect or retain metadata, measurements, or contexts. */
4
+ export declare const NOOP_TELEMETRY_METRICS: TelemetryMetrics;
5
+ /** Shared propagation capability that neither inspects nor retains its inputs. */
6
+ export declare const NOOP_TELEMETRY_PROPAGATION: TelemetryPropagation;
2
7
  /** Shared telemetry context used when an application does not provide one. */
3
- export declare const NOOP_TELEMETRY_CONTEXT: TelemetryContext;
8
+ export declare const NOOP_TELEMETRY_CONTEXT: TelemetrySpan;
4
9
  //# sourceMappingURL=noop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"noop.d.ts","sourceRoot":"","sources":["../src/noop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,gBAAgB,EAAiB,MAAM,YAAY,CAAC;AAkB/E,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,EAAE,gBAAoC,CAAC"}
1
+ {"version":3,"file":"noop.d.ts","sourceRoot":"","sources":["../src/noop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,oBAAoB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnF,OAAO,KAAK,EAAwC,gBAAgB,EAA0B,MAAM,cAAc,CAAC;AAUnH,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,EAAE,gBAInC,CAAC;AAEH,kFAAkF;AAClF,eAAO,MAAM,0BAA0B,EAAE,oBAGvC,CAAC;AAaH,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,EAAE,aAAiC,CAAC"}
package/dist/noop.js CHANGED
@@ -1,13 +1,25 @@
1
- function startNoopSpan(_options, callback) {
2
- try {
3
- return Promise.resolve(callback(noopTelemetrySpan));
4
- }
5
- catch (error) {
6
- return Promise.reject(error);
7
- }
1
+ async function startNoopSpan(_options, callback) {
2
+ return callback(noopTelemetrySpan);
8
3
  }
4
+ const noopCounter = Object.freeze({ add: () => { } });
5
+ const noopUpDownCounter = Object.freeze({ add: () => { } });
6
+ const noopHistogram = Object.freeze({ record: () => { } });
7
+ /** Shared instruments that never inspect or retain metadata, measurements, or contexts. */
8
+ export const NOOP_TELEMETRY_METRICS = Object.freeze({
9
+ counter: () => noopCounter,
10
+ upDownCounter: () => noopUpDownCounter,
11
+ histogram: () => noopHistogram,
12
+ });
13
+ /** Shared propagation capability that neither inspects nor retains its inputs. */
14
+ export const NOOP_TELEMETRY_PROPAGATION = Object.freeze({
15
+ inject: () => undefined,
16
+ extract: () => NOOP_TELEMETRY_CONTEXT,
17
+ });
9
18
  const noopTelemetrySpan = {
19
+ metrics: NOOP_TELEMETRY_METRICS,
20
+ propagation: NOOP_TELEMETRY_PROPAGATION,
10
21
  startSpan: startNoopSpan,
22
+ addLink: () => { },
11
23
  addEvent: () => { },
12
24
  setAttributes: () => { },
13
25
  setStatus: () => { },
package/dist/noop.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"noop.js","sourceRoot":"","sources":["../src/noop.ts"],"names":[],"mappings":"AAEA,SAAS,aAAa,CAAI,QAAqB,EAAE,QAAiD;IACjG,IAAI,CAAC;QACJ,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;AACF,CAAC;AAED,MAAM,iBAAiB,GAAkB;IACxC,SAAS,EAAE,aAAa;IACxB,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;IAClB,aAAa,EAAE,GAAG,EAAE,GAAE,CAAC;IACvB,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;CACnB,CAAC;AACF,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAEjC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,sBAAsB,GAAqB,iBAAiB,CAAC","sourcesContent":["import type { SpanOptions, TelemetryContext, TelemetrySpan } from \"./index.ts\";\n\nfunction startNoopSpan<T>(_options: SpanOptions, callback: (span: TelemetrySpan) => T | Promise<T>): Promise<T> {\n\ttry {\n\t\treturn Promise.resolve(callback(noopTelemetrySpan));\n\t} catch (error) {\n\t\treturn Promise.reject(error);\n\t}\n}\n\nconst noopTelemetrySpan: TelemetrySpan = {\n\tstartSpan: startNoopSpan,\n\taddEvent: () => {},\n\tsetAttributes: () => {},\n\tsetStatus: () => {},\n};\nObject.freeze(noopTelemetrySpan);\n\n/** Shared telemetry context used when an application does not provide one. */\nexport const NOOP_TELEMETRY_CONTEXT: TelemetryContext = noopTelemetrySpan;\n"]}
1
+ {"version":3,"file":"noop.js","sourceRoot":"","sources":["../src/noop.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,aAAa,CAAI,QAAqB,EAAE,QAAiD;IACvG,OAAO,QAAQ,CAAC,iBAAiB,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,WAAW,GAAqB,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;AACvE,MAAM,iBAAiB,GAA2B,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;AACnF,MAAM,aAAa,GAAuB,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;AAE9E,2FAA2F;AAC3F,MAAM,CAAC,MAAM,sBAAsB,GAAqB,MAAM,CAAC,MAAM,CAAC;IACrE,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW;IAC1B,aAAa,EAAE,GAAG,EAAE,CAAC,iBAAiB;IACtC,SAAS,EAAE,GAAG,EAAE,CAAC,aAAa;CAC9B,CAAC,CAAC;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,0BAA0B,GAAyB,MAAM,CAAC,MAAM,CAAC;IAC7E,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS;IACvB,OAAO,EAAE,GAAG,EAAE,CAAC,sBAAsB;CACrC,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAkB;IACxC,OAAO,EAAE,sBAAsB;IAC/B,WAAW,EAAE,0BAA0B;IACvC,SAAS,EAAE,aAAa;IACxB,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;IACjB,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;IAClB,aAAa,EAAE,GAAG,EAAE,GAAE,CAAC;IACvB,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;CACnB,CAAC;AACF,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAEjC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,sBAAsB,GAAkB,iBAAiB,CAAC","sourcesContent":["import type { SpanOptions, TelemetryPropagation, TelemetrySpan } from \"./index.ts\";\nimport type { TelemetryCounter, TelemetryHistogram, TelemetryMetrics, TelemetryUpDownCounter } from \"./metrics.ts\";\n\nasync function startNoopSpan<T>(_options: SpanOptions, callback: (span: TelemetrySpan) => T | Promise<T>): Promise<T> {\n\treturn callback(noopTelemetrySpan);\n}\n\nconst noopCounter: TelemetryCounter = Object.freeze({ add: () => {} });\nconst noopUpDownCounter: TelemetryUpDownCounter = Object.freeze({ add: () => {} });\nconst noopHistogram: TelemetryHistogram = Object.freeze({ record: () => {} });\n\n/** Shared instruments that never inspect or retain metadata, measurements, or contexts. */\nexport const NOOP_TELEMETRY_METRICS: TelemetryMetrics = Object.freeze({\n\tcounter: () => noopCounter,\n\tupDownCounter: () => noopUpDownCounter,\n\thistogram: () => noopHistogram,\n});\n\n/** Shared propagation capability that neither inspects nor retains its inputs. */\nexport const NOOP_TELEMETRY_PROPAGATION: TelemetryPropagation = Object.freeze({\n\tinject: () => undefined,\n\textract: () => NOOP_TELEMETRY_CONTEXT,\n});\n\nconst noopTelemetrySpan: TelemetrySpan = {\n\tmetrics: NOOP_TELEMETRY_METRICS,\n\tpropagation: NOOP_TELEMETRY_PROPAGATION,\n\tstartSpan: startNoopSpan,\n\taddLink: () => {},\n\taddEvent: () => {},\n\tsetAttributes: () => {},\n\tsetStatus: () => {},\n};\nObject.freeze(noopTelemetrySpan);\n\n/** Shared telemetry context used when an application does not provide one. */\nexport const NOOP_TELEMETRY_CONTEXT: TelemetrySpan = noopTelemetrySpan;\n"]}
@@ -0,0 +1,8 @@
1
+ import type { StoredSpan } from "./index.ts";
2
+ /**
3
+ * Render stored spans as a chronological forest, treating absent parents as roots. Lengths of time are milliseconds to
4
+ * the microsecond. Control characters in ids, names, attribute keys and error messages are escaped (attribute values
5
+ * are JSON).
6
+ */
7
+ export declare function renderTrace(spans: readonly StoredSpan[]): string;
8
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,YAAY,CAAC;AAsB7D;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CA4ChE"}
package/dist/render.js ADDED
@@ -0,0 +1,65 @@
1
+ // C0 and C1 control characters and DEL: a stored id, name or message may come from another process (a carrier), so
2
+ // none reaches a terminal or a viewer raw.
3
+ const CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
4
+ /** `text` with every control character written as a `\uXXXX` escape. */
5
+ function escaped(text) {
6
+ return text.replace(CONTROL, (char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
7
+ }
8
+ /** `ms` rounded to the microsecond, as the adapters record span times. */
9
+ function microseconds(ms) {
10
+ return Math.round(ms * 1000) / 1000;
11
+ }
12
+ function formatAttributes(attributes = {}) {
13
+ return Object.entries(attributes)
14
+ .map(([key, value]) => ` ${escaped(key)}=${JSON.stringify(value)}`)
15
+ .join("");
16
+ }
17
+ /**
18
+ * Render stored spans as a chronological forest, treating absent parents as roots. Lengths of time are milliseconds to
19
+ * the microsecond. Control characters in ids, names, attribute keys and error messages are escaped (attribute values
20
+ * are JSON).
21
+ */
22
+ export function renderTrace(spans) {
23
+ const byId = new Map(spans.map((span) => [span.id, span]));
24
+ const roots = [];
25
+ const children = new Map();
26
+ for (const span of [...byId.values()].sort((left, right) => left.startedMs - right.startedMs)) {
27
+ if (span.parentId === undefined || !byId.has(span.parentId)) {
28
+ roots.push(span);
29
+ }
30
+ else {
31
+ const siblings = children.get(span.parentId);
32
+ if (siblings === undefined)
33
+ children.set(span.parentId, [span]);
34
+ else
35
+ siblings.push(span);
36
+ }
37
+ }
38
+ const lines = [];
39
+ const pending = roots.reverse().map((span) => ({ span, depth: 0 }));
40
+ while (pending.length > 0) {
41
+ const { span, depth } = pending.pop();
42
+ const indent = " ".repeat(depth);
43
+ let error = "";
44
+ if (span.status.status === "error") {
45
+ error = " error";
46
+ if (span.status.error !== undefined) {
47
+ error += ` ${escaped(span.status.error.name)}: ${escaped(span.status.error.message)}`;
48
+ }
49
+ }
50
+ lines.push(`${indent}${escaped(span.name)}${formatAttributes(span.attributes)} (${microseconds(span.endedMs - span.startedMs)}ms)${error}`);
51
+ for (const event of span.events) {
52
+ lines.push(`${indent} · ${escaped(event.name)}${formatAttributes(event.attributes)} (+${microseconds(event.timeMs)}ms)`);
53
+ }
54
+ for (const link of span.links) {
55
+ const ids = `trace ${escaped(link.traceId)} span ${escaped(link.spanId)}`;
56
+ lines.push(`${indent} → link ${ids}${formatAttributes(link.attributes)}`);
57
+ }
58
+ const descendants = children.get(span.id) ?? [];
59
+ for (let index = descendants.length - 1; index >= 0; index--) {
60
+ pending.push({ span: descendants[index], depth: depth + 1 });
61
+ }
62
+ }
63
+ return lines.join("\n");
64
+ }
65
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SpanAttributes, StoredSpan } from \"./index.ts\";\n\n// C0 and C1 control characters and DEL: a stored id, name or message may come from another process (a carrier), so\n// none reaches a terminal or a viewer raw.\nconst CONTROL = /[\\u0000-\\u001f\\u007f-\\u009f]/g;\n\n/** `text` with every control character written as a `\\uXXXX` escape. */\nfunction escaped(text: string): string {\n\treturn text.replace(CONTROL, (char) => `\\\\u${char.charCodeAt(0).toString(16).padStart(4, \"0\")}`);\n}\n\n/** `ms` rounded to the microsecond, as the adapters record span times. */\nfunction microseconds(ms: number): number {\n\treturn Math.round(ms * 1000) / 1000;\n}\n\nfunction formatAttributes(attributes: Readonly<SpanAttributes> = {}): string {\n\treturn Object.entries(attributes)\n\t\t.map(([key, value]) => ` ${escaped(key)}=${JSON.stringify(value)}`)\n\t\t.join(\"\");\n}\n\n/**\n * Render stored spans as a chronological forest, treating absent parents as roots. Lengths of time are milliseconds to\n * the microsecond. Control characters in ids, names, attribute keys and error messages are escaped (attribute values\n * are JSON).\n */\nexport function renderTrace(spans: readonly StoredSpan[]): string {\n\tconst byId = new Map(spans.map((span) => [span.id, span]));\n\tconst roots: StoredSpan[] = [];\n\tconst children = new Map<string, StoredSpan[]>();\n\tfor (const span of [...byId.values()].sort((left, right) => left.startedMs - right.startedMs)) {\n\t\tif (span.parentId === undefined || !byId.has(span.parentId)) {\n\t\t\troots.push(span);\n\t\t} else {\n\t\t\tconst siblings = children.get(span.parentId);\n\t\t\tif (siblings === undefined) children.set(span.parentId, [span]);\n\t\t\telse siblings.push(span);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\tconst pending = roots.reverse().map((span) => ({ span, depth: 0 }));\n\twhile (pending.length > 0) {\n\t\tconst { span, depth } = pending.pop()!;\n\t\tconst indent = \" \".repeat(depth);\n\t\tlet error = \"\";\n\t\tif (span.status.status === \"error\") {\n\t\t\terror = \" error\";\n\t\t\tif (span.status.error !== undefined) {\n\t\t\t\terror += ` ${escaped(span.status.error.name)}: ${escaped(span.status.error.message)}`;\n\t\t\t}\n\t\t}\n\t\tlines.push(\n\t\t\t`${indent}${escaped(span.name)}${formatAttributes(span.attributes)} (${microseconds(span.endedMs - span.startedMs)}ms)${error}`,\n\t\t);\n\t\tfor (const event of span.events) {\n\t\t\tlines.push(\n\t\t\t\t`${indent} · ${escaped(event.name)}${formatAttributes(event.attributes)} (+${microseconds(event.timeMs)}ms)`,\n\t\t\t);\n\t\t}\n\t\tfor (const link of span.links) {\n\t\t\tconst ids = `trace ${escaped(link.traceId)} span ${escaped(link.spanId)}`;\n\t\t\tlines.push(`${indent} → link ${ids}${formatAttributes(link.attributes)}`);\n\t\t}\n\t\tconst descendants = children.get(span.id) ?? [];\n\t\tfor (let index = descendants.length - 1; index >= 0; index--) {\n\t\t\tpending.push({ span: descendants[index]!, depth: depth + 1 });\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../src/testing/conformance.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,+BAA+B,EAE/B,8BAA8B,EAC9B,MAAM,YAAY,CAAC;AAoDpB,oFAAoF;AACpF,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,8BAA8B,GACrC,SAAS,+BAA+B,EAAE,CA4P5C"}
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../src/testing/conformance.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,+BAA+B,EAE/B,8BAA8B,EAC9B,MAAM,YAAY,CAAC;AAmCpB,oFAAoF;AACpF,wBAAgB,iCAAiC,CAChD,OAAO,EAAE,8BAA8B,GACrC,SAAS,+BAA+B,EAAE,CAoV5C"}
@@ -50,7 +50,8 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
- import { deepStrictEqual, doesNotThrow, fail, ok, strictEqual } from "node:assert/strict";
53
+ import { deepStrictEqual, fail, ok, strictEqual } from "node:assert/strict";
54
+ import { NOOP_TELEMETRY_CONTEXT } from "../index.js";
54
55
  function createCase(factory, group, name, test) {
55
56
  return {
56
57
  group,
@@ -87,25 +88,174 @@ async function rejectsWithSameValue(operation, expected) {
87
88
  strictEqual(error, expected);
88
89
  }
89
90
  }
90
- function unreadable(value) {
91
- return new Proxy(value, {
92
- get: () => {
93
- throw new Error("read");
94
- },
95
- getOwnPropertyDescriptor: () => {
96
- throw new Error("inspect");
97
- },
98
- getPrototypeOf: () => {
99
- throw new Error("prototype");
100
- },
101
- ownKeys: () => {
102
- throw new Error("enumerate");
103
- },
104
- });
105
- }
106
91
  /** Creates runner-independent cases for the callback telemetry adapter contract. */
107
92
  export function createTelemetryAdapterConformance(factory) {
108
93
  return [
94
+ createCase(factory, "capabilities", "carries adapter capabilities through contexts and spans", async (fixture) => {
95
+ const metrics = fixture.metrics ?? fixture.context.metrics;
96
+ const propagation = fixture.propagation ?? fixture.context.propagation;
97
+ const assertCapabilities = (context) => {
98
+ strictEqual(context.metrics, metrics);
99
+ strictEqual(context.propagation, propagation);
100
+ };
101
+ assertCapabilities(fixture.context);
102
+ let settled;
103
+ await fixture.context.startSpan({ name: "capabilities" }, async (span) => {
104
+ settled = span;
105
+ assertCapabilities(span);
106
+ await span.startSpan({ name: "capabilities.child" }, assertCapabilities);
107
+ if (propagation) {
108
+ const parent = propagation.extract(propagation.inject(span));
109
+ assertCapabilities(parent);
110
+ await parent.startSpan({ name: "capabilities.extracted" }, assertCapabilities);
111
+ }
112
+ });
113
+ ok(settled);
114
+ await settled.startSpan({ name: "capabilities.inert" }, assertCapabilities);
115
+ }),
116
+ createCase(factory, "metrics", "records ordered measurements and optional histogram exemplars", async (fixture) => {
117
+ if (!fixture.metrics)
118
+ return;
119
+ ok(fixture.getMetrics, "Metrics fixtures require a normalized measurement reader");
120
+ fixture.metrics
121
+ .counter("bytes", { description: "Encoded bytes", unit: "By" })
122
+ .add(12, { direction: "out" });
123
+ const active = fixture.metrics.upDownCounter("active");
124
+ active.add(1);
125
+ active.add(-1);
126
+ const histogram = fixture.metrics.histogram("duration", { unit: "ms", boundaries: [1, 10] });
127
+ await fixture.context.startSpan({ name: "measured" }, (span) => histogram.record(5, { method: "read" }, span));
128
+ const measurements = await fixture.getMetrics();
129
+ deepStrictEqual(measurements.map((measurement) => [measurement.kind, measurement.name, measurement.value]), [
130
+ ["counter", "bytes", 12],
131
+ ["upDownCounter", "active", 1],
132
+ ["upDownCounter", "active", -1],
133
+ ["histogram", "duration", 5],
134
+ ]);
135
+ deepStrictEqual(measurements[0]?.attributes, { direction: "out" });
136
+ strictEqual(measurements[0]?.description, "Encoded bytes");
137
+ strictEqual(measurements[0]?.unit, "By");
138
+ deepStrictEqual(measurements[3]?.boundaries, [1, 10]);
139
+ deepStrictEqual(measurements[3]?.context, findSpan(await fixture.getSpans(), "measured").context);
140
+ }),
141
+ createCase(factory, "metrics", "copies instrument metadata and measurement attributes", async (fixture) => {
142
+ if (!fixture.metrics)
143
+ return;
144
+ ok(fixture.getMetrics, "Metrics fixtures require a normalized measurement reader");
145
+ const boundaries = [1, 10];
146
+ const options = { description: "Original", boundaries };
147
+ const histogram = fixture.metrics.histogram("snapshot", options);
148
+ boundaries.push(100);
149
+ options.description = "Changed";
150
+ const attributes = { tags: ["original"] };
151
+ histogram.record(-1, attributes);
152
+ attributes.tags.push("changed");
153
+ const measurements = await fixture.getMetrics();
154
+ strictEqual(measurements.length, 1);
155
+ deepStrictEqual(measurements[0]?.attributes, { tags: ["original"] });
156
+ deepStrictEqual(measurements[0]?.boundaries, [1, 10]);
157
+ strictEqual(measurements[0]?.description, "Original");
158
+ strictEqual(measurements[0]?.context, null);
159
+ }),
160
+ createCase(factory, "links", "records initial and late links without changing parents", async (fixture) => {
161
+ let target;
162
+ await fixture.context.startSpan({ name: "target" }, (span) => {
163
+ target = span;
164
+ });
165
+ if (!target)
166
+ throw new Error("Expected target span");
167
+ const targetSpan = target;
168
+ const attributes = { reason: ["initial"] };
169
+ await fixture.context.startSpan({ name: "linked", links: [{ context: target, attributes }] }, (span) => {
170
+ attributes.reason.push("mutated");
171
+ span.addLink({ context: targetSpan, attributes: { reason: "late" } });
172
+ span.addLink({ context: NOOP_TELEMETRY_CONTEXT });
173
+ });
174
+ const spans = await fixture.getSpans();
175
+ const linked = findSpan(spans, "linked");
176
+ strictEqual(linked.parentId, null);
177
+ strictEqual(linked.parentContext, null);
178
+ deepStrictEqual(linked.links, [
179
+ { context: findSpan(spans, "target").context, attributes: { reason: ["initial"] } },
180
+ { context: findSpan(spans, "target").context, attributes: { reason: "late" } },
181
+ ]);
182
+ }),
183
+ createCase(factory, "links", "ignores links added after source settlement", async (fixture) => {
184
+ let source;
185
+ await fixture.context.startSpan({ name: "link-source" }, (span) => {
186
+ source = span;
187
+ });
188
+ if (!source)
189
+ throw new Error("Expected source span");
190
+ source.addLink({ context: source });
191
+ deepStrictEqual(findSpan(await fixture.getSpans(), "link-source").links, []);
192
+ }),
193
+ createCase(factory, "propagation", "roundtrips parent identity through JSON without retaining the carrier", async (fixture) => {
194
+ if (!fixture.propagation)
195
+ return;
196
+ const propagation = fixture.propagation;
197
+ await fixture.context.startSpan({ name: "sender" }, async (span) => {
198
+ const carrier = propagation.inject(span);
199
+ ok(carrier);
200
+ const wire = JSON.parse(JSON.stringify(carrier));
201
+ const parent = propagation.extract(wire);
202
+ for (const key of Object.keys(wire))
203
+ wire[key] = "mutated";
204
+ await parent.startSpan({ name: "receiver" }, () => 42);
205
+ deepStrictEqual(propagation.inject(span), carrier);
206
+ });
207
+ const spans = await fixture.getSpans();
208
+ deepStrictEqual(findSpan(spans, "receiver").parentContext, findSpan(spans, "sender").context);
209
+ }),
210
+ createCase(factory, "propagation", "absent and unknown carriers produce recorded roots", async (fixture) => {
211
+ if (!fixture.propagation)
212
+ return;
213
+ strictEqual(fixture.propagation.inject(NOOP_TELEMETRY_CONTEXT), undefined);
214
+ const carriers = [undefined, {}, { unknown: "format" }];
215
+ for (const [index, carrier] of carriers.entries()) {
216
+ const context = fixture.propagation.extract(carrier);
217
+ strictEqual(await context.startSpan({ name: `fallback-${index}` }, () => 42), 42);
218
+ }
219
+ for (const span of await fixture.getSpans())
220
+ strictEqual(span.parentContext, null);
221
+ }),
222
+ createCase(factory, "propagation", "propagates across fresh adapters and multiple hops", async (fixture) => {
223
+ const env_2 = { stack: [], error: void 0, hasError: false };
224
+ try {
225
+ if (!fixture.propagation)
226
+ return;
227
+ const receiver = __addDisposableResource(env_2, await factory(), true);
228
+ const worker = __addDisposableResource(env_2, await factory(), true);
229
+ if (!receiver.propagation || !worker.propagation)
230
+ throw new Error("Expected propagation on each fixture");
231
+ let senderCarrier;
232
+ await fixture.context.startSpan({ name: "sender" }, (span) => {
233
+ senderCarrier = fixture.propagation?.inject(span);
234
+ });
235
+ ok(senderCarrier);
236
+ let receiverCarrier;
237
+ await receiver.propagation
238
+ .extract(JSON.parse(JSON.stringify(senderCarrier)))
239
+ .startSpan({ name: "receiver" }, (span) => {
240
+ receiverCarrier = receiver.propagation?.inject(span);
241
+ });
242
+ ok(receiverCarrier);
243
+ await worker.propagation
244
+ .extract(JSON.parse(JSON.stringify(receiverCarrier)))
245
+ .startSpan({ name: "worker" }, () => { });
246
+ deepStrictEqual(findSpan(await receiver.getSpans(), "receiver").parentContext, senderCarrier);
247
+ deepStrictEqual(findSpan(await worker.getSpans(), "worker").parentContext, receiverCarrier);
248
+ }
249
+ catch (e_2) {
250
+ env_2.error = e_2;
251
+ env_2.hasError = true;
252
+ }
253
+ finally {
254
+ const result_2 = __disposeResources(env_2);
255
+ if (result_2)
256
+ await result_2;
257
+ }
258
+ }),
109
259
  createCase(factory, "callback lifecycle", "admits once synchronously and preserves the result", async (fixture) => {
110
260
  let admitted = false;
111
261
  let calls = 0;
@@ -131,20 +281,8 @@ export function createTelemetryAdapterConformance(factory) {
131
281
  throw asyncError;
132
282
  }), asyncError);
133
283
  await rejectsWithSameValue(fixture.context.startSpan({ name: "undefined-error" }, () => Promise.reject(undefined)), undefined);
134
- const unreadableError = unreadable({ kind: "unreadable" });
135
- await rejectsWithSameValue(fixture.context.startSpan({ name: "unreadable-error" }, () => {
136
- throw unreadableError;
137
- }), unreadableError);
138
- const asyncUnreadableError = unreadable({ kind: "async-unreadable" });
139
- await rejectsWithSameValue(fixture.context.startSpan({ name: "async-unreadable-error" }, () => Promise.reject(asyncUnreadableError)), asyncUnreadableError);
140
284
  const spans = await fixture.getSpans();
141
- for (const name of [
142
- "sync-error",
143
- "async-error",
144
- "undefined-error",
145
- "unreadable-error",
146
- "async-unreadable-error",
147
- ]) {
285
+ for (const name of ["sync-error", "async-error", "undefined-error"]) {
148
286
  strictEqual(findSpan(spans, name).status.status, "error");
149
287
  }
150
288
  }),
@@ -196,18 +334,6 @@ export function createTelemetryAdapterConformance(factory) {
196
334
  { name: "second", attributes: { index: 2 } },
197
335
  ]);
198
336
  }),
199
- createCase(factory, "recording", "ignores failed attribute calls atomically", async (fixture) => {
200
- await fixture.context.startSpan({ name: "atomic-attributes", attributes: { retained: "value" } }, (span) => {
201
- const attributes = {
202
- partial: "must not survive",
203
- unreadable: unreadable(["value"]),
204
- };
205
- doesNotThrow(() => span.setAttributes(attributes));
206
- });
207
- deepStrictEqual(findSpan(await fixture.getSpans(), "atomic-attributes").attributes, {
208
- retained: "value",
209
- });
210
- }),
211
337
  createCase(factory, "recording", "makes calls after settlement inert", async (fixture) => {
212
338
  let settledSpan;
213
339
  await fixture.context.startSpan({ name: "settled", attributes: { value: "initial" } }, (span) => {
@@ -216,9 +342,9 @@ export function createTelemetryAdapterConformance(factory) {
216
342
  const capturedSpan = settledSpan;
217
343
  if (!capturedSpan)
218
344
  throw new Error("Expected callback span");
219
- doesNotThrow(() => capturedSpan.setAttributes({ value: "late" }));
220
- doesNotThrow(() => capturedSpan.addEvent("late", { value: true }));
221
- doesNotThrow(() => capturedSpan.setStatus({ status: "error" }));
345
+ capturedSpan.setAttributes({ value: "late" });
346
+ capturedSpan.addEvent("late", { value: true });
347
+ capturedSpan.setStatus({ status: "error" });
222
348
  let childAdmitted = false;
223
349
  const childResult = capturedSpan.startSpan({ name: "late-child" }, () => {
224
350
  childAdmitted = true;
@@ -257,38 +383,6 @@ export function createTelemetryAdapterConformance(factory) {
257
383
  ok(second.endSequence < first.endSequence);
258
384
  ok(first.endSequence < parent.endSequence);
259
385
  }),
260
- createCase(factory, "passivity", "suppresses unreadable telemetry payload failures", async (fixture) => {
261
- let calls = 0;
262
- const options = unreadable({ name: "unreadable-options", attributes: { secret: "value" } });
263
- const result = fixture.context.startSpan(options, () => {
264
- calls++;
265
- return 9;
266
- });
267
- strictEqual(calls, 1);
268
- strictEqual(await result, 9);
269
- deepStrictEqual(await fixture.getSpans(), []);
270
- await fixture.context.startSpan({ name: "unreadable-recording" }, (span) => {
271
- const attributes = unreadable({ secret: "value" });
272
- const status = unreadable({ status: "ok" });
273
- doesNotThrow(() => span.setAttributes(attributes));
274
- doesNotThrow(() => span.addEvent("unreadable-event", attributes));
275
- doesNotThrow(() => span.setStatus(status));
276
- });
277
- const recorded = await fixture.getSpans();
278
- strictEqual(recorded.length, 1);
279
- deepStrictEqual(recorded[0]?.attributes, {});
280
- deepStrictEqual(recorded[0]?.events, []);
281
- deepStrictEqual(recorded[0]?.status, { status: "ok" });
282
- }),
283
- createCase(factory, "passivity", "ignores failed status calls atomically", async (fixture) => {
284
- const rejection = new Error("rejected after unreadable status");
285
- await rejectsWithSameValue(fixture.context.startSpan({ name: "unreadable-status" }, (span) => {
286
- const status = unreadable({ status: "ok" });
287
- doesNotThrow(() => span.setStatus(status));
288
- return Promise.reject(rejection);
289
- }), rejection);
290
- strictEqual(findSpan(await fixture.getSpans(), "unreadable-status").status.status, "error");
291
- }),
292
386
  ];
293
387
  }
294
388
  //# sourceMappingURL=conformance.js.map