autotel 4.2.5 → 4.3.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.
Files changed (43) hide show
  1. package/dist/{functional-BCtxvYir.cjs → functional-CwsPyc4l.cjs} +17 -34
  2. package/dist/functional-CwsPyc4l.cjs.map +1 -0
  3. package/dist/{functional-CrAq8Az8.js → functional-dAYhXQPA.js} +17 -34
  4. package/dist/functional-dAYhXQPA.js.map +1 -0
  5. package/dist/functional.cjs +1 -1
  6. package/dist/functional.d.cts.map +1 -1
  7. package/dist/functional.d.ts.map +1 -1
  8. package/dist/functional.js +1 -1
  9. package/dist/index.cjs +4 -8
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +5 -9
  14. package/dist/index.js.map +1 -1
  15. package/dist/messaging.cjs +1 -1
  16. package/dist/messaging.js +1 -1
  17. package/dist/semantic-helpers.cjs +1 -1
  18. package/dist/semantic-helpers.js +1 -1
  19. package/dist/test-span-collector.cjs +13 -1
  20. package/dist/test-span-collector.cjs.map +1 -1
  21. package/dist/test-span-collector.d.cts +8 -0
  22. package/dist/test-span-collector.d.cts.map +1 -1
  23. package/dist/test-span-collector.d.ts +8 -0
  24. package/dist/test-span-collector.d.ts.map +1 -1
  25. package/dist/test-span-collector.js +13 -1
  26. package/dist/test-span-collector.js.map +1 -1
  27. package/dist/tracer-provider.cjs +27 -0
  28. package/dist/tracer-provider.cjs.map +1 -1
  29. package/dist/tracer-provider.d.cts +19 -1
  30. package/dist/tracer-provider.d.cts.map +1 -1
  31. package/dist/tracer-provider.d.ts +19 -1
  32. package/dist/tracer-provider.d.ts.map +1 -1
  33. package/dist/tracer-provider.js +27 -1
  34. package/dist/tracer-provider.js.map +1 -1
  35. package/dist/webhook.cjs +1 -1
  36. package/dist/webhook.js +1 -1
  37. package/dist/workflow-distributed.cjs +1 -1
  38. package/dist/workflow-distributed.js +1 -1
  39. package/dist/workflow.cjs +1 -1
  40. package/dist/workflow.js +1 -1
  41. package/package.json +1 -1
  42. package/dist/functional-BCtxvYir.cjs.map +0 -1
  43. package/dist/functional-CrAq8Az8.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { a as trace } from "./functional-CrAq8Az8.js";
1
+ import { a as trace } from "./functional-dAYhXQPA.js";
2
2
 
3
3
  //#region src/semantic-helpers.ts
4
4
  /**
@@ -27,9 +27,21 @@ var TestSpanCollector = class {
27
27
  * Removes the entire traceId entry from the collector.
28
28
  */
29
29
  drainTrace(traceId, rootSpanId) {
30
- const allSpans = this.traces.get(traceId);
30
+ const spans = this.peekTrace(traceId, rootSpanId);
31
31
  this.traces.delete(traceId);
32
+ return spans;
33
+ }
34
+ /**
35
+ * Serialize the finished spans of `traceId` without removing them — safe to
36
+ * call repeatedly while a trace is still growing (e.g. a scenario checker
37
+ * polling until an async flow's completion boundary closes).
38
+ *
39
+ * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.
40
+ */
41
+ peekTrace(traceId, rootSpanId) {
42
+ const allSpans = this.traces.get(traceId);
32
43
  if (!allSpans?.length) return [];
44
+ if (rootSpanId === void 0) return allSpans.map((s) => serializeSpan(s));
33
45
  const byId = /* @__PURE__ */ new Map();
34
46
  for (const s of allSpans) byId.set(s.spanContext().spanId, s);
35
47
  return allSpans.filter((s) => {
@@ -1 +1 @@
1
- {"version":3,"file":"test-span-collector.cjs","names":["SpanStatusCode"],"sources":["../src/test-span-collector.ts"],"sourcesContent":["/**\n * TestSpanCollector — SpanExporter that groups finished spans by traceId\n * and drains per-trace for embedding in test metadata.\n *\n * Wire it to a tracer provider, register that provider with autotel, then\n * create spans through autotel's tracer so finished spans reach the collector.\n *\n * @example\n * ```typescript\n * import { TestSpanCollector } from 'autotel/test-span-collector';\n * import { SimpleSpanProcessor } from 'autotel/processors';\n * import { setAutotelTracerProvider, getAutotelTracer } from 'autotel';\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n *\n * const collector = new TestSpanCollector();\n *\n * // OTel SDK v2: pass processors at construction, then register the provider.\n * const provider = new NodeTracerProvider({\n * spanProcessors: [new SimpleSpanProcessor(collector)],\n * });\n * setAutotelTracerProvider(provider);\n *\n * const tracer = getAutotelTracer('test');\n * const root = tracer.startSpan('operation');\n * // ...work, child spans...\n * root.end();\n *\n * const spans = collector.drainTrace(\n * root.spanContext().traceId,\n * root.spanContext().spanId,\n * );\n * // spans contains the root span and its descendants\n * ```\n *\n * For high-level instrumentation written with `span()` / `track()`, prefer\n * `createTraceCollector()` from `autotel/testing`. For a plain low-level\n * exporter, use `InMemorySpanExporter` from `autotel/exporters`.\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\n/** @see ExportResultCode from @opentelemetry/core */\nconst ExportResultCode = { SUCCESS: 0, FAILED: 1 } as const;\n\n/** Attribute value types that survive serialization */\ntype SerializableValue =\n | string\n | number\n | boolean\n | string[]\n | number[]\n | boolean[];\n\n/**\n * Portable serialized span for embedding in test metadata.\n * `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: 'ok' | 'error' | 'unset';\n statusMessage?: string;\n attributes?: Record<string, SerializableValue>;\n};\n\nexport class TestSpanCollector implements SpanExporter {\n private traces = new Map<string, ReadableSpan[]>();\n\n export(\n spans: ReadableSpan[],\n callback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n let list = this.traces.get(traceId);\n if (!list) {\n list = [];\n this.traces.set(traceId, list);\n }\n list.push(span);\n }\n callback({ code: ExportResultCode.SUCCESS });\n }\n\n /**\n * Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.\n * Filters to the subtree rooted at the test span to prevent cross-test mixing.\n * Removes the entire traceId entry from the collector.\n */\n drainTrace(traceId: string, rootSpanId: string): SerializedSpan[] {\n const allSpans = this.traces.get(traceId);\n this.traces.delete(traceId);\n if (!allSpans?.length) return [];\n\n // Build spanId → span index for efficient parent-chain walking\n const byId = new Map<string, ReadableSpan>();\n for (const s of allSpans) byId.set(s.spanContext().spanId, s);\n\n // Filter to spans that are the root or descendants of rootSpanId\n const included = allSpans.filter((s) => {\n let id: string | undefined = s.spanContext().spanId;\n while (id) {\n if (id === rootSpanId) return true;\n const parent = byId.get(id);\n const parentId = parent?.parentSpanContext?.spanId || undefined;\n if (parentId === id) break; // cycle guard\n id = parentId;\n }\n return false;\n });\n\n return included.map((s) => serializeSpan(s));\n }\n\n shutdown(): Promise<void> {\n this.traces.clear();\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nfunction hrTimeToMs(hr: [number, number]): number {\n return hr[0] * 1000 + hr[1] / 1_000_000;\n}\n\nfunction isSerializable(v: unknown): v is SerializableValue {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')\n return true;\n if (Array.isArray(v) && v.length > 0) {\n const t = typeof v[0];\n return (\n (t === 'string' || t === 'number' || t === 'boolean') &&\n v.every((e) => typeof e === t)\n );\n }\n return false;\n}\n\nexport function serializeSpan(span: ReadableSpan): SerializedSpan {\n const attrs: Record<string, SerializableValue> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (isSerializable(v)) attrs[k] = v;\n }\n return {\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId || undefined,\n name: span.name,\n startTimeMs: hrTimeToMs(span.startTime as [number, number]),\n durationMs: hrTimeToMs(span.duration as [number, number]),\n status:\n span.status.code === SpanStatusCode.ERROR\n ? 'error'\n : span.status.code === SpanStatusCode.OK\n ? 'ok'\n : 'unset',\n statusMessage: span.status.message || undefined,\n attributes: Object.keys(attrs).length > 0 ? attrs : undefined,\n };\n}\n"],"mappings":";;;;;AA2CA,MAAM,mBAAmB;CAAE,SAAS;CAAG,QAAQ;AAAE;AA6BjD,IAAa,oBAAb,MAAuD;CACrD,AAAQ,yBAAS,IAAI,IAA4B;CAEjD,OACE,OACA,UACM;EACN,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,YAAY,CAAC,CAAC;GACnC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO;GAClC,IAAI,CAAC,MAAM;IACT,OAAO,CAAC;IACR,KAAK,OAAO,IAAI,SAAS,IAAI;GAC/B;GACA,KAAK,KAAK,IAAI;EAChB;EACA,SAAS,EAAE,MAAM,iBAAiB,QAAQ,CAAC;CAC7C;;;;;;CAOA,WAAW,SAAiB,YAAsC;EAChE,MAAM,WAAW,KAAK,OAAO,IAAI,OAAO;EACxC,KAAK,OAAO,OAAO,OAAO;EAC1B,IAAI,CAAC,UAAU,QAAQ,OAAO,CAAC;EAG/B,MAAM,uBAAO,IAAI,IAA0B;EAC3C,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC;EAe5D,OAZiB,SAAS,QAAQ,MAAM;GACtC,IAAI,KAAyB,EAAE,YAAY,CAAC,CAAC;GAC7C,OAAO,IAAI;IACT,IAAI,OAAO,YAAY,OAAO;IAE9B,MAAM,WADS,KAAK,IAAI,EACF,CAAC,EAAE,mBAAmB,UAAU;IACtD,IAAI,aAAa,IAAI;IACrB,KAAK;GACP;GACA,OAAO;EACT,CAEc,CAAC,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC;CAC7C;CAEA,WAA0B;EACxB,KAAK,OAAO,MAAM;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,aAA4B;EAC1B,OAAO,QAAQ,QAAQ;CACzB;AACF;AAEA,SAAS,WAAW,IAA8B;CAChD,OAAO,GAAG,KAAK,MAAO,GAAG,KAAK;AAChC;AAEA,SAAS,eAAe,GAAoC;CAC1D,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WACjE,OAAO;CACT,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG;EACpC,MAAM,IAAI,OAAO,EAAE;EACnB,QACG,MAAM,YAAY,MAAM,YAAY,MAAM,cAC3C,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;CAEjC;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,MAAoC;CAChE,MAAM,QAA2C,CAAC;CAClD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,UAAU,GACjD,IAAI,eAAe,CAAC,GAAG,MAAM,KAAK;CAEpC,OAAO;EACL,QAAQ,KAAK,YAAY,CAAC,CAAC;EAC3B,cAAc,KAAK,mBAAmB,UAAU;EAChD,MAAM,KAAK;EACX,aAAa,WAAW,KAAK,SAA6B;EAC1D,YAAY,WAAW,KAAK,QAA4B;EACxD,QACE,KAAK,OAAO,SAASA,kCAAe,QAChC,UACA,KAAK,OAAO,SAASA,kCAAe,KAClC,OACA;EACR,eAAe,KAAK,OAAO,WAAW;EACtC,YAAY,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACtD;AACF"}
1
+ {"version":3,"file":"test-span-collector.cjs","names":["SpanStatusCode"],"sources":["../src/test-span-collector.ts"],"sourcesContent":["/**\n * TestSpanCollector — SpanExporter that groups finished spans by traceId\n * and drains per-trace for embedding in test metadata.\n *\n * Wire it to a tracer provider, register that provider with autotel, then\n * create spans through autotel's tracer so finished spans reach the collector.\n *\n * @example\n * ```typescript\n * import { TestSpanCollector } from 'autotel/test-span-collector';\n * import { SimpleSpanProcessor } from 'autotel/processors';\n * import { setAutotelTracerProvider, getAutotelTracer } from 'autotel';\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n *\n * const collector = new TestSpanCollector();\n *\n * // OTel SDK v2: pass processors at construction, then register the provider.\n * const provider = new NodeTracerProvider({\n * spanProcessors: [new SimpleSpanProcessor(collector)],\n * });\n * setAutotelTracerProvider(provider);\n *\n * const tracer = getAutotelTracer('test');\n * const root = tracer.startSpan('operation');\n * // ...work, child spans...\n * root.end();\n *\n * const spans = collector.drainTrace(\n * root.spanContext().traceId,\n * root.spanContext().spanId,\n * );\n * // spans contains the root span and its descendants\n * ```\n *\n * For high-level instrumentation written with `span()` / `track()`, prefer\n * `createTraceCollector()` from `autotel/testing`. For a plain low-level\n * exporter, use `InMemorySpanExporter` from `autotel/exporters`.\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\n/** @see ExportResultCode from @opentelemetry/core */\nconst ExportResultCode = { SUCCESS: 0, FAILED: 1 } as const;\n\n/** Attribute value types that survive serialization */\ntype SerializableValue =\n | string\n | number\n | boolean\n | string[]\n | number[]\n | boolean[];\n\n/**\n * Portable serialized span for embedding in test metadata.\n * `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: 'ok' | 'error' | 'unset';\n statusMessage?: string;\n attributes?: Record<string, SerializableValue>;\n};\n\nexport class TestSpanCollector implements SpanExporter {\n private traces = new Map<string, ReadableSpan[]>();\n\n export(\n spans: ReadableSpan[],\n callback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n let list = this.traces.get(traceId);\n if (!list) {\n list = [];\n this.traces.set(traceId, list);\n }\n list.push(span);\n }\n callback({ code: ExportResultCode.SUCCESS });\n }\n\n /**\n * Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.\n * Filters to the subtree rooted at the test span to prevent cross-test mixing.\n * Removes the entire traceId entry from the collector.\n */\n drainTrace(traceId: string, rootSpanId: string): SerializedSpan[] {\n const spans = this.peekTrace(traceId, rootSpanId);\n this.traces.delete(traceId);\n return spans;\n }\n\n /**\n * Serialize the finished spans of `traceId` without removing them — safe to\n * call repeatedly while a trace is still growing (e.g. a scenario checker\n * polling until an async flow's completion boundary closes).\n *\n * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.\n */\n peekTrace(traceId: string, rootSpanId?: string): SerializedSpan[] {\n const allSpans = this.traces.get(traceId);\n if (!allSpans?.length) return [];\n if (rootSpanId === undefined) return allSpans.map((s) => serializeSpan(s));\n\n // Build spanId → span index for efficient parent-chain walking\n const byId = new Map<string, ReadableSpan>();\n for (const s of allSpans) byId.set(s.spanContext().spanId, s);\n\n // Filter to spans that are the root or descendants of rootSpanId\n const included = allSpans.filter((s) => {\n let id: string | undefined = s.spanContext().spanId;\n while (id) {\n if (id === rootSpanId) return true;\n const parent = byId.get(id);\n const parentId = parent?.parentSpanContext?.spanId || undefined;\n if (parentId === id) break; // cycle guard\n id = parentId;\n }\n return false;\n });\n\n return included.map((s) => serializeSpan(s));\n }\n\n shutdown(): Promise<void> {\n this.traces.clear();\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nfunction hrTimeToMs(hr: [number, number]): number {\n return hr[0] * 1000 + hr[1] / 1_000_000;\n}\n\nfunction isSerializable(v: unknown): v is SerializableValue {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')\n return true;\n if (Array.isArray(v) && v.length > 0) {\n const t = typeof v[0];\n return (\n (t === 'string' || t === 'number' || t === 'boolean') &&\n v.every((e) => typeof e === t)\n );\n }\n return false;\n}\n\nexport function serializeSpan(span: ReadableSpan): SerializedSpan {\n const attrs: Record<string, SerializableValue> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (isSerializable(v)) attrs[k] = v;\n }\n return {\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId || undefined,\n name: span.name,\n startTimeMs: hrTimeToMs(span.startTime as [number, number]),\n durationMs: hrTimeToMs(span.duration as [number, number]),\n status:\n span.status.code === SpanStatusCode.ERROR\n ? 'error'\n : span.status.code === SpanStatusCode.OK\n ? 'ok'\n : 'unset',\n statusMessage: span.status.message || undefined,\n attributes: Object.keys(attrs).length > 0 ? attrs : undefined,\n };\n}\n"],"mappings":";;;;;AA2CA,MAAM,mBAAmB;CAAE,SAAS;CAAG,QAAQ;AAAE;AA6BjD,IAAa,oBAAb,MAAuD;CACrD,AAAQ,yBAAS,IAAI,IAA4B;CAEjD,OACE,OACA,UACM;EACN,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,YAAY,CAAC,CAAC;GACnC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO;GAClC,IAAI,CAAC,MAAM;IACT,OAAO,CAAC;IACR,KAAK,OAAO,IAAI,SAAS,IAAI;GAC/B;GACA,KAAK,KAAK,IAAI;EAChB;EACA,SAAS,EAAE,MAAM,iBAAiB,QAAQ,CAAC;CAC7C;;;;;;CAOA,WAAW,SAAiB,YAAsC;EAChE,MAAM,QAAQ,KAAK,UAAU,SAAS,UAAU;EAChD,KAAK,OAAO,OAAO,OAAO;EAC1B,OAAO;CACT;;;;;;;;CASA,UAAU,SAAiB,YAAuC;EAChE,MAAM,WAAW,KAAK,OAAO,IAAI,OAAO;EACxC,IAAI,CAAC,UAAU,QAAQ,OAAO,CAAC;EAC/B,IAAI,eAAe,QAAW,OAAO,SAAS,KAAK,MAAM,cAAc,CAAC,CAAC;EAGzE,MAAM,uBAAO,IAAI,IAA0B;EAC3C,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC;EAe5D,OAZiB,SAAS,QAAQ,MAAM;GACtC,IAAI,KAAyB,EAAE,YAAY,CAAC,CAAC;GAC7C,OAAO,IAAI;IACT,IAAI,OAAO,YAAY,OAAO;IAE9B,MAAM,WADS,KAAK,IAAI,EACF,CAAC,EAAE,mBAAmB,UAAU;IACtD,IAAI,aAAa,IAAI;IACrB,KAAK;GACP;GACA,OAAO;EACT,CAEc,CAAC,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC;CAC7C;CAEA,WAA0B;EACxB,KAAK,OAAO,MAAM;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,aAA4B;EAC1B,OAAO,QAAQ,QAAQ;CACzB;AACF;AAEA,SAAS,WAAW,IAA8B;CAChD,OAAO,GAAG,KAAK,MAAO,GAAG,KAAK;AAChC;AAEA,SAAS,eAAe,GAAoC;CAC1D,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WACjE,OAAO;CACT,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG;EACpC,MAAM,IAAI,OAAO,EAAE;EACnB,QACG,MAAM,YAAY,MAAM,YAAY,MAAM,cAC3C,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;CAEjC;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,MAAoC;CAChE,MAAM,QAA2C,CAAC;CAClD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,UAAU,GACjD,IAAI,eAAe,CAAC,GAAG,MAAM,KAAK;CAEpC,OAAO;EACL,QAAQ,KAAK,YAAY,CAAC,CAAC;EAC3B,cAAc,KAAK,mBAAmB,UAAU;EAChD,MAAM,KAAK;EACX,aAAa,WAAW,KAAK,SAA6B;EAC1D,YAAY,WAAW,KAAK,QAA4B;EACxD,QACE,KAAK,OAAO,SAASA,kCAAe,QAChC,UACA,KAAK,OAAO,SAASA,kCAAe,KAClC,OACA;EACR,eAAe,KAAK,OAAO,WAAW;EACtC,YAAY,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACtD;AACF"}
@@ -31,6 +31,14 @@ declare class TestSpanCollector implements SpanExporter {
31
31
  * Removes the entire traceId entry from the collector.
32
32
  */
33
33
  drainTrace(traceId: string, rootSpanId: string): SerializedSpan[];
34
+ /**
35
+ * Serialize the finished spans of `traceId` without removing them — safe to
36
+ * call repeatedly while a trace is still growing (e.g. a scenario checker
37
+ * polling until an async flow's completion boundary closes).
38
+ *
39
+ * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.
40
+ */
41
+ peekTrace(traceId: string, rootSpanId?: string): SerializedSpan[];
34
42
  shutdown(): Promise<void>;
35
43
  forceFlush(): Promise<void>;
36
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"test-span-collector.d.cts","names":[],"sources":["../src/test-span-collector.ts"],"mappings":";;;;KA8CK,iBAAA;;;;;;;;KAeO,cAAA;EACV,MAAA;EACA,YAAA;EACA,IAAA;EACA,WAAA;EACA,UAAA;EACA,MAAA;EACA,aAAA;EACA,UAAA,GAAa,MAAM,SAAS,iBAAA;AAAA;AAAA,cAGjB,iBAAA,YAA6B,YAAA;EAAA,QAChC,MAAA;EAER,MAAA,CACE,KAAA,EAAO,YAAA,IACP,QAAA,GAAW,MAAA;IAAU,IAAA;EAAA;;;;;;EAmBvB,UAAA,CAAW,OAAA,UAAiB,UAAA,WAAqB,cAAA;EAyBjD,QAAA,IAAY,OAAA;EAKZ,UAAA,IAAc,OAAA;AAAA;AAAA,iBAsBA,aAAA,CAAc,IAAA,EAAM,YAAA,GAAe,cAAc"}
1
+ {"version":3,"file":"test-span-collector.d.cts","names":[],"sources":["../src/test-span-collector.ts"],"mappings":";;;;KA8CK,iBAAA;;;;;;;;KAeO,cAAA;EACV,MAAA;EACA,YAAA;EACA,IAAA;EACA,WAAA;EACA,UAAA;EACA,MAAA;EACA,aAAA;EACA,UAAA,GAAa,MAAM,SAAS,iBAAA;AAAA;AAAA,cAGjB,iBAAA,YAA6B,YAAA;EAAA,QAChC,MAAA;EAER,MAAA,CACE,KAAA,EAAO,YAAA,IACP,QAAA,GAAW,MAAA;IAAU,IAAA;EAAA;EAoF0B;;AAAc;;;EAjE/D,UAAA,CAAW,OAAA,UAAiB,UAAA,WAAqB,cAAA;;;;;;;;EAajD,SAAA,CAAU,OAAA,UAAiB,UAAA,YAAsB,cAAA;EAyBjD,QAAA,IAAY,OAAA;EAKZ,UAAA,IAAc,OAAA;AAAA;AAAA,iBAsBA,aAAA,CAAc,IAAA,EAAM,YAAA,GAAe,cAAc"}
@@ -31,6 +31,14 @@ declare class TestSpanCollector implements SpanExporter {
31
31
  * Removes the entire traceId entry from the collector.
32
32
  */
33
33
  drainTrace(traceId: string, rootSpanId: string): SerializedSpan[];
34
+ /**
35
+ * Serialize the finished spans of `traceId` without removing them — safe to
36
+ * call repeatedly while a trace is still growing (e.g. a scenario checker
37
+ * polling until an async flow's completion boundary closes).
38
+ *
39
+ * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.
40
+ */
41
+ peekTrace(traceId: string, rootSpanId?: string): SerializedSpan[];
34
42
  shutdown(): Promise<void>;
35
43
  forceFlush(): Promise<void>;
36
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"test-span-collector.d.ts","names":[],"sources":["../src/test-span-collector.ts"],"mappings":";;;;KA8CK,iBAAA;;;;;;;;KAeO,cAAA;EACV,MAAA;EACA,YAAA;EACA,IAAA;EACA,WAAA;EACA,UAAA;EACA,MAAA;EACA,aAAA;EACA,UAAA,GAAa,MAAM,SAAS,iBAAA;AAAA;AAAA,cAGjB,iBAAA,YAA6B,YAAA;EAAA,QAChC,MAAA;EAER,MAAA,CACE,KAAA,EAAO,YAAA,IACP,QAAA,GAAW,MAAA;IAAU,IAAA;EAAA;;;;;;EAmBvB,UAAA,CAAW,OAAA,UAAiB,UAAA,WAAqB,cAAA;EAyBjD,QAAA,IAAY,OAAA;EAKZ,UAAA,IAAc,OAAA;AAAA;AAAA,iBAsBA,aAAA,CAAc,IAAA,EAAM,YAAA,GAAe,cAAc"}
1
+ {"version":3,"file":"test-span-collector.d.ts","names":[],"sources":["../src/test-span-collector.ts"],"mappings":";;;;KA8CK,iBAAA;;;;;;;;KAeO,cAAA;EACV,MAAA;EACA,YAAA;EACA,IAAA;EACA,WAAA;EACA,UAAA;EACA,MAAA;EACA,aAAA;EACA,UAAA,GAAa,MAAM,SAAS,iBAAA;AAAA;AAAA,cAGjB,iBAAA,YAA6B,YAAA;EAAA,QAChC,MAAA;EAER,MAAA,CACE,KAAA,EAAO,YAAA,IACP,QAAA,GAAW,MAAA;IAAU,IAAA;EAAA;EAoF0B;;AAAc;;;EAjE/D,UAAA,CAAW,OAAA,UAAiB,UAAA,WAAqB,cAAA;;;;;;;;EAajD,SAAA,CAAU,OAAA,UAAiB,UAAA,YAAsB,cAAA;EAyBjD,QAAA,IAAY,OAAA;EAKZ,UAAA,IAAc,OAAA;AAAA;AAAA,iBAsBA,aAAA,CAAc,IAAA,EAAM,YAAA,GAAe,cAAc"}
@@ -26,9 +26,21 @@ var TestSpanCollector = class {
26
26
  * Removes the entire traceId entry from the collector.
27
27
  */
28
28
  drainTrace(traceId, rootSpanId) {
29
- const allSpans = this.traces.get(traceId);
29
+ const spans = this.peekTrace(traceId, rootSpanId);
30
30
  this.traces.delete(traceId);
31
+ return spans;
32
+ }
33
+ /**
34
+ * Serialize the finished spans of `traceId` without removing them — safe to
35
+ * call repeatedly while a trace is still growing (e.g. a scenario checker
36
+ * polling until an async flow's completion boundary closes).
37
+ *
38
+ * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.
39
+ */
40
+ peekTrace(traceId, rootSpanId) {
41
+ const allSpans = this.traces.get(traceId);
31
42
  if (!allSpans?.length) return [];
43
+ if (rootSpanId === void 0) return allSpans.map((s) => serializeSpan(s));
32
44
  const byId = /* @__PURE__ */ new Map();
33
45
  for (const s of allSpans) byId.set(s.spanContext().spanId, s);
34
46
  return allSpans.filter((s) => {
@@ -1 +1 @@
1
- {"version":3,"file":"test-span-collector.js","names":[],"sources":["../src/test-span-collector.ts"],"sourcesContent":["/**\n * TestSpanCollector — SpanExporter that groups finished spans by traceId\n * and drains per-trace for embedding in test metadata.\n *\n * Wire it to a tracer provider, register that provider with autotel, then\n * create spans through autotel's tracer so finished spans reach the collector.\n *\n * @example\n * ```typescript\n * import { TestSpanCollector } from 'autotel/test-span-collector';\n * import { SimpleSpanProcessor } from 'autotel/processors';\n * import { setAutotelTracerProvider, getAutotelTracer } from 'autotel';\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n *\n * const collector = new TestSpanCollector();\n *\n * // OTel SDK v2: pass processors at construction, then register the provider.\n * const provider = new NodeTracerProvider({\n * spanProcessors: [new SimpleSpanProcessor(collector)],\n * });\n * setAutotelTracerProvider(provider);\n *\n * const tracer = getAutotelTracer('test');\n * const root = tracer.startSpan('operation');\n * // ...work, child spans...\n * root.end();\n *\n * const spans = collector.drainTrace(\n * root.spanContext().traceId,\n * root.spanContext().spanId,\n * );\n * // spans contains the root span and its descendants\n * ```\n *\n * For high-level instrumentation written with `span()` / `track()`, prefer\n * `createTraceCollector()` from `autotel/testing`. For a plain low-level\n * exporter, use `InMemorySpanExporter` from `autotel/exporters`.\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\n/** @see ExportResultCode from @opentelemetry/core */\nconst ExportResultCode = { SUCCESS: 0, FAILED: 1 } as const;\n\n/** Attribute value types that survive serialization */\ntype SerializableValue =\n | string\n | number\n | boolean\n | string[]\n | number[]\n | boolean[];\n\n/**\n * Portable serialized span for embedding in test metadata.\n * `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: 'ok' | 'error' | 'unset';\n statusMessage?: string;\n attributes?: Record<string, SerializableValue>;\n};\n\nexport class TestSpanCollector implements SpanExporter {\n private traces = new Map<string, ReadableSpan[]>();\n\n export(\n spans: ReadableSpan[],\n callback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n let list = this.traces.get(traceId);\n if (!list) {\n list = [];\n this.traces.set(traceId, list);\n }\n list.push(span);\n }\n callback({ code: ExportResultCode.SUCCESS });\n }\n\n /**\n * Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.\n * Filters to the subtree rooted at the test span to prevent cross-test mixing.\n * Removes the entire traceId entry from the collector.\n */\n drainTrace(traceId: string, rootSpanId: string): SerializedSpan[] {\n const allSpans = this.traces.get(traceId);\n this.traces.delete(traceId);\n if (!allSpans?.length) return [];\n\n // Build spanId → span index for efficient parent-chain walking\n const byId = new Map<string, ReadableSpan>();\n for (const s of allSpans) byId.set(s.spanContext().spanId, s);\n\n // Filter to spans that are the root or descendants of rootSpanId\n const included = allSpans.filter((s) => {\n let id: string | undefined = s.spanContext().spanId;\n while (id) {\n if (id === rootSpanId) return true;\n const parent = byId.get(id);\n const parentId = parent?.parentSpanContext?.spanId || undefined;\n if (parentId === id) break; // cycle guard\n id = parentId;\n }\n return false;\n });\n\n return included.map((s) => serializeSpan(s));\n }\n\n shutdown(): Promise<void> {\n this.traces.clear();\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nfunction hrTimeToMs(hr: [number, number]): number {\n return hr[0] * 1000 + hr[1] / 1_000_000;\n}\n\nfunction isSerializable(v: unknown): v is SerializableValue {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')\n return true;\n if (Array.isArray(v) && v.length > 0) {\n const t = typeof v[0];\n return (\n (t === 'string' || t === 'number' || t === 'boolean') &&\n v.every((e) => typeof e === t)\n );\n }\n return false;\n}\n\nexport function serializeSpan(span: ReadableSpan): SerializedSpan {\n const attrs: Record<string, SerializableValue> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (isSerializable(v)) attrs[k] = v;\n }\n return {\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId || undefined,\n name: span.name,\n startTimeMs: hrTimeToMs(span.startTime as [number, number]),\n durationMs: hrTimeToMs(span.duration as [number, number]),\n status:\n span.status.code === SpanStatusCode.ERROR\n ? 'error'\n : span.status.code === SpanStatusCode.OK\n ? 'ok'\n : 'unset',\n statusMessage: span.status.message || undefined,\n attributes: Object.keys(attrs).length > 0 ? attrs : undefined,\n };\n}\n"],"mappings":";;;;AA2CA,MAAM,mBAAmB;CAAE,SAAS;CAAG,QAAQ;AAAE;AA6BjD,IAAa,oBAAb,MAAuD;CACrD,AAAQ,yBAAS,IAAI,IAA4B;CAEjD,OACE,OACA,UACM;EACN,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,YAAY,CAAC,CAAC;GACnC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO;GAClC,IAAI,CAAC,MAAM;IACT,OAAO,CAAC;IACR,KAAK,OAAO,IAAI,SAAS,IAAI;GAC/B;GACA,KAAK,KAAK,IAAI;EAChB;EACA,SAAS,EAAE,MAAM,iBAAiB,QAAQ,CAAC;CAC7C;;;;;;CAOA,WAAW,SAAiB,YAAsC;EAChE,MAAM,WAAW,KAAK,OAAO,IAAI,OAAO;EACxC,KAAK,OAAO,OAAO,OAAO;EAC1B,IAAI,CAAC,UAAU,QAAQ,OAAO,CAAC;EAG/B,MAAM,uBAAO,IAAI,IAA0B;EAC3C,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC;EAe5D,OAZiB,SAAS,QAAQ,MAAM;GACtC,IAAI,KAAyB,EAAE,YAAY,CAAC,CAAC;GAC7C,OAAO,IAAI;IACT,IAAI,OAAO,YAAY,OAAO;IAE9B,MAAM,WADS,KAAK,IAAI,EACF,CAAC,EAAE,mBAAmB,UAAU;IACtD,IAAI,aAAa,IAAI;IACrB,KAAK;GACP;GACA,OAAO;EACT,CAEc,CAAC,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC;CAC7C;CAEA,WAA0B;EACxB,KAAK,OAAO,MAAM;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,aAA4B;EAC1B,OAAO,QAAQ,QAAQ;CACzB;AACF;AAEA,SAAS,WAAW,IAA8B;CAChD,OAAO,GAAG,KAAK,MAAO,GAAG,KAAK;AAChC;AAEA,SAAS,eAAe,GAAoC;CAC1D,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WACjE,OAAO;CACT,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG;EACpC,MAAM,IAAI,OAAO,EAAE;EACnB,QACG,MAAM,YAAY,MAAM,YAAY,MAAM,cAC3C,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;CAEjC;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,MAAoC;CAChE,MAAM,QAA2C,CAAC;CAClD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,UAAU,GACjD,IAAI,eAAe,CAAC,GAAG,MAAM,KAAK;CAEpC,OAAO;EACL,QAAQ,KAAK,YAAY,CAAC,CAAC;EAC3B,cAAc,KAAK,mBAAmB,UAAU;EAChD,MAAM,KAAK;EACX,aAAa,WAAW,KAAK,SAA6B;EAC1D,YAAY,WAAW,KAAK,QAA4B;EACxD,QACE,KAAK,OAAO,SAAS,eAAe,QAChC,UACA,KAAK,OAAO,SAAS,eAAe,KAClC,OACA;EACR,eAAe,KAAK,OAAO,WAAW;EACtC,YAAY,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACtD;AACF"}
1
+ {"version":3,"file":"test-span-collector.js","names":[],"sources":["../src/test-span-collector.ts"],"sourcesContent":["/**\n * TestSpanCollector — SpanExporter that groups finished spans by traceId\n * and drains per-trace for embedding in test metadata.\n *\n * Wire it to a tracer provider, register that provider with autotel, then\n * create spans through autotel's tracer so finished spans reach the collector.\n *\n * @example\n * ```typescript\n * import { TestSpanCollector } from 'autotel/test-span-collector';\n * import { SimpleSpanProcessor } from 'autotel/processors';\n * import { setAutotelTracerProvider, getAutotelTracer } from 'autotel';\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n *\n * const collector = new TestSpanCollector();\n *\n * // OTel SDK v2: pass processors at construction, then register the provider.\n * const provider = new NodeTracerProvider({\n * spanProcessors: [new SimpleSpanProcessor(collector)],\n * });\n * setAutotelTracerProvider(provider);\n *\n * const tracer = getAutotelTracer('test');\n * const root = tracer.startSpan('operation');\n * // ...work, child spans...\n * root.end();\n *\n * const spans = collector.drainTrace(\n * root.spanContext().traceId,\n * root.spanContext().spanId,\n * );\n * // spans contains the root span and its descendants\n * ```\n *\n * For high-level instrumentation written with `span()` / `track()`, prefer\n * `createTraceCollector()` from `autotel/testing`. For a plain low-level\n * exporter, use `InMemorySpanExporter` from `autotel/exporters`.\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\n/** @see ExportResultCode from @opentelemetry/core */\nconst ExportResultCode = { SUCCESS: 0, FAILED: 1 } as const;\n\n/** Attribute value types that survive serialization */\ntype SerializableValue =\n | string\n | number\n | boolean\n | string[]\n | number[]\n | boolean[];\n\n/**\n * Portable serialized span for embedding in test metadata.\n * `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.\n *\n * Defined as a `type` (not `interface`) so it is assignable to\n * `Record<string, unknown>` in TypeScript 6+ strict mode.\n */\nexport type SerializedSpan = {\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeMs: number;\n durationMs: number;\n status: 'ok' | 'error' | 'unset';\n statusMessage?: string;\n attributes?: Record<string, SerializableValue>;\n};\n\nexport class TestSpanCollector implements SpanExporter {\n private traces = new Map<string, ReadableSpan[]>();\n\n export(\n spans: ReadableSpan[],\n callback: (result: { code: number }) => void,\n ): void {\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n let list = this.traces.get(traceId);\n if (!list) {\n list = [];\n this.traces.set(traceId, list);\n }\n list.push(span);\n }\n callback({ code: ExportResultCode.SUCCESS });\n }\n\n /**\n * Drain and serialize spans that are descendants of `rootSpanId` within `traceId`.\n * Filters to the subtree rooted at the test span to prevent cross-test mixing.\n * Removes the entire traceId entry from the collector.\n */\n drainTrace(traceId: string, rootSpanId: string): SerializedSpan[] {\n const spans = this.peekTrace(traceId, rootSpanId);\n this.traces.delete(traceId);\n return spans;\n }\n\n /**\n * Serialize the finished spans of `traceId` without removing them — safe to\n * call repeatedly while a trace is still growing (e.g. a scenario checker\n * polling until an async flow's completion boundary closes).\n *\n * With `rootSpanId`, filters to that span's subtree like {@link drainTrace}.\n */\n peekTrace(traceId: string, rootSpanId?: string): SerializedSpan[] {\n const allSpans = this.traces.get(traceId);\n if (!allSpans?.length) return [];\n if (rootSpanId === undefined) return allSpans.map((s) => serializeSpan(s));\n\n // Build spanId → span index for efficient parent-chain walking\n const byId = new Map<string, ReadableSpan>();\n for (const s of allSpans) byId.set(s.spanContext().spanId, s);\n\n // Filter to spans that are the root or descendants of rootSpanId\n const included = allSpans.filter((s) => {\n let id: string | undefined = s.spanContext().spanId;\n while (id) {\n if (id === rootSpanId) return true;\n const parent = byId.get(id);\n const parentId = parent?.parentSpanContext?.spanId || undefined;\n if (parentId === id) break; // cycle guard\n id = parentId;\n }\n return false;\n });\n\n return included.map((s) => serializeSpan(s));\n }\n\n shutdown(): Promise<void> {\n this.traces.clear();\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nfunction hrTimeToMs(hr: [number, number]): number {\n return hr[0] * 1000 + hr[1] / 1_000_000;\n}\n\nfunction isSerializable(v: unknown): v is SerializableValue {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')\n return true;\n if (Array.isArray(v) && v.length > 0) {\n const t = typeof v[0];\n return (\n (t === 'string' || t === 'number' || t === 'boolean') &&\n v.every((e) => typeof e === t)\n );\n }\n return false;\n}\n\nexport function serializeSpan(span: ReadableSpan): SerializedSpan {\n const attrs: Record<string, SerializableValue> = {};\n for (const [k, v] of Object.entries(span.attributes)) {\n if (isSerializable(v)) attrs[k] = v;\n }\n return {\n spanId: span.spanContext().spanId,\n parentSpanId: span.parentSpanContext?.spanId || undefined,\n name: span.name,\n startTimeMs: hrTimeToMs(span.startTime as [number, number]),\n durationMs: hrTimeToMs(span.duration as [number, number]),\n status:\n span.status.code === SpanStatusCode.ERROR\n ? 'error'\n : span.status.code === SpanStatusCode.OK\n ? 'ok'\n : 'unset',\n statusMessage: span.status.message || undefined,\n attributes: Object.keys(attrs).length > 0 ? attrs : undefined,\n };\n}\n"],"mappings":";;;;AA2CA,MAAM,mBAAmB;CAAE,SAAS;CAAG,QAAQ;AAAE;AA6BjD,IAAa,oBAAb,MAAuD;CACrD,AAAQ,yBAAS,IAAI,IAA4B;CAEjD,OACE,OACA,UACM;EACN,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,YAAY,CAAC,CAAC;GACnC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO;GAClC,IAAI,CAAC,MAAM;IACT,OAAO,CAAC;IACR,KAAK,OAAO,IAAI,SAAS,IAAI;GAC/B;GACA,KAAK,KAAK,IAAI;EAChB;EACA,SAAS,EAAE,MAAM,iBAAiB,QAAQ,CAAC;CAC7C;;;;;;CAOA,WAAW,SAAiB,YAAsC;EAChE,MAAM,QAAQ,KAAK,UAAU,SAAS,UAAU;EAChD,KAAK,OAAO,OAAO,OAAO;EAC1B,OAAO;CACT;;;;;;;;CASA,UAAU,SAAiB,YAAuC;EAChE,MAAM,WAAW,KAAK,OAAO,IAAI,OAAO;EACxC,IAAI,CAAC,UAAU,QAAQ,OAAO,CAAC;EAC/B,IAAI,eAAe,QAAW,OAAO,SAAS,KAAK,MAAM,cAAc,CAAC,CAAC;EAGzE,MAAM,uBAAO,IAAI,IAA0B;EAC3C,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC;EAe5D,OAZiB,SAAS,QAAQ,MAAM;GACtC,IAAI,KAAyB,EAAE,YAAY,CAAC,CAAC;GAC7C,OAAO,IAAI;IACT,IAAI,OAAO,YAAY,OAAO;IAE9B,MAAM,WADS,KAAK,IAAI,EACF,CAAC,EAAE,mBAAmB,UAAU;IACtD,IAAI,aAAa,IAAI;IACrB,KAAK;GACP;GACA,OAAO;EACT,CAEc,CAAC,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC;CAC7C;CAEA,WAA0B;EACxB,KAAK,OAAO,MAAM;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,aAA4B;EAC1B,OAAO,QAAQ,QAAQ;CACzB;AACF;AAEA,SAAS,WAAW,IAA8B;CAChD,OAAO,GAAG,KAAK,MAAO,GAAG,KAAK;AAChC;AAEA,SAAS,eAAe,GAAoC;CAC1D,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WACjE,OAAO;CACT,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG;EACpC,MAAM,IAAI,OAAO,EAAE;EACnB,QACG,MAAM,YAAY,MAAM,YAAY,MAAM,cAC3C,EAAE,OAAO,MAAM,OAAO,MAAM,CAAC;CAEjC;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,MAAoC;CAChE,MAAM,QAA2C,CAAC;CAClD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,UAAU,GACjD,IAAI,eAAe,CAAC,GAAG,MAAM,KAAK;CAEpC,OAAO;EACL,QAAQ,KAAK,YAAY,CAAC,CAAC;EAC3B,cAAc,KAAK,mBAAmB,UAAU;EAChD,MAAM,KAAK;EACX,aAAa,WAAW,KAAK,SAA6B;EAC1D,YAAY,WAAW,KAAK,QAA4B;EACxD,QACE,KAAK,OAAO,SAAS,eAAe,QAChC,UACA,KAAK,OAAO,SAAS,eAAe,KAClC,OACA;EACR,eAAe,KAAK,OAAO,WAAW;EACtC,YAAY,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;CACtD;AACF"}
@@ -188,6 +188,32 @@ function getAutotelTracerProvider() {
188
188
  if (isolatedTracerProvider) return isolatedTracerProvider;
189
189
  return _opentelemetry_api.trace.getTracerProvider();
190
190
  }
191
+ function isForceFlushable(value) {
192
+ return typeof value === "object" && value !== null && typeof value.forceFlush === "function";
193
+ }
194
+ /**
195
+ * Resolve a tracer provider that can be `forceFlush()`ed, so `flush()` and
196
+ * flush-on-shutdown actually export pending spans.
197
+ *
198
+ * `NodeSDK.getTracerProvider()` returns `undefined` on `@opentelemetry/sdk-node`
199
+ * 0.220+ (OpenTelemetry 2.x), so code that force-flushes only via the SDK
200
+ * handle silently exports nothing. This falls back to the globally registered
201
+ * provider and unwraps the API's `ProxyTracerProvider` to reach the real
202
+ * delegate, which is where `forceFlush` actually lives.
203
+ *
204
+ * @param sdk - Optional NodeSDK handle to try first.
205
+ * @returns Something with `forceFlush()`, or `undefined` if none is registered.
206
+ */
207
+ function getForceFlushableProvider(sdk) {
208
+ const candidates = [];
209
+ const sdkAny = sdk;
210
+ if (sdkAny && typeof sdkAny.getTracerProvider === "function") candidates.push(sdkAny.getTracerProvider());
211
+ const globalProvider = _opentelemetry_api.trace.getTracerProvider();
212
+ candidates.push(globalProvider);
213
+ const proxy = globalProvider;
214
+ if (proxy && typeof proxy.getDelegate === "function") candidates.push(proxy.getDelegate());
215
+ return candidates.find(isForceFlushable);
216
+ }
191
217
  /**
192
218
  * Gets the OpenTelemetry tracer instance for Autotel.
193
219
  *
@@ -227,5 +253,6 @@ function getAutotelTracer(name = "autotel", version) {
227
253
  //#endregion
228
254
  exports.getAutotelTracer = getAutotelTracer;
229
255
  exports.getAutotelTracerProvider = getAutotelTracerProvider;
256
+ exports.getForceFlushableProvider = getForceFlushableProvider;
230
257
  exports.setAutotelTracerProvider = setAutotelTracerProvider;
231
258
  //# sourceMappingURL=tracer-provider.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracer-provider.cjs","names":["trace"],"sources":["../src/tracer-provider.ts"],"sourcesContent":["/**\n * Isolated tracer provider support for Autotel\n *\n * Allows Autotel to use a separate TracerProvider instance, avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Use Case:** Library authors who want to use Autotel without interfering\n * with the application's global OpenTelemetry setup.\n *\n * **Limitation:** While this isolates span processing and export, OpenTelemetry\n * context (trace IDs, parent spans) is still shared globally. Spans created with\n * the isolated provider may inherit trace context from global spans.\n */\n\nimport { trace } from '@opentelemetry/api';\nimport type { TracerProvider } from '@opentelemetry/api';\n\n/**\n * Symbol for storing isolated tracer provider in global scope\n * Using Symbol.for() ensures the same symbol across module boundaries\n */\nconst AUTOTEL_GLOBAL_SYMBOL = Symbol.for('autotel');\n\n/**\n * Global state for Autotel\n */\ntype AutotelGlobalState = {\n isolatedTracerProvider: TracerProvider | null;\n};\n\n/**\n * Create initial state\n */\nfunction createState(): AutotelGlobalState {\n return {\n isolatedTracerProvider: null,\n };\n}\n\n/**\n * Extend globalThis to include our symbol\n */\ninterface GlobalThis {\n [AUTOTEL_GLOBAL_SYMBOL]?: AutotelGlobalState;\n}\n\n/**\n * Get the global state, creating it if it doesn't exist\n * Handles edge cases like missing globalThis\n */\nfunction getGlobalState(): AutotelGlobalState {\n const initialState = createState();\n\n try {\n const g = globalThis as typeof globalThis & GlobalThis;\n\n if (typeof g !== 'object' || g === null) {\n console.warn(\n '[autotel] globalThis is not available, using fallback state',\n );\n return initialState;\n }\n\n if (!g[AUTOTEL_GLOBAL_SYMBOL]) {\n Object.defineProperty(g, AUTOTEL_GLOBAL_SYMBOL, {\n value: initialState,\n writable: false, // Lock the slot (not the contents)\n configurable: false,\n enumerable: false,\n });\n }\n\n return g[AUTOTEL_GLOBAL_SYMBOL]!;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `[autotel] Failed to access global state: ${error.message}`,\n );\n } else {\n console.error(\n `[autotel] Failed to access global state: ${String(error)}`,\n );\n }\n\n return initialState;\n }\n}\n\n/**\n * Sets an isolated TracerProvider for Autotel tracing operations.\n *\n * This allows Autotel to use its own TracerProvider instance, separate from\n * the global OpenTelemetry TracerProvider. This is useful for avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Limitation: Span Context Sharing**\n *\n * While this function isolates span processing and export, it does NOT provide\n * complete trace isolation. OpenTelemetry context (trace IDs, parent spans) is\n * still shared between the global and isolated providers. This means:\n *\n * - Spans created with the isolated provider inherit trace IDs from global spans\n * - Spans created with the isolated provider inherit parent relationships from global spans\n * - This can result in spans from different providers being part of the same logical trace\n *\n * **Why this happens:**\n * OpenTelemetry uses a global context propagation mechanism that operates at the\n * JavaScript runtime level, independent of individual TracerProvider instances.\n * The context (containing trace ID, span ID) flows through async boundaries and\n * is inherited by all spans created within that context, regardless of which\n * TracerProvider creates them.\n *\n * **When to use this:**\n * - Library code that ships with embedded Autotel\n * - SDKs that want observability without requiring users to set up OpenTelemetry\n * - Applications that need separate span processing for different subsystems\n * - Testing scenarios where you want to isolate trace collection\n *\n * @param provider - The TracerProvider instance to use, or null to clear the isolated provider\n *\n * @example Library with embedded Autotel\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'\n * import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'\n * import { setAutolem\n\netryTracerProvider } from 'autotel/tracer-provider'\n *\n * // Create provider with span processors in constructor\n * const exporter = new OTLPTraceExporter({\n * url: 'https://your-backend.com/v1/traces'\n * })\n *\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new BatchSpanProcessor(exporter))\n *\n * // Set as Autotel's isolated provider (doesn't call provider.register())\n * setAutotelTracerProvider(provider)\n *\n * // Now all Autotel trace() calls use this provider\n * // But won't interfere with the application's global OpenTelemetry setup\n * ```\n *\n * @example Testing with isolated provider\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Test setup\n * const exporter = new InMemorySpanExporter()\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new SimpleSpanProcessor(exporter))\n *\n * setAutotelTracerProvider(provider)\n *\n * // Run tests...\n * const spans = exporter.getFinishedSpans()\n *\n * // Cleanup\n * setAutotelTracerProvider(null)\n * ```\n *\n * @example Multiple subsystems with different exporters\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Payment subsystem - send to payment team's backend\n * const paymentProvider = new NodeTracerProvider()\n * paymentProvider.addSpanProcessor(new BatchSpanProcessor(\n * new OTLPTraceExporter({ url: 'https://payment-team-backend.com/v1/traces' })\n * ))\n *\n * // In payment module initialization\n * setAutotelTracerProvider(paymentProvider)\n * ```\n *\n * @public\n */\nexport function setAutotelTracerProvider(\n provider: TracerProvider | null,\n): void {\n getGlobalState().isolatedTracerProvider = provider;\n}\n\n/**\n * Gets the TracerProvider for Autotel tracing operations.\n *\n * Returns the isolated TracerProvider if one has been set via setAutotelTracerProvider(),\n * otherwise falls back to the global OpenTelemetry TracerProvider.\n *\n * This function is used internally by Autotel's trace functions. Most users\n * will not need to call this directly.\n *\n * @returns The TracerProvider instance to use for Autotel tracing\n *\n * @example Getting the current provider\n * ```typescript\n * import { getAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * const provider = getAutotelTracerProvider()\n * const tracer = provider.getTracer('my-service', '1.0.0')\n * ```\n *\n * @example Checking if isolated provider is active\n * ```typescript\n * import { getAutotelTracerProvider, setAutotelTracerProvider } from 'autotel/tracer-provider'\n * import { trace } from '@opentelemetry/api'\n *\n * const currentProvider = getAutotelTracerProvider()\n * const globalProvider = trace.getTracerProvider()\n *\n * if (currentProvider === globalProvider) {\n * console.log('Using global provider')\n * } else {\n * console.log('Using isolated provider')\n * }\n * ```\n *\n * @public\n */\nexport function getAutotelTracerProvider(): TracerProvider {\n const { isolatedTracerProvider } = getGlobalState();\n\n if (isolatedTracerProvider) return isolatedTracerProvider;\n\n return trace.getTracerProvider();\n}\n\n/**\n * Gets the OpenTelemetry tracer instance for Autotel.\n *\n * This function returns a tracer specifically configured for Autotel\n * with the correct tracer name and version. Used internally by all\n * Autotel tracing functions to ensure consistent trace creation.\n *\n * Uses the isolated provider if set, otherwise uses the global provider.\n *\n * @param name - Tracer name (default: 'autotel')\n * @param version - Optional version string\n * @returns The Autotel OpenTelemetry tracer instance\n *\n * @example Basic usage\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer()\n * const span = tracer.startSpan('my-operation')\n * // ... use span\n * span.end()\n * ```\n *\n * @example Custom tracer name\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer('my-library', '2.1.0')\n * ```\n *\n * @public\n */\nexport function getAutotelTracer(name = 'autotel', version?: string) {\n return getAutotelTracerProvider().getTracer(name, version);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,wBAAwB,OAAO,IAAI,SAAS;;;;AAYlD,SAAS,cAAkC;CACzC,OAAO,EACL,wBAAwB,KAC1B;AACF;;;;;AAaA,SAAS,iBAAqC;CAC5C,MAAM,eAAe,YAAY;CAEjC,IAAI;EACF,MAAM,IAAI;EAEV,IAAI,OAAO,MAAM,YAAY,MAAM,MAAM;GACvC,QAAQ,KACN,6DACF;GACA,OAAO;EACT;EAEA,IAAI,CAAC,EAAE,wBACL,OAAO,eAAe,GAAG,uBAAuB;GAC9C,OAAO;GACP,UAAU;GACV,cAAc;GACd,YAAY;EACd,CAAC;EAGH,OAAO,EAAE;CACX,SAAS,OAAO;EACd,IAAI,iBAAiB,OACnB,QAAQ,MACN,4CAA4C,MAAM,SACpD;OAEA,QAAQ,MACN,4CAA4C,OAAO,KAAK,GAC1D;EAGF,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA,SAAgB,yBACd,UACM;CACN,eAAe,CAAC,CAAC,yBAAyB;AAC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,2BAA2C;CACzD,MAAM,EAAE,2BAA2B,eAAe;CAElD,IAAI,wBAAwB,OAAO;CAEnC,OAAOA,yBAAM,kBAAkB;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,iBAAiB,OAAO,WAAW,SAAkB;CACnE,OAAO,yBAAyB,CAAC,CAAC,UAAU,MAAM,OAAO;AAC3D"}
1
+ {"version":3,"file":"tracer-provider.cjs","names":["trace"],"sources":["../src/tracer-provider.ts"],"sourcesContent":["/**\n * Isolated tracer provider support for Autotel\n *\n * Allows Autotel to use a separate TracerProvider instance, avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Use Case:** Library authors who want to use Autotel without interfering\n * with the application's global OpenTelemetry setup.\n *\n * **Limitation:** While this isolates span processing and export, OpenTelemetry\n * context (trace IDs, parent spans) is still shared globally. Spans created with\n * the isolated provider may inherit trace context from global spans.\n */\n\nimport { trace } from '@opentelemetry/api';\nimport type { TracerProvider } from '@opentelemetry/api';\n\n/**\n * Symbol for storing isolated tracer provider in global scope\n * Using Symbol.for() ensures the same symbol across module boundaries\n */\nconst AUTOTEL_GLOBAL_SYMBOL = Symbol.for('autotel');\n\n/**\n * Global state for Autotel\n */\ntype AutotelGlobalState = {\n isolatedTracerProvider: TracerProvider | null;\n};\n\n/**\n * Create initial state\n */\nfunction createState(): AutotelGlobalState {\n return {\n isolatedTracerProvider: null,\n };\n}\n\n/**\n * Extend globalThis to include our symbol\n */\ninterface GlobalThis {\n [AUTOTEL_GLOBAL_SYMBOL]?: AutotelGlobalState;\n}\n\n/**\n * Get the global state, creating it if it doesn't exist\n * Handles edge cases like missing globalThis\n */\nfunction getGlobalState(): AutotelGlobalState {\n const initialState = createState();\n\n try {\n const g = globalThis as typeof globalThis & GlobalThis;\n\n if (typeof g !== 'object' || g === null) {\n console.warn(\n '[autotel] globalThis is not available, using fallback state',\n );\n return initialState;\n }\n\n if (!g[AUTOTEL_GLOBAL_SYMBOL]) {\n Object.defineProperty(g, AUTOTEL_GLOBAL_SYMBOL, {\n value: initialState,\n writable: false, // Lock the slot (not the contents)\n configurable: false,\n enumerable: false,\n });\n }\n\n return g[AUTOTEL_GLOBAL_SYMBOL]!;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `[autotel] Failed to access global state: ${error.message}`,\n );\n } else {\n console.error(\n `[autotel] Failed to access global state: ${String(error)}`,\n );\n }\n\n return initialState;\n }\n}\n\n/**\n * Sets an isolated TracerProvider for Autotel tracing operations.\n *\n * This allows Autotel to use its own TracerProvider instance, separate from\n * the global OpenTelemetry TracerProvider. This is useful for avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Limitation: Span Context Sharing**\n *\n * While this function isolates span processing and export, it does NOT provide\n * complete trace isolation. OpenTelemetry context (trace IDs, parent spans) is\n * still shared between the global and isolated providers. This means:\n *\n * - Spans created with the isolated provider inherit trace IDs from global spans\n * - Spans created with the isolated provider inherit parent relationships from global spans\n * - This can result in spans from different providers being part of the same logical trace\n *\n * **Why this happens:**\n * OpenTelemetry uses a global context propagation mechanism that operates at the\n * JavaScript runtime level, independent of individual TracerProvider instances.\n * The context (containing trace ID, span ID) flows through async boundaries and\n * is inherited by all spans created within that context, regardless of which\n * TracerProvider creates them.\n *\n * **When to use this:**\n * - Library code that ships with embedded Autotel\n * - SDKs that want observability without requiring users to set up OpenTelemetry\n * - Applications that need separate span processing for different subsystems\n * - Testing scenarios where you want to isolate trace collection\n *\n * @param provider - The TracerProvider instance to use, or null to clear the isolated provider\n *\n * @example Library with embedded Autotel\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'\n * import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'\n * import { setAutolem\n\netryTracerProvider } from 'autotel/tracer-provider'\n *\n * // Create provider with span processors in constructor\n * const exporter = new OTLPTraceExporter({\n * url: 'https://your-backend.com/v1/traces'\n * })\n *\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new BatchSpanProcessor(exporter))\n *\n * // Set as Autotel's isolated provider (doesn't call provider.register())\n * setAutotelTracerProvider(provider)\n *\n * // Now all Autotel trace() calls use this provider\n * // But won't interfere with the application's global OpenTelemetry setup\n * ```\n *\n * @example Testing with isolated provider\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Test setup\n * const exporter = new InMemorySpanExporter()\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new SimpleSpanProcessor(exporter))\n *\n * setAutotelTracerProvider(provider)\n *\n * // Run tests...\n * const spans = exporter.getFinishedSpans()\n *\n * // Cleanup\n * setAutotelTracerProvider(null)\n * ```\n *\n * @example Multiple subsystems with different exporters\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Payment subsystem - send to payment team's backend\n * const paymentProvider = new NodeTracerProvider()\n * paymentProvider.addSpanProcessor(new BatchSpanProcessor(\n * new OTLPTraceExporter({ url: 'https://payment-team-backend.com/v1/traces' })\n * ))\n *\n * // In payment module initialization\n * setAutotelTracerProvider(paymentProvider)\n * ```\n *\n * @public\n */\nexport function setAutotelTracerProvider(\n provider: TracerProvider | null,\n): void {\n getGlobalState().isolatedTracerProvider = provider;\n}\n\n/**\n * Gets the TracerProvider for Autotel tracing operations.\n *\n * Returns the isolated TracerProvider if one has been set via setAutotelTracerProvider(),\n * otherwise falls back to the global OpenTelemetry TracerProvider.\n *\n * This function is used internally by Autotel's trace functions. Most users\n * will not need to call this directly.\n *\n * @returns The TracerProvider instance to use for Autotel tracing\n *\n * @example Getting the current provider\n * ```typescript\n * import { getAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * const provider = getAutotelTracerProvider()\n * const tracer = provider.getTracer('my-service', '1.0.0')\n * ```\n *\n * @example Checking if isolated provider is active\n * ```typescript\n * import { getAutotelTracerProvider, setAutotelTracerProvider } from 'autotel/tracer-provider'\n * import { trace } from '@opentelemetry/api'\n *\n * const currentProvider = getAutotelTracerProvider()\n * const globalProvider = trace.getTracerProvider()\n *\n * if (currentProvider === globalProvider) {\n * console.log('Using global provider')\n * } else {\n * console.log('Using isolated provider')\n * }\n * ```\n *\n * @public\n */\nexport function getAutotelTracerProvider(): TracerProvider {\n const { isolatedTracerProvider } = getGlobalState();\n\n if (isolatedTracerProvider) return isolatedTracerProvider;\n\n return trace.getTracerProvider();\n}\n\n/** A tracer provider that can be force-flushed (SDK providers implement this). */\ninterface ForceFlushable {\n forceFlush(): Promise<void>;\n}\n\nfunction isForceFlushable(value: unknown): value is ForceFlushable {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ForceFlushable).forceFlush === 'function'\n );\n}\n\n/**\n * Resolve a tracer provider that can be `forceFlush()`ed, so `flush()` and\n * flush-on-shutdown actually export pending spans.\n *\n * `NodeSDK.getTracerProvider()` returns `undefined` on `@opentelemetry/sdk-node`\n * 0.220+ (OpenTelemetry 2.x), so code that force-flushes only via the SDK\n * handle silently exports nothing. This falls back to the globally registered\n * provider and unwraps the API's `ProxyTracerProvider` to reach the real\n * delegate, which is where `forceFlush` actually lives.\n *\n * @param sdk - Optional NodeSDK handle to try first.\n * @returns Something with `forceFlush()`, or `undefined` if none is registered.\n */\nexport function getForceFlushableProvider(\n sdk?: unknown,\n): ForceFlushable | undefined {\n const candidates: unknown[] = [];\n\n const sdkAny = sdk as { getTracerProvider?: () => unknown } | undefined;\n if (sdkAny && typeof sdkAny.getTracerProvider === 'function') {\n candidates.push(sdkAny.getTracerProvider());\n }\n\n const globalProvider = trace.getTracerProvider() as unknown;\n candidates.push(globalProvider);\n // The API hands back a ProxyTracerProvider; the real provider (with\n // forceFlush) sits behind getDelegate().\n const proxy = globalProvider as { getDelegate?: () => unknown };\n if (proxy && typeof proxy.getDelegate === 'function') {\n candidates.push(proxy.getDelegate());\n }\n\n return candidates.find(isForceFlushable);\n}\n\n/**\n * Gets the OpenTelemetry tracer instance for Autotel.\n *\n * This function returns a tracer specifically configured for Autotel\n * with the correct tracer name and version. Used internally by all\n * Autotel tracing functions to ensure consistent trace creation.\n *\n * Uses the isolated provider if set, otherwise uses the global provider.\n *\n * @param name - Tracer name (default: 'autotel')\n * @param version - Optional version string\n * @returns The Autotel OpenTelemetry tracer instance\n *\n * @example Basic usage\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer()\n * const span = tracer.startSpan('my-operation')\n * // ... use span\n * span.end()\n * ```\n *\n * @example Custom tracer name\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer('my-library', '2.1.0')\n * ```\n *\n * @public\n */\nexport function getAutotelTracer(name = 'autotel', version?: string) {\n return getAutotelTracerProvider().getTracer(name, version);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,wBAAwB,OAAO,IAAI,SAAS;;;;AAYlD,SAAS,cAAkC;CACzC,OAAO,EACL,wBAAwB,KAC1B;AACF;;;;;AAaA,SAAS,iBAAqC;CAC5C,MAAM,eAAe,YAAY;CAEjC,IAAI;EACF,MAAM,IAAI;EAEV,IAAI,OAAO,MAAM,YAAY,MAAM,MAAM;GACvC,QAAQ,KACN,6DACF;GACA,OAAO;EACT;EAEA,IAAI,CAAC,EAAE,wBACL,OAAO,eAAe,GAAG,uBAAuB;GAC9C,OAAO;GACP,UAAU;GACV,cAAc;GACd,YAAY;EACd,CAAC;EAGH,OAAO,EAAE;CACX,SAAS,OAAO;EACd,IAAI,iBAAiB,OACnB,QAAQ,MACN,4CAA4C,MAAM,SACpD;OAEA,QAAQ,MACN,4CAA4C,OAAO,KAAK,GAC1D;EAGF,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA,SAAgB,yBACd,UACM;CACN,eAAe,CAAC,CAAC,yBAAyB;AAC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,2BAA2C;CACzD,MAAM,EAAE,2BAA2B,eAAe;CAElD,IAAI,wBAAwB,OAAO;CAEnC,OAAOA,yBAAM,kBAAkB;AACjC;AAOA,SAAS,iBAAiB,OAAyC;CACjE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAyB,eAAe;AAEpD;;;;;;;;;;;;;;AAeA,SAAgB,0BACd,KAC4B;CAC5B,MAAM,aAAwB,CAAC;CAE/B,MAAM,SAAS;CACf,IAAI,UAAU,OAAO,OAAO,sBAAsB,YAChD,WAAW,KAAK,OAAO,kBAAkB,CAAC;CAG5C,MAAM,iBAAiBA,yBAAM,kBAAkB;CAC/C,WAAW,KAAK,cAAc;CAG9B,MAAM,QAAQ;CACd,IAAI,SAAS,OAAO,MAAM,gBAAgB,YACxC,WAAW,KAAK,MAAM,YAAY,CAAC;CAGrC,OAAO,WAAW,KAAK,gBAAgB;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,iBAAiB,OAAO,WAAW,SAAkB;CACnE,OAAO,yBAAyB,CAAC,CAAC,UAAU,MAAM,OAAO;AAC3D"}
@@ -132,6 +132,24 @@ declare function setAutotelTracerProvider(provider: TracerProvider | null): void
132
132
  * @public
133
133
  */
134
134
  declare function getAutotelTracerProvider(): TracerProvider;
135
+ /** A tracer provider that can be force-flushed (SDK providers implement this). */
136
+ interface ForceFlushable {
137
+ forceFlush(): Promise<void>;
138
+ }
139
+ /**
140
+ * Resolve a tracer provider that can be `forceFlush()`ed, so `flush()` and
141
+ * flush-on-shutdown actually export pending spans.
142
+ *
143
+ * `NodeSDK.getTracerProvider()` returns `undefined` on `@opentelemetry/sdk-node`
144
+ * 0.220+ (OpenTelemetry 2.x), so code that force-flushes only via the SDK
145
+ * handle silently exports nothing. This falls back to the globally registered
146
+ * provider and unwraps the API's `ProxyTracerProvider` to reach the real
147
+ * delegate, which is where `forceFlush` actually lives.
148
+ *
149
+ * @param sdk - Optional NodeSDK handle to try first.
150
+ * @returns Something with `forceFlush()`, or `undefined` if none is registered.
151
+ */
152
+ declare function getForceFlushableProvider(sdk?: unknown): ForceFlushable | undefined;
135
153
  /**
136
154
  * Gets the OpenTelemetry tracer instance for Autotel.
137
155
  *
@@ -166,5 +184,5 @@ declare function getAutotelTracerProvider(): TracerProvider;
166
184
  */
167
185
  declare function getAutotelTracer(name?: string, version?: string): import("@opentelemetry/api").Tracer;
168
186
  //#endregion
169
- export { getAutotelTracer, getAutotelTracerProvider, setAutotelTracerProvider };
187
+ export { getAutotelTracer, getAutotelTracerProvider, getForceFlushableProvider, setAutotelTracerProvider };
170
188
  //# sourceMappingURL=tracer-provider.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracer-provider.d.cts","names":[],"sources":["../src/tracer-provider.ts"],"mappings":";;;;;;;;;;AAuQmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAlFnD,wBAAA,CACd,QAA+B,EAArB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCV,wBAAA,IAA4B,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC1C,gBAAA,CAAiB,IAAA,WAAkB,OAAA,yCAAgB,MAAA"}
1
+ {"version":3,"file":"tracer-provider.d.cts","names":[],"sources":["../src/tracer-provider.ts"],"mappings":";;;;;;AAyOuB;AAwBvB;;;;AAEiB;AAoDjB;;;;;;;;AAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAlInD,wBAAA,CACd,QAA+B,EAArB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCV,wBAAA,IAA4B,cAAc;;UAShD,cAAA;EACR,UAAA,IAAc,OAAO;AAAA;;;;;;;;;;;;;;iBAwBP,yBAAA,CACd,GAAA,aACC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoDD,gBAAA,CAAiB,IAAA,WAAkB,OAAA,yCAAgB,MAAA"}
@@ -132,6 +132,24 @@ declare function setAutotelTracerProvider(provider: TracerProvider | null): void
132
132
  * @public
133
133
  */
134
134
  declare function getAutotelTracerProvider(): TracerProvider;
135
+ /** A tracer provider that can be force-flushed (SDK providers implement this). */
136
+ interface ForceFlushable {
137
+ forceFlush(): Promise<void>;
138
+ }
139
+ /**
140
+ * Resolve a tracer provider that can be `forceFlush()`ed, so `flush()` and
141
+ * flush-on-shutdown actually export pending spans.
142
+ *
143
+ * `NodeSDK.getTracerProvider()` returns `undefined` on `@opentelemetry/sdk-node`
144
+ * 0.220+ (OpenTelemetry 2.x), so code that force-flushes only via the SDK
145
+ * handle silently exports nothing. This falls back to the globally registered
146
+ * provider and unwraps the API's `ProxyTracerProvider` to reach the real
147
+ * delegate, which is where `forceFlush` actually lives.
148
+ *
149
+ * @param sdk - Optional NodeSDK handle to try first.
150
+ * @returns Something with `forceFlush()`, or `undefined` if none is registered.
151
+ */
152
+ declare function getForceFlushableProvider(sdk?: unknown): ForceFlushable | undefined;
135
153
  /**
136
154
  * Gets the OpenTelemetry tracer instance for Autotel.
137
155
  *
@@ -166,5 +184,5 @@ declare function getAutotelTracerProvider(): TracerProvider;
166
184
  */
167
185
  declare function getAutotelTracer(name?: string, version?: string): import("@opentelemetry/api").Tracer;
168
186
  //#endregion
169
- export { getAutotelTracer, getAutotelTracerProvider, setAutotelTracerProvider };
187
+ export { getAutotelTracer, getAutotelTracerProvider, getForceFlushableProvider, setAutotelTracerProvider };
170
188
  //# sourceMappingURL=tracer-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracer-provider.d.ts","names":[],"sources":["../src/tracer-provider.ts"],"mappings":";;;;;;;;;;AAuQmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAlFnD,wBAAA,CACd,QAA+B,EAArB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCV,wBAAA,IAA4B,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC1C,gBAAA,CAAiB,IAAA,WAAkB,OAAA,yCAAgB,MAAA"}
1
+ {"version":3,"file":"tracer-provider.d.ts","names":[],"sources":["../src/tracer-provider.ts"],"mappings":";;;;;;AAyOuB;AAwBvB;;;;AAEiB;AAoDjB;;;;;;;;AAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAlInD,wBAAA,CACd,QAA+B,EAArB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCV,wBAAA,IAA4B,cAAc;;UAShD,cAAA;EACR,UAAA,IAAc,OAAO;AAAA;;;;;;;;;;;;;;iBAwBP,yBAAA,CACd,GAAA,aACC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoDD,gBAAA,CAAiB,IAAA,WAAkB,OAAA,yCAAgB,MAAA"}
@@ -187,6 +187,32 @@ function getAutotelTracerProvider() {
187
187
  if (isolatedTracerProvider) return isolatedTracerProvider;
188
188
  return trace.getTracerProvider();
189
189
  }
190
+ function isForceFlushable(value) {
191
+ return typeof value === "object" && value !== null && typeof value.forceFlush === "function";
192
+ }
193
+ /**
194
+ * Resolve a tracer provider that can be `forceFlush()`ed, so `flush()` and
195
+ * flush-on-shutdown actually export pending spans.
196
+ *
197
+ * `NodeSDK.getTracerProvider()` returns `undefined` on `@opentelemetry/sdk-node`
198
+ * 0.220+ (OpenTelemetry 2.x), so code that force-flushes only via the SDK
199
+ * handle silently exports nothing. This falls back to the globally registered
200
+ * provider and unwraps the API's `ProxyTracerProvider` to reach the real
201
+ * delegate, which is where `forceFlush` actually lives.
202
+ *
203
+ * @param sdk - Optional NodeSDK handle to try first.
204
+ * @returns Something with `forceFlush()`, or `undefined` if none is registered.
205
+ */
206
+ function getForceFlushableProvider(sdk) {
207
+ const candidates = [];
208
+ const sdkAny = sdk;
209
+ if (sdkAny && typeof sdkAny.getTracerProvider === "function") candidates.push(sdkAny.getTracerProvider());
210
+ const globalProvider = trace.getTracerProvider();
211
+ candidates.push(globalProvider);
212
+ const proxy = globalProvider;
213
+ if (proxy && typeof proxy.getDelegate === "function") candidates.push(proxy.getDelegate());
214
+ return candidates.find(isForceFlushable);
215
+ }
190
216
  /**
191
217
  * Gets the OpenTelemetry tracer instance for Autotel.
192
218
  *
@@ -224,5 +250,5 @@ function getAutotelTracer(name = "autotel", version) {
224
250
  }
225
251
 
226
252
  //#endregion
227
- export { getAutotelTracer, getAutotelTracerProvider, setAutotelTracerProvider };
253
+ export { getAutotelTracer, getAutotelTracerProvider, getForceFlushableProvider, setAutotelTracerProvider };
228
254
  //# sourceMappingURL=tracer-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracer-provider.js","names":[],"sources":["../src/tracer-provider.ts"],"sourcesContent":["/**\n * Isolated tracer provider support for Autotel\n *\n * Allows Autotel to use a separate TracerProvider instance, avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Use Case:** Library authors who want to use Autotel without interfering\n * with the application's global OpenTelemetry setup.\n *\n * **Limitation:** While this isolates span processing and export, OpenTelemetry\n * context (trace IDs, parent spans) is still shared globally. Spans created with\n * the isolated provider may inherit trace context from global spans.\n */\n\nimport { trace } from '@opentelemetry/api';\nimport type { TracerProvider } from '@opentelemetry/api';\n\n/**\n * Symbol for storing isolated tracer provider in global scope\n * Using Symbol.for() ensures the same symbol across module boundaries\n */\nconst AUTOTEL_GLOBAL_SYMBOL = Symbol.for('autotel');\n\n/**\n * Global state for Autotel\n */\ntype AutotelGlobalState = {\n isolatedTracerProvider: TracerProvider | null;\n};\n\n/**\n * Create initial state\n */\nfunction createState(): AutotelGlobalState {\n return {\n isolatedTracerProvider: null,\n };\n}\n\n/**\n * Extend globalThis to include our symbol\n */\ninterface GlobalThis {\n [AUTOTEL_GLOBAL_SYMBOL]?: AutotelGlobalState;\n}\n\n/**\n * Get the global state, creating it if it doesn't exist\n * Handles edge cases like missing globalThis\n */\nfunction getGlobalState(): AutotelGlobalState {\n const initialState = createState();\n\n try {\n const g = globalThis as typeof globalThis & GlobalThis;\n\n if (typeof g !== 'object' || g === null) {\n console.warn(\n '[autotel] globalThis is not available, using fallback state',\n );\n return initialState;\n }\n\n if (!g[AUTOTEL_GLOBAL_SYMBOL]) {\n Object.defineProperty(g, AUTOTEL_GLOBAL_SYMBOL, {\n value: initialState,\n writable: false, // Lock the slot (not the contents)\n configurable: false,\n enumerable: false,\n });\n }\n\n return g[AUTOTEL_GLOBAL_SYMBOL]!;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `[autotel] Failed to access global state: ${error.message}`,\n );\n } else {\n console.error(\n `[autotel] Failed to access global state: ${String(error)}`,\n );\n }\n\n return initialState;\n }\n}\n\n/**\n * Sets an isolated TracerProvider for Autotel tracing operations.\n *\n * This allows Autotel to use its own TracerProvider instance, separate from\n * the global OpenTelemetry TracerProvider. This is useful for avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Limitation: Span Context Sharing**\n *\n * While this function isolates span processing and export, it does NOT provide\n * complete trace isolation. OpenTelemetry context (trace IDs, parent spans) is\n * still shared between the global and isolated providers. This means:\n *\n * - Spans created with the isolated provider inherit trace IDs from global spans\n * - Spans created with the isolated provider inherit parent relationships from global spans\n * - This can result in spans from different providers being part of the same logical trace\n *\n * **Why this happens:**\n * OpenTelemetry uses a global context propagation mechanism that operates at the\n * JavaScript runtime level, independent of individual TracerProvider instances.\n * The context (containing trace ID, span ID) flows through async boundaries and\n * is inherited by all spans created within that context, regardless of which\n * TracerProvider creates them.\n *\n * **When to use this:**\n * - Library code that ships with embedded Autotel\n * - SDKs that want observability without requiring users to set up OpenTelemetry\n * - Applications that need separate span processing for different subsystems\n * - Testing scenarios where you want to isolate trace collection\n *\n * @param provider - The TracerProvider instance to use, or null to clear the isolated provider\n *\n * @example Library with embedded Autotel\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'\n * import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'\n * import { setAutolem\n\netryTracerProvider } from 'autotel/tracer-provider'\n *\n * // Create provider with span processors in constructor\n * const exporter = new OTLPTraceExporter({\n * url: 'https://your-backend.com/v1/traces'\n * })\n *\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new BatchSpanProcessor(exporter))\n *\n * // Set as Autotel's isolated provider (doesn't call provider.register())\n * setAutotelTracerProvider(provider)\n *\n * // Now all Autotel trace() calls use this provider\n * // But won't interfere with the application's global OpenTelemetry setup\n * ```\n *\n * @example Testing with isolated provider\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Test setup\n * const exporter = new InMemorySpanExporter()\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new SimpleSpanProcessor(exporter))\n *\n * setAutotelTracerProvider(provider)\n *\n * // Run tests...\n * const spans = exporter.getFinishedSpans()\n *\n * // Cleanup\n * setAutotelTracerProvider(null)\n * ```\n *\n * @example Multiple subsystems with different exporters\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Payment subsystem - send to payment team's backend\n * const paymentProvider = new NodeTracerProvider()\n * paymentProvider.addSpanProcessor(new BatchSpanProcessor(\n * new OTLPTraceExporter({ url: 'https://payment-team-backend.com/v1/traces' })\n * ))\n *\n * // In payment module initialization\n * setAutotelTracerProvider(paymentProvider)\n * ```\n *\n * @public\n */\nexport function setAutotelTracerProvider(\n provider: TracerProvider | null,\n): void {\n getGlobalState().isolatedTracerProvider = provider;\n}\n\n/**\n * Gets the TracerProvider for Autotel tracing operations.\n *\n * Returns the isolated TracerProvider if one has been set via setAutotelTracerProvider(),\n * otherwise falls back to the global OpenTelemetry TracerProvider.\n *\n * This function is used internally by Autotel's trace functions. Most users\n * will not need to call this directly.\n *\n * @returns The TracerProvider instance to use for Autotel tracing\n *\n * @example Getting the current provider\n * ```typescript\n * import { getAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * const provider = getAutotelTracerProvider()\n * const tracer = provider.getTracer('my-service', '1.0.0')\n * ```\n *\n * @example Checking if isolated provider is active\n * ```typescript\n * import { getAutotelTracerProvider, setAutotelTracerProvider } from 'autotel/tracer-provider'\n * import { trace } from '@opentelemetry/api'\n *\n * const currentProvider = getAutotelTracerProvider()\n * const globalProvider = trace.getTracerProvider()\n *\n * if (currentProvider === globalProvider) {\n * console.log('Using global provider')\n * } else {\n * console.log('Using isolated provider')\n * }\n * ```\n *\n * @public\n */\nexport function getAutotelTracerProvider(): TracerProvider {\n const { isolatedTracerProvider } = getGlobalState();\n\n if (isolatedTracerProvider) return isolatedTracerProvider;\n\n return trace.getTracerProvider();\n}\n\n/**\n * Gets the OpenTelemetry tracer instance for Autotel.\n *\n * This function returns a tracer specifically configured for Autotel\n * with the correct tracer name and version. Used internally by all\n * Autotel tracing functions to ensure consistent trace creation.\n *\n * Uses the isolated provider if set, otherwise uses the global provider.\n *\n * @param name - Tracer name (default: 'autotel')\n * @param version - Optional version string\n * @returns The Autotel OpenTelemetry tracer instance\n *\n * @example Basic usage\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer()\n * const span = tracer.startSpan('my-operation')\n * // ... use span\n * span.end()\n * ```\n *\n * @example Custom tracer name\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer('my-library', '2.1.0')\n * ```\n *\n * @public\n */\nexport function getAutotelTracer(name = 'autotel', version?: string) {\n return getAutotelTracerProvider().getTracer(name, version);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,wBAAwB,OAAO,IAAI,SAAS;;;;AAYlD,SAAS,cAAkC;CACzC,OAAO,EACL,wBAAwB,KAC1B;AACF;;;;;AAaA,SAAS,iBAAqC;CAC5C,MAAM,eAAe,YAAY;CAEjC,IAAI;EACF,MAAM,IAAI;EAEV,IAAI,OAAO,MAAM,YAAY,MAAM,MAAM;GACvC,QAAQ,KACN,6DACF;GACA,OAAO;EACT;EAEA,IAAI,CAAC,EAAE,wBACL,OAAO,eAAe,GAAG,uBAAuB;GAC9C,OAAO;GACP,UAAU;GACV,cAAc;GACd,YAAY;EACd,CAAC;EAGH,OAAO,EAAE;CACX,SAAS,OAAO;EACd,IAAI,iBAAiB,OACnB,QAAQ,MACN,4CAA4C,MAAM,SACpD;OAEA,QAAQ,MACN,4CAA4C,OAAO,KAAK,GAC1D;EAGF,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA,SAAgB,yBACd,UACM;CACN,eAAe,CAAC,CAAC,yBAAyB;AAC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,2BAA2C;CACzD,MAAM,EAAE,2BAA2B,eAAe;CAElD,IAAI,wBAAwB,OAAO;CAEnC,OAAO,MAAM,kBAAkB;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,iBAAiB,OAAO,WAAW,SAAkB;CACnE,OAAO,yBAAyB,CAAC,CAAC,UAAU,MAAM,OAAO;AAC3D"}
1
+ {"version":3,"file":"tracer-provider.js","names":[],"sources":["../src/tracer-provider.ts"],"sourcesContent":["/**\n * Isolated tracer provider support for Autotel\n *\n * Allows Autotel to use a separate TracerProvider instance, avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Use Case:** Library authors who want to use Autotel without interfering\n * with the application's global OpenTelemetry setup.\n *\n * **Limitation:** While this isolates span processing and export, OpenTelemetry\n * context (trace IDs, parent spans) is still shared globally. Spans created with\n * the isolated provider may inherit trace context from global spans.\n */\n\nimport { trace } from '@opentelemetry/api';\nimport type { TracerProvider } from '@opentelemetry/api';\n\n/**\n * Symbol for storing isolated tracer provider in global scope\n * Using Symbol.for() ensures the same symbol across module boundaries\n */\nconst AUTOTEL_GLOBAL_SYMBOL = Symbol.for('autotel');\n\n/**\n * Global state for Autotel\n */\ntype AutotelGlobalState = {\n isolatedTracerProvider: TracerProvider | null;\n};\n\n/**\n * Create initial state\n */\nfunction createState(): AutotelGlobalState {\n return {\n isolatedTracerProvider: null,\n };\n}\n\n/**\n * Extend globalThis to include our symbol\n */\ninterface GlobalThis {\n [AUTOTEL_GLOBAL_SYMBOL]?: AutotelGlobalState;\n}\n\n/**\n * Get the global state, creating it if it doesn't exist\n * Handles edge cases like missing globalThis\n */\nfunction getGlobalState(): AutotelGlobalState {\n const initialState = createState();\n\n try {\n const g = globalThis as typeof globalThis & GlobalThis;\n\n if (typeof g !== 'object' || g === null) {\n console.warn(\n '[autotel] globalThis is not available, using fallback state',\n );\n return initialState;\n }\n\n if (!g[AUTOTEL_GLOBAL_SYMBOL]) {\n Object.defineProperty(g, AUTOTEL_GLOBAL_SYMBOL, {\n value: initialState,\n writable: false, // Lock the slot (not the contents)\n configurable: false,\n enumerable: false,\n });\n }\n\n return g[AUTOTEL_GLOBAL_SYMBOL]!;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\n `[autotel] Failed to access global state: ${error.message}`,\n );\n } else {\n console.error(\n `[autotel] Failed to access global state: ${String(error)}`,\n );\n }\n\n return initialState;\n }\n}\n\n/**\n * Sets an isolated TracerProvider for Autotel tracing operations.\n *\n * This allows Autotel to use its own TracerProvider instance, separate from\n * the global OpenTelemetry TracerProvider. This is useful for avoiding conflicts\n * with other OpenTelemetry instrumentation in the application.\n *\n * **Limitation: Span Context Sharing**\n *\n * While this function isolates span processing and export, it does NOT provide\n * complete trace isolation. OpenTelemetry context (trace IDs, parent spans) is\n * still shared between the global and isolated providers. This means:\n *\n * - Spans created with the isolated provider inherit trace IDs from global spans\n * - Spans created with the isolated provider inherit parent relationships from global spans\n * - This can result in spans from different providers being part of the same logical trace\n *\n * **Why this happens:**\n * OpenTelemetry uses a global context propagation mechanism that operates at the\n * JavaScript runtime level, independent of individual TracerProvider instances.\n * The context (containing trace ID, span ID) flows through async boundaries and\n * is inherited by all spans created within that context, regardless of which\n * TracerProvider creates them.\n *\n * **When to use this:**\n * - Library code that ships with embedded Autotel\n * - SDKs that want observability without requiring users to set up OpenTelemetry\n * - Applications that need separate span processing for different subsystems\n * - Testing scenarios where you want to isolate trace collection\n *\n * @param provider - The TracerProvider instance to use, or null to clear the isolated provider\n *\n * @example Library with embedded Autotel\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'\n * import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'\n * import { setAutolem\n\netryTracerProvider } from 'autotel/tracer-provider'\n *\n * // Create provider with span processors in constructor\n * const exporter = new OTLPTraceExporter({\n * url: 'https://your-backend.com/v1/traces'\n * })\n *\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new BatchSpanProcessor(exporter))\n *\n * // Set as Autotel's isolated provider (doesn't call provider.register())\n * setAutotelTracerProvider(provider)\n *\n * // Now all Autotel trace() calls use this provider\n * // But won't interfere with the application's global OpenTelemetry setup\n * ```\n *\n * @example Testing with isolated provider\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Test setup\n * const exporter = new InMemorySpanExporter()\n * const provider = new NodeTracerProvider()\n * provider.addSpanProcessor(new SimpleSpanProcessor(exporter))\n *\n * setAutotelTracerProvider(provider)\n *\n * // Run tests...\n * const spans = exporter.getFinishedSpans()\n *\n * // Cleanup\n * setAutotelTracerProvider(null)\n * ```\n *\n * @example Multiple subsystems with different exporters\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'\n * import { setAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * // Payment subsystem - send to payment team's backend\n * const paymentProvider = new NodeTracerProvider()\n * paymentProvider.addSpanProcessor(new BatchSpanProcessor(\n * new OTLPTraceExporter({ url: 'https://payment-team-backend.com/v1/traces' })\n * ))\n *\n * // In payment module initialization\n * setAutotelTracerProvider(paymentProvider)\n * ```\n *\n * @public\n */\nexport function setAutotelTracerProvider(\n provider: TracerProvider | null,\n): void {\n getGlobalState().isolatedTracerProvider = provider;\n}\n\n/**\n * Gets the TracerProvider for Autotel tracing operations.\n *\n * Returns the isolated TracerProvider if one has been set via setAutotelTracerProvider(),\n * otherwise falls back to the global OpenTelemetry TracerProvider.\n *\n * This function is used internally by Autotel's trace functions. Most users\n * will not need to call this directly.\n *\n * @returns The TracerProvider instance to use for Autotel tracing\n *\n * @example Getting the current provider\n * ```typescript\n * import { getAutotelTracerProvider } from 'autotel/tracer-provider'\n *\n * const provider = getAutotelTracerProvider()\n * const tracer = provider.getTracer('my-service', '1.0.0')\n * ```\n *\n * @example Checking if isolated provider is active\n * ```typescript\n * import { getAutotelTracerProvider, setAutotelTracerProvider } from 'autotel/tracer-provider'\n * import { trace } from '@opentelemetry/api'\n *\n * const currentProvider = getAutotelTracerProvider()\n * const globalProvider = trace.getTracerProvider()\n *\n * if (currentProvider === globalProvider) {\n * console.log('Using global provider')\n * } else {\n * console.log('Using isolated provider')\n * }\n * ```\n *\n * @public\n */\nexport function getAutotelTracerProvider(): TracerProvider {\n const { isolatedTracerProvider } = getGlobalState();\n\n if (isolatedTracerProvider) return isolatedTracerProvider;\n\n return trace.getTracerProvider();\n}\n\n/** A tracer provider that can be force-flushed (SDK providers implement this). */\ninterface ForceFlushable {\n forceFlush(): Promise<void>;\n}\n\nfunction isForceFlushable(value: unknown): value is ForceFlushable {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ForceFlushable).forceFlush === 'function'\n );\n}\n\n/**\n * Resolve a tracer provider that can be `forceFlush()`ed, so `flush()` and\n * flush-on-shutdown actually export pending spans.\n *\n * `NodeSDK.getTracerProvider()` returns `undefined` on `@opentelemetry/sdk-node`\n * 0.220+ (OpenTelemetry 2.x), so code that force-flushes only via the SDK\n * handle silently exports nothing. This falls back to the globally registered\n * provider and unwraps the API's `ProxyTracerProvider` to reach the real\n * delegate, which is where `forceFlush` actually lives.\n *\n * @param sdk - Optional NodeSDK handle to try first.\n * @returns Something with `forceFlush()`, or `undefined` if none is registered.\n */\nexport function getForceFlushableProvider(\n sdk?: unknown,\n): ForceFlushable | undefined {\n const candidates: unknown[] = [];\n\n const sdkAny = sdk as { getTracerProvider?: () => unknown } | undefined;\n if (sdkAny && typeof sdkAny.getTracerProvider === 'function') {\n candidates.push(sdkAny.getTracerProvider());\n }\n\n const globalProvider = trace.getTracerProvider() as unknown;\n candidates.push(globalProvider);\n // The API hands back a ProxyTracerProvider; the real provider (with\n // forceFlush) sits behind getDelegate().\n const proxy = globalProvider as { getDelegate?: () => unknown };\n if (proxy && typeof proxy.getDelegate === 'function') {\n candidates.push(proxy.getDelegate());\n }\n\n return candidates.find(isForceFlushable);\n}\n\n/**\n * Gets the OpenTelemetry tracer instance for Autotel.\n *\n * This function returns a tracer specifically configured for Autotel\n * with the correct tracer name and version. Used internally by all\n * Autotel tracing functions to ensure consistent trace creation.\n *\n * Uses the isolated provider if set, otherwise uses the global provider.\n *\n * @param name - Tracer name (default: 'autotel')\n * @param version - Optional version string\n * @returns The Autotel OpenTelemetry tracer instance\n *\n * @example Basic usage\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer()\n * const span = tracer.startSpan('my-operation')\n * // ... use span\n * span.end()\n * ```\n *\n * @example Custom tracer name\n * ```typescript\n * import { getAutotelTracer } from 'autotel/tracer-provider'\n *\n * const tracer = getAutotelTracer('my-library', '2.1.0')\n * ```\n *\n * @public\n */\nexport function getAutotelTracer(name = 'autotel', version?: string) {\n return getAutotelTracerProvider().getTracer(name, version);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,wBAAwB,OAAO,IAAI,SAAS;;;;AAYlD,SAAS,cAAkC;CACzC,OAAO,EACL,wBAAwB,KAC1B;AACF;;;;;AAaA,SAAS,iBAAqC;CAC5C,MAAM,eAAe,YAAY;CAEjC,IAAI;EACF,MAAM,IAAI;EAEV,IAAI,OAAO,MAAM,YAAY,MAAM,MAAM;GACvC,QAAQ,KACN,6DACF;GACA,OAAO;EACT;EAEA,IAAI,CAAC,EAAE,wBACL,OAAO,eAAe,GAAG,uBAAuB;GAC9C,OAAO;GACP,UAAU;GACV,cAAc;GACd,YAAY;EACd,CAAC;EAGH,OAAO,EAAE;CACX,SAAS,OAAO;EACd,IAAI,iBAAiB,OACnB,QAAQ,MACN,4CAA4C,MAAM,SACpD;OAEA,QAAQ,MACN,4CAA4C,OAAO,KAAK,GAC1D;EAGF,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FA,SAAgB,yBACd,UACM;CACN,eAAe,CAAC,CAAC,yBAAyB;AAC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,2BAA2C;CACzD,MAAM,EAAE,2BAA2B,eAAe;CAElD,IAAI,wBAAwB,OAAO;CAEnC,OAAO,MAAM,kBAAkB;AACjC;AAOA,SAAS,iBAAiB,OAAyC;CACjE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAyB,eAAe;AAEpD;;;;;;;;;;;;;;AAeA,SAAgB,0BACd,KAC4B;CAC5B,MAAM,aAAwB,CAAC;CAE/B,MAAM,SAAS;CACf,IAAI,UAAU,OAAO,OAAO,sBAAsB,YAChD,WAAW,KAAK,OAAO,kBAAkB,CAAC;CAG5C,MAAM,iBAAiB,MAAM,kBAAkB;CAC/C,WAAW,KAAK,cAAc;CAG9B,MAAM,QAAQ;CACd,IAAI,SAAS,OAAO,MAAM,gBAAgB,YACxC,WAAW,KAAK,MAAM,YAAY,CAAC;CAGrC,OAAO,WAAW,KAAK,gBAAgB;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,iBAAiB,OAAO,WAAW,SAAkB;CACnE,OAAO,yBAAyB,CAAC,CAAC,UAAU,MAAM,OAAO;AAC3D"}
package/dist/webhook.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_structured_error = require('./structured-error-CHg7DoIQ.cjs');
3
- const require_functional = require('./functional-BCtxvYir.cjs');
3
+ const require_functional = require('./functional-CwsPyc4l.cjs');
4
4
  const require_correlated_events = require('./correlated-events-kSwLo3mi.cjs');
5
5
  let _opentelemetry_api = require("@opentelemetry/api");
6
6
 
package/dist/webhook.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { r as recordStructuredError } from "./structured-error-9--cxBay.js";
2
- import { a as trace$1 } from "./functional-CrAq8Az8.js";
2
+ import { a as trace$1 } from "./functional-dAYhXQPA.js";
3
3
  import { t as emitCorrelatedEvent } from "./correlated-events-Bzh5y-UB.js";
4
4
  import { SpanKind, trace } from "@opentelemetry/api";
5
5
 
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_functional = require('./functional-BCtxvYir.cjs');
2
+ const require_functional = require('./functional-CwsPyc4l.cjs');
3
3
  const require_correlated_events = require('./correlated-events-kSwLo3mi.cjs');
4
4
  const require_business_baggage = require('./business-baggage.cjs');
5
5
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -1,4 +1,4 @@
1
- import { a as trace$1 } from "./functional-CrAq8Az8.js";
1
+ import { a as trace$1 } from "./functional-dAYhXQPA.js";
2
2
  import { t as emitCorrelatedEvent } from "./correlated-events-Bzh5y-UB.js";
3
3
  import { createSafeBaggageSchema } from "./business-baggage.js";
4
4
  import { SpanKind, context, propagation } from "@opentelemetry/api";
package/dist/workflow.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_rolldown_runtime = require('./rolldown-runtime-C_NdSu1c.cjs');
3
3
  const require_trace_helpers = require('./trace-helpers.cjs');
4
- const require_functional = require('./functional-BCtxvYir.cjs');
4
+ const require_functional = require('./functional-CwsPyc4l.cjs');
5
5
  const require_correlated_events = require('./correlated-events-kSwLo3mi.cjs');
6
6
  let node_async_hooks = require("node:async_hooks");
7
7
  node_async_hooks = require_rolldown_runtime.__toESM(node_async_hooks, 1);
package/dist/workflow.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getActiveSpan } from "./trace-helpers.js";
2
- import { a as trace } from "./functional-CrAq8Az8.js";
2
+ import { a as trace } from "./functional-dAYhXQPA.js";
3
3
  import { t as emitCorrelatedEvent } from "./correlated-events-Bzh5y-UB.js";
4
4
  import * as nodeAsyncHooks from "node:async_hooks";
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel",
3
- "version": "4.2.5",
3
+ "version": "4.3.0",
4
4
  "description": "Write Once, Observe Anywhere",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",