@glasstrace/sdk 1.9.0 → 1.10.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 (68) hide show
  1. package/dist/async-context/index.cjs +53 -4
  2. package/dist/async-context/index.cjs.map +1 -1
  3. package/dist/async-context/index.js +2 -2
  4. package/dist/{chunk-JHUNLPSS.js → chunk-6RKS3DNA.js} +45 -1
  5. package/dist/{chunk-JHUNLPSS.js.map → chunk-6RKS3DNA.js.map} +1 -1
  6. package/dist/{chunk-HD6JIFKN.js → chunk-BSVWJSVX.js} +2 -2
  7. package/dist/{chunk-QHV7NFON.js → chunk-D54FMQHF.js} +49 -40
  8. package/dist/chunk-D54FMQHF.js.map +1 -0
  9. package/dist/chunk-I2DVVSKW.js +419 -0
  10. package/dist/chunk-I2DVVSKW.js.map +1 -0
  11. package/dist/{chunk-H6WJ63X2.js → chunk-M5GO2SSO.js} +2 -2
  12. package/dist/{chunk-XEPC4NFL.js → chunk-OXA4IHQX.js} +39 -405
  13. package/dist/chunk-OXA4IHQX.js.map +1 -0
  14. package/dist/{chunk-RQ5BIWDT.js → chunk-OXM2BZMF.js} +11 -6
  15. package/dist/{chunk-RQ5BIWDT.js.map → chunk-OXM2BZMF.js.map} +1 -1
  16. package/dist/{chunk-M6EWJCAT.js → chunk-QVTONMVZ.js} +2 -2
  17. package/dist/{chunk-DKV53A2C.js → chunk-RL43PU2X.js} +2 -2
  18. package/dist/{chunk-GWIEUBFR.js → chunk-UMGZJYC4.js} +3 -3
  19. package/dist/{chunk-QXITSNYM.js → chunk-XG6WR2KS.js} +3 -3
  20. package/dist/cli/init.cjs +4 -4
  21. package/dist/cli/init.cjs.map +1 -1
  22. package/dist/cli/init.js +7 -7
  23. package/dist/cli/init.js.map +1 -1
  24. package/dist/cli/mcp-add.cjs +1 -1
  25. package/dist/cli/mcp-add.cjs.map +1 -1
  26. package/dist/cli/mcp-add.js +3 -3
  27. package/dist/cli/mcp-add.js.map +1 -1
  28. package/dist/cli/uninit.js +3 -3
  29. package/dist/cli/upgrade-instructions.cjs +1 -1
  30. package/dist/cli/upgrade-instructions.cjs.map +1 -1
  31. package/dist/cli/upgrade-instructions.js +3 -3
  32. package/dist/cli/upgrade-instructions.js.map +1 -1
  33. package/dist/cli/validate.cjs.map +1 -1
  34. package/dist/cli/validate.js +2 -2
  35. package/dist/edge-entry.cjs +100 -42
  36. package/dist/edge-entry.cjs.map +1 -1
  37. package/dist/edge-entry.js +4 -4
  38. package/dist/index.cjs +58 -5
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.js +10 -8
  41. package/dist/index.js.map +1 -1
  42. package/dist/middleware/index.cjs +91 -38
  43. package/dist/middleware/index.cjs.map +1 -1
  44. package/dist/middleware/index.d.cts +8 -0
  45. package/dist/middleware/index.d.ts +8 -0
  46. package/dist/middleware/index.js +2 -2
  47. package/dist/node-entry.cjs +58 -5
  48. package/dist/node-entry.cjs.map +1 -1
  49. package/dist/node-entry.js +12 -10
  50. package/dist/node-subpath.cjs.map +1 -1
  51. package/dist/node-subpath.js +3 -3
  52. package/dist/{source-map-uploader-MMJ2WCL4.js → source-map-uploader-CLYCE2TZ.js} +3 -3
  53. package/dist/trpc/index.cjs +15164 -503
  54. package/dist/trpc/index.cjs.map +1 -1
  55. package/dist/trpc/index.d.cts +62 -1
  56. package/dist/trpc/index.d.ts +62 -1
  57. package/dist/trpc/index.js +200 -1
  58. package/dist/trpc/index.js.map +1 -1
  59. package/package.json +1 -1
  60. package/dist/chunk-QHV7NFON.js.map +0 -1
  61. package/dist/chunk-XEPC4NFL.js.map +0 -1
  62. /package/dist/{chunk-HD6JIFKN.js.map → chunk-BSVWJSVX.js.map} +0 -0
  63. /package/dist/{chunk-H6WJ63X2.js.map → chunk-M5GO2SSO.js.map} +0 -0
  64. /package/dist/{chunk-M6EWJCAT.js.map → chunk-QVTONMVZ.js.map} +0 -0
  65. /package/dist/{chunk-DKV53A2C.js.map → chunk-RL43PU2X.js.map} +0 -0
  66. /package/dist/{chunk-GWIEUBFR.js.map → chunk-UMGZJYC4.js.map} +0 -0
  67. /package/dist/{chunk-QXITSNYM.js.map → chunk-XG6WR2KS.js.map} +0 -0
  68. /package/dist/{source-map-uploader-MMJ2WCL4.js.map → source-map-uploader-CLYCE2TZ.js.map} +0 -0
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-DQ25VOKK.js";
9
9
  import {
10
10
  GLASSTRACE_ATTRIBUTE_NAMES
11
- } from "./chunk-JHUNLPSS.js";
11
+ } from "./chunk-6RKS3DNA.js";
12
12
 
13
13
  // src/async-context/index.ts
14
14
  var ATTR = GLASSTRACE_ATTRIBUTE_NAMES;
@@ -42,10 +42,15 @@ function withAsyncCausality(options, fn) {
42
42
  })();
43
43
  return async () => {
44
44
  const tracer = trace.getTracer(TRACER_NAME);
45
- const span = tracer.startSpan(options.name, {
46
- root: true,
47
- links: capturedContext !== void 0 ? [{ context: capturedContext }] : void 0
48
- });
45
+ let span;
46
+ try {
47
+ span = tracer.startSpan(options.name, {
48
+ root: true,
49
+ links: capturedContext !== void 0 ? [{ context: capturedContext }] : void 0
50
+ });
51
+ } catch {
52
+ return Promise.resolve(fn());
53
+ }
49
54
  if (isNoopSpan(span)) {
50
55
  if (!_skippedUninstalledEmitted) {
51
56
  _skippedUninstalledEmitted = true;
@@ -116,4 +121,4 @@ export {
116
121
  _resetForTesting,
117
122
  withAsyncCausality
118
123
  };
119
- //# sourceMappingURL=chunk-RQ5BIWDT.js.map
124
+ //# sourceMappingURL=chunk-OXM2BZMF.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/async-context/index.ts"],"sourcesContent":["/**\n * Post-response async causality instrumentation for Glasstrace.\n *\n * Subpath: `@glasstrace/sdk/async-context`\n *\n * This module exposes {@link withAsyncCausality}, a continuation-\n * passing wrapper that captures the active OTel `SpanContext` at call\n * time and binds it to a callback. When the callback runs later\n * (Next.js `after()`, queue dispatchers, webhook fire-and-forget),\n * the wrapper opens a span linked to the originating trace via two\n * channels:\n *\n * - An OTel `Link` to the captured `SpanContext` — the OTel-native\n * pointer between two spans in different traces. Surfaces in\n * standard OTel-aware UIs (Jaeger, Honeycomb, etc.) as a\n * \"follows from\" relationship.\n * - A `glasstrace.causal.post_response_async` attribute carrying\n * the captured trace ID (32-char hex). Used by the product-side\n * trace-summary transform (per DISC-1539's product handoff) to\n * reconstruct ownership without resolving the Link. Two\n * companion booleans\n * (`glasstrace.causal.affects_http_status` and\n * `glasstrace.causal.affects_http_duration`) document that the\n * async work does NOT participate in the root request's outcome.\n *\n * Both channels are emitted together so the SDK is robust to\n * downstream transforms that resolve causality through either form.\n *\n * Edge-runtime safety\n * -------------------\n * The wrapper is included in the SDK's edge bundle\n * (`packages/sdk/src/edge-entry.ts`). Its closure imports only the\n * OTel API, the protocol constants, and the\n * `./optional-lifecycle.js` bridge — none of which reach into\n * `node:*` built-ins or the `process` global. The F003 closure scan\n * (`packages/sdk/scripts/check-edge-bundle.mjs`) enforces this on\n * every build.\n *\n * Strategy: continuation-passing, NOT global ALS propagation\n * ---------------------------------------------------------\n * Per the SDK-046 brief §2.3: ALS continuity across Next.js `after()`\n * is uncertain (the framework may schedule via `queueMicrotask`\n * (preserves ALS) or via cross-tick scheduling (drops ALS)). Relying\n * on ALS would couple the SDK to Next internals. Continuation-passing\n * makes the causality explicit — the user wraps the callback they\n * pass to `after()` / their queue, and the captured `SpanContext`\n * travels with the closure regardless of how the framework schedules\n * it.\n *\n * @module @glasstrace/sdk/async-context\n */\n\nimport {\n trace,\n context,\n SpanStatusCode,\n type AttributeValue,\n type SpanContext,\n} from \"@opentelemetry/api\";\nimport { GLASSTRACE_ATTRIBUTE_NAMES } from \"@glasstrace/protocol\";\nimport { tryEmitLifecycleEvent } from \"../optional-lifecycle.js\";\n\nconst ATTR = GLASSTRACE_ATTRIBUTE_NAMES;\n\n/**\n * Module-level OTel tracer name for the async-context subpath.\n * Resolves through the global `ProxyTracerProvider` so the wrapper\n * picks up whatever provider the SDK has registered. Re-resolved on\n * every call site rather than cached at module top level so test\n * harnesses can install a provider after this module is imported.\n * Mirrors the tRPC subpath at `packages/sdk/src/trpc/index.ts:128`.\n */\nconst TRACER_NAME = \"@glasstrace/sdk/async-context\";\n\n/**\n * The W3C-spec-defined invalid trace ID. The OTel API noop tracer\n * returns this value from `Span.spanContext().traceId`. We use this\n * to detect both (a) the SDK-not-registered case via the\n * noop-tracer probe and (b) the no-active-span case via the\n * captured `SpanContext`.\n */\nconst INVALID_TRACE_ID = \"00000000000000000000000000000000\";\n\n/**\n * Module-level once-flags for lifecycle events. Cleared via\n * {@link _resetForTesting}.\n */\nlet _skippedUninstalledEmitted = false;\nlet _noOriginatingContextEmitted = false;\n\n/**\n * INTERNAL — clears once-flags for unit tests; not part of the\n * public surface.\n */\nexport function _resetForTesting(): void {\n _skippedUninstalledEmitted = false;\n _noOriginatingContextEmitted = false;\n}\n\n/**\n * Options for {@link withAsyncCausality}.\n *\n * @example\n * ```ts\n * import { withAsyncCausality } from \"@glasstrace/sdk/async-context\";\n * import { after } from \"next/server\";\n *\n * export async function POST(req: Request) {\n * const result = await processRequest(req);\n * after(\n * withAsyncCausality(\n * { name: \"send-confirmation-email\" },\n * async () => sendEmail(result.userId),\n * ),\n * );\n * return Response.json({ ok: true });\n * }\n * ```\n */\nexport interface WithAsyncCausalityOptions {\n /**\n * Span name for the async work. Required, non-empty string. Used as\n * the OTel span name and appears in trace timelines. Names should\n * be stable across runs (e.g., \"send-confirmation-email\",\n * \"enqueue-webhook-dispatch\"); avoid embedding payload data in the\n * name.\n */\n name: string;\n /**\n * Optional attributes attached to the span before the wrapped\n * callback runs. Forwarded to OTel as-is via `span.setAttributes()`.\n * The SDK does not redact, sanitize, or scan values here — callers\n * MUST avoid placing tokens, credentials, or other sensitive data\n * in `attributes`.\n */\n attributes?: Record<string, AttributeValue>;\n}\n\n/**\n * Capture the active OTel `SpanContext` at call time, bind it to a\n * callback, and return a continuation that will emit a\n * causally-linked span when invoked.\n *\n * The returned continuation:\n *\n * 1. Detects the SDK's registration state. When the OTel API is\n * still on the noop tracer, runs the wrapped callback directly\n * and emits an `async:skipped_uninstalled` lifecycle event (at\n * most once per process). No span is opened.\n * 2. Otherwise opens a span named `options.name` as a NEW root\n * span (not parented to the captured context — `after()` /\n * queue dispatchers run outside the originating request's OTel\n * context, so the async work belongs to a separate trace).\n * 3. When a captured `SpanContext` exists with a valid trace ID,\n * attaches:\n * - an OTel `Link` to that `SpanContext` (the OTel-native\n * form),\n * - the `glasstrace.causal.post_response_async` attribute\n * carrying the trace ID (the transform-readable form),\n * - `glasstrace.causal.affects_http_status = false` and\n * `glasstrace.causal.affects_http_duration = false`\n * documenting that the async work does NOT participate in\n * the root request's outcome.\n * When no valid `SpanContext` was captured, none of these are\n * emitted (per SDK-046's \"missing or unknown evidence is\n * preferable to guessed evidence\" rule) and an\n * `async:no_originating_context` lifecycle event fires (at\n * most once per process).\n * 4. Awaits the wrapped callback.\n * 5. On a thrown error: normalizes the throwable; sets `ERROR`\n * status with `recordException`; rethrows the original error\n * verbatim.\n * 6. On a successful return: leaves status `UNSET`.\n * 7. Always ends the span.\n *\n * The continuation returns a Promise resolving to the callback's\n * return value (Promise-or-value semantics: a sync callback's value\n * is wrapped in `Promise.resolve()`).\n *\n * @param options - Span name and optional pre-start attributes.\n * @param fn - The async callback to run later. May be sync or async;\n * the wrapper always returns a Promise to give a consistent\n * continuation shape regardless of `fn`'s synchronicity.\n * @returns A continuation `() => Promise<T>` that, when invoked,\n * emits the causally-linked span and runs `fn`.\n *\n * @example Next.js after() — typical use\n * ```ts\n * import { withAsyncCausality } from \"@glasstrace/sdk/async-context\";\n * import { after } from \"next/server\";\n *\n * export async function POST(req: Request) {\n * const result = await processRequest(req);\n * after(\n * withAsyncCausality(\n * { name: \"send-confirmation-email\" },\n * async () => sendEmail(result.userId),\n * ),\n * );\n * return Response.json({ ok: true });\n * }\n * ```\n *\n * @example Queue dispatcher — capture before enqueue\n * ```ts\n * const dispatch = withAsyncCausality(\n * { name: \"process-webhook\" },\n * async () => handler(payload),\n * );\n * await queue.enqueue(dispatch);\n * ```\n */\nexport function withAsyncCausality<T>(\n options: WithAsyncCausalityOptions,\n fn: () => Promise<T> | T,\n): () => Promise<T> {\n if (typeof options.name !== \"string\" || options.name.length === 0) {\n throw new TypeError(\n \"withAsyncCausality: options.name must be a non-empty string\",\n );\n }\n if (typeof fn !== \"function\") {\n throw new TypeError(\"withAsyncCausality: fn must be a function\");\n }\n\n // Capture-time: snapshot the active SpanContext, if any. The\n // capture happens synchronously in the wrapper-construction call,\n // BEFORE the user passes the continuation to `after()` /\n // `queue.enqueue()`. Reading via `trace.getActiveSpan()` rather\n // than `context.active()` mirrors `captureCorrelationId()` at\n // `packages/sdk/src/correlation-id.ts:83` — the active-span API is\n // the public OTel surface for this.\n const capturedContext: SpanContext | undefined = (() => {\n try {\n const active = trace.getActiveSpan();\n if (!active) return undefined;\n const ctx = active.spanContext();\n // Reject the noop-tracer's invalid trace ID. Treating it as\n // captured would emit a Link to all-zeros, which is misleading.\n if (ctx.traceId === INVALID_TRACE_ID) return undefined;\n return ctx;\n } catch {\n return undefined;\n }\n })();\n\n return async (): Promise<T> => {\n const tracer = trace.getTracer(TRACER_NAME);\n // The async span is a NEW root: post-response work runs outside\n // the originating request's OTel context, so parenting to the\n // captured context would put two unrelated runtime executions in\n // the same trace tree. Causality is communicated via the Link +\n // attribute pair instead.\n const span = tracer.startSpan(options.name, {\n root: true,\n links:\n capturedContext !== undefined\n ? [{ context: capturedContext }]\n : undefined,\n });\n\n // SDK-not-registered fast path. The public `isRecording()` probe\n // returns `false` on noop spans without producing an exported\n // span; this avoids emitting a useless probe span on every\n // continuation when a real provider is registered.\n if (isNoopSpan(span)) {\n if (!_skippedUninstalledEmitted) {\n _skippedUninstalledEmitted = true;\n tryEmitLifecycleEvent(\"async:skipped_uninstalled\", {});\n }\n endSpanSafely(span);\n return Promise.resolve(fn());\n }\n\n if (capturedContext === undefined && !_noOriginatingContextEmitted) {\n _noOriginatingContextEmitted = true;\n tryEmitLifecycleEvent(\"async:no_originating_context\", {});\n }\n\n try {\n if (options.attributes) {\n span.setAttributes(options.attributes);\n }\n if (capturedContext !== undefined) {\n span.setAttribute(\n ATTR.CAUSAL_POST_RESPONSE_ASYNC,\n capturedContext.traceId,\n );\n span.setAttribute(ATTR.CAUSAL_AFFECTS_HTTP_STATUS, false);\n span.setAttribute(ATTR.CAUSAL_AFFECTS_HTTP_DURATION, false);\n }\n } catch {\n // Attribute failures are advisory; do not block fn().\n }\n\n try {\n // Activate the span as the current OTel context during fn()\n // execution so any nested spans (auto-instrumented OR manual)\n // are parented under this span. Without `context.with`, the\n // started span exists but is not in the active context, so\n // child spans become orphan roots (Copilot review 2026-05-08).\n // We use `context.with` rather than `tracer.startActiveSpan`\n // so the existing setup (Link/attribute application above)\n // can run pre-activation; the existing setup is small but\n // not trivial to reorder.\n const value = await context.with(\n trace.setSpan(context.active(), span),\n fn,\n );\n return value;\n } catch (error) {\n recordSpanError(span, error);\n throw error;\n } finally {\n endSpanSafely(span);\n }\n };\n}\n\n/**\n * Type guard for OTel noop spans. Mirrors the same check in\n * `../middleware/index.ts`. The OTel API's noop tracer\n * (`@opentelemetry/api`'s `NonRecordingSpan`) returns the all-zeros\n * sentinel trace ID `00000000000000000000000000000000` from\n * `Span.spanContext().traceId`. Real SDK-emitted spans — including\n * spans that a sampler chose to DROP (which legitimately return\n * `isRecording() === false`) — return a valid 32-char hex trace ID\n * because the SDK assigns a trace ID before sampler invocation for\n * propagation purposes. Using the SpanContext discriminator keeps\n * the SDK-not-registered fast path from misfiring under normal head\n * sampling configurations (Copilot review 2026-05-08).\n */\nfunction isNoopSpan(\n span: ReturnType<ReturnType<typeof trace.getTracer>[\"startSpan\"]>,\n): boolean {\n try {\n return span.spanContext().traceId === INVALID_TRACE_ID;\n } catch {\n return false;\n }\n}\n\n/**\n * See {@link ../middleware/index.ts} — duplicated here rather than\n * shared because the OTel `Span` type is structural and importing\n * a helper from a sibling module would force the modules to share a\n * deeper dependency. Both copies are exactly two non-throwing\n * `try`/`catch` blocks; the duplication is intentional and trivial.\n */\nfunction recordSpanError(\n span: ReturnType<ReturnType<typeof trace.getTracer>[\"startSpan\"]>,\n error: unknown,\n): void {\n const normalized: Error | string =\n error instanceof Error\n ? error\n : typeof error === \"string\"\n ? error\n : new Error(String(error));\n const statusMessage =\n normalized instanceof Error ? normalized.message : normalized;\n try {\n span.recordException(normalized);\n } catch {\n /* swallow */\n }\n try {\n span.setStatus({ code: SpanStatusCode.ERROR, message: statusMessage });\n } catch {\n /* swallow */\n }\n}\n\n/** See {@link ../middleware/index.ts}. */\nfunction endSpanSafely(\n span: ReturnType<ReturnType<typeof trace.getTracer>[\"startSpan\"]>,\n): void {\n try {\n span.end();\n } catch {\n /* swallow */\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA8DA,IAAM,OAAO;AAUb,IAAM,cAAc;AASpB,IAAM,mBAAmB;AAMzB,IAAI,6BAA6B;AACjC,IAAI,+BAA+B;AAM5B,SAAS,mBAAyB;AACvC,+BAA6B;AAC7B,iCAA+B;AACjC;AAmHO,SAAS,mBACd,SACA,IACkB;AAClB,MAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,WAAW,GAAG;AACjE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAM,IAAI,UAAU,2CAA2C;AAAA,EACjE;AASA,QAAM,mBAA4C,MAAM;AACtD,QAAI;AACF,YAAM,SAAS,MAAM,cAAc;AACnC,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,MAAM,OAAO,YAAY;AAG/B,UAAI,IAAI,YAAY,iBAAkB,QAAO;AAC7C,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AAEH,SAAO,YAAwB;AAC7B,UAAM,SAAS,MAAM,UAAU,WAAW;AAM1C,UAAM,OAAO,OAAO,UAAU,QAAQ,MAAM;AAAA,MAC1C,MAAM;AAAA,MACN,OACE,oBAAoB,SAChB,CAAC,EAAE,SAAS,gBAAgB,CAAC,IAC7B;AAAA,IACR,CAAC;AAMD,QAAI,WAAW,IAAI,GAAG;AACpB,UAAI,CAAC,4BAA4B;AAC/B,qCAA6B;AAC7B,8BAAsB,6BAA6B,CAAC,CAAC;AAAA,MACvD;AACA,oBAAc,IAAI;AAClB,aAAO,QAAQ,QAAQ,GAAG,CAAC;AAAA,IAC7B;AAEA,QAAI,oBAAoB,UAAa,CAAC,8BAA8B;AAClE,qCAA+B;AAC/B,4BAAsB,gCAAgC,CAAC,CAAC;AAAA,IAC1D;AAEA,QAAI;AACF,UAAI,QAAQ,YAAY;AACtB,aAAK,cAAc,QAAQ,UAAU;AAAA,MACvC;AACA,UAAI,oBAAoB,QAAW;AACjC,aAAK;AAAA,UACH,KAAK;AAAA,UACL,gBAAgB;AAAA,QAClB;AACA,aAAK,aAAa,KAAK,4BAA4B,KAAK;AACxD,aAAK,aAAa,KAAK,8BAA8B,KAAK;AAAA,MAC5D;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI;AAUF,YAAM,QAAQ,MAAM,QAAQ;AAAA,QAC1B,MAAM,QAAQ,QAAQ,OAAO,GAAG,IAAI;AAAA,QACpC;AAAA,MACF;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,sBAAgB,MAAM,KAAK;AAC3B,YAAM;AAAA,IACR,UAAE;AACA,oBAAc,IAAI;AAAA,IACpB;AAAA,EACF;AACF;AAeA,SAAS,WACP,MACS;AACT,MAAI;AACF,WAAO,KAAK,YAAY,EAAE,YAAY;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBACP,MACA,OACM;AACN,QAAM,aACJ,iBAAiB,QACb,QACA,OAAO,UAAU,WACf,QACA,IAAI,MAAM,OAAO,KAAK,CAAC;AAC/B,QAAM,gBACJ,sBAAsB,QAAQ,WAAW,UAAU;AACrD,MAAI;AACF,SAAK,gBAAgB,UAAU;AAAA,EACjC,QAAQ;AAAA,EAER;AACA,MAAI;AACF,SAAK,UAAU,EAAE,MAAM,eAAe,OAAO,SAAS,cAAc,CAAC;AAAA,EACvE,QAAQ;AAAA,EAER;AACF;AAGA,SAAS,cACP,MACM;AACN,MAAI;AACF,SAAK,IAAI;AAAA,EACX,QAAQ;AAAA,EAER;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/async-context/index.ts"],"sourcesContent":["/**\n * Post-response async causality instrumentation for Glasstrace.\n *\n * Subpath: `@glasstrace/sdk/async-context`\n *\n * This module exposes {@link withAsyncCausality}, a continuation-\n * passing wrapper that captures the active OTel `SpanContext` at call\n * time and binds it to a callback. When the callback runs later\n * (Next.js `after()`, queue dispatchers, webhook fire-and-forget),\n * the wrapper opens a span linked to the originating trace via two\n * channels:\n *\n * - An OTel `Link` to the captured `SpanContext` — the OTel-native\n * pointer between two spans in different traces. Surfaces in\n * standard OTel-aware UIs (Jaeger, Honeycomb, etc.) as a\n * \"follows from\" relationship.\n * - A `glasstrace.causal.post_response_async` attribute carrying\n * the captured trace ID (32-char hex). Used by the product-side\n * trace-summary transform (per DISC-1539's product handoff) to\n * reconstruct ownership without resolving the Link. Two\n * companion booleans\n * (`glasstrace.causal.affects_http_status` and\n * `glasstrace.causal.affects_http_duration`) document that the\n * async work does NOT participate in the root request's outcome.\n *\n * Both channels are emitted together so the SDK is robust to\n * downstream transforms that resolve causality through either form.\n *\n * Edge-runtime safety\n * -------------------\n * The wrapper is included in the SDK's edge bundle\n * (`packages/sdk/src/edge-entry.ts`). Its closure imports only the\n * OTel API, the protocol constants, and the\n * `./optional-lifecycle.js` bridge — none of which reach into\n * `node:*` built-ins or the `process` global. The F003 closure scan\n * (`packages/sdk/scripts/check-edge-bundle.mjs`) enforces this on\n * every build.\n *\n * Strategy: continuation-passing, NOT global ALS propagation\n * ---------------------------------------------------------\n * Per the SDK-046 brief §2.3: ALS continuity across Next.js `after()`\n * is uncertain (the framework may schedule via `queueMicrotask`\n * (preserves ALS) or via cross-tick scheduling (drops ALS)). Relying\n * on ALS would couple the SDK to Next internals. Continuation-passing\n * makes the causality explicit — the user wraps the callback they\n * pass to `after()` / their queue, and the captured `SpanContext`\n * travels with the closure regardless of how the framework schedules\n * it.\n *\n * @module @glasstrace/sdk/async-context\n */\n\nimport {\n trace,\n context,\n SpanStatusCode,\n type AttributeValue,\n type SpanContext,\n} from \"@opentelemetry/api\";\nimport { GLASSTRACE_ATTRIBUTE_NAMES } from \"@glasstrace/protocol\";\nimport { tryEmitLifecycleEvent } from \"../optional-lifecycle.js\";\n\nconst ATTR = GLASSTRACE_ATTRIBUTE_NAMES;\n\n/**\n * Module-level OTel tracer name for the async-context subpath.\n * Resolves through the global `ProxyTracerProvider` so the wrapper\n * picks up whatever provider the SDK has registered. Re-resolved on\n * every call site rather than cached at module top level so test\n * harnesses can install a provider after this module is imported.\n * Mirrors the tRPC subpath at `packages/sdk/src/trpc/index.ts:128`.\n */\nconst TRACER_NAME = \"@glasstrace/sdk/async-context\";\n\n/**\n * The W3C-spec-defined invalid trace ID. The OTel API noop tracer\n * returns this value from `Span.spanContext().traceId`. We use this\n * to detect both (a) the SDK-not-registered case via the\n * noop-tracer probe and (b) the no-active-span case via the\n * captured `SpanContext`.\n */\nconst INVALID_TRACE_ID = \"00000000000000000000000000000000\";\n\n/**\n * Module-level once-flags for lifecycle events. Cleared via\n * {@link _resetForTesting}.\n */\nlet _skippedUninstalledEmitted = false;\nlet _noOriginatingContextEmitted = false;\n\n/**\n * INTERNAL — clears once-flags for unit tests; not part of the\n * public surface.\n */\nexport function _resetForTesting(): void {\n _skippedUninstalledEmitted = false;\n _noOriginatingContextEmitted = false;\n}\n\n/**\n * Options for {@link withAsyncCausality}.\n *\n * @example\n * ```ts\n * import { withAsyncCausality } from \"@glasstrace/sdk/async-context\";\n * import { after } from \"next/server\";\n *\n * export async function POST(req: Request) {\n * const result = await processRequest(req);\n * after(\n * withAsyncCausality(\n * { name: \"send-confirmation-email\" },\n * async () => sendEmail(result.userId),\n * ),\n * );\n * return Response.json({ ok: true });\n * }\n * ```\n */\nexport interface WithAsyncCausalityOptions {\n /**\n * Span name for the async work. Required, non-empty string. Used as\n * the OTel span name and appears in trace timelines. Names should\n * be stable across runs (e.g., \"send-confirmation-email\",\n * \"enqueue-webhook-dispatch\"); avoid embedding payload data in the\n * name.\n */\n name: string;\n /**\n * Optional attributes attached to the span before the wrapped\n * callback runs. Forwarded to OTel as-is via `span.setAttributes()`.\n * The SDK does not redact, sanitize, or scan values here — callers\n * MUST avoid placing tokens, credentials, or other sensitive data\n * in `attributes`.\n */\n attributes?: Record<string, AttributeValue>;\n}\n\n/**\n * Capture the active OTel `SpanContext` at call time, bind it to a\n * callback, and return a continuation that will emit a\n * causally-linked span when invoked.\n *\n * The returned continuation:\n *\n * 1. Detects the SDK's registration state. When the OTel API is\n * still on the noop tracer, runs the wrapped callback directly\n * and emits an `async:skipped_uninstalled` lifecycle event (at\n * most once per process). No span is opened.\n * 2. Otherwise opens a span named `options.name` as a NEW root\n * span (not parented to the captured context — `after()` /\n * queue dispatchers run outside the originating request's OTel\n * context, so the async work belongs to a separate trace).\n * 3. When a captured `SpanContext` exists with a valid trace ID,\n * attaches:\n * - an OTel `Link` to that `SpanContext` (the OTel-native\n * form),\n * - the `glasstrace.causal.post_response_async` attribute\n * carrying the trace ID (the transform-readable form),\n * - `glasstrace.causal.affects_http_status = false` and\n * `glasstrace.causal.affects_http_duration = false`\n * documenting that the async work does NOT participate in\n * the root request's outcome.\n * When no valid `SpanContext` was captured, none of these are\n * emitted (per SDK-046's \"missing or unknown evidence is\n * preferable to guessed evidence\" rule) and an\n * `async:no_originating_context` lifecycle event fires (at\n * most once per process).\n * 4. Awaits the wrapped callback.\n * 5. On a thrown error: normalizes the throwable; sets `ERROR`\n * status with `recordException`; rethrows the original error\n * verbatim.\n * 6. On a successful return: leaves status `UNSET`.\n * 7. Always ends the span.\n *\n * The continuation returns a Promise resolving to the callback's\n * return value (Promise-or-value semantics: a sync callback's value\n * is wrapped in `Promise.resolve()`).\n *\n * @param options - Span name and optional pre-start attributes.\n * @param fn - The async callback to run later. May be sync or async;\n * the wrapper always returns a Promise to give a consistent\n * continuation shape regardless of `fn`'s synchronicity.\n * @returns A continuation `() => Promise<T>` that, when invoked,\n * emits the causally-linked span and runs `fn`.\n *\n * @example Next.js after() — typical use\n * ```ts\n * import { withAsyncCausality } from \"@glasstrace/sdk/async-context\";\n * import { after } from \"next/server\";\n *\n * export async function POST(req: Request) {\n * const result = await processRequest(req);\n * after(\n * withAsyncCausality(\n * { name: \"send-confirmation-email\" },\n * async () => sendEmail(result.userId),\n * ),\n * );\n * return Response.json({ ok: true });\n * }\n * ```\n *\n * @example Queue dispatcher — capture before enqueue\n * ```ts\n * const dispatch = withAsyncCausality(\n * { name: \"process-webhook\" },\n * async () => handler(payload),\n * );\n * await queue.enqueue(dispatch);\n * ```\n */\nexport function withAsyncCausality<T>(\n options: WithAsyncCausalityOptions,\n fn: () => Promise<T> | T,\n): () => Promise<T> {\n if (typeof options.name !== \"string\" || options.name.length === 0) {\n throw new TypeError(\n \"withAsyncCausality: options.name must be a non-empty string\",\n );\n }\n if (typeof fn !== \"function\") {\n throw new TypeError(\"withAsyncCausality: fn must be a function\");\n }\n\n // Capture-time: snapshot the active SpanContext, if any. The\n // capture happens synchronously in the wrapper-construction call,\n // BEFORE the user passes the continuation to `after()` /\n // `queue.enqueue()`. Reading via `trace.getActiveSpan()` rather\n // than `context.active()` mirrors `captureCorrelationId()` at\n // `packages/sdk/src/correlation-id.ts:83` — the active-span API is\n // the public OTel surface for this.\n const capturedContext: SpanContext | undefined = (() => {\n try {\n const active = trace.getActiveSpan();\n if (!active) return undefined;\n const ctx = active.spanContext();\n // Reject the noop-tracer's invalid trace ID. Treating it as\n // captured would emit a Link to all-zeros, which is misleading.\n if (ctx.traceId === INVALID_TRACE_ID) return undefined;\n return ctx;\n } catch {\n return undefined;\n }\n })();\n\n return async (): Promise<T> => {\n const tracer = trace.getTracer(TRACER_NAME);\n // The async span is a NEW root: post-response work runs outside\n // the originating request's OTel context, so parenting to the\n // captured context would put two unrelated runtime executions in\n // the same trace tree. Causality is communicated via the Link +\n // attribute pair instead.\n //\n // Defensive wrap around `tracer.startSpan` itself. OTel's noop\n // tracer never throws; a real provider's startSpan could throw\n // under a misbehaving custom processor in coexistence. If the\n // tracer call throws, instrumentation must not break the user's\n // continuation — fall back to running fn() directly.\n let span: ReturnType<typeof tracer.startSpan>;\n try {\n span = tracer.startSpan(options.name, {\n root: true,\n links:\n capturedContext !== undefined\n ? [{ context: capturedContext }]\n : undefined,\n });\n } catch {\n return Promise.resolve(fn());\n }\n\n // SDK-not-registered fast path. The public `isRecording()` probe\n // returns `false` on noop spans without producing an exported\n // span; this avoids emitting a useless probe span on every\n // continuation when a real provider is registered.\n if (isNoopSpan(span)) {\n if (!_skippedUninstalledEmitted) {\n _skippedUninstalledEmitted = true;\n tryEmitLifecycleEvent(\"async:skipped_uninstalled\", {});\n }\n endSpanSafely(span);\n return Promise.resolve(fn());\n }\n\n if (capturedContext === undefined && !_noOriginatingContextEmitted) {\n _noOriginatingContextEmitted = true;\n tryEmitLifecycleEvent(\"async:no_originating_context\", {});\n }\n\n try {\n if (options.attributes) {\n span.setAttributes(options.attributes);\n }\n if (capturedContext !== undefined) {\n span.setAttribute(\n ATTR.CAUSAL_POST_RESPONSE_ASYNC,\n capturedContext.traceId,\n );\n span.setAttribute(ATTR.CAUSAL_AFFECTS_HTTP_STATUS, false);\n span.setAttribute(ATTR.CAUSAL_AFFECTS_HTTP_DURATION, false);\n }\n } catch {\n // Attribute failures are advisory; do not block fn().\n }\n\n try {\n // Activate the span as the current OTel context during fn()\n // execution so any nested spans (auto-instrumented OR manual)\n // are parented under this span. Without `context.with`, the\n // started span exists but is not in the active context, so\n // child spans become orphan roots (Copilot review 2026-05-08).\n // We use `context.with` rather than `tracer.startActiveSpan`\n // so the existing setup (Link/attribute application above)\n // can run pre-activation; the existing setup is small but\n // not trivial to reorder.\n const value = await context.with(\n trace.setSpan(context.active(), span),\n fn,\n );\n return value;\n } catch (error) {\n recordSpanError(span, error);\n throw error;\n } finally {\n endSpanSafely(span);\n }\n };\n}\n\n/**\n * Type guard for OTel noop spans. Mirrors the same check in\n * `../middleware/index.ts`. The OTel API's noop tracer\n * (`@opentelemetry/api`'s `NonRecordingSpan`) returns the all-zeros\n * sentinel trace ID `00000000000000000000000000000000` from\n * `Span.spanContext().traceId`. Real SDK-emitted spans — including\n * spans that a sampler chose to DROP (which legitimately return\n * `isRecording() === false`) — return a valid 32-char hex trace ID\n * because the SDK assigns a trace ID before sampler invocation for\n * propagation purposes. Using the SpanContext discriminator keeps\n * the SDK-not-registered fast path from misfiring under normal head\n * sampling configurations (Copilot review 2026-05-08).\n */\nfunction isNoopSpan(\n span: ReturnType<ReturnType<typeof trace.getTracer>[\"startSpan\"]>,\n): boolean {\n try {\n return span.spanContext().traceId === INVALID_TRACE_ID;\n } catch {\n return false;\n }\n}\n\n/**\n * See {@link ../middleware/index.ts} — duplicated here rather than\n * shared because the OTel `Span` type is structural and importing\n * a helper from a sibling module would force the modules to share a\n * deeper dependency. Both copies are exactly two non-throwing\n * `try`/`catch` blocks; the duplication is intentional and trivial.\n */\nfunction recordSpanError(\n span: ReturnType<ReturnType<typeof trace.getTracer>[\"startSpan\"]>,\n error: unknown,\n): void {\n const normalized: Error | string =\n error instanceof Error\n ? error\n : typeof error === \"string\"\n ? error\n : new Error(String(error));\n const statusMessage =\n normalized instanceof Error ? normalized.message : normalized;\n try {\n span.recordException(normalized);\n } catch {\n /* swallow */\n }\n try {\n span.setStatus({ code: SpanStatusCode.ERROR, message: statusMessage });\n } catch {\n /* swallow */\n }\n}\n\n/** See {@link ../middleware/index.ts}. */\nfunction endSpanSafely(\n span: ReturnType<ReturnType<typeof trace.getTracer>[\"startSpan\"]>,\n): void {\n try {\n span.end();\n } catch {\n /* swallow */\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA8DA,IAAM,OAAO;AAUb,IAAM,cAAc;AASpB,IAAM,mBAAmB;AAMzB,IAAI,6BAA6B;AACjC,IAAI,+BAA+B;AAM5B,SAAS,mBAAyB;AACvC,+BAA6B;AAC7B,iCAA+B;AACjC;AAmHO,SAAS,mBACd,SACA,IACkB;AAClB,MAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,WAAW,GAAG;AACjE,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAM,IAAI,UAAU,2CAA2C;AAAA,EACjE;AASA,QAAM,mBAA4C,MAAM;AACtD,QAAI;AACF,YAAM,SAAS,MAAM,cAAc;AACnC,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,MAAM,OAAO,YAAY;AAG/B,UAAI,IAAI,YAAY,iBAAkB,QAAO;AAC7C,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AAEH,SAAO,YAAwB;AAC7B,UAAM,SAAS,MAAM,UAAU,WAAW;AAY1C,QAAI;AACJ,QAAI;AACF,aAAO,OAAO,UAAU,QAAQ,MAAM;AAAA,QACpC,MAAM;AAAA,QACN,OACE,oBAAoB,SAChB,CAAC,EAAE,SAAS,gBAAgB,CAAC,IAC7B;AAAA,MACR,CAAC;AAAA,IACH,QAAQ;AACN,aAAO,QAAQ,QAAQ,GAAG,CAAC;AAAA,IAC7B;AAMA,QAAI,WAAW,IAAI,GAAG;AACpB,UAAI,CAAC,4BAA4B;AAC/B,qCAA6B;AAC7B,8BAAsB,6BAA6B,CAAC,CAAC;AAAA,MACvD;AACA,oBAAc,IAAI;AAClB,aAAO,QAAQ,QAAQ,GAAG,CAAC;AAAA,IAC7B;AAEA,QAAI,oBAAoB,UAAa,CAAC,8BAA8B;AAClE,qCAA+B;AAC/B,4BAAsB,gCAAgC,CAAC,CAAC;AAAA,IAC1D;AAEA,QAAI;AACF,UAAI,QAAQ,YAAY;AACtB,aAAK,cAAc,QAAQ,UAAU;AAAA,MACvC;AACA,UAAI,oBAAoB,QAAW;AACjC,aAAK;AAAA,UACH,KAAK;AAAA,UACL,gBAAgB;AAAA,QAClB;AACA,aAAK,aAAa,KAAK,4BAA4B,KAAK;AACxD,aAAK,aAAa,KAAK,8BAA8B,KAAK;AAAA,MAC5D;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI;AAUF,YAAM,QAAQ,MAAM,QAAQ;AAAA,QAC1B,MAAM,QAAQ,QAAQ,OAAO,GAAG,IAAI;AAAA,QACpC;AAAA,MACF;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,sBAAgB,MAAM,KAAK;AAC3B,YAAM;AAAA,IACR,UAAE;AACA,oBAAc,IAAI;AAAA,IACpB;AAAA,EACF;AACF;AAeA,SAAS,WACP,MACS;AACT,MAAI;AACF,WAAO,KAAK,YAAY,EAAE,YAAY;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBACP,MACA,OACM;AACN,QAAM,aACJ,iBAAiB,QACb,QACA,OAAO,UAAU,WACf,QACA,IAAI,MAAM,OAAO,KAAK,CAAC;AAC/B,QAAM,gBACJ,sBAAsB,QAAQ,WAAW,UAAU;AACrD,MAAI;AACF,SAAK,gBAAgB,UAAU;AAAA,EACjC,QAAQ;AAAA,EAER;AACA,MAAI;AACF,SAAK,UAAU,EAAE,MAAM,eAAe,OAAO,SAAS,cAAc,CAAC;AAAA,EACvE,QAAQ;AAAA,EAER;AACF;AAGA,SAAS,cACP,MACM;AACN,MAAI;AACF,SAAK,IAAI;AAAA,EACX,QAAQ;AAAA,EAER;AACF;","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  PresignedUploadResponseSchema,
6
6
  SourceMapManifestResponseSchema,
7
7
  SourceMapUploadResponseSchema
8
- } from "./chunk-JHUNLPSS.js";
8
+ } from "./chunk-6RKS3DNA.js";
9
9
 
10
10
  // src/source-map-uploader.ts
11
11
  import * as fs from "node:fs/promises";
@@ -331,4 +331,4 @@ export {
331
331
  uploadSourceMapsPresigned,
332
332
  uploadSourceMapsAuto
333
333
  };
334
- //# sourceMappingURL=chunk-M6EWJCAT.js.map
334
+ //# sourceMappingURL=chunk-QVTONMVZ.js.map
@@ -2,7 +2,7 @@ import {
2
2
  AnonApiKeySchema,
3
3
  DevApiKeySchema,
4
4
  createAnonApiKey
5
- } from "./chunk-JHUNLPSS.js";
5
+ } from "./chunk-6RKS3DNA.js";
6
6
  import {
7
7
  __require
8
8
  } from "./chunk-NSBPE2FW.js";
@@ -659,4 +659,4 @@ export {
659
659
  writeMcpMarker,
660
660
  refreshGenericMcpConfigAtRuntime
661
661
  };
662
- //# sourceMappingURL=chunk-DKV53A2C.js.map
662
+ //# sourceMappingURL=chunk-RL43PU2X.js.map
@@ -5,10 +5,10 @@ import {
5
5
  isDevApiKey,
6
6
  readEnvLocalApiKey,
7
7
  writeAndFsyncTempSync
8
- } from "./chunk-DKV53A2C.js";
8
+ } from "./chunk-RL43PU2X.js";
9
9
  import {
10
10
  AnonApiKeySchema
11
- } from "./chunk-JHUNLPSS.js";
11
+ } from "./chunk-6RKS3DNA.js";
12
12
  import {
13
13
  NEXT_CONFIG_NAMES
14
14
  } from "./chunk-NB7GJE4S.js";
@@ -917,4 +917,4 @@ export {
917
917
  writeShutdownMarker,
918
918
  runUninit
919
919
  };
920
- //# sourceMappingURL=chunk-GWIEUBFR.js.map
920
+ //# sourceMappingURL=chunk-UMGZJYC4.js.map
@@ -2,12 +2,12 @@ import {
2
2
  atomicWriteFile,
3
3
  refreshGenericMcpConfigAtRuntime,
4
4
  resolveEffectiveMcpCredential
5
- } from "./chunk-DKV53A2C.js";
5
+ } from "./chunk-RL43PU2X.js";
6
6
  import {
7
7
  DEFAULT_CAPTURE_CONFIG,
8
8
  SdkCachedConfigSchema,
9
9
  SdkInitResponseSchema
10
- } from "./chunk-JHUNLPSS.js";
10
+ } from "./chunk-6RKS3DNA.js";
11
11
  import {
12
12
  __require
13
13
  } from "./chunk-NSBPE2FW.js";
@@ -692,4 +692,4 @@ export {
692
692
  didLastInitSucceed,
693
693
  verifyInitReachable
694
694
  };
695
- //# sourceMappingURL=chunk-QXITSNYM.js.map
695
+ //# sourceMappingURL=chunk-XG6WR2KS.js.map
package/dist/cli/init.cjs CHANGED
@@ -16994,7 +16994,7 @@ async function mcpAdd(options) {
16994
16994
  const bearer = resolved.effective.key;
16995
16995
  for (const agent of targetAgents) {
16996
16996
  const name = formatAgentName(agent.name);
16997
- const sdkVersion = true ? "1.9.0" : "0.0.0-dev";
16997
+ const sdkVersion = true ? "1.10.0" : "0.0.0-dev";
16998
16998
  if (agent.name !== "generic") {
16999
16999
  const cliSuccess = await registerViaCli(agent, bearer);
17000
17000
  if (cliSuccess) {
@@ -17262,7 +17262,7 @@ async function runUpgradeInstructions(options) {
17262
17262
  );
17263
17263
  return { exitCode: 1, refreshed, skipped, warnings, errors };
17264
17264
  }
17265
- const sdkVersion = true ? "1.9.0" : "0.0.0-dev";
17265
+ const sdkVersion = true ? "1.10.0" : "0.0.0-dev";
17266
17266
  for (const agent of agents) {
17267
17267
  if (agent.infoFilePath === null) {
17268
17268
  continue;
@@ -18902,7 +18902,7 @@ Then add this as the first statement in your register() function:
18902
18902
  }
18903
18903
  anyConfigWritten = true;
18904
18904
  anyConfigRewrittenWithBearer = true;
18905
- const sdkVersionForInject = true ? "1.9.0" : "0.0.0-dev";
18905
+ const sdkVersionForInject = true ? "1.10.0" : "0.0.0-dev";
18906
18906
  const infoContent = generateInfoSection(
18907
18907
  agent,
18908
18908
  MCP_ENDPOINT,
@@ -19008,7 +19008,7 @@ async function verifyAnonKeyRegistration(projectRoot) {
19008
19008
  }
19009
19009
  const baseConfig = resolveConfig({ apiKey: devKey });
19010
19010
  const config2 = { ...baseConfig, apiKey: devKey };
19011
- const sdkVersion = true ? "1.9.0" : "0.0.0-dev";
19011
+ const sdkVersion = true ? "1.10.0" : "0.0.0-dev";
19012
19012
  const result = await verifyInitReachable(config2, anonKey, sdkVersion);
19013
19013
  if (result.ok) {
19014
19014
  return { outcome: "verified" };