@bitfab/sdk 0.51.0 → 0.52.1
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.
- package/dist/{chunk-RGC7IEOI.js → chunk-6DIR65JW.js} +63 -3
- package/dist/chunk-6DIR65JW.js.map +1 -0
- package/dist/{chunk-DNYZJLNY.js → chunk-A5UGSRMK.js} +2 -2
- package/dist/{chunk-4CLJF2DZ.js → chunk-UQLT25PG.js} +40 -3
- package/dist/chunk-UQLT25PG.js.map +1 -0
- package/dist/{chunk-VXD27AJ3.js → chunk-XAT6WLCS.js} +41 -19
- package/dist/chunk-XAT6WLCS.js.map +1 -0
- package/dist/{http-HLRWCSFF.js → http-B2AK43BN.js} +2 -2
- package/dist/{http-AKFJONE7.js → http-RI4YCAS2.js} +2 -2
- package/dist/index.cjs +100 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +3 -3
- package/dist/node.cjs +100 -15
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +3 -3
- package/dist/{replay-ISXRCXUP.js → replay-24F3F5RD.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-4CLJF2DZ.js.map +0 -1
- package/dist/chunk-RGC7IEOI.js.map +0 -1
- package/dist/chunk-VXD27AJ3.js.map +0 -1
- /package/dist/{chunk-DNYZJLNY.js.map → chunk-A5UGSRMK.js.map} +0 -0
- /package/dist/{http-AKFJONE7.js.map → http-B2AK43BN.js.map} +0 -0
- /package/dist/{http-HLRWCSFF.js.map → http-RI4YCAS2.js.map} +0 -0
- /package/dist/{replay-ISXRCXUP.js.map → replay-24F3F5RD.js.map} +0 -0
|
@@ -99,7 +99,7 @@ function encodeRequestBody(body) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// src/version.generated.ts
|
|
102
|
-
var __version__ = "0.
|
|
102
|
+
var __version__ = "0.52.1";
|
|
103
103
|
var __packageName__ = "@bitfab/sdk";
|
|
104
104
|
|
|
105
105
|
// src/constants.ts
|
|
@@ -405,6 +405,62 @@ function encodePayloadBody(payload) {
|
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
// src/traceMetadata.ts
|
|
409
|
+
var callerMetadata = /* @__PURE__ */ new Map();
|
|
410
|
+
var derivedMetadata = /* @__PURE__ */ new Map();
|
|
411
|
+
function recordCallerTraceMetadata(traceId, metadata) {
|
|
412
|
+
if (typeof traceId !== "string" || traceId === "") {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (typeof metadata !== "object" || metadata === null) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (Object.keys(metadata).length === 0) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
callerMetadata.set(traceId, { ...callerMetadata.get(traceId), ...metadata });
|
|
422
|
+
}
|
|
423
|
+
function callerTraceMetadata(traceId) {
|
|
424
|
+
const recorded = callerMetadata.get(traceId);
|
|
425
|
+
return recorded ? { ...recorded } : void 0;
|
|
426
|
+
}
|
|
427
|
+
function forgetTraceMetadata(traceId) {
|
|
428
|
+
callerMetadata.delete(traceId);
|
|
429
|
+
derivedMetadata.delete(traceId);
|
|
430
|
+
}
|
|
431
|
+
function mergeCallerMetadataIntoTracePayload(payload) {
|
|
432
|
+
const externalTrace = payload.externalTrace;
|
|
433
|
+
if (typeof externalTrace !== "object" || externalTrace === null) {
|
|
434
|
+
return payload;
|
|
435
|
+
}
|
|
436
|
+
const external = externalTrace;
|
|
437
|
+
const traceId = typeof payload.id === "string" && payload.id !== "" ? payload.id : external.id;
|
|
438
|
+
if (typeof traceId !== "string" || traceId === "") {
|
|
439
|
+
return payload;
|
|
440
|
+
}
|
|
441
|
+
const caller = callerMetadata.get(traceId);
|
|
442
|
+
if (!caller) {
|
|
443
|
+
return payload;
|
|
444
|
+
}
|
|
445
|
+
const derived = derivedMetadata.get(traceId) ?? {};
|
|
446
|
+
const exported = external.metadata;
|
|
447
|
+
if (typeof exported === "object" && exported !== null) {
|
|
448
|
+
Object.assign(derived, exported);
|
|
449
|
+
}
|
|
450
|
+
derivedMetadata.set(traceId, derived);
|
|
451
|
+
const shadowed = Object.keys(derived).filter((key) => key in caller && caller[key] !== derived[key]).sort();
|
|
452
|
+
if (shadowed.length > 0) {
|
|
453
|
+
warnOnce(
|
|
454
|
+
`trace-metadata-shadowed:${shadowed.join(",")}`,
|
|
455
|
+
`trace metadata key(s) ${shadowed.join(", ")} were set both by the caller and by an integration's own trace export; the caller's value is the one kept on the trace.`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
...payload,
|
|
460
|
+
externalTrace: { ...external, metadata: { ...derived, ...caller } }
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
408
464
|
// src/otel.ts
|
|
409
465
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
410
466
|
import {
|
|
@@ -1639,7 +1695,8 @@ var HttpClient = class {
|
|
|
1639
1695
|
* {@link HttpClient.sendExternalSpan}; replay confirms persistence with the
|
|
1640
1696
|
* server-authoritative barrier in `replay.ts`, not by awaiting this call.
|
|
1641
1697
|
*/
|
|
1642
|
-
sendExternalTrace(
|
|
1698
|
+
sendExternalTrace(rawPayload) {
|
|
1699
|
+
const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
|
|
1643
1700
|
this.getTraceTransport()?.submit(
|
|
1644
1701
|
"external_trace",
|
|
1645
1702
|
{
|
|
@@ -1874,10 +1931,13 @@ export {
|
|
|
1874
1931
|
MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES,
|
|
1875
1932
|
warnOnce,
|
|
1876
1933
|
serializePayloadBody,
|
|
1934
|
+
recordCallerTraceMetadata,
|
|
1935
|
+
callerTraceMetadata,
|
|
1936
|
+
forgetTraceMetadata,
|
|
1877
1937
|
awaitOnExit,
|
|
1878
1938
|
flushTraces,
|
|
1879
1939
|
awaitPendingRequests,
|
|
1880
1940
|
parseRetryAfterMs,
|
|
1881
1941
|
HttpClient
|
|
1882
1942
|
};
|
|
1883
|
-
//# sourceMappingURL=chunk-
|
|
1943
|
+
//# sourceMappingURL=chunk-6DIR65JW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/readEnv.ts","../src/compress.ts","../src/version.generated.ts","../src/constants.ts","../src/errors.ts","../src/replayContext.ts","../src/payloadBudget.ts","../src/warnOnce.ts","../src/serializePayload.ts","../src/traceMetadata.ts","../src/otel.ts","../src/transportTypes.ts","../src/unrefTimer.ts","../src/transport.ts","../src/http.ts"],"sourcesContent":["/**\n * Read an environment variable without throwing in non-Node runtimes\n * (browsers, edge workers) where `process` is absent. The SDK ships to\n * browsers, so this must never assume `process` exists.\n */\nexport function readEnv(name: string): string | undefined {\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[name]\n }\n return undefined\n}\n","import { readEnv } from \"./readEnv.js\"\n\nconst DISABLE_COMPRESSION_ENV = \"BITFAB_DISABLE_COMPRESSION\"\n\n/**\n * Below this, compressing costs more than the saved bytes are worth, so small\n * requests (function lookups, replay status polls, single-span batches) ride\n * uncompressed.\n */\nconst MIN_COMPRESSED_BYTES = 8_192\n\nexport interface EncodedRequestBody {\n body: string | ArrayBuffer\n contentEncoding?: \"gzip\"\n rawBytes: number\n wireBytes: number\n}\n\n/**\n * Node's gzip, loaded dynamically so browser bundlers never have to resolve\n * `node:zlib`. Deliberately the async form: it runs on libuv's threadpool\n * rather than the event loop. Measured on 8 concurrent 1 MB bodies, the\n * synchronous form stalled the loop for 326ms and the async form for 1ms,\n * while also finishing 3.8x sooner because the threadpool compresses in\n * parallel. A tracing SDK must not block its host's event loop.\n */\nlet gzipNode: ((data: Uint8Array) => Promise<Uint8Array>) | undefined\n\ntype NodeZlib = {\n gzip: (\n data: Uint8Array,\n callback: (error: Error | null, result: Uint8Array) => void,\n ) => void\n}\n\nexport const _nodeGzipReady: Promise<void> = (\n typeof process !== \"undefined\" && process.versions?.node\n ? // The join trick hides \"node:zlib\" from static analysis so bundlers that\n // ban Node.js built-ins don't fail at build time. webpackIgnore tells\n // webpack/turbopack to emit a native import() so Node.js can resolve the\n // module at runtime. Same pattern as `asyncStorage.ts`.\n import(\n /* webpackIgnore: true */\n [\"node\", \"zlib\"].join(\":\")\n )\n .then(({ gzip }: NodeZlib) => {\n gzipNode = (data) =>\n new Promise((resolve, reject) => {\n gzip(data, (error, result) => {\n if (error) {\n reject(error)\n } else {\n resolve(result)\n }\n })\n })\n })\n .catch(() => {})\n : Promise.resolve()\n).then(() => {})\n\n/** Test seam for exercising the browser path on Node. */\nexport function _setNodeGzip(\n impl: ((data: Uint8Array) => Promise<Uint8Array>) | undefined,\n): void {\n gzipNode = impl\n}\n\nfunction toArrayBuffer(view: Uint8Array): ArrayBuffer {\n return view.buffer.slice(\n view.byteOffset,\n view.byteOffset + view.byteLength,\n ) as ArrayBuffer\n}\n\nfunction compressedRequest(\n body: string,\n rawBytes: number,\n compressed: Uint8Array | ArrayBuffer,\n): EncodedRequestBody {\n if (compressed.byteLength >= rawBytes) {\n return { body, rawBytes, wireBytes: rawBytes }\n }\n return {\n body:\n compressed instanceof Uint8Array ? toArrayBuffer(compressed) : compressed,\n contentEncoding: \"gzip\",\n rawBytes,\n wireBytes: compressed.byteLength,\n }\n}\n\nasync function gzipViaStream(bytes: Uint8Array): Promise<ArrayBuffer> {\n const stream = new Blob([bytes as BlobPart])\n .stream()\n .pipeThrough(new CompressionStream(\"gzip\"))\n return await new Response(stream).arrayBuffer()\n}\n\n/**\n * Compression is best-effort: any failure sends the original body rather than\n * dropping the span. `CompressionStream` is absent on older browsers, so its\n * presence is checked rather than assumed.\n *\n * Returns a plain value (not a promise) whenever it can, so a request still\n * reaches `fetch` in the caller's tick rather than one microtask later. Callers\n * await the union.\n */\nexport function encodeRequestBody(\n body: string,\n): EncodedRequestBody | Promise<EncodedRequestBody> {\n if (readEnv(DISABLE_COMPRESSION_ENV)) {\n const rawBytes = new TextEncoder().encode(body).byteLength\n return { body, rawBytes, wireBytes: rawBytes }\n }\n const bytes = new TextEncoder().encode(body)\n if (bytes.byteLength < MIN_COMPRESSED_BYTES) {\n return {\n body,\n rawBytes: bytes.byteLength,\n wireBytes: bytes.byteLength,\n }\n }\n if (gzipNode) {\n return gzipNode(bytes).then(\n (compressed) => compressedRequest(body, bytes.byteLength, compressed),\n () => ({\n body,\n rawBytes: bytes.byteLength,\n wireBytes: bytes.byteLength,\n }),\n )\n }\n if (typeof CompressionStream === \"undefined\") {\n return {\n body,\n rawBytes: bytes.byteLength,\n wireBytes: bytes.byteLength,\n }\n }\n return gzipViaStream(bytes).then(\n (compressed) => compressedRequest(body, bytes.byteLength, compressed),\n () => ({\n body,\n rawBytes: bytes.byteLength,\n wireBytes: bytes.byteLength,\n }),\n )\n}\n","/**\n * Auto-generated package metadata.\n * This file is generated by scripts/generate-version.ts during build.\n * DO NOT EDIT MANUALLY.\n */\n\n/**\n * SDK version from package.json (injected at build time)\n */\nexport const __version__ = \"0.52.1\"\n\n/**\n * Published npm package name from package.json (injected at build time)\n */\nexport const __packageName__ = \"@bitfab/sdk\"\n","/**\n * Constants for the Bitfab SDK.\n */\n\n/**\n * Default service URL for Bitfab API.\n */\nexport const DEFAULT_SERVICE_URL = \"https://bitfab.ai\"\n\n/**\n * SDK version from package.json (injected at build time)\n *\n * The version is generated at build time by scripts/generate-version.ts\n * to ensure compatibility with both Node.js and browser environments.\n */\nexport { __packageName__, __version__ } from \"./version.generated.js\"\n","/**\n * Shared error type for Bitfab SDK runtime errors. Lives in its own\n * module to avoid import cycles between `http.ts` and modules that need\n * to throw structured errors (e.g. `dbSnapshot.ts` validation).\n */\n\nexport class BitfabError extends Error {\n constructor(\n message: string,\n public readonly url?: string,\n /**\n * HTTP status the request failed with, when it failed with one. The\n * transport's retry policy needs the code itself (retry 408/425/429/5xx,\n * never a 4xx the server will reject again), which a formatted message\n * cannot supply. Absent for network failures and non-HTTP errors.\n */\n public readonly status?: number,\n /**\n * `Retry-After` in milliseconds, when the server sent one. A 429 or 503\n * carries the server's own instruction about when to come back; retrying\n * on our own schedule ignores it and keeps the pressure on.\n */\n public readonly retryAfterMs?: number,\n ) {\n super(message)\n this.name = \"BitfabError\"\n }\n}\n\nexport class MixedTracingError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"MixedTracingError\"\n }\n}\n","/**\n * Replay context propagation via AsyncLocalStorage.\n *\n * When set, the withSpan wrapper injects testRunId into the span payload\n * so that new spans created during replay are linked to the test run.\n * Optionally carries a mock tree so child spans can return historical\n * outputs instead of executing.\n */\n\nimport {\n type AsyncLocalStorageLike,\n asyncStorageReady,\n createAsyncLocalStorage,\n} from \"./asyncStorage.js\"\nimport type { MockOverride } from \"./mockOverride.js\"\n\n/**\n * A single span entry in the mock tree.\n *\n * Under the eager path (`mock: \"all\"`) `output`/`outputMeta` are populated\n * inline, even when overrides are present. Under a non-`all` path that needs a\n * tree (`marked`, or `none` with overrides), they are absent and the recorded\n * output is fetched on demand via `externalSpanId` - see\n * {@link ReplayContext.fetchSpanOutput}.\n */\nexport interface MockSpan {\n sourceSpanId: string\n /** Row id accepted by `getExternalSpan`, for the lazy per-span output fetch. */\n externalSpanId?: string\n output?: unknown\n outputMeta?: unknown\n}\n\n/**\n * Per-item DB branch resolved by the Bitfab service from the source\n * trace's `dbSnapshotRef`. Carried on the replay context so that\n * customer code reads `databaseUrl` through `getCurrentReplayBranch()`, and so\n * the process-isolated replay runner can materialize it into a `.env`\n * overlay file before customer code initializes its DB client.\n *\n * `neonBranchId` is the literal Neon branch id; passing it to\n * `releaseDbBranchLease` deletes that branch.\n */\nexport interface DbBranchLease {\n neonBranchId: string\n /** Env var name the customer's app reads, e.g. \"DATABASE_URL\". */\n envKey: string\n databaseUrl: string\n expiresAt: string\n /**\n * The instant the branch was pinned to (the source trace's wall clock).\n * Echoed back in `db_snapshot_usage` on the replayed trace's completion.\n */\n snapshotTimestamp?: string\n providerConsoleUrl?: string\n readOnly?: boolean\n /**\n * The branch's region, e.g. `aws-us-east-1`. A compute runs in its project's\n * region, so a runner elsewhere pays that round trip on every query.\n */\n region?: string\n}\n\n/**\n * How long each phase of provisioning one replay branch took, measured\n * server-side. A runner in another region sees these plus its own round trip.\n *\n * Durations, not instants: an instant is approximately `startedAt` plus the\n * running sum, and per-phase wall-clock stamps would make clock skew between\n * the server and your runner look like latency. Approximately, because\n * `totalMs` is the resolve's true wall time and covers a little work no phase\n * owns, so the phases account for it without summing to it exactly.\n *\n * `startedAt` and `totalMs` are always present. The phases are optional\n * because a failed resolve reports only the ones it reached, and `totalMs` is\n * then time-to-failure. On success every phase is present except `warmupMs`,\n * which is absent when no warm-up SQL was supplied.\n */\nexport interface DbBranchTimings {\n /** When the resolve began, ISO. */\n startedAt: string\n /** Resolving the project, plus its retention and region reads. */\n projectResolveMs?: number\n /** Creating the branch, through its provider operations reaching terminal. */\n branchCreateMs?: number\n /** Resolving the connection URI. 0 when the provider returns one inline. */\n connectionUriMs?: number\n /** The compute accepting a connection. */\n computeConnectMs?: number\n /** The branch answering a readiness query. */\n baseProbeMs?: number\n /** Your warm-up SQL. Absent when you supplied none. */\n warmupMs?: number\n /** The whole resolve, or time-to-failure when it threw. */\n totalMs: number\n}\n\n/**\n * Wire shape of the caller's `ReplayOptions.dbBranch`, sent to\n * `/api/sdk/replay/start` and applied per lease.\n */\nexport interface DbBranchSettings {\n minCu?: number\n maxCu?: number\n warmupSql?: string\n}\n\n/**\n * Pre-built lookup table of historical span outputs.\n * Keys are `${traceFunctionKey}:${spanName}:${callIndex}` so that repeated\n * calls with the same (key, name) are matched by call order, but spans\n * sharing only the traceFunctionKey (different name) do not collide.\n */\nexport interface MockTree {\n spans: Map<string, MockSpan>\n}\n\nexport interface ReplayContext {\n testRunId: string\n traceId?: string\n inputSourceSpanId?: string\n /**\n * External trace ID from `external_traces.id`. Used for span-chain\n * lookup against the source platform's trace tree (Braintrust, etc.).\n * NOT the same as the Bitfab `traceId` - see `sourceBitfabTraceId`.\n */\n inputSourceTraceId?: string\n /**\n * The Bitfab `traces.id` of the historical trace that produced this\n * replay item's input. This is what customer-facing surfaces (e.g.\n * `ReplayBranch.traceId`) should expose, since it's the ID the\n * customer sees in the Bitfab dashboard.\n */\n sourceBitfabTraceId?: string\n replayAttempt?: number\n mockTree?: MockTree\n callCounters?: Map<string, number>\n mockStrategy?: \"none\" | \"all\" | \"marked\"\n /**\n * Resolved override chain for this replay, per-call overrides first then\n * registered ones (first matcher wins). Empty/absent when no overrides apply.\n */\n mockOverrides?: MockOverride[]\n /**\n * Memoized lazy fetch of a span's recorded output (deserialized), keyed by\n * `externalSpanId`. Present ONLY on a non-`all` path that needs a tree\n * (`marked`, or `none` with overrides); absent under `mock: \"all\"`, where\n * outputs are inline even when overrides are present. Its presence is the\n * signal that outputs must be fetched rather than read inline.\n */\n fetchSpanOutput?: (externalSpanId: string) => Promise<unknown>\n dbBranchLease?: DbBranchLease\n /**\n * Server-measured provisioning timings for this item's branch, echoed back\n * on the trace completion so the trace records what it cost to set up. Kept\n * off `ReplayBranch`: customer code reads that mid-replay to reach the\n * branch, and provisioning latency is a property of the run, not of the\n * connection.\n */\n dbBranchTimings?: DbBranchTimings\n /**\n * Set to true by `ReplayBranch` the first time customer code actually\n * obtains `databaseUrl` for this item. Reported on the trace completion inside\n * `db_snapshot_usage` so the server can distinguish \"branch was\n * provisioned and exposed\" from \"branch URL was actually consumed\".\n * Only an explicit `databaseUrl` read may set it. A path that hands the URL\n * over by other means (e.g. a process-isolated runner writing an env\n * overlay) must leave it alone: setting it there would make every such\n * replay report `accessed` for free, and the flag would stop separating\n * \"branch was used\" from \"branch was offered\".\n */\n dbSnapshotAccessed?: boolean\n}\n\nlet replayContextStorage: AsyncLocalStorageLike<ReplayContext | null> | null =\n null\nconst REPLAY_CONTEXT_STORAGE_SYMBOL = Symbol.for(\"bitfab.replayContextStorage\")\n\nexport const replayContextReady: Promise<void> = asyncStorageReady.then(() => {\n const shared = globalThis as typeof globalThis & Record<symbol, unknown>\n const existing = shared[REPLAY_CONTEXT_STORAGE_SYMBOL] as\n | AsyncLocalStorageLike<ReplayContext | null>\n | undefined\n if (existing) {\n replayContextStorage = existing\n return\n }\n const created = createAsyncLocalStorage<ReplayContext | null>()\n if (created) {\n shared[REPLAY_CONTEXT_STORAGE_SYMBOL] = created\n replayContextStorage = created\n }\n})\n\n/** Get the current replay context, if any. */\nexport function getReplayContext(): ReplayContext | null {\n return replayContextStorage?.getStore() ?? null\n}\n\n/** Run a function within a replay context. */\nexport function runWithReplayContext<T>(ctx: ReplayContext, fn: () => T): T {\n if (replayContextStorage) {\n return replayContextStorage.run(ctx, fn)\n }\n return fn()\n}\n","/**\n * The ceiling on a span's encoded carrier, and the trimming that enforces it.\n *\n * A span's whole payload (input, output, contexts, prompt, metadata) ships as\n * a single `bitfab.payload` string attribute, and the exporter drops any\n * carrier that exceeds the per-request byte ceiling outright rather than\n * trimming it. Capping each value on its own cannot prevent that: two values\n * that each fit can still add up to an undeliverable span. So the budget is\n * enforced on the whole span, and an oversized one ships with its largest\n * fields stubbed instead of vanishing.\n *\n * The budget is measured on the *carrier* (the payload re-escaped into the\n * OTLP attribute), not on the payload body, because the carrier is what the\n * exporter weighs. Bounding the body instead leaves escape-heavy content to\n * blow the request ceiling anyway: a body of escaped JSON, Windows paths, or\n * regexes is nearly all backslashes, and every one of them doubles. Measured\n * on a body sized exactly to a 2.4 MB cap, prose produced a 2.4 MB carrier but\n * backslash-dense content produced 4.8 MB, which the exporter dropped.\n *\n * The normal 2.8 MB fallback leaves room beneath the 3 MB wire target. Trace\n * transport may first preserve a carrier up to 7.8 MB when its single-span\n * request compresses below that wire target and remains below ingress's 8 MB\n * decompressed ceiling.\n */\nexport const MAX_SPAN_CARRIER_BYTES = 2_800_000\n\n/**\n * A larger carrier may still fit when its single-span request is compressed.\n * This leaves 200 kB beneath ingress's 8 MB decompressed-body ceiling for the\n * OTLP span and request envelopes.\n */\nexport const MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES = 7_800_000\n\nconst textEncoder =\n typeof TextEncoder !== \"undefined\" ? new TextEncoder() : null\n\nexport function byteLength(value: string): number {\n return textEncoder ? textEncoder.encode(value).length : value.length\n}\n\n/**\n * The byte length `body` occupies once re-escaped as a JSON string value.\n *\n * `body` is itself JSON text, so the first encode already replaced every\n * control character with a `\\uXXXX` sequence, leaving only `\"` and `\\` to\n * escape at one extra byte each.\n *\n * Counts UTF-8 width and escapes in the same pass and allocates nothing.\n * `TextEncoder.encode().length` would be the obvious way to get the byte count,\n * but it copies the entire body into a fresh array just to read its length,\n * which on a multi-megabyte span costs more than producing the body did.\n */\nexport function carrierByteLength(body: string): number {\n return carrierBytesOf(textEncoder ? textEncoder.encode(body) : null, body)\n}\n\n/**\n * Counts escapes over the UTF-8 bytes rather than the UTF-16 string. Bytes\n * `0x22` and `0x5c` are unambiguous there (a multi-byte sequence never uses a\n * byte below `0x80`), so a flat byte scan is exact, and it reuses the array the\n * byte count already had to produce instead of walking the string a second\n * time. Measured ~2x faster than the equivalent `charCodeAt` loop.\n */\nfunction carrierBytesOf(encoded: Uint8Array | null, body: string): number {\n if (!encoded) {\n // No TextEncoder (a browser old enough to lack it). Fall back to the string,\n // where `length` is the best available byte estimate.\n return body.length + 2\n }\n let extra = 2 // the quotes wrapping the attribute value\n for (let i = 0; i < encoded.length; i++) {\n const byte = encoded[i]\n if (byte === 34 || byte === 92) {\n extra += 1 // `\"` and `\\` take a leading backslash\n } else if (byte < 0x20) {\n extra +=\n byte === 8 || byte === 9 || byte === 10 || byte === 12 || byte === 13\n ? 1 // \\b \\f \\n \\r \\t\n : 5 // \\uXXXX\n }\n }\n return encoded.length + extra\n}\n\n/**\n * The most carrier bytes one UTF-16 code unit of a *JSON body* can become.\n *\n * A unit is at most 3 UTF-8 bytes, and the only characters that grow under\n * escaping are `\"` and `\\`, which are one byte and become two. A unit cannot be\n * both, so 3 is the ceiling. This holds because every caller passes the output\n * of a JSON encoder, which by specification never emits a raw control character\n * (the case that would otherwise expand to a 6-byte `\\uXXXX`); the invariant is\n * pinned by a test so a future caller that broke it would fail loudly rather\n * than silently ship an oversized carrier.\n */\nconst MAX_BYTES_PER_UNIT = 3\n\n/**\n * Whether `body` fits the carrier budget, escalating only as far as it must.\n *\n * `body.length` is O(1) and brackets the answer for both ordinary spans (far\n * under the budget) and hopeless ones (already past it on raw length alone),\n * which is every span in normal traffic: neither case touches the string. Only\n * a body near the budget is measured exactly, and that costs one encode plus\n * one byte scan.\n */\nexport function fitsCarrierBudget(\n body: string,\n maxBytes: number = MAX_SPAN_CARRIER_BYTES,\n): boolean {\n const units = body.length\n if (units * MAX_BYTES_PER_UNIT + 2 <= maxBytes) {\n return true\n }\n if (units + 2 > maxBytes) {\n return false\n }\n return carrierByteLength(body) <= maxBytes\n}\n\n/**\n * Span fields that identify the span rather than carry user data. Trimming one\n * would leave a span that no longer says what it is, so they stay whatever the\n * payload costs.\n */\nconst STRUCTURAL_SPAN_KEYS = new Set([\n \"name\",\n \"type\",\n \"function_name\",\n \"error_source\",\n])\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n}\n\ninterface Candidate {\n container: Record<string, unknown>\n key: string\n size: number\n}\n\n/**\n * The records holding user data, cloned so trimming never mutates the caller's\n * objects. Returns the payload copy to encode plus the containers to trim.\n */\nfunction cloneTrimmable(payload: Record<string, unknown>): {\n copy: Record<string, unknown>\n containers: Record<string, unknown>[]\n} {\n const copy = { ...payload }\n const containers: Record<string, unknown>[] = []\n\n const spanData = asRecord(copy.span_data)\n if (spanData) {\n const clone = { ...spanData }\n copy.span_data = clone\n containers.push(clone)\n }\n\n const rawSpan = asRecord(copy.rawSpan)\n const rawSpanData = rawSpan && asRecord(rawSpan.span_data)\n if (rawSpan && rawSpanData) {\n const clone = { ...rawSpanData }\n copy.rawSpan = { ...rawSpan, span_data: clone }\n containers.push(clone)\n }\n\n // No span_data anywhere: a trace-level or otherwise unfamiliar payload. Trim\n // its own fields rather than give up, so an oversized body still ships.\n if (containers.length === 0) {\n containers.push(copy)\n }\n\n return { copy, containers }\n}\n\nfunction collectCandidates(containers: Record<string, unknown>[]): Candidate[] {\n const candidates: Candidate[] = []\n for (const container of containers) {\n for (const [key, value] of Object.entries(container)) {\n if (STRUCTURAL_SPAN_KEYS.has(key) || value == null) {\n continue\n }\n let size: number\n try {\n size = byteLength(JSON.stringify(value) ?? \"\")\n } catch {\n continue\n }\n candidates.push({ container, key, size })\n }\n }\n return candidates.sort((a, b) => b.size - a.size)\n}\n\n/**\n * Stub the largest payload fields until the encoded body fits the budget.\n *\n * Returns the trimmed payload and the names of the fields that were stubbed, or\n * `undefined` when nothing could be trimmed (the caller then ships the\n * oversized body and lets the exporter report the drop, which is still better\n * than silently emptying a span).\n */\nexport function trimPayloadToBudget(\n payload: Record<string, unknown>,\n encode: (value: Record<string, unknown>) => string,\n maxBytes: number = MAX_SPAN_CARRIER_BYTES,\n): { value: Record<string, unknown>; trimmed: string[] } | undefined {\n const { copy, containers } = cloneTrimmable(payload)\n const candidates = collectCandidates(containers)\n if (candidates.length === 0) {\n return undefined\n }\n\n const trimmed: string[] = []\n for (const candidate of candidates) {\n candidate.container[candidate.key] =\n `<unserializable: too_large_${candidate.size}_bytes>`\n trimmed.push(candidate.key)\n let body: string\n try {\n body = encode(copy)\n } catch {\n return undefined\n }\n if (fitsCarrierBudget(body, maxBytes)) {\n return { value: copy, trimmed }\n }\n }\n return undefined\n}\n\n/**\n * Record a trim in the payload's own `errors`, which is what the server reads\n * to flag a trace as incomplete.\n */\nexport function markPayloadTrimmed(\n value: Record<string, unknown>,\n trimmed: string[],\n maxBytes: number = MAX_SPAN_CARRIER_BYTES,\n): void {\n const existing = Array.isArray(value.errors) ? value.errors : []\n value.errors = [\n ...existing,\n {\n source: \"sdk\",\n step: \"payload_budget\",\n error: `trimmed oversized field(s) to fit the ${maxBytes}-byte span carrier budget: ${[\n ...new Set(trimmed),\n ].join(\", \")}`,\n },\n ]\n}\n","/**\n * Emit a `console.warn` at most once per distinct `key` for the life of the\n * process.\n *\n * The SDK must NEVER crash a host app, so every failure on the user's path\n * degrades silently (a span is dropped, a call runs untraced, a payload is\n * stubbed). Silent is safe but undebuggable: a user who suddenly has no traces,\n * or sees `<unserializable>` in a span, has no signal as to why. A one-time\n * warning per distinct issue restores that signal without spamming the console\n * from a hot path.\n *\n * Keys should identify the specific degradation (e.g. include the traced\n * function key) so each distinct issue warns once, not just the first one seen.\n */\nconst warned = new Set<string>()\n\nexport function warnOnce(key: string, message: string): void {\n if (warned.has(key)) {\n return\n }\n warned.add(key)\n try {\n console.warn(`[bitfab] ${message}`)\n } catch {\n // Logging must never crash the host app (e.g. a closed/replaced console).\n }\n}\n\n/** Test-only: clear the dedup set so a warning can fire again. */\nexport function _resetWarnOnce(): void {\n warned.clear()\n}\n","/**\n * Defensive payload encoding, shared by the HTTP path and the OTel carrier\n * path. Lives in its own module because `otel.ts` needs it and importing it\n * from `http.ts` would close an http -> transport -> otel -> http cycle.\n */\n\nimport {\n fitsCarrierBudget,\n MAX_SPAN_CARRIER_BYTES,\n markPayloadTrimmed,\n trimPayloadToBudget,\n} from \"./payloadBudget.js\"\nimport { warnOnce } from \"./warnOnce.js\"\n\n/**\n * JSON-encode a request body without ever throwing on a stray value, and\n * within the per-span byte budget.\n *\n * Upstream serialization (`serializeValue` / the LangGraph handler's\n * `safeSerialize`) should already have flattened user data. This is the\n * boundary backstop: if anything non-serializable still slips through\n * (BigInt, function, symbol, circular ref), it is stubbed in place instead of\n * letting `JSON.stringify` throw and drop the whole span/trace silently.\n *\n * The fast path is a plain `JSON.stringify`; the sanitizing replacer only runs\n * when that throws, so happy-path payloads (and shared non-circular refs) are\n * untouched. Returns `dropped` (the stubbed type names) so the caller can warn\n * loudly rather than ship a degraded payload in silence.\n */\nexport function serializePayloadBody(payload: Record<string, unknown>): {\n body: string\n dropped: string[]\n}\nexport function serializePayloadBody(\n payload: Record<string, unknown>,\n maxCarrierBytes: number,\n): { body: string; dropped: string[] }\nexport function serializePayloadBody(\n payload: Record<string, unknown>,\n maxCarrierBytes: number = MAX_SPAN_CARRIER_BYTES,\n): { body: string; dropped: string[] } {\n const encoded = encodePayloadBody(payload)\n if (fitsCarrierBudget(encoded.body, maxCarrierBytes)) {\n return { body: encoded.body, dropped: encoded.dropped }\n }\n return applyPayloadBudget(encoded, maxCarrierBytes)\n}\n\n/**\n * Trim an over-budget payload, preferring its largest fields, so the span\n * ships degraded rather than being dropped whole by the exporter.\n *\n * Trims the value that was actually encoded, not the caller's original: a\n * cyclic or otherwise non-encodable field cannot be sized (`JSON.stringify`\n * throws on it), so on the original graph the biggest field is skipped as a\n * trim candidate and the oversized body ships anyway. The sanitized copy has\n * those values already replaced with stubs, so every field is sizeable.\n */\nfunction applyPayloadBudget(\n encoded: EncodedPayload,\n maxCarrierBytes: number,\n): {\n body: string\n dropped: string[]\n} {\n const result = encoded.value\n ? trimPayloadToBudget(\n encoded.value,\n (value) => encodePayloadBody(value).body,\n maxCarrierBytes,\n )\n : undefined\n if (!result) {\n return { body: encoded.body, dropped: encoded.dropped }\n }\n warnOnce(\n \"payload:over-budget\",\n `a span payload exceeded the ${maxCarrierBytes}-byte carrier budget; its largest field(s) (${[\n ...new Set(result.trimmed),\n ].join(\n \", \",\n )}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`,\n )\n markPayloadTrimmed(result.value, result.trimmed, maxCarrierBytes)\n // `dropped` names values that could not be encoded, which drives the\n // \"non-serializable value(s)\" warning. A budget trim is a size decision, not\n // an encoding failure, and already has its own warning and `payload_budget`\n // error entry, so it must not be reported as one.\n return {\n body: encodePayloadBody(result.value).body,\n dropped: encoded.dropped,\n }\n}\n\ninterface EncodedPayload {\n body: string\n dropped: string[]\n /**\n * The value the body was encoded from: the payload itself, or its sanitized\n * copy. Undefined when the encoded value isn't an object, which leaves\n * nothing with named fields to trim.\n */\n value: Record<string, unknown> | undefined\n}\n\nfunction encodePayloadBody(payload: Record<string, unknown>): EncodedPayload {\n try {\n return { body: JSON.stringify(payload), dropped: [], value: payload }\n } catch {\n const dropped: string[] = []\n // An explicit backtracking walk, not a JSON.stringify replacer: a replacer\n // gets no subtree-exit signal, so a single WeakSet would mis-tag a shared\n // (DAG) reference under sibling keys as a cycle. Tracking only the\n // current-path ancestors stubs real cycles while serializing DAGs in full.\n const sanitize = (value: unknown, seen: WeakSet<object>): unknown => {\n const t = typeof value\n if (\n value === null ||\n t === \"string\" ||\n t === \"number\" ||\n t === \"boolean\"\n ) {\n return value\n }\n if (t === \"bigint\") {\n dropped.push(\"BigInt\")\n return \"<unserializable: BigInt>\"\n }\n if (t === \"function\") {\n const name = (value as { name?: string }).name || \"Function\"\n dropped.push(name)\n return `<unserializable: ${name}>`\n }\n if (t === \"symbol\") {\n dropped.push(\"Symbol\")\n return \"<unserializable: Symbol>\"\n }\n if (t !== \"object\") {\n return undefined // e.g. undefined; JSON omits/normalizes it\n }\n const obj = value as object\n const className =\n (obj as { constructor?: { name?: string } }).constructor?.name ||\n \"object\"\n if (seen.has(obj)) {\n dropped.push(className)\n return `<cycle: ${className}>`\n }\n seen.add(obj)\n let result: unknown\n if (Array.isArray(obj)) {\n result = obj.map((item) => sanitize(item, seen))\n } else if (typeof (obj as { toJSON?: unknown }).toJSON === \"function\") {\n try {\n result = sanitize((obj as { toJSON(): unknown }).toJSON(), seen)\n } catch {\n dropped.push(className)\n result = `<unserializable: ${className}>`\n }\n } else {\n try {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(obj)) {\n out[k] = sanitize(v, seen)\n }\n result = out\n } catch {\n // A throwing getter or Proxy on `obj` can make `Object.entries`\n // throw. Stub just this object instead of failing the whole payload\n // (which would drop every span field). Mirrors the toJSON branch.\n warnOnce(\n \"payload:field-getter-threw\",\n \"a value with a throwing getter/proxy could not be serialized into a span payload; it was replaced with a placeholder. The span still ships with its other fields intact.\",\n )\n dropped.push(className)\n result = `<unserializable: ${className}>`\n }\n }\n seen.delete(obj) // backtrack: only ancestors stay tracked\n return result\n }\n let sanitized: unknown\n try {\n sanitized = sanitize(payload, new WeakSet())\n } catch (error) {\n // Truly pathological. Still never drop silently: send a marker body.\n const message = error instanceof Error ? error.message : String(error)\n const marker = { error: `payload_serialize_failed: ${message}` }\n return { body: JSON.stringify(marker), dropped, value: marker }\n }\n // Keep the server-side signal that the SDK had to stub values, so the\n // trace can be flagged as possibly incomplete / not replayable, while the\n // span content (everything that did serialize) is preserved.\n const isRecord =\n typeof sanitized === \"object\" &&\n sanitized !== null &&\n !Array.isArray(sanitized)\n if (dropped.length > 0 && isRecord) {\n const obj = sanitized as Record<string, unknown>\n const existing = Array.isArray(obj.errors) ? obj.errors : []\n obj.errors = [\n ...existing,\n {\n source: \"sdk\",\n step: \"json_serialize\",\n error: `stubbed non-serializable value(s): ${[\n ...new Set(dropped),\n ].join(\", \")}`,\n },\n ]\n }\n return {\n body: JSON.stringify(sanitized),\n dropped,\n value: isRecord ? (sanitized as Record<string, unknown>) : undefined,\n }\n }\n}\n","import { warnOnce } from \"./warnOnce.js\"\n\nconst callerMetadata = new Map<string, Record<string, unknown>>()\nconst derivedMetadata = new Map<string, Record<string, unknown>>()\n\nexport function recordCallerTraceMetadata(\n traceId: string,\n metadata: Record<string, unknown>,\n): void {\n if (typeof traceId !== \"string\" || traceId === \"\") {\n return\n }\n if (typeof metadata !== \"object\" || metadata === null) {\n return\n }\n if (Object.keys(metadata).length === 0) {\n return\n }\n callerMetadata.set(traceId, { ...callerMetadata.get(traceId), ...metadata })\n}\n\nexport function callerTraceMetadata(\n traceId: string,\n): Record<string, unknown> | undefined {\n const recorded = callerMetadata.get(traceId)\n return recorded ? { ...recorded } : undefined\n}\n\nexport function forgetTraceMetadata(traceId: string): void {\n callerMetadata.delete(traceId)\n derivedMetadata.delete(traceId)\n}\n\nexport function mergeCallerMetadataIntoTracePayload(\n payload: Record<string, unknown>,\n): Record<string, unknown> {\n const externalTrace = payload.externalTrace\n if (typeof externalTrace !== \"object\" || externalTrace === null) {\n return payload\n }\n const external = externalTrace as Record<string, unknown>\n const traceId =\n typeof payload.id === \"string\" && payload.id !== \"\"\n ? payload.id\n : external.id\n if (typeof traceId !== \"string\" || traceId === \"\") {\n return payload\n }\n const caller = callerMetadata.get(traceId)\n if (!caller) {\n return payload\n }\n const derived = derivedMetadata.get(traceId) ?? {}\n const exported = external.metadata\n if (typeof exported === \"object\" && exported !== null) {\n Object.assign(derived, exported)\n }\n derivedMetadata.set(traceId, derived)\n const shadowed = Object.keys(derived)\n .filter((key) => key in caller && caller[key] !== derived[key])\n .sort()\n if (shadowed.length > 0) {\n warnOnce(\n `trace-metadata-shadowed:${shadowed.join(\",\")}`,\n `trace metadata key(s) ${shadowed.join(\", \")} were set both by the ` +\n \"caller and by an integration's own trace export; the caller's value \" +\n \"is the one kept on the trace.\",\n )\n }\n return {\n ...payload,\n externalTrace: { ...external, metadata: { ...derived, ...caller } },\n }\n}\n\nexport function _resetTraceMetadata(): void {\n callerMetadata.clear()\n derivedMetadata.clear()\n}\n","/**\n * OpenTelemetry transport for Bitfab spans and trace completions.\n *\n * OTel is used here as a queueing, batching, and delivery engine only. Bitfab\n * keeps ownership of logical trace identity: every payload travels inside an\n * internal *carrier* span whose `bitfab.payload` attribute holds the encoded\n * Bitfab body, and the server reconstructs the stored tree from that payload\n * rather than from the carrier's OTel topology. The provider and processor are\n * private to each client, so an application's own OTel traces are never mixed\n * into Bitfab traces and the global provider is never replaced.\n */\n\nimport { type Span, SpanStatusCode, type Tracer } from \"@opentelemetry/api\"\nimport {\n type ExportResult,\n ExportResultCode,\n type InstrumentationScope,\n} from \"@opentelemetry/core\"\nimport { resourceFromAttributes } from \"@opentelemetry/resources\"\nimport {\n AlwaysOnSampler,\n BasicTracerProvider,\n BatchSpanProcessor,\n type ReadableSpan,\n type SpanExporter,\n} from \"@opentelemetry/sdk-trace-base\"\nimport { type EncodedRequestBody, encodeRequestBody } from \"./compress.js\"\nimport { __version__ } from \"./constants.js\"\nimport { BitfabError } from \"./errors.js\"\nimport {\n byteLength,\n MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES,\n MAX_SPAN_CARRIER_BYTES,\n} from \"./payloadBudget.js\"\nimport { readEnv } from \"./readEnv.js\"\nimport { serializePayloadBody } from \"./serializePayload.js\"\nimport type {\n CarrierMeta,\n CarrierRef,\n DirectBatchSender,\n TraceOperation,\n TraceTransport,\n} from \"./transportTypes.js\"\nimport { DeliveryError } from \"./transportTypes.js\"\nimport { unrefTimer } from \"./unrefTimer.js\"\nimport { warnOnce } from \"./warnOnce.js\"\n\nconst OPERATION_ATTRIBUTE = \"bitfab.operation\"\nconst PAYLOAD_ATTRIBUTE = \"bitfab.payload\"\nconst MAX_EXPORT_REQUEST_BYTES = 3_000_000\nconst MAX_DECOMPRESSED_REQUEST_BYTES = 8_000_000\nconst MAX_REQUEST_BYTES_ENV = \"BITFAB_OTEL_MAX_REQUEST_BYTES\"\nconst EXPORT_CONCURRENCY_ENV = \"BITFAB_OTEL_EXPORT_CONCURRENCY\"\nconst MAX_QUEUE_SIZE = 8_192\nconst DIRECT_MAX_EXPORT_BATCH_SIZE = 512\nconst DIRECT_MAX_REQUEST_BATCH_SIZE = 128\nconst DEFAULT_EXPORT_CONCURRENCY = 32\nconst MAX_EXPORT_CONCURRENCY = 64\nconst SCHEDULE_DELAY_MILLIS = 5_000\nconst EXPORT_TIMEOUT_MILLIS = 30_000\nconst RETRY_BASE_DELAY_MILLIS = 100\n// Ceiling on the exponential growth of our OWN backoff. It does not bound a\n// wait the server asked for: OTLP says to honor Retry-After, and warns that a\n// delay big enough to make the client drop data is the server's mistake to\n// avoid, not the client's cue to discard. What bounds an honored wait is the\n// export budget below, since a wait outliving the export cannot be served.\nconst RETRY_BACKOFF_CEILING_MILLIS = 5_000\nconst MAX_SEND_ATTEMPTS = 3\nconst DEFAULT_LIFECYCLE_TIMEOUT_MS = 30_000\n\nconst liveTransports = new Set<OtelBatchTransport>()\n\n// Keyed by the carrier span object so a dropped span needs no cleanup. The ref\n// cannot ride on the span as an attribute: `spanLimits` caps carriers at two,\n// and `bitfab.operation` and `bitfab.payload` hold both.\nconst carrierRefs = new WeakMap<object, CarrierRef>()\n\nfunction readBoundedIntEnv(\n name: string,\n max: number,\n fallback: number,\n warnKey: string,\n): number {\n const raw = readEnv(name)\n if (raw === undefined) {\n return fallback\n }\n const value = Number(raw)\n if (Number.isInteger(value) && value > 0 && value <= max) {\n return value\n }\n warnOnce(\n warnKey,\n `${name} must be a positive integer no greater than ${max}; using ${fallback}`,\n )\n return fallback\n}\n\nfunction logError(message: string, error?: unknown): void {\n try {\n if (error === undefined) {\n console.error(`[bitfab] ${message}`)\n } else {\n console.error(`[bitfab] ${message}`, error)\n }\n } catch {\n // Logging must never crash the host app.\n }\n}\n\nfunction otlpValue(value: unknown): Record<string, unknown> {\n if (typeof value === \"boolean\") {\n return { boolValue: value }\n }\n if (typeof value === \"number\") {\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value }\n }\n if (typeof value === \"string\") {\n return { stringValue: value }\n }\n if (Array.isArray(value)) {\n return { arrayValue: { values: value.map(otlpValue) } }\n }\n return { stringValue: String(value) }\n}\n\nfunction otlpAttributes(\n attributes: Record<string, unknown> | undefined,\n): Record<string, unknown>[] {\n if (!attributes) {\n return []\n }\n return Object.entries(attributes)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => ({ key, value: otlpValue(value) }))\n}\n\n/**\n * Nanoseconds since the epoch as a decimal string. Built by concatenation\n * rather than arithmetic because the value exceeds `Number.MAX_SAFE_INTEGER`,\n * so multiplying seconds out would silently lose the low digits.\n */\nfunction hrTimeToNanoString(time: [number, number] | undefined): string {\n if (!time) {\n return \"0\"\n }\n return `${time[0]}${String(time[1]).padStart(9, \"0\")}`\n}\n\nfunction spanToOtlp(span: ReadableSpan): Record<string, unknown> {\n const spanContext = span.spanContext()\n const result: Record<string, unknown> = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n name: span.name,\n kind: span.kind + 1,\n startTimeUnixNano: hrTimeToNanoString(span.startTime),\n endTimeUnixNano: hrTimeToNanoString(span.endTime),\n attributes: otlpAttributes(span.attributes as Record<string, unknown>),\n droppedAttributesCount: span.droppedAttributesCount,\n droppedEventsCount: span.droppedEventsCount,\n droppedLinksCount: span.droppedLinksCount,\n status: {\n code: span.status.code,\n ...(span.status.message ? { message: span.status.message } : {}),\n },\n flags: spanContext.traceFlags,\n }\n const parentSpanId = span.parentSpanContext?.spanId\n if (parentSpanId) {\n result.parentSpanId = parentSpanId\n }\n if (spanContext.traceState) {\n result.traceState = spanContext.traceState.serialize()\n }\n return result\n}\n\n/**\n * A span encoded exactly as it will appear on the wire, carrying its own byte\n * count. Encoding once and remembering the size is what keeps request packing\n * linear: sizing a candidate batch by re-encoding the whole request re-escapes\n * every carrier's `bitfab.payload` string on every span considered.\n */\ninterface EncodedSpan {\n json: string\n size: number\n ref?: CarrierRef\n}\n\ninterface RequestBatch {\n spans: EncodedSpan[]\n size: number\n}\n\n/**\n * The invariant head and tail of an OTLP request for one export window. Key\n * order matches what `JSON.stringify` emits for the equivalent object, so a\n * body assembled by concatenation is byte-identical to encoding that object.\n */\ninterface RequestEnvelope {\n head: string\n tail: string\n size: number\n}\n\n/** The comma `join` puts between adjacent spans in the request's span list. */\nconst SPAN_SEPARATOR_BYTES = 1\n\nfunction encodeSpan(span: ReadableSpan): EncodedSpan {\n const json = JSON.stringify(spanToOtlp(span))\n return {\n json,\n size: byteLength(json),\n ref: carrierRefs.get(span),\n }\n}\n\nfunction trimEncodedSpan(span: EncodedSpan): EncodedSpan | undefined {\n try {\n const carrier = JSON.parse(span.json) as {\n attributes?: Array<{\n key?: string\n value?: { stringValue?: string }\n }>\n }\n const attribute = carrier.attributes?.find(\n (entry) => entry.key === PAYLOAD_ATTRIBUTE,\n )\n const payloadBody = attribute?.value?.stringValue\n if (!attribute?.value || payloadBody === undefined) {\n return undefined\n }\n const payload = JSON.parse(payloadBody) as Record<string, unknown>\n attribute.value.stringValue = serializePayloadBody(\n payload,\n MAX_SPAN_CARRIER_BYTES,\n ).body\n const json = JSON.stringify(carrier)\n return { json, size: byteLength(json) }\n } catch {\n return undefined\n }\n}\n\nasync function prepareRequest(body: string): Promise<EncodedRequestBody> {\n const prepared = encodeRequestBody(body)\n return prepared instanceof Promise ? await prepared : prepared\n}\n\nfunction requestEnvelope(first: ReadableSpan): RequestEnvelope {\n const scope = first.instrumentationScope as InstrumentationScope\n const resource = JSON.stringify({\n attributes: otlpAttributes(\n first.resource.attributes as Record<string, unknown>,\n ),\n })\n const scopeJson = JSON.stringify({\n name: scope.name,\n version: scope.version ?? \"\",\n })\n const head = `{\"resourceSpans\":[{\"resource\":${resource},\"scopeSpans\":[{\"scope\":${scopeJson},\"spans\":[`\n const tail = \"]}]}]}\"\n return { head, tail, size: byteLength(head) + byteLength(tail) }\n}\n\nfunction encodeRequest(\n envelope: RequestEnvelope,\n spans: EncodedSpan[],\n): string {\n return (\n envelope.head + spans.map((span) => span.json).join(\",\") + envelope.tail\n )\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms)\n unrefTimer(timer)\n })\n}\n\n/**\n * Race `work` against `timeoutMs`. Resolves `false` when the deadline wins, so\n * a wedged export can never hold a flush or shutdown open past its budget.\n */\nasync function withDeadline(\n work: Promise<boolean>,\n timeoutMs: number,\n): Promise<boolean> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return await Promise.race([\n work,\n new Promise<boolean>((resolve) => {\n timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs))\n unrefTimer(timer)\n }),\n ])\n } finally {\n if (timer) {\n clearTimeout(timer)\n }\n }\n}\n\n/** Run `task` over `items` with at most `limit` in flight at any moment. */\nasync function mapWithConcurrency<T, R>(\n items: T[],\n limit: number,\n task: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length)\n let next = 0\n const workers = Array.from(\n { length: Math.min(Math.max(limit, 1), items.length) },\n async () => {\n while (next < items.length) {\n const index = next\n next += 1\n results[index] = await task(items[index])\n }\n },\n )\n await Promise.all(workers)\n return results\n}\n\n/**\n * Only what the sender classified. Anything else reaching here is a fault in\n * the sender itself, and retrying a deterministic bug just delays it.\n */\nfunction isRetryable(error: unknown): boolean {\n return error instanceof DeliveryError && error.retryable\n}\n\nfunction isOversized(error: unknown): boolean {\n return error instanceof DeliveryError && error.oversized\n}\n\n/**\n * How long to wait before the next send attempt, or `null` to stop trying.\n *\n * A server that sent `Retry-After` has told us when it wants us back, so that\n * wait is honored exactly. Clamping it would return early, which is the single\n * thing the server asked us not to do; when the wait is longer than we are\n * willing to hold a batch, the honest answer is to give up rather than come\n * back sooner and add load to something already struggling.\n *\n * Absent an instruction, back off exponentially so a struggling server is not\n * hit on a fixed cadence, and jitter it so every client in a fleet does not\n * return in lockstep.\n */\n/**\n * How long to wait before the next attempt, or null when the wait cannot be\n * served inside `remainingMillis` and the batch has to be given up.\n *\n * A server that sent Retry-After told us when it wants us back, so that wait is\n * honored whole rather than shortened: coming back early is the one thing it\n * asked us not to do. It is refused only when it outlasts the export budget,\n * where waiting would mean being killed mid-wait and losing the batch anyway.\n *\n * Absent an instruction, back off exponentially so a struggling server is not\n * hit on a fixed cadence, and jitter it so a fleet does not return in lockstep.\n */\nfunction retryWaitMillis(\n error: unknown,\n attempt: number,\n remainingMillis: number,\n): number | null {\n const requested =\n error instanceof DeliveryError ? error.retryAfterMs : undefined\n // Half the remaining budget, not all of it: a wait is only worth taking if\n // what is left afterwards can still carry the request. Spending the whole\n // budget waiting means being killed mid-wait, which loses the batch AND holds\n // an export slot for the duration.\n const affordable = remainingMillis / 2\n if (requested !== undefined) {\n return requested < affordable ? requested : null\n }\n const backoff = Math.min(\n RETRY_BASE_DELAY_MILLIS * 2 ** attempt,\n RETRY_BACKOFF_CEILING_MILLIS,\n )\n const jittered = backoff / 2 + Math.random() * (backoff / 2)\n return jittered < affordable ? jittered : null\n}\n\n/**\n * Direct delivery to Bitfab's OTLP/JSON ingress.\n *\n * OTel hands this exporter one batch as a candidate window. The window is\n * repacked into requests bounded by both a carrier count and the exact encoded\n * request size, and those complete requests are sent concurrently. That keeps\n * OTel's queue, scheduling, force-flush and shutdown while restoring the small\n * independent requests Bitfab's serverless ingress is built to scale.\n */\nexport class BitfabSpanExporter implements SpanExporter {\n constructor(\n private readonly directSender: DirectBatchSender,\n private readonly maxRequestBytes: number,\n private readonly maxRequestBatchSize: number,\n private readonly exportConcurrency: number,\n private readonly onDelivered?: (refs: CarrierRef[]) => void,\n // The same budget the processor enforces around this export. Waits are\n // measured against it, so a configured timeout and the deadline a wait is\n // judged by can never drift apart.\n private readonly exportTimeoutMillis: number = EXPORT_TIMEOUT_MILLIS,\n ) {}\n\n /** Epoch ms until which the server has asked this exporter to stay away. */\n private throttledUntil = 0\n\n export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): void {\n void this.exportAsync(spans).then(\n (succeeded) => {\n resultCallback({\n code: succeeded ? ExportResultCode.SUCCESS : ExportResultCode.FAILED,\n })\n },\n (error) => {\n resultCallback({ code: ExportResultCode.FAILED, error })\n },\n )\n }\n\n private async exportAsync(spans: ReadableSpan[]): Promise<boolean> {\n if (spans.length === 0) {\n return true\n }\n let encoded: EncodedSpan[]\n let envelope: RequestEnvelope\n try {\n encoded = spans.map(encodeSpan)\n envelope = requestEnvelope(spans[0])\n } catch (error) {\n logError(\"failed to encode an OpenTelemetry span batch\", error)\n return false\n }\n\n const batches = this.buildRequestBatches(envelope, encoded)\n const results = await mapWithConcurrency(\n batches,\n this.exportConcurrency,\n (batch) => this.send(envelope, batch),\n )\n return results.every(Boolean)\n }\n\n private buildRequestBatches(\n envelope: RequestEnvelope,\n spans: EncodedSpan[],\n ): RequestBatch[] {\n const batches: RequestBatch[] = []\n let current: EncodedSpan[] = []\n let size = envelope.size\n\n for (const span of spans) {\n const addition =\n span.size + (current.length > 0 ? SPAN_SEPARATOR_BYTES : 0)\n if (\n current.length > 0 &&\n (current.length >= this.maxRequestBatchSize ||\n size + addition > this.maxRequestBytes)\n ) {\n batches.push({ spans: current, size })\n current = []\n size = envelope.size\n }\n current.push(span)\n size += span.size + (current.length > 1 ? SPAN_SEPARATOR_BYTES : 0)\n }\n\n if (current.length > 0) {\n batches.push({ spans: current, size })\n }\n return batches\n }\n\n private async send(\n envelope: RequestEnvelope,\n batch: RequestBatch,\n ): Promise<boolean> {\n try {\n let requestSpans = batch.spans\n let requestRawBytes = batch.size\n let alreadyTrimmed = false\n while (true) {\n if (requestRawBytes <= MAX_DECOMPRESSED_REQUEST_BYTES) {\n const prepared = await prepareRequest(\n encodeRequest(envelope, requestSpans),\n )\n if (prepared.wireBytes <= this.maxRequestBytes) {\n await this.sendWithRetries(prepared)\n // Refs come from the batch, not the possibly-trimmed request:\n // trimming rebuilds a span without its ref, and a trimmed carrier\n // still reached the server under its original identity.\n this.reportDelivered(batch.spans)\n return true\n }\n }\n\n if (batch.spans.length !== 1) {\n logError(\n \"an OpenTelemetry span batch exceeded the configured request-size target and could not be exported\",\n )\n return false\n }\n if (alreadyTrimmed) {\n logError(\n \"a single OpenTelemetry span exceeded the configured request-size target after trimming\",\n )\n return false\n }\n const trimmed = trimEncodedSpan(batch.spans[0])\n if (!trimmed) {\n logError(\n \"a single OpenTelemetry span exceeded the configured request-size target and could not be trimmed\",\n )\n return false\n }\n requestSpans = [trimmed]\n requestRawBytes = envelope.size + trimmed.size\n alreadyTrimmed = true\n }\n } catch (error) {\n if (isOversized(error)) {\n logError(\n batch.spans.length === 1\n ? \"a single OpenTelemetry span exceeded the ingestion request limit and could not be exported\"\n : \"an OpenTelemetry span batch exceeded the ingestion request limit and could not be exported\",\n )\n return false\n }\n logError(\"failed to export an OpenTelemetry span batch\", error)\n return false\n }\n }\n\n /**\n * Retries transient failures. Span and trace-completion carriers are safe to\n * retry: the server keys them idempotently on `sourceSpanId`/`sourceTraceId`,\n * so a duplicate delivery cannot create a duplicate row.\n *\n * KNOWN LIMITATION: an `internal_trace` (a `call()` BAML trace) carries no\n * such key, so retrying a batch that holds one can create a duplicate trace -\n * including when a request times out client-side but the server goes on to\n * persist it. Accepted deliberately for now, matching the other SDKs, rather\n * than skipping retries for a whole batch or inventing an idempotency scheme\n * the server does not yet understand. The fix is a client-supplied\n * idempotency key that ingestion dedupes on.\n */\n /**\n * Remember a throttle the server asked for, so the requests fanned out\n * alongside this one respect it too. Delaying only the request that was\n * refused leaves the other seven in the window hitting a server that just\n * asked for room.\n */\n private recordThrottle(error: unknown): void {\n const requested =\n error instanceof DeliveryError ? error.retryAfterMs : undefined\n if (requested !== undefined) {\n this.throttledUntil = Math.max(\n this.throttledUntil,\n Date.now() + requested,\n )\n }\n }\n\n /**\n * Waits out an active throttle, or reports the batch undeliverable when the\n * throttle outlasts what we are willing to hold it for. Either way nothing is\n * sent while the server has asked us to stay away.\n */\n private async awaitThrottle(deadline: number): Promise<void> {\n const remaining = this.throttledUntil - Date.now()\n if (remaining <= 0) {\n return\n }\n // Waited out, not refused: OTLP asks the client to hold off until the\n // window passes, and treats data dropped while throttled as the outcome to\n // avoid. Only a throttle outliving the export budget is refused, because\n // the processor would kill the wait before it could send anyway.\n if (remaining >= (deadline - Date.now()) / 2) {\n throw new DeliveryError(\n `OTLP ingestion is throttled for another ${remaining}ms, longer than the export budget`,\n )\n }\n await delay(remaining)\n }\n\n private async sendWithRetries(request: EncodedRequestBody): Promise<void> {\n // One budget for the whole exchange, waits included: the processor kills\n // the export at this deadline, so a wait past it cannot be served.\n const deadline = Date.now() + this.exportTimeoutMillis\n for (let attempt = 0; attempt < MAX_SEND_ATTEMPTS; attempt += 1) {\n try {\n await this.awaitThrottle(deadline)\n await this.directSender(request, Math.max(0, deadline - Date.now()))\n return\n } catch (error) {\n if (isOversized(error)) {\n throw error\n }\n this.recordThrottle(error)\n if (attempt === MAX_SEND_ATTEMPTS - 1 || !isRetryable(error)) {\n throw error\n }\n const wait = retryWaitMillis(error, attempt, deadline - Date.now())\n if (wait === null) {\n throw error\n }\n await delay(wait)\n }\n }\n }\n\n /**\n * Announce the carriers a request delivered. Wrapped because a listener that\n * throws must never turn a delivered batch into a failed export.\n */\n private reportDelivered(spans: EncodedSpan[]): void {\n if (this.onDelivered === undefined) {\n return\n }\n const refs = spans\n .map((span) => span.ref)\n .filter((ref): ref is CarrierRef => ref !== undefined)\n if (refs.length === 0) {\n return\n }\n try {\n this.onDelivered(refs)\n } catch (error) {\n logError(\"a delivery listener threw\", error)\n }\n }\n\n async shutdown(): Promise<void> {}\n\n async forceFlush(): Promise<void> {}\n}\n\n/**\n * Counts export failures so `flush` can answer \"was this delivered?\" instead of\n * only \"did the processor queue drain?\". Without it a flush would report\n * success for a batch the exporter dropped, and replay would finalize a run\n * whose traces never landed.\n */\nclass DeliveryTrackingExporter implements SpanExporter {\n // Deliberately unscoped, matching the Python SDK. An export can outlive\n // OTel's export timeout and report failure after the flush that was waiting\n // on it already returned, so that failure surfaces on the NEXT flush instead.\n // That over-reports: a good flush can inherit an older failure. The\n // alternative - discarding failures from completed flush windows - under-\n // reports, and `BatchSpanProcessor` also runs scheduled exports that belong\n // to no flush at all, so their failures would vanish entirely. For a\n // telemetry SDK a false \"flush failed\" is investigable; a false \"flush\n // succeeded\" silently loses traces. We take the noisy direction on purpose.\n private failedExports = 0\n\n constructor(private readonly exporter: SpanExporter) {}\n\n export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): void {\n try {\n this.exporter.export(spans, (result) => {\n if (result.code !== ExportResultCode.SUCCESS) {\n this.failedExports += 1\n }\n resultCallback(result)\n })\n } catch (error) {\n this.failedExports += 1\n resultCallback({ code: ExportResultCode.FAILED, error: error as Error })\n }\n }\n\n takeFailedExports(): number {\n const failed = this.failedExports\n this.failedExports = 0\n return failed\n }\n\n shutdown(): Promise<void> {\n return this.exporter.shutdown()\n }\n\n forceFlush(): Promise<void> {\n return this.exporter.forceFlush?.() ?? Promise.resolve()\n }\n}\n\nexport interface OtelBatchTransportOptions {\n directSender: DirectBatchSender\n /** Called with the refs of every carrier a request delivered. */\n onDelivered?: (refs: CarrierRef[]) => void\n maxExportBatchSize?: number\n maxRequestBatchSize?: number\n maxQueueSize?: number\n exportConcurrency?: number\n maxRequestBytes?: number\n /** Overridable so tests can drive OTel's export-timeout path in ms, not 30s. */\n exportTimeoutMillis?: number\n}\n\nexport class OtelBatchTransport implements TraceTransport {\n private readonly provider: BasicTracerProvider\n private readonly processor: BatchSpanProcessor\n private readonly deliveryTracker: DeliveryTrackingExporter\n private readonly tracer: Tracer\n private closed = false\n private pendingFlush: Promise<boolean> | undefined\n\n constructor(options: OtelBatchTransportOptions) {\n const maxRequestBytes = options.maxRequestBytes ?? MAX_EXPORT_REQUEST_BYTES\n const maxRequestBatchSize =\n options.maxRequestBatchSize ?? DIRECT_MAX_REQUEST_BATCH_SIZE\n if (maxRequestBatchSize <= 0) {\n throw new BitfabError(\"maxRequestBatchSize must be a positive integer\")\n }\n\n this.deliveryTracker = new DeliveryTrackingExporter(\n new BitfabSpanExporter(\n options.directSender,\n maxRequestBytes,\n maxRequestBatchSize,\n options.exportConcurrency ?? DEFAULT_EXPORT_CONCURRENCY,\n options.onDelivered,\n options.exportTimeoutMillis ?? EXPORT_TIMEOUT_MILLIS,\n ),\n )\n\n this.processor = new BatchSpanProcessor(this.deliveryTracker, {\n maxQueueSize: options.maxQueueSize ?? MAX_QUEUE_SIZE,\n maxExportBatchSize:\n options.maxExportBatchSize ?? DIRECT_MAX_EXPORT_BATCH_SIZE,\n scheduledDelayMillis: SCHEDULE_DELAY_MILLIS,\n exportTimeoutMillis: options.exportTimeoutMillis ?? EXPORT_TIMEOUT_MILLIS,\n })\n\n // Every option is passed explicitly: `BasicTracerProvider` otherwise reads\n // OTEL_* defaults, so a host application's sampler or attribute-length\n // limit would silently drop or truncate Bitfab payloads.\n this.provider = new BasicTracerProvider({\n sampler: new AlwaysOnSampler(),\n resource: resourceFromAttributes({\n \"service.name\": \"bitfab-typescript-sdk\",\n \"service.version\": __version__,\n }),\n spanLimits: {\n attributeCountLimit: 2,\n attributeValueLengthLimit: Number.POSITIVE_INFINITY,\n },\n spanProcessors: [this.processor],\n })\n this.tracer = this.provider.getTracer(\"bitfab\", __version__)\n liveTransports.add(this)\n }\n\n submit(\n operation: TraceOperation,\n payload: Record<string, unknown>,\n meta: CarrierMeta = {},\n ): void {\n if (this.closed) {\n warnOnce(\n \"otel-submit-after-shutdown\",\n \"OpenTelemetry transport is shut down; dropping spans\",\n )\n return\n }\n try {\n // Not a bare JSON.stringify: contexts, metadata and `call()` inputs\n // never pass through `serializeValue`, so one stray value here would\n // throw and drop the whole span rather than being stubbed. This is the\n // same backstop the pre-transport HTTP path applied.\n const { body, dropped } = serializePayloadBody(\n payload,\n MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES,\n )\n if (dropped.length > 0) {\n warnOnce(\n \"otel-carrier-payload-stubbed\",\n `a span payload held non-serializable value(s) (${[\n ...new Set(dropped),\n ].join(\", \")}); they were stubbed so the span still ships, but the ` +\n \"trace may be incomplete or not replayable.\",\n )\n }\n const span = this.tracer.startSpan(meta.name ?? `bitfab.${operation}`, {\n attributes: {\n [OPERATION_ATTRIBUTE]: operation,\n [PAYLOAD_ATTRIBUTE]: body,\n },\n startTime: meta.startTime,\n })\n if (meta.ref !== undefined) {\n carrierRefs.set(span, meta.ref)\n }\n if (meta.errored === true) {\n span.setStatus({ code: SpanStatusCode.ERROR })\n }\n endSpan(span, meta.endTime)\n } catch (error) {\n logError(\"failed to queue an OpenTelemetry span\", error)\n }\n }\n\n async flush(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n ): Promise<boolean> {\n // Serialized: two concurrent force-flushes would race for the same\n // delivery counter and one would report the other's failures as success.\n const pending = (this.pendingFlush ?? Promise.resolve(true)).then(() =>\n this.forceFlushOnce(),\n )\n this.pendingFlush = pending.catch(() => false)\n return withDeadline(pending, timeoutMs)\n }\n\n private async forceFlushOnce(): Promise<boolean> {\n try {\n await this.processor.forceFlush()\n } catch (error) {\n logError(\"failed to flush OpenTelemetry spans\", error)\n this.deliveryTracker.takeFailedExports()\n return false\n }\n return this.deliveryTracker.takeFailedExports() === 0\n }\n\n async shutdown(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n ): Promise<boolean> {\n const deadline = Date.now() + Math.max(timeoutMs, 0)\n this.closed = true\n const flushed = await this.flush(Math.max(0, deadline - Date.now()))\n liveTransports.delete(this)\n const shutdownCompleted = await withDeadline(\n this.provider\n .shutdown()\n .then(() => true)\n .catch((error) => {\n logError(\"failed to shut down the OpenTelemetry transport\", error)\n return false\n }),\n Math.max(0, deadline - Date.now()),\n )\n return flushed && shutdownCompleted\n }\n}\n\nfunction endSpan(span: Span, endTime: number | undefined): void {\n span.end(endTime)\n}\n\nexport function createOtelTransport(options: {\n directSender: DirectBatchSender\n onDelivered?: (refs: CarrierRef[]) => void\n}): OtelBatchTransport {\n return new OtelBatchTransport({\n ...options,\n exportConcurrency: readBoundedIntEnv(\n EXPORT_CONCURRENCY_ENV,\n MAX_EXPORT_CONCURRENCY,\n DEFAULT_EXPORT_CONCURRENCY,\n \"otel-export-concurrency-invalid\",\n ),\n maxRequestBytes: readBoundedIntEnv(\n MAX_REQUEST_BYTES_ENV,\n MAX_EXPORT_REQUEST_BYTES,\n MAX_EXPORT_REQUEST_BYTES,\n \"otel-max-request-bytes-invalid\",\n ),\n })\n}\n\nasync function forEachLiveTransport(\n timeoutMs: number,\n run: (transport: OtelBatchTransport, remainingMs: number) => Promise<boolean>,\n): Promise<boolean> {\n const deadline = Date.now() + Math.max(timeoutMs, 0)\n let succeeded = true\n for (const transport of [...liveTransports]) {\n succeeded =\n (await run(transport, Math.max(0, deadline - Date.now()))) && succeeded\n }\n return succeeded\n}\n\nexport function flushOtelTransports(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n): Promise<boolean> {\n return forEachLiveTransport(timeoutMs, (transport, remaining) =>\n transport.flush(remaining),\n )\n}\n\nexport function shutdownOtelTransports(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n): Promise<boolean> {\n return forEachLiveTransport(timeoutMs, (transport, remaining) =>\n transport.shutdown(remaining),\n )\n}\n","/**\n * The boundary every instrumentation path crosses to hand a Bitfab payload to\n * the network. Kept in its own module, free of both `http.ts` and `otel.ts`,\n * so the HTTP client can depend on the transport contract without importing\n * the OpenTelemetry implementation (and vice versa).\n */\n\nimport type { EncodedRequestBody } from \"./compress.js\"\n\n/** Which Bitfab payload a carrier span holds. */\nexport type TraceOperation =\n | \"external_span\"\n | \"external_trace\"\n | \"internal_trace\"\n\n/**\n * Posts one fully-encoded request body and resolves once the server has\n * accepted it whole. Supplied by `HttpClient`, which owns the endpoint, the\n * auth, and what the server's answer means: a rejection arrives here as a\n * {@link DeliveryError}, so the transport decides whether to retry without ever\n * reading a response.\n *\n * The request arrives already encoded, carrying its own content encoding and\n * byte counts: the exporter assembles it from per-span encodes it has to\n * produce anyway to size a request, so handing over an object here would make\n * the client encode the same batch a second time.\n */\nexport type DirectBatchSender = (\n request: EncodedRequestBody,\n timeoutMs: number,\n) => Promise<void>\n\n/**\n * Why a delivery failed, in the only two terms the transport acts on. A sender\n * classifies everything it can see, including a network fault carrying no\n * verdict; anything else reaching the transport is a fault in the sender and is\n * not retried.\n */\nexport class DeliveryError extends Error {\n readonly retryable: boolean\n readonly oversized: boolean\n /** How long the server asked us to wait, when it said so. */\n readonly retryAfterMs?: number\n\n constructor(\n message: string,\n options: {\n retryable?: boolean\n oversized?: boolean\n retryAfterMs?: number\n } = {},\n ) {\n super(message)\n this.name = \"DeliveryError\"\n this.retryable = options.retryable ?? false\n this.oversized = options.oversized ?? false\n this.retryAfterMs = options.retryAfterMs\n }\n}\n\n/**\n * Which carrier a payload is, for delivery accounting only. Supplied by the\n * caller that built the payload: the transport never reads inside one.\n *\n * `spanId` is omitted for the carrier that closes a trace, which is what tells\n * the transport the trace's expected set has stopped growing.\n */\nexport interface CarrierRef {\n traceId: string\n spanId?: string\n}\n\n/**\n * Everything the transport needs to know ABOUT a payload without reading one.\n * Supplied by the caller that built it; each field falls back to something the\n * transport can decide without looking inside.\n */\nexport interface CarrierMeta {\n /** Delivery identity. Omitted for carriers nobody accounts for. */\n ref?: CarrierRef\n /** Carrier span name. Defaults to `bitfab.<operation>`. */\n name?: string\n /** Epoch ms. Omitted lets OTel stamp the carrier as it is created. */\n startTime?: number\n endTime?: number\n /** Marks the carrier span errored. */\n errored?: boolean\n}\n\nexport interface TraceTransport {\n /** Queue a payload. Never throws; delivery failures degrade silently. */\n submit(\n operation: TraceOperation,\n payload: Record<string, unknown>,\n meta?: CarrierMeta,\n ): void\n /** Drain the queue within `timeoutMs`. False on export failure or timeout. */\n flush(timeoutMs?: number): Promise<boolean>\n /** Flush, then permanently stop this transport. */\n shutdown(timeoutMs?: number): Promise<boolean>\n}\n","/**\n * Best-effort `unref()` on a timer handle so a pending timeout never keeps the\n * Node.js event loop alive on its own. An un-unref'd timeout would delay\n * process exit, prolong a serverless function's billed lifetime, and hang test\n * runners until it fires.\n *\n * In the browser `setTimeout` returns a number with no `unref`, so this is a\n * no-op there. Callers should still `clearTimeout` the handle once the work it\n * guards has settled.\n */\nexport function unrefTimer(timer: ReturnType<typeof setTimeout>): void {\n const handle = timer as { unref?: () => void }\n if (typeof handle.unref === \"function\") {\n handle.unref()\n }\n}\n","/**\n * The single seam between the HTTP client and whichever transport implements\n * span delivery. Keeping the factory here (rather than importing `otel.ts`\n * from `http.ts` directly) is what lets the OpenTelemetry implementation\n * depend on `HttpClient`'s request path without an import cycle.\n */\n\nimport {\n createOtelTransport,\n flushOtelTransports,\n shutdownOtelTransports,\n} from \"./otel.js\"\nimport type {\n CarrierRef,\n DirectBatchSender,\n TraceTransport,\n} from \"./transportTypes.js\"\n\nexport function createTraceTransport(options: {\n directSender: DirectBatchSender\n onDelivered?: (refs: CarrierRef[]) => void\n}): TraceTransport {\n return createOtelTransport(options)\n}\n\nexport function flushTraceTransports(timeoutMs?: number): Promise<boolean> {\n return flushOtelTransports(timeoutMs)\n}\n\nexport function shutdownTraceTransports(timeoutMs?: number): Promise<boolean> {\n return shutdownOtelTransports(timeoutMs)\n}\n","/**\n * HTTP client utilities for Bitfab API requests.\n *\n * This module provides:\n * - HttpClient class for making API requests\n * - awaitOnExit helper so deferred span work still gates process exit\n */\n\nimport { type EncodedRequestBody, encodeRequestBody } from \"./compress.js\"\nimport { __packageName__, __version__ } from \"./constants.js\"\nimport type { DbSnapshotRef } from \"./dbSnapshot.js\"\nimport { BitfabError } from \"./errors.js\"\nimport {\n type DbBranchLease,\n type DbBranchSettings,\n type DbBranchTimings,\n replayContextReady,\n} from \"./replayContext.js\"\nimport { serializePayloadBody } from \"./serializePayload.js\"\nimport { mergeCallerMetadataIntoTracePayload } from \"./traceMetadata.js\"\nimport {\n createTraceTransport,\n flushTraceTransports,\n shutdownTraceTransports,\n} from \"./transport.js\"\nimport {\n type CarrierMeta,\n type CarrierRef,\n DeliveryError,\n type TraceOperation,\n type TraceTransport,\n} from \"./transportTypes.js\"\nimport { unrefTimer } from \"./unrefTimer.js\"\nimport { warnOnce } from \"./warnOnce.js\"\n\n// BitfabError lives in `errors.ts` to break the http ↔ dbSnapshot import\n// cycle. Re-exported here for backwards compatibility with existing\n// callers that import it from \"./http.js\".\nexport { BitfabError }\nexport { serializePayloadBody }\n\nconst REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS = 300_000\nconst REPLAY_COMPLETE_REQUEST_TIMEOUT_MS = 120_000\nconst OTLP_TRACES_ENDPOINT = \"/api/sdk/otel/v1/traces\"\n// OTLP's retryable set, plus 500. Every other 4xx is the server's verdict on\n// the payload and will be the same next time.\n//\n// 500 is a deliberate deviation: OTLP treats it as the app being broken, which\n// assumes a collector that fails deterministically. Bitfab ingestion answers\n// every unhandled error with 500, so a connection blip or a cold start arrives\n// here indistinguishable from a real fault, and giving up on the first one\n// drops spans that a second attempt would have delivered.\nconst RETRYABLE_STATUSES = new Set([429, 500, 502, 503, 504])\nconst EXIT_FLUSH_TIMEOUT_MS = 5_000\nconst DEFAULT_LIFECYCLE_TIMEOUT_MS = 30_000\n\n// Global set to track pending trace creation promises\n// This prevents promises from being garbage collected before they complete\nconst pendingTracePromises = new Set<Promise<unknown>>()\n\n/**\n * Track a promise so `flushTraces()` and the exit hook wait for it.\n *\n * Exactly one caller remains: the deferred `finalize` chain, which hands its\n * span to the transport only after finalize settles. Everything else submits\n * synchronously, so the transport's own queue is the complete picture. Python\n * has no equivalent because its finalize runs inline.\n *\n * @param promise - The promise to track\n * @returns The same promise (for chaining)\n */\nexport function awaitOnExit<T>(promise: Promise<T>): Promise<T> {\n pendingTracePromises.add(promise)\n // Use void to prevent unhandled rejection warnings from the .finally() chain\n // The actual error handling is done by the caller's .catch() on the returned promise\n void promise\n .finally(() => {\n pendingTracePromises.delete(promise)\n })\n .catch(() => {\n // Swallow rejection in this chain - the caller handles errors via their own .catch()\n })\n return promise\n}\n\n/**\n * Wait for pending fire-and-forget requests AND every live span transport to\n * deliver, within one total deadline. Useful in tests and scripts to ensure all\n * data has been sent before asserting or exiting.\n *\n * Returns `false` when delivery failed or the deadline expired, so a caller\n * that depends on persistence (replay does) can react instead of assuming a\n * drained queue means the server has the data.\n *\n * @param timeoutMs - Maximum total time to wait in milliseconds (default: 5000)\n */\nexport async function flushTraces(timeoutMs: number = 5000): Promise<boolean> {\n const deadline = Date.now() + Math.max(timeoutMs, 0)\n const requestsFlushed = await awaitPendingRequests(timeoutMs)\n const transportsFlushed = await flushTraceTransports(\n Math.max(0, deadline - Date.now()),\n )\n return requestsFlushed && transportsFlushed\n}\n\n/**\n * Wait for in-flight fire-and-forget requests and deferred span work, WITHOUT\n * flushing the transports.\n *\n * Replay needs this half on its own: a `finalize` span reaches the transport\n * only after its deferred chain settles, so the expected-span tally has to be\n * read after that work lands but before a flush is issued (which would be\n * wasted, and would emit a request, when the run submitted nothing).\n */\nexport async function awaitPendingRequests(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n): Promise<boolean> {\n // Async-context storage loads asynchronously, and spans traced before it\n // resolves have their recording deferred behind it. Without this, a script\n // that traces and immediately flushes or closes races its own first spans.\n await replayContextReady.catch(() => {})\n return waitForPromises(Array.from(pendingTracePromises), timeoutMs)\n}\n\n/**\n * Await `promises` within `timeoutMs`, reporting whether they all settled in\n * time rather than throwing. Rejections count as settled: a failed span upload\n * is already reported by its own catch handler, and the caller is asking about\n * completion, not success.\n */\nasync function waitForPromises(\n promises: Promise<unknown>[],\n timeoutMs: number,\n): Promise<boolean> {\n if (promises.length === 0) {\n return true\n }\n // Clear and unref the timeout so the loser of the race never leaves a\n // dangling timer holding the event loop open after flush resolves.\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return await Promise.race([\n Promise.allSettled(promises).then(() => true),\n new Promise<boolean>((resolve) => {\n timer = setTimeout(() => resolve(false), timeoutMs)\n unrefTimer(timer)\n }),\n ])\n } finally {\n if (timer) {\n clearTimeout(timer)\n }\n }\n}\n\n// Register beforeExit handler to wait for pending traces (Node.js only)\n// This ensures traces are sent before the process exits (for scripts).\n// The transport is included: its batch worker sits on an unref'd timer, so a\n// script that ends without an explicit flush would otherwise exit with a queue\n// of spans still waiting on the scheduled delay.\nif (\n typeof process !== \"undefined\" &&\n process.versions != null &&\n process.versions.node != null\n) {\n let isFlushing = false\n process.on(\"beforeExit\", () => {\n if (isFlushing) {\n return\n }\n isFlushing = true\n // Awaiting here keeps the event loop alive until delivery settles.\n void Promise.allSettled([\n ...Array.from(pendingTracePromises).map((p) => p.catch(() => {})),\n shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false),\n ]).then(() => {\n isFlushing = false\n })\n })\n}\n\n/**\n * How the API key is supplied internally: either a literal string or a\n * function resolved each time the key is needed (at request/send time). The\n * function form is what defers key resolution past module-load construction\n * so an env var loaded after the client is built (the ESM dotenv-hoisting\n * case) is still picked up.\n */\nexport type ApiKeyInput = string | (() => string | undefined)\n\nexport interface HttpClientConfig {\n apiKey?: ApiKeyInput\n serviceUrl: string\n timeout?: number\n}\n\nexport type SpanOccurrence = \"first\" | \"last\" | number\n\nexport type SpanLookup =\n | { id: string; name?: never; occurrence?: never }\n | { name: string; id?: never; occurrence?: SpanOccurrence }\n\nexport interface CapturedSpan {\n id: string\n traceId: string\n parentSpanId: string | null\n name: string | null\n type: string\n input: unknown\n output: unknown\n contexts: Record<string, unknown>[]\n prompt: string | null\n metadata: Record<string, unknown>\n metrics: Record<string, unknown> | null\n errors: unknown\n startedAt: string | null\n endedAt: string | null\n}\n\n/**\n * HTTP client for Bitfab API requests.\n *\n * Provides methods for different API endpoints with proper error handling,\n * timeouts, and authentication.\n */\n/**\n * `Retry-After` as milliseconds. The header is either a delay in seconds or an\n * HTTP date; both forms appear in the wild, so both are read. Anything else, or\n * a date already in the past, yields `undefined` so the caller falls back to\n * its own backoff.\n */\n/**\n * Read one response header without letting it break delivery. `Response.headers`\n * is always present from a real `fetch`, but polyfills and doubles are looser,\n * and an optional header must never be the reason a batch fails to send.\n */\nfunction readHeader(response: Response, name: string): string | null {\n try {\n return response.headers?.get(name) ?? null\n } catch {\n return null\n }\n}\n\nexport function parseRetryAfterMs(header: string | null): number | undefined {\n // Trimmed and emptiness-checked before Number(), which reads \"\" and \" \" as\n // 0 and would turn a blank header into \"retry immediately\", skipping the\n // backoff entirely. The other SDKs treat a blank header as no instruction.\n const value = header?.trim()\n if (!value) {\n return undefined\n }\n const seconds = Number(value)\n if (Number.isFinite(seconds)) {\n return seconds >= 0 ? seconds * 1_000 : undefined\n }\n const at = Date.parse(value)\n if (Number.isNaN(at)) {\n return undefined\n }\n return Math.max(0, at - Date.now())\n}\n\n/**\n * The delivery identity of a carrier, read from the payload here because this\n * is where the payload shape is owned. The transport is handed the result and\n * never looks inside a payload itself.\n */\n/**\n * Everything the transport needs to know about a carrier, derived here because\n * this is where the payload shape is owned. The transport applies these and\n * never looks inside a payload itself.\n */\nfunction carrierMeta(\n operation: TraceOperation,\n payload: Record<string, unknown>,\n ref: CarrierRef | undefined,\n): CarrierMeta {\n return {\n ref,\n name: carrierName(operation, payload),\n startTime: payloadTimestamp(payload, \"started_at\"),\n endTime: payloadTimestamp(payload, \"ended_at\"),\n errored: payloadHasError(payload),\n }\n}\n\nfunction carrierName(\n operation: TraceOperation,\n payload: Record<string, unknown>,\n): string {\n if (operation === \"external_span\") {\n const spanData = asPayloadRecord(\n asPayloadRecord(payload.rawSpan)?.span_data,\n )\n if (typeof spanData?.name === \"string\") {\n return spanData.name\n }\n }\n if (typeof payload.traceFunctionKey === \"string\") {\n return payload.traceFunctionKey\n }\n return `bitfab.${operation}`\n}\n\n/**\n * Milliseconds since the epoch for a payload timestamp, or `undefined` to let\n * OTel stamp the carrier with the current time.\n */\nfunction payloadTimestamp(\n payload: Record<string, unknown>,\n field: string,\n): number | undefined {\n const rawSpan = asPayloadRecord(payload.rawSpan)\n const rawTrace =\n asPayloadRecord(payload.externalTrace) ?? asPayloadRecord(payload.rawTrace)\n const raw = rawSpan?.[field] ?? rawTrace?.[field]\n if (typeof raw !== \"string\") {\n return undefined\n }\n const parsed = Date.parse(raw)\n return Number.isNaN(parsed) ? undefined : parsed\n}\n\nfunction payloadHasError(payload: Record<string, unknown>): boolean {\n const spanData = asPayloadRecord(asPayloadRecord(payload.rawSpan)?.span_data)\n if (spanData?.error != null) {\n return true\n }\n const errors = payload.errors\n return Array.isArray(errors) ? errors.length > 0 : Boolean(errors)\n}\n\nfunction asPayloadRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n}\n\n/** What a caller learns about one tracked trace once it takes it back. */\nexport interface DeliveryReport {\n spanCount: number\n /** A closing carrier was submitted, so the expected set is final. */\n closed: boolean\n /** Every carrier submitted under this trace came back accepted. */\n delivered: boolean\n /**\n * The server's assigned `traces.id`, read back from the OTLP ingest response.\n * Absent when talking to a server that predates this field, or before any\n * carrier for the trace has been acked.\n */\n serverTraceId?: string\n}\n\ninterface TraceDelivery {\n submittedSpanIds: Set<string>\n ackedSpanIds: Set<string>\n closed: boolean\n closingAcked: boolean\n serverTraceId?: string\n}\n\nfunction carrierRef(payload: Record<string, unknown>): CarrierRef | undefined {\n const traceId = sourceTraceIdOf(payload)\n if (traceId === undefined) {\n return undefined\n }\n const rawSpan = payload.rawSpan\n if (rawSpan === undefined) {\n return { traceId }\n }\n const spanId = (rawSpan as Record<string, unknown>)?.id\n return {\n traceId,\n spanId: typeof spanId === \"string\" ? spanId : `submission-${++carrierSeq}`,\n }\n}\n\nfunction sourceTraceIdOf(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.sourceTraceId === \"string\") {\n return payload.sourceTraceId\n }\n const rawTrace = (payload.externalTrace ?? payload.rawTrace) as\n | Record<string, unknown>\n | undefined\n const id = rawTrace?.id\n return typeof id === \"string\" ? id : undefined\n}\n\nlet carrierSeq = 0\n\nexport class HttpClient {\n private readonly apiKey: ApiKeyInput | undefined\n private readonly serviceUrl: string\n private readonly timeout: number\n private traceTransport: TraceTransport | undefined\n // Only traces a caller asked about are tracked, so ordinary tracing stores\n // nothing here.\n private readonly traceDeliveries = new Map<string, TraceDelivery>()\n // Deferred span work owned by THIS client. The module-global set backs the\n // process-wide `flushTraces()` and the exit hook, but per-client lifecycle\n // must not wait on another client's slow finalize: a false `close()` failure\n // caused by unrelated work is worse than no signal at all.\n private readonly deferredWork = new Set<Promise<unknown>>()\n private closed = false\n private closing: Promise<boolean> | undefined\n\n constructor(config: HttpClientConfig) {\n this.apiKey = config.apiKey\n this.serviceUrl = config.serviceUrl\n this.timeout = config.timeout ?? 120000\n }\n\n /**\n * Resolve the API key at the moment it is needed (request time), invoking\n * the function form if one was supplied. Never read at construction.\n */\n private resolveApiKey(): string | undefined {\n return typeof this.apiKey === \"function\" ? this.apiKey() : this.apiKey\n }\n\n /**\n * This client's span transport, built on first use.\n *\n * Lazy on purpose: a client that never sends a span must never start a batch\n * worker. Every framework integration created from a `Bitfab` client shares\n * the owning client's `HttpClient`, so handlers reuse this one worker instead\n * of each spinning up their own.\n */\n private getTraceTransport(): TraceTransport | undefined {\n if (this.closed) {\n warnOnce(\n \"http-client-closed\",\n \"the Bitfab client is closed; dropping spans\",\n )\n return undefined\n }\n if (!this.traceTransport) {\n this.traceTransport = createTraceTransport({\n directSender: (request, timeoutMs) =>\n this.deliverCarriers(request, timeoutMs),\n onDelivered: (refs) => this.recordDeliveredCarriers(refs),\n })\n }\n return this.traceTransport\n }\n\n /**\n * Post one encoded batch and decide what the server's answer means, so the\n * transport never reads a response. Rejections and permanent statuses come\n * back as a non-retryable {@link DeliveryError}; anything the server might\n * still accept on a second try comes back retryable.\n */\n private async deliverCarriers(\n request: EncodedRequestBody,\n timeoutMs: number,\n ): Promise<void> {\n let response: Record<string, unknown>\n try {\n // sendPrepared, not sendEncoded: the exporter already encoded this batch\n // to size the request, and re-encoding here would do that work twice.\n response = await this.sendPrepared<Record<string, unknown>>(\n OTLP_TRACES_ENDPOINT,\n request,\n { timeout: timeoutMs },\n )\n } catch (error) {\n const status = error instanceof BitfabError ? error.status : undefined\n if (status === undefined) {\n // No verdict from the server (a network fault): worth another attempt.\n throw new DeliveryError(`OTLP ingestion failed: ${String(error)}`, {\n retryable: true,\n })\n }\n throw new DeliveryError(`OTLP ingestion failed with HTTP ${status}`, {\n retryable: RETRYABLE_STATUSES.has(status),\n oversized: status === 413,\n ...(error instanceof BitfabError && error.retryAfterMs !== undefined\n ? { retryAfterMs: error.retryAfterMs }\n : {}),\n })\n }\n\n const serverTraceIds = asPayloadRecord(response?.traceIds)\n if (serverTraceIds !== undefined) {\n this.recordServerTraceIds(serverTraceIds)\n }\n\n const partialSuccess = asPayloadRecord(response?.partialSuccess)\n const rejected = partialSuccess?.rejectedSpans\n if (rejected !== undefined && rejected !== \"0\" && rejected !== 0) {\n // The server's verdict on the payload, not a transient fault.\n throw new DeliveryError(\n `OTLP ingestion rejected ${rejected} span(s): ${\n partialSuccess?.errorMessage ?? \"no reason provided\"\n }`,\n )\n }\n }\n\n /**\n * Start tracking delivery for `traceIds`. Nothing is recorded for a trace\n * that was never tracked, so ordinary tracing costs no bookkeeping at all.\n */\n trackTraceDeliveries(traceIds: string[]): void {\n for (const traceId of traceIds) {\n if (!this.traceDeliveries.has(traceId)) {\n this.traceDeliveries.set(traceId, {\n submittedSpanIds: new Set(),\n ackedSpanIds: new Set(),\n closed: false,\n closingAcked: false,\n })\n }\n }\n }\n\n /**\n * The server's assigned `traces.id` for a tracked trace if it has already\n * been read back off an ingest response, without stopping tracking. Lets a\n * replay surface the id mid-run for items whose spans already landed.\n */\n peekServerTraceId(traceId: string): string | undefined {\n return this.traceDeliveries.get(traceId)?.serverTraceId\n }\n\n /** Whether any tracked trace has had its closing carrier submitted. */\n hasClosedDeliveries(traceIds: string[]): boolean {\n return traceIds.some((traceId) => this.traceDeliveries.get(traceId)?.closed)\n }\n\n /**\n * Report what each tracked trace submitted and whether the server confirmed\n * it, and stop tracking them. Every id passed is freed, so a caller cannot\n * leak a record for a trace that never closed.\n *\n * `delivered` is only meaningful once a flush has settled: acks land before\n * an export resolves, so a flush that reported success has already collected\n * every ack it is going to collect.\n */\n takeTraceDeliveries(traceIds: string[]): Record<string, DeliveryReport> {\n const reports: Record<string, DeliveryReport> = {}\n for (const traceId of traceIds) {\n const delivery = this.traceDeliveries.get(traceId)\n if (delivery === undefined) {\n continue\n }\n this.traceDeliveries.delete(traceId)\n reports[traceId] = {\n spanCount: delivery.submittedSpanIds.size,\n closed: delivery.closed,\n delivered:\n delivery.closingAcked &&\n [...delivery.submittedSpanIds].every((spanId) =>\n delivery.ackedSpanIds.has(spanId),\n ),\n serverTraceId: delivery.serverTraceId,\n }\n }\n return reports\n }\n\n /** Build a carrier's meta and record what it adds to its trace's expected set. */\n private recordedMeta(\n operation: TraceOperation,\n payload: Record<string, unknown>,\n ref: CarrierRef | undefined,\n ): CarrierMeta {\n this.recordSubmittedCarrier(ref)\n return carrierMeta(operation, payload, ref)\n }\n\n private recordSubmittedCarrier(ref: CarrierRef | undefined): void {\n if (ref === undefined) {\n return\n }\n const delivery = this.traceDeliveries.get(ref.traceId)\n if (delivery === undefined) {\n return\n }\n if (ref.spanId === undefined) {\n delivery.closed = true\n } else {\n delivery.submittedSpanIds.add(ref.spanId)\n }\n }\n\n /**\n * Ingestion commits every carrier in a request before it answers, so a\n * delivered ref is proof its row exists: the same fact the replay status\n * endpoint would report, already in hand.\n */\n /**\n * Record the server's assigned `traces.id` for each tracked source trace,\n * read back from the OTLP ingest response. Keyed by source trace id, the same\n * key the delivery ledger uses. Untracked ids are ignored.\n */\n private recordServerTraceIds(map: Record<string, unknown>): void {\n for (const [sourceTraceId, serverTraceId] of Object.entries(map)) {\n if (typeof serverTraceId !== \"string\") {\n continue\n }\n const delivery = this.traceDeliveries.get(sourceTraceId)\n if (delivery === undefined) {\n continue\n }\n delivery.serverTraceId = serverTraceId\n }\n }\n\n private recordDeliveredCarriers(refs: CarrierRef[]): void {\n for (const ref of refs) {\n const delivery = this.traceDeliveries.get(ref.traceId)\n if (delivery === undefined) {\n continue\n }\n if (ref.spanId === undefined) {\n delivery.closingAcked = true\n } else {\n delivery.ackedSpanIds.add(ref.spanId)\n }\n }\n }\n\n /**\n * Track deferred span work so this client's own lifecycle waits for it, and\n * so the process-wide flush and exit hook do too.\n */\n trackDeferred<T>(promise: Promise<T>): Promise<T> {\n this.deferredWork.add(promise)\n void promise\n .finally(() => this.deferredWork.delete(promise))\n .catch(() => {})\n return awaitOnExit(promise)\n }\n\n /**\n * Settle only THIS client's deferred span work. Scoped deliberately: the\n * global set can contain another client's long-running finalize, and\n * attributing its timeout here would fail a client whose own work succeeded.\n */\n async settleDeferredWork(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n ): Promise<boolean> {\n await replayContextReady.catch(() => {})\n return waitForPromises(Array.from(this.deferredWork), timeoutMs)\n }\n\n /**\n * Wait for spans queued by this client to be delivered, within one deadline.\n * Returns false on delivery failure or timeout.\n */\n async waitForPendingRequests(\n timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS,\n ): Promise<boolean> {\n const deadline = Date.now() + Math.max(timeoutMs, 0)\n const settled = await this.settleDeferredWork(timeoutMs)\n const flushed =\n (await this.traceTransport?.flush(Math.max(0, deadline - Date.now()))) ??\n true\n return settled && flushed\n }\n\n /**\n * Flush and permanently close this client's tracing transport. Idempotent:\n * a second call joins the first rather than tearing down a pipeline the\n * first call already owns.\n */\n close(timeoutMs: number = DEFAULT_LIFECYCLE_TIMEOUT_MS): Promise<boolean> {\n if (this.closing) {\n return this.closing\n }\n const deadline = Date.now() + Math.max(timeoutMs, 0)\n this.closing = (async () => {\n // Settle deferred span work BEFORE refusing submissions. A span whose\n // recording is still queued (the `finalize` chain, or any call made\n // before async-context storage finished loading) has not reached the\n // transport yet; flipping `closed` first would reject it on arrival and\n // silently drop a span the caller had every reason to think was captured.\n const settled = await this.settleDeferredWork(\n Math.max(0, deadline - Date.now()),\n )\n this.closed = true\n const transport = this.traceTransport\n this.traceTransport = undefined\n const shutdownOk =\n (await transport?.shutdown(Math.max(0, deadline - Date.now()))) ?? true\n // Deferred work that outran the deadline will submit into a closed\n // client and be dropped, so close cannot report success for it.\n return settled && shutdownOk\n })()\n return this.closing\n }\n\n /**\n * Make an HTTP request to the Bitfab API. Defaults to POST; pass\n * `options.method` to use a different verb (e.g. \"PATCH\").\n *\n * @param endpoint - The API endpoint (without base URL)\n * @param payload - The request body\n * @param options - Optional request options\n * @returns The parsed JSON response\n * @throws {BitfabError} If the request fails\n */\n async request<T>(\n endpoint: string,\n payload: Record<string, unknown>,\n options?: { timeout?: number; method?: \"POST\" | \"PATCH\" | \"PUT\" },\n ): Promise<T> {\n // Serialize the payload so a stray non-serializable value (BigInt,\n // function, circular ref, a class instance that slipped past upstream\n // serialization) can never abort the send and silently drop the span.\n // Strays are stubbed in place, preserving span content, and a degraded\n // payload warns loudly.\n const { body, dropped } = serializePayloadBody(payload)\n if (dropped.length > 0) {\n try {\n console.warn(\n `Bitfab: request body to ${endpoint} held ${dropped.length} ` +\n `non-serializable value(s) (${[...new Set(dropped)].join(\", \")}); ` +\n \"they were stubbed so the span still sends, but the trace may be \" +\n \"incomplete or not replayable. Capture a JSON-safe projection of \" +\n \"this input to make it replayable.\",\n )\n } catch {}\n }\n return this.sendEncoded<T>(endpoint, body, options)\n }\n\n /**\n * POST an already-encoded body. The span transport encodes its own batches,\n * so routing them back through {@link HttpClient.request} would encode the\n * same data twice.\n */\n async sendEncoded<T>(\n endpoint: string,\n body: string,\n options?: { timeout?: number; method?: \"POST\" | \"PATCH\" | \"PUT\" },\n ): Promise<T> {\n // Awaited only when compression actually runs, so an uncompressed request\n // still calls `fetch` synchronously the way it did before compression.\n const prepared = encodeRequestBody(body)\n const encoded = prepared instanceof Promise ? await prepared : prepared\n return this.sendPrepared<T>(endpoint, encoded, options)\n }\n\n private async sendPrepared<T>(\n endpoint: string,\n encoded: EncodedRequestBody,\n options?: { timeout?: number; method?: \"POST\" | \"PATCH\" | \"PUT\" },\n ): Promise<T> {\n const url = `${this.serviceUrl}${endpoint}`\n const timeout = options?.timeout ?? this.timeout\n const method = options?.method ?? \"POST\"\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.resolveApiKey() ?? \"\"}`,\n }\n if (encoded.contentEncoding) {\n headers[\"Content-Encoding\"] = encoded.contentEncoding\n }\n\n try {\n const response = await fetch(url, {\n method,\n headers,\n body: encoded.body,\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new BitfabError(\n `HTTP ${response.status}: ${errorText.slice(0, 500)}`,\n undefined,\n response.status,\n parseRetryAfterMs(readHeader(response, \"retry-after\")),\n )\n }\n\n const result = await response.json()\n\n // Check for errors in the response\n if (result.error) {\n if (result.url) {\n throw new BitfabError(\n `${result.error} Configure it at: ${this.serviceUrl}${result.url}`,\n result.url,\n )\n }\n throw new BitfabError(result.error)\n }\n\n return result as T\n } catch (error) {\n if (error instanceof BitfabError) {\n throw error\n }\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new BitfabError(`Request timed out after ${timeout}ms`)\n }\n throw new BitfabError(error.message)\n }\n throw new BitfabError(\"Unknown error occurred\")\n } finally {\n clearTimeout(timeoutId)\n }\n }\n\n /**\n * Look up a function by name.\n * Blocks until complete - needed for function execution.\n */\n async lookupFunction<T>(name: string): Promise<T> {\n return this.request<T>(\"/api/sdk/functions/lookup\", { name })\n }\n\n async getAutoTracePolicy<T>(\n traceFunctionKey: string,\n protocol: string,\n ): Promise<T> {\n return this.request<T>(\"/api/sdk/auto-trace/policy\", {\n traceFunctionKey,\n protocol,\n })\n }\n\n async getTraceSpan(\n traceId: string,\n lookup: SpanLookup,\n ): Promise<CapturedSpan | null> {\n const searchParams = new URLSearchParams()\n if (lookup.id !== undefined) {\n searchParams.set(\"id\", lookup.id)\n } else {\n searchParams.set(\"name\", lookup.name)\n searchParams.set(\"occurrence\", String(lookup.occurrence ?? \"last\"))\n }\n\n const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}/span?${searchParams.toString()}`\n const response = await this.get<{ span: CapturedSpan | null }>(endpoint)\n return response.span\n }\n\n /**\n * GET a JSON endpoint on the service with the client's API key. Throws a\n * `BitfabError` carrying the status text for any non-2xx response.\n */\n async get<T>(endpoint: string): Promise<T> {\n const url = `${this.serviceUrl}${endpoint}`\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.timeout)\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.resolveApiKey() ?? \"\"}` },\n signal: controller.signal,\n })\n if (!response.ok) {\n const errorText = await response.text()\n throw new BitfabError(\n `HTTP ${response.status}: ${errorText.slice(0, 500)}`,\n undefined,\n response.status,\n parseRetryAfterMs(readHeader(response, \"retry-after\")),\n )\n }\n return (await response.json()) as T\n } catch (error) {\n if (error instanceof BitfabError) {\n throw error\n }\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new BitfabError(`Request timed out after ${this.timeout}ms`)\n }\n throw new BitfabError(error.message)\n }\n throw new BitfabError(\"Unknown error occurred\")\n } finally {\n clearTimeout(timeoutId)\n }\n }\n\n /**\n * Queue an internal trace (from local BAML execution via `call()`) onto this\n * client's batching transport. `functionId` moves into the payload because\n * the OTLP carrier has no path to carry it.\n */\n sendInternalTrace(\n functionId: string,\n payload: Record<string, unknown>,\n ): void {\n const body = {\n ...payload,\n functionId,\n sdkPackage: __packageName__,\n sdkVersion: __version__,\n }\n this.getTraceTransport()?.submit(\n \"internal_trace\",\n body,\n carrierMeta(\"internal_trace\", body, undefined),\n )\n }\n\n /**\n * Queue an external span (from withSpan wrapper or OpenAI tracing) onto this\n * client's batching transport. Fire-and-forget: the transport owns delivery,\n * so callers await `flushTraces()` or `close()` rather than a per-span\n * promise.\n */\n sendExternalSpan(payload: Record<string, unknown>): void {\n this.getTraceTransport()?.submit(\n \"external_span\",\n { ...payload, sdkVersion: __version__ },\n this.recordedMeta(\"external_span\", payload, carrierRef(payload)),\n )\n }\n\n /**\n * Queue an external trace completion (from OpenAI tracing) onto this\n * client's batching transport. Fire-and-forget for the same reason as\n * {@link HttpClient.sendExternalSpan}; replay confirms persistence with the\n * server-authoritative barrier in `replay.ts`, not by awaiting this call.\n */\n sendExternalTrace(rawPayload: Record<string, unknown>): void {\n const payload = mergeCallerMetadataIntoTracePayload(rawPayload)\n this.getTraceTransport()?.submit(\n \"external_trace\",\n {\n ...payload,\n sdkPackage: __packageName__,\n sdkVersion: __version__,\n },\n this.recordedMeta(\n \"external_trace\",\n payload,\n payload.completed === true ? carrierRef(payload) : undefined,\n ),\n )\n }\n\n /**\n * Partial update of an existing trace identified by its Bitfab trace ID.\n * Used by the detached `client.getTrace(id)` handle.\n *\n * Blocking, like the other trace-API calls: it resolves once the server has\n * applied the change and rejects if the server refused it. A patch targets a\n * trace that is already closed, so there is no batch for it to ride along\n * with and no later signal that would reveal a silent failure.\n */\n async patchTrace(\n traceId: string,\n payload: {\n appendContexts?: Record<string, unknown>[]\n mergeMetadata?: Record<string, unknown>\n setSessionId?: string\n setName?: string\n },\n ): Promise<void> {\n const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}`\n await this.request(endpoint, payload, { method: \"PATCH\" })\n }\n\n /**\n * Start a replay session by fetching historical traces.\n * Blocking call - creates a test run and returns lightweight item references.\n */\n async startReplay(\n traceFunctionKey: string,\n limit: number | undefined,\n traceIds?: string[],\n name?: string,\n codeChangeDescription?: string | null,\n codeChangeFiles?: CodeChangeFile[] | null,\n includeDbBranchLease?: boolean,\n experimentGroupId?: string,\n datasetIds?: string[],\n graderIds?: string[],\n dbBranchSettings?: DbBranchSettings,\n attempts?: number,\n includeOriginalMetadata?: boolean,\n onlyWithAssertions?: boolean,\n ): Promise<StartReplayResponse> {\n // limit is only meaningful without traceIds (an explicit ID list\n // already determines the count), so it's omitted when undefined.\n const payload: Record<string, unknown> = { traceFunctionKey }\n if (limit !== undefined) {\n payload.limit = limit\n }\n if (traceIds) {\n payload.traceIds = traceIds\n }\n if (name !== undefined) {\n payload.name = name\n }\n if (codeChangeDescription !== undefined) {\n payload.codeChangeDescription = codeChangeDescription\n }\n if (codeChangeFiles !== undefined) {\n payload.codeChangeFiles = codeChangeFiles\n }\n if (includeDbBranchLease) {\n payload.includeDbBranchLease = true\n payload.lazyDbBranchLease = true\n }\n if (experimentGroupId !== undefined) {\n payload.experimentGroupId = experimentGroupId\n }\n if (datasetIds !== undefined) {\n if (datasetIds.length === 1) {\n payload.datasetId = datasetIds[0]\n } else {\n payload.datasetIds = datasetIds\n }\n }\n if (graderIds !== undefined) {\n payload.graderIds = graderIds\n }\n if (dbBranchSettings !== undefined) {\n payload.dbBranchSettings = dbBranchSettings\n }\n if (attempts !== undefined && attempts > 1) {\n payload.attempts = attempts\n }\n if (includeOriginalMetadata) {\n payload.includeOriginalMetadata = true\n }\n if (onlyWithAssertions) {\n payload.onlyWithAssertions = true\n }\n // When DB branching is on, the server resolves a Neon preview branch\n // per item (snapshot + restore + poll), which can run ~5-10s each, and\n // runs any `warmupSql` against each branch on a 240s budget of its own.\n // The server gives up at 280s and answers, so this is a backstop for a\n // reply that never comes rather than the thing that normally fires; it\n // sits above the server's own ceiling so the server's error is the one\n // callers see. Not raisable in practice either: undici's default\n // `headersTimeout` is also 300s and `fetch` cannot override it per\n // request.\n const timeout = includeDbBranchLease\n ? REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS\n : 30_000\n return this.request<StartReplayResponse>(\"/api/sdk/replay/start\", payload, {\n timeout,\n })\n }\n\n /**\n * Fetch an external span by ID.\n * Blocking GET request.\n * The replay view limits rawData to input/output serialization fields.\n */\n async getExternalSpan(\n spanId: string,\n options?: { view?: \"full\" | \"replay\" },\n ): Promise<ExternalSpanResponse> {\n const query = options?.view === \"replay\" ? \"?view=replay\" : \"\"\n const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}${query}`\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 30_000)\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.resolveApiKey() ?? \"\"}` },\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new BitfabError(\n `HTTP ${response.status}: ${errorText.slice(0, 500)}`,\n )\n }\n\n return (await response.json()) as ExternalSpanResponse\n } catch (error) {\n if (error instanceof BitfabError) {\n throw error\n }\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new BitfabError(\"Request timed out after 30000ms\")\n }\n throw new BitfabError(error.message)\n }\n throw new BitfabError(\"Unknown error occurred\")\n } finally {\n clearTimeout(timeoutId)\n }\n }\n\n /**\n * Fetch the span tree for a root span.\n * Blocking GET request.\n *\n * Pass `includeOutputs: false` for a payload-free tree (structure +\n * `externalSpanId` only), so recorded outputs are fetched lazily per mocked\n * span instead of all up front. Omit it (default eager) for `mock: \"all\"`.\n * Pass `includeRootOutput: false` when the root was already fetched.\n */\n async getSpanTree(\n externalSpanId: string,\n options?: { includeOutputs?: boolean; includeRootOutput?: boolean },\n ): Promise<SpanTreeResponse> {\n const searchParams = new URLSearchParams()\n if (options?.includeOutputs === false) {\n searchParams.set(\"includeOutputs\", \"false\")\n }\n if (options?.includeRootOutput === false) {\n searchParams.set(\"includeRootOutput\", \"false\")\n }\n const encodedQuery = searchParams.toString()\n const query = encodedQuery ? `?${encodedQuery}` : \"\"\n const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}${query}`\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 30_000)\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.resolveApiKey() ?? \"\"}` },\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new BitfabError(\n `HTTP ${response.status}: ${errorText.slice(0, 500)}`,\n )\n }\n\n return (await response.json()) as SpanTreeResponse\n } catch (error) {\n if (error instanceof BitfabError) {\n throw error\n }\n if (error instanceof Error) {\n if (error.name === \"AbortError\") {\n throw new BitfabError(\"Request timed out after 30000ms\")\n }\n throw new BitfabError(error.message)\n }\n throw new BitfabError(\"Unknown error occurred\")\n } finally {\n clearTimeout(timeoutId)\n }\n }\n\n /**\n * Read which of a replay run's traces the server has fully persisted.\n *\n * With `expectedSpanCounts`, a trace appears in the response only once it\n * has a final status AND at least that many persisted spans, which is what\n * makes this a real barrier rather than a \"the row exists\" check.\n */\n async getReplayStatus(\n testRunId: string,\n expectedSpanCounts: Record<string, number>,\n ): Promise<ReplayStatusResponse> {\n return this.request<ReplayStatusResponse>(\n \"/api/sdk/replay/status\",\n { testRunId, expectedSpanCounts },\n { timeout: 30_000 },\n )\n }\n\n /**\n * Mark a replay test run as completed.\n * Blocking call.\n */\n async completeReplay(testRunId: string): Promise<CompleteReplayResponse> {\n return this.request<CompleteReplayResponse>(\n \"/api/sdk/replay/complete\",\n { testRunId },\n { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS },\n )\n }\n\n /**\n * Ask the server to materialize a per-trace DB branch lease from a\n * captured `dbSnapshotRef`. Blocking - the resolver creates a Neon\n * snapshot + preview branch and polls operations to readiness, which\n * can take seconds.\n */\n async resolveDbBranchLease(\n testRunId: string,\n traceId: string,\n dbBranchSettings?: DbBranchSettings,\n attempt?: number,\n ): Promise<{\n dbSnapshotRef: DbSnapshotRef | null\n lease: DbBranchLease | null\n leaseError: { code: string; message: string } | null\n timings: DbBranchTimings | null\n }> {\n return this.request<{\n dbSnapshotRef: DbSnapshotRef | null\n lease: DbBranchLease | null\n leaseError: { code: string; message: string } | null\n timings: DbBranchTimings | null\n }>(\n \"/api/sdk/replay/resolveDbBranchLease\",\n {\n testRunId,\n traceId,\n dbBranchSettings,\n ...(attempt !== undefined && attempt > 0 ? { attempt } : {}),\n },\n { timeout: REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS },\n )\n }\n\n /** Release a previously-resolved DB branch by deleting its Neon branch. Idempotent server-side. */\n async releaseDbBranchLease(neonBranchId: string): Promise<void> {\n await this.request<{ released: true }>(\n \"/api/sdk/replay/releaseDbBranchLease\",\n { neonBranchId },\n { timeout: 30_000 },\n )\n }\n}\n\nexport interface TokenUsage {\n input: number | null\n output: number | null\n cached: number | null\n total: number | null\n}\n\nexport interface TraceOutlineSpanError {\n source: string\n error: string\n step?: string\n}\n\nexport interface TraceOutlineSpan {\n spanId: string\n name: string | null\n type: string\n traceFunctionKey: string | null\n durationMs: number | null\n tokens: TokenUsage | null\n model: string | null\n errors: TraceOutlineSpanError[] | null\n mocked: boolean\n children: TraceOutlineSpan[]\n}\n\nexport interface TraceOutline {\n traceId: string\n name: string | null\n status: string\n traceFunctionKey: string | null\n durationMs: number | null\n spanCount: number\n spans: TraceOutlineSpan[]\n}\n\n/**\n * Describes a single file edited as part of a code change.\n *\n * - `path`: file path (relative to the repo root, or any consistent root)\n * - `before`: file contents before the change (\"\" for newly created files)\n * - `after`: file contents after the change (\"\" for deleted files)\n */\nexport interface CodeChangeFile {\n path: string\n before: string\n after: string\n}\n\nexport interface StartReplayResponse {\n testRunId: string\n testRunUrl: string\n attempts?: number\n items: Array<{\n /** Bitfab trace ID of the original (historical) trace being replayed. */\n originalTraceId?: string\n /** External span ID the recorded inputs were read from (the original root span). */\n originalSpanId?: string\n /** @deprecated alias for `originalTraceId`; the only key emitted by servers that predate the rename. */\n sourceTraceId: string\n /** @deprecated alias for `originalSpanId`; the only key emitted by servers that predate the rename. */\n sourceSpanId: string\n durationMs: number | null\n tokens: TokenUsage | null\n model: string | null\n /**\n * The DB snapshot ref captured by the SDK at trace open. Surfaced so\n * the SDK can pass it to the lease-resolver step (or report when no\n * snapshot was captured for this trace).\n */\n dbSnapshotRef?: DbSnapshotRef\n /**\n * Populated once the server-side resolver has materialized a per-item\n * branch from `dbSnapshotRef`. The SDK exposes this to customer code\n * via `getCurrentReplayBranch()`. Absent until the resolver lands.\n */\n dbBranchLease?: DbBranchLease\n /**\n * Why the branch could not be resolved, when one was requested and the\n * attempt failed. Distinct from both fields being absent, which means the\n * trace carried no snapshot ref so nothing was attempted.\n */\n dbBranchLeaseError?: { code: string; message: string }\n /**\n * How long provisioning took, per phase. Sits beside the two fields above\n * rather than inside either: it is reported on both outcomes, complete on\n * success and partial up to the failing phase on error. Absent from\n * servers that predate it.\n */\n dbBranchTimings?: DbBranchTimings\n originalMetadata?: Record<string, unknown>\n }>\n}\n\nexport interface ExternalSpanResponse {\n id: string\n externalTraceId: string\n rawData: {\n span_data: {\n input: unknown\n output: unknown\n input_meta?: unknown\n output_meta?: unknown\n input_serialized?: { json: unknown; meta: unknown }\n output_serialized?: { json: unknown; meta: unknown }\n }\n }\n}\n\nexport interface ReplayStatusResponse {\n /**\n * Local replay trace id -> server trace row id, for the traces the server\n * considers fully persisted. Traces still short of their expected span count\n * are simply absent.\n */\n traceIds?: Record<string, string>\n}\n\nexport interface CompleteReplayResponse {\n id: string\n status: string\n traceIds?: Record<string, string>\n /**\n * Per-replay-trace token usage, keyed by the server trace id (the values of\n * `traceIds`). Aggregated server-side from the freshly-uploaded replay spans,\n * so it's the REPLAYED run's tokens (the same source Studio reads), not the\n * original trace's. The SDK maps each item onto this to set\n * `ReplayItem.tokens`. Absent on servers that predate this field.\n */\n tokens?: Record<string, TokenUsage | null>\n traceOutlines?: Record<string, TraceOutline>\n originalTraceOutlines?: Record<string, TraceOutline>\n /**\n * Number of traces the server has persisted for this test run at\n * completion time. Lets the SDK distinguish \"uploads failed\" from\n * \"server never saw them\" when the trace-ID mapping is incomplete.\n */\n traceCount?: number\n}\n\nexport interface SpanTreeNode {\n /** Upstream platform span id. Stable structural identity; NOT the row id. */\n sourceSpanId: string\n /**\n * The `externalSpans` row id, accepted by {@link HttpClient.getExternalSpan}.\n * Distinct from `sourceSpanId`; used to lazily fetch this node's output when\n * the tree was fetched with `includeOutputs: false`. Optional so trees from\n * older servers (which omit it) still deserialize.\n */\n externalSpanId?: string\n traceFunctionKey: string\n spanName: string\n type: string\n /** Omitted when the tree was fetched payload-free (`includeOutputs: false`). */\n output?: unknown\n outputMeta?: unknown\n children: SpanTreeNode[]\n}\n\nexport interface SpanTreeResponse {\n root: SpanTreeNode\n}\n"],"mappings":";;;;;;AAKO,SAAS,QAAQ,MAAkC;AACxD,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,WAAO,QAAQ,IAAI,IAAI;AAAA,EACzB;AACA,SAAO;AACT;;;ACRA,IAAM,0BAA0B;AAOhC,IAAM,uBAAuB;AAiB7B,IAAI;AASG,IAAM,kBACX,OAAO,YAAY,eAAe,QAAQ,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhD;AAAA;AAAA,IAEE,CAAC,QAAQ,MAAM,EAAE,KAAK,GAAG;AAAA,IAExB,KAAK,CAAC,EAAE,KAAK,MAAgB;AAC5B,eAAW,CAAC,SACV,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/B,WAAK,MAAM,CAAC,OAAO,WAAW;AAC5B,YAAI,OAAO;AACT,iBAAO,KAAK;AAAA,QACd,OAAO;AACL,kBAAQ,MAAM;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACL,CAAC,EACA,MAAM,MAAM;AAAA,EAAC,CAAC;AAAA,IACjB,QAAQ,QAAQ,GACpB,KAAK,MAAM;AAAC,CAAC;AASf,SAAS,cAAc,MAA+B;AACpD,SAAO,KAAK,OAAO;AAAA,IACjB,KAAK;AAAA,IACL,KAAK,aAAa,KAAK;AAAA,EACzB;AACF;AAEA,SAAS,kBACP,MACA,UACA,YACoB;AACpB,MAAI,WAAW,cAAc,UAAU;AACrC,WAAO,EAAE,MAAM,UAAU,WAAW,SAAS;AAAA,EAC/C;AACA,SAAO;AAAA,IACL,MACE,sBAAsB,aAAa,cAAc,UAAU,IAAI;AAAA,IACjE,iBAAiB;AAAA,IACjB;AAAA,IACA,WAAW,WAAW;AAAA,EACxB;AACF;AAEA,eAAe,cAAc,OAAyC;AACpE,QAAM,SAAS,IAAI,KAAK,CAAC,KAAiB,CAAC,EACxC,OAAO,EACP,YAAY,IAAI,kBAAkB,MAAM,CAAC;AAC5C,SAAO,MAAM,IAAI,SAAS,MAAM,EAAE,YAAY;AAChD;AAWO,SAAS,kBACd,MACkD;AAClD,MAAI,QAAQ,uBAAuB,GAAG;AACpC,UAAM,WAAW,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE;AAChD,WAAO,EAAE,MAAM,UAAU,WAAW,SAAS;AAAA,EAC/C;AACA,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI;AAC3C,MAAI,MAAM,aAAa,sBAAsB;AAC3C,WAAO;AAAA,MACL;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,IACnB;AAAA,EACF;AACA,MAAI,UAAU;AACZ,WAAO,SAAS,KAAK,EAAE;AAAA,MACrB,CAAC,eAAe,kBAAkB,MAAM,MAAM,YAAY,UAAU;AAAA,MACpE,OAAO;AAAA,QACL;AAAA,QACA,UAAU,MAAM;AAAA,QAChB,WAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,sBAAsB,aAAa;AAC5C,WAAO;AAAA,MACL;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,IACnB;AAAA,EACF;AACA,SAAO,cAAc,KAAK,EAAE;AAAA,IAC1B,CAAC,eAAe,kBAAkB,MAAM,MAAM,YAAY,UAAU;AAAA,IACpE,OAAO;AAAA,MACL;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,IACnB;AAAA,EACF;AACF;;;AC3IO,IAAM,cAAc;AAKpB,IAAM,kBAAkB;;;ACPxB,IAAM,sBAAsB;;;ACD5B,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YACE,SACgB,KAOA,QAMA,cAChB;AACA,UAAM,OAAO;AAfG;AAOA;AAMA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;AC4IA,IAAI,uBACF;AACF,IAAM,gCAAgC,uBAAO,IAAI,6BAA6B;AAEvE,IAAM,qBAAoC,kBAAkB,KAAK,MAAM;AAC5E,QAAM,SAAS;AACf,QAAM,WAAW,OAAO,6BAA6B;AAGrD,MAAI,UAAU;AACZ,2BAAuB;AACvB;AAAA,EACF;AACA,QAAM,UAAU,wBAA8C;AAC9D,MAAI,SAAS;AACX,WAAO,6BAA6B,IAAI;AACxC,2BAAuB;AAAA,EACzB;AACF,CAAC;AAGM,SAAS,mBAAyC;AACvD,SAAO,sBAAsB,SAAS,KAAK;AAC7C;AAGO,SAAS,qBAAwB,KAAoB,IAAgB;AAC1E,MAAI,sBAAsB;AACxB,WAAO,qBAAqB,IAAI,KAAK,EAAE;AAAA,EACzC;AACA,SAAO,GAAG;AACZ;;;ACrLO,IAAM,yBAAyB;AAO/B,IAAM,sCAAsC;AAEnD,IAAM,cACJ,OAAO,gBAAgB,cAAc,IAAI,YAAY,IAAI;AAEpD,SAAS,WAAW,OAAuB;AAChD,SAAO,cAAc,YAAY,OAAO,KAAK,EAAE,SAAS,MAAM;AAChE;AAcO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,eAAe,cAAc,YAAY,OAAO,IAAI,IAAI,MAAM,IAAI;AAC3E;AASA,SAAS,eAAe,SAA4B,MAAsB;AACxE,MAAI,CAAC,SAAS;AAGZ,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,OAAO,QAAQ,CAAC;AACtB,QAAI,SAAS,MAAM,SAAS,IAAI;AAC9B,eAAS;AAAA,IACX,WAAW,OAAO,IAAM;AACtB,eACE,SAAS,KAAK,SAAS,KAAK,SAAS,MAAM,SAAS,MAAM,SAAS,KAC/D,IACA;AAAA,IACR;AAAA,EACF;AACA,SAAO,QAAQ,SAAS;AAC1B;AAaA,IAAM,qBAAqB;AAWpB,SAAS,kBACd,MACA,WAAmB,wBACV;AACT,QAAM,QAAQ,KAAK;AACnB,MAAI,QAAQ,qBAAqB,KAAK,UAAU;AAC9C,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,IAAI,UAAU;AACxB,WAAO;AAAA,EACT;AACA,SAAO,kBAAkB,IAAI,KAAK;AACpC;AAOA,IAAM,uBAAuB,oBAAI,IAAI;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,SAAS,OAAqD;AACrE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAYA,SAAS,eAAe,SAGtB;AACA,QAAM,OAAO,EAAE,GAAG,QAAQ;AAC1B,QAAM,aAAwC,CAAC;AAE/C,QAAM,WAAW,SAAS,KAAK,SAAS;AACxC,MAAI,UAAU;AACZ,UAAM,QAAQ,EAAE,GAAG,SAAS;AAC5B,SAAK,YAAY;AACjB,eAAW,KAAK,KAAK;AAAA,EACvB;AAEA,QAAM,UAAU,SAAS,KAAK,OAAO;AACrC,QAAM,cAAc,WAAW,SAAS,QAAQ,SAAS;AACzD,MAAI,WAAW,aAAa;AAC1B,UAAM,QAAQ,EAAE,GAAG,YAAY;AAC/B,SAAK,UAAU,EAAE,GAAG,SAAS,WAAW,MAAM;AAC9C,eAAW,KAAK,KAAK;AAAA,EACvB;AAIA,MAAI,WAAW,WAAW,GAAG;AAC3B,eAAW,KAAK,IAAI;AAAA,EACtB;AAEA,SAAO,EAAE,MAAM,WAAW;AAC5B;AAEA,SAAS,kBAAkB,YAAoD;AAC7E,QAAM,aAA0B,CAAC;AACjC,aAAW,aAAa,YAAY;AAClC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AACpD,UAAI,qBAAqB,IAAI,GAAG,KAAK,SAAS,MAAM;AAClD;AAAA,MACF;AACA,UAAI;AACJ,UAAI;AACF,eAAO,WAAW,KAAK,UAAU,KAAK,KAAK,EAAE;AAAA,MAC/C,QAAQ;AACN;AAAA,MACF;AACA,iBAAW,KAAK,EAAE,WAAW,KAAK,KAAK,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO,WAAW,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AAClD;AAUO,SAAS,oBACd,SACA,QACA,WAAmB,wBACgD;AACnE,QAAM,EAAE,MAAM,WAAW,IAAI,eAAe,OAAO;AACnD,QAAM,aAAa,kBAAkB,UAAU;AAC/C,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,UAAoB,CAAC;AAC3B,aAAW,aAAa,YAAY;AAClC,cAAU,UAAU,UAAU,GAAG,IAC/B,8BAA8B,UAAU,IAAI;AAC9C,YAAQ,KAAK,UAAU,GAAG;AAC1B,QAAI;AACJ,QAAI;AACF,aAAO,OAAO,IAAI;AAAA,IACpB,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,kBAAkB,MAAM,QAAQ,GAAG;AACrC,aAAO,EAAE,OAAO,MAAM,QAAQ;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,mBACd,OACA,SACA,WAAmB,wBACb;AACN,QAAM,WAAW,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC;AAC/D,QAAM,SAAS;AAAA,IACb,GAAG;AAAA,IACH;AAAA,MACE,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO,yCAAyC,QAAQ,8BAA8B;AAAA,QACpF,GAAG,IAAI,IAAI,OAAO;AAAA,MACpB,EAAE,KAAK,IAAI,CAAC;AAAA,IACd;AAAA,EACF;AACF;;;ACjPA,IAAM,SAAS,oBAAI,IAAY;AAExB,SAAS,SAAS,KAAa,SAAuB;AAC3D,MAAI,OAAO,IAAI,GAAG,GAAG;AACnB;AAAA,EACF;AACA,SAAO,IAAI,GAAG;AACd,MAAI;AACF,YAAQ,KAAK,YAAY,OAAO,EAAE;AAAA,EACpC,QAAQ;AAAA,EAER;AACF;;;ACWO,SAAS,qBACd,SACA,kBAA0B,wBACW;AACrC,QAAM,UAAU,kBAAkB,OAAO;AACzC,MAAI,kBAAkB,QAAQ,MAAM,eAAe,GAAG;AACpD,WAAO,EAAE,MAAM,QAAQ,MAAM,SAAS,QAAQ,QAAQ;AAAA,EACxD;AACA,SAAO,mBAAmB,SAAS,eAAe;AACpD;AAYA,SAAS,mBACP,SACA,iBAIA;AACA,QAAM,SAAS,QAAQ,QACnB;AAAA,IACE,QAAQ;AAAA,IACR,CAAC,UAAU,kBAAkB,KAAK,EAAE;AAAA,IACpC;AAAA,EACF,IACA;AACJ,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,MAAM,QAAQ,MAAM,SAAS,QAAQ,QAAQ;AAAA,EACxD;AACA;AAAA,IACE;AAAA,IACA,+BAA+B,eAAe,+CAA+C;AAAA,MAC3F,GAAG,IAAI,IAAI,OAAO,OAAO;AAAA,IAC3B,EAAE;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACA,qBAAmB,OAAO,OAAO,OAAO,SAAS,eAAe;AAKhE,SAAO;AAAA,IACL,MAAM,kBAAkB,OAAO,KAAK,EAAE;AAAA,IACtC,SAAS,QAAQ;AAAA,EACnB;AACF;AAaA,SAAS,kBAAkB,SAAkD;AAC3E,MAAI;AACF,WAAO,EAAE,MAAM,KAAK,UAAU,OAAO,GAAG,SAAS,CAAC,GAAG,OAAO,QAAQ;AAAA,EACtE,QAAQ;AACN,UAAM,UAAoB,CAAC;AAK3B,UAAM,WAAW,CAAC,OAAgB,SAAmC;AACnE,YAAM,IAAI,OAAO;AACjB,UACE,UAAU,QACV,MAAM,YACN,MAAM,YACN,MAAM,WACN;AACA,eAAO;AAAA,MACT;AACA,UAAI,MAAM,UAAU;AAClB,gBAAQ,KAAK,QAAQ;AACrB,eAAO;AAAA,MACT;AACA,UAAI,MAAM,YAAY;AACpB,cAAM,OAAQ,MAA4B,QAAQ;AAClD,gBAAQ,KAAK,IAAI;AACjB,eAAO,oBAAoB,IAAI;AAAA,MACjC;AACA,UAAI,MAAM,UAAU;AAClB,gBAAQ,KAAK,QAAQ;AACrB,eAAO;AAAA,MACT;AACA,UAAI,MAAM,UAAU;AAClB,eAAO;AAAA,MACT;AACA,YAAM,MAAM;AACZ,YAAM,YACH,IAA4C,aAAa,QAC1D;AACF,UAAI,KAAK,IAAI,GAAG,GAAG;AACjB,gBAAQ,KAAK,SAAS;AACtB,eAAO,WAAW,SAAS;AAAA,MAC7B;AACA,WAAK,IAAI,GAAG;AACZ,UAAI;AACJ,UAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,iBAAS,IAAI,IAAI,CAAC,SAAS,SAAS,MAAM,IAAI,CAAC;AAAA,MACjD,WAAW,OAAQ,IAA6B,WAAW,YAAY;AACrE,YAAI;AACF,mBAAS,SAAU,IAA8B,OAAO,GAAG,IAAI;AAAA,QACjE,QAAQ;AACN,kBAAQ,KAAK,SAAS;AACtB,mBAAS,oBAAoB,SAAS;AAAA,QACxC;AAAA,MACF,OAAO;AACL,YAAI;AACF,gBAAM,MAA+B,CAAC;AACtC,qBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACxC,gBAAI,CAAC,IAAI,SAAS,GAAG,IAAI;AAAA,UAC3B;AACA,mBAAS;AAAA,QACX,QAAQ;AAIN;AAAA,YACE;AAAA,YACA;AAAA,UACF;AACA,kBAAQ,KAAK,SAAS;AACtB,mBAAS,oBAAoB,SAAS;AAAA,QACxC;AAAA,MACF;AACA,WAAK,OAAO,GAAG;AACf,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AACF,kBAAY,SAAS,SAAS,oBAAI,QAAQ,CAAC;AAAA,IAC7C,SAAS,OAAO;AAEd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,SAAS,EAAE,OAAO,6BAA6B,OAAO,GAAG;AAC/D,aAAO,EAAE,MAAM,KAAK,UAAU,MAAM,GAAG,SAAS,OAAO,OAAO;AAAA,IAChE;AAIA,UAAM,WACJ,OAAO,cAAc,YACrB,cAAc,QACd,CAAC,MAAM,QAAQ,SAAS;AAC1B,QAAI,QAAQ,SAAS,KAAK,UAAU;AAClC,YAAM,MAAM;AACZ,YAAM,WAAW,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,SAAS,CAAC;AAC3D,UAAI,SAAS;AAAA,QACX,GAAG;AAAA,QACH;AAAA,UACE,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO,sCAAsC;AAAA,YAC3C,GAAG,IAAI,IAAI,OAAO;AAAA,UACpB,EAAE,KAAK,IAAI,CAAC;AAAA,QACd;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,MAAM,KAAK,UAAU,SAAS;AAAA,MAC9B;AAAA,MACA,OAAO,WAAY,YAAwC;AAAA,IAC7D;AAAA,EACF;AACF;;;ACvNA,IAAM,iBAAiB,oBAAI,IAAqC;AAChE,IAAM,kBAAkB,oBAAI,IAAqC;AAE1D,SAAS,0BACd,SACA,UACM;AACN,MAAI,OAAO,YAAY,YAAY,YAAY,IAAI;AACjD;AAAA,EACF;AACA,MAAI,OAAO,aAAa,YAAY,aAAa,MAAM;AACrD;AAAA,EACF;AACA,MAAI,OAAO,KAAK,QAAQ,EAAE,WAAW,GAAG;AACtC;AAAA,EACF;AACA,iBAAe,IAAI,SAAS,EAAE,GAAG,eAAe,IAAI,OAAO,GAAG,GAAG,SAAS,CAAC;AAC7E;AAEO,SAAS,oBACd,SACqC;AACrC,QAAM,WAAW,eAAe,IAAI,OAAO;AAC3C,SAAO,WAAW,EAAE,GAAG,SAAS,IAAI;AACtC;AAEO,SAAS,oBAAoB,SAAuB;AACzD,iBAAe,OAAO,OAAO;AAC7B,kBAAgB,OAAO,OAAO;AAChC;AAEO,SAAS,oCACd,SACyB;AACzB,QAAM,gBAAgB,QAAQ;AAC9B,MAAI,OAAO,kBAAkB,YAAY,kBAAkB,MAAM;AAC/D,WAAO;AAAA,EACT;AACA,QAAM,WAAW;AACjB,QAAM,UACJ,OAAO,QAAQ,OAAO,YAAY,QAAQ,OAAO,KAC7C,QAAQ,KACR,SAAS;AACf,MAAI,OAAO,YAAY,YAAY,YAAY,IAAI;AACjD,WAAO;AAAA,EACT;AACA,QAAM,SAAS,eAAe,IAAI,OAAO;AACzC,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AACA,QAAM,UAAU,gBAAgB,IAAI,OAAO,KAAK,CAAC;AACjD,QAAM,WAAW,SAAS;AAC1B,MAAI,OAAO,aAAa,YAAY,aAAa,MAAM;AACrD,WAAO,OAAO,SAAS,QAAQ;AAAA,EACjC;AACA,kBAAgB,IAAI,SAAS,OAAO;AACpC,QAAM,WAAW,OAAO,KAAK,OAAO,EACjC,OAAO,CAAC,QAAQ,OAAO,UAAU,OAAO,GAAG,MAAM,QAAQ,GAAG,CAAC,EAC7D,KAAK;AACR,MAAI,SAAS,SAAS,GAAG;AACvB;AAAA,MACE,2BAA2B,SAAS,KAAK,GAAG,CAAC;AAAA,MAC7C,yBAAyB,SAAS,KAAK,IAAI,CAAC;AAAA,IAG9C;AAAA,EACF;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,eAAe,EAAE,GAAG,UAAU,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE;AAAA,EACpE;AACF;;;AC7DA,SAAoB,sBAAmC;AACvD;AAAA,EAEE;AAAA,OAEK;AACP,SAAS,8BAA8B;AACvC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;;;ACaA,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAMvC,YACE,SACA,UAII,CAAC,GACL;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,eAAe,QAAQ;AAAA,EAC9B;AACF;;;AChDO,SAAS,WAAW,OAA4C;AACrE,QAAM,SAAS;AACf,MAAI,OAAO,OAAO,UAAU,YAAY;AACtC,WAAO,MAAM;AAAA,EACf;AACF;;;AFgCA,IAAM,sBAAsB;AAC5B,IAAM,oBAAoB;AAC1B,IAAM,2BAA2B;AACjC,IAAM,iCAAiC;AACvC,IAAM,wBAAwB;AAC9B,IAAM,yBAAyB;AAC/B,IAAM,iBAAiB;AACvB,IAAM,+BAA+B;AACrC,IAAM,gCAAgC;AACtC,IAAM,6BAA6B;AACnC,IAAM,yBAAyB;AAC/B,IAAM,wBAAwB;AAC9B,IAAM,wBAAwB;AAC9B,IAAM,0BAA0B;AAMhC,IAAM,+BAA+B;AACrC,IAAM,oBAAoB;AAC1B,IAAM,+BAA+B;AAErC,IAAM,iBAAiB,oBAAI,IAAwB;AAKnD,IAAM,cAAc,oBAAI,QAA4B;AAEpD,SAAS,kBACP,MACA,KACA,UACA,SACQ;AACR,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,QAAW;AACrB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,GAAG;AACxB,MAAI,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,SAAS,KAAK;AACxD,WAAO;AAAA,EACT;AACA;AAAA,IACE;AAAA,IACA,GAAG,IAAI,+CAA+C,GAAG,WAAW,QAAQ;AAAA,EAC9E;AACA,SAAO;AACT;AAEA,SAAS,SAAS,SAAiB,OAAuB;AACxD,MAAI;AACF,QAAI,UAAU,QAAW;AACvB,cAAQ,MAAM,YAAY,OAAO,EAAE;AAAA,IACrC,OAAO;AACL,cAAQ,MAAM,YAAY,OAAO,IAAI,KAAK;AAAA,IAC5C;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,UAAU,OAAyC;AAC1D,MAAI,OAAO,UAAU,WAAW;AAC9B,WAAO,EAAE,WAAW,MAAM;AAAA,EAC5B;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,OAAO,UAAU,KAAK,IACzB,EAAE,UAAU,OAAO,KAAK,EAAE,IAC1B,EAAE,aAAa,MAAM;AAAA,EAC3B;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,aAAa,MAAM;AAAA,EAC9B;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,EAAE,YAAY,EAAE,QAAQ,MAAM,IAAI,SAAS,EAAE,EAAE;AAAA,EACxD;AACA,SAAO,EAAE,aAAa,OAAO,KAAK,EAAE;AACtC;AAEA,SAAS,eACP,YAC2B;AAC3B,MAAI,CAAC,YAAY;AACf,WAAO,CAAC;AAAA,EACV;AACA,SAAO,OAAO,QAAQ,UAAU,EAC7B,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS,EACzC,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,EAAE,KAAK,OAAO,UAAU,KAAK,EAAE,EAAE;AAC7D;AAOA,SAAS,mBAAmB,MAA4C;AACtE,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,SAAO,GAAG,KAAK,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AACtD;AAEA,SAAS,WAAW,MAA6C;AAC/D,QAAM,cAAc,KAAK,YAAY;AACrC,QAAM,SAAkC;AAAA,IACtC,SAAS,YAAY;AAAA,IACrB,QAAQ,YAAY;AAAA,IACpB,MAAM,KAAK;AAAA,IACX,MAAM,KAAK,OAAO;AAAA,IAClB,mBAAmB,mBAAmB,KAAK,SAAS;AAAA,IACpD,iBAAiB,mBAAmB,KAAK,OAAO;AAAA,IAChD,YAAY,eAAe,KAAK,UAAqC;AAAA,IACrE,wBAAwB,KAAK;AAAA,IAC7B,oBAAoB,KAAK;AAAA,IACzB,mBAAmB,KAAK;AAAA,IACxB,QAAQ;AAAA,MACN,MAAM,KAAK,OAAO;AAAA,MAClB,GAAI,KAAK,OAAO,UAAU,EAAE,SAAS,KAAK,OAAO,QAAQ,IAAI,CAAC;AAAA,IAChE;AAAA,IACA,OAAO,YAAY;AAAA,EACrB;AACA,QAAM,eAAe,KAAK,mBAAmB;AAC7C,MAAI,cAAc;AAChB,WAAO,eAAe;AAAA,EACxB;AACA,MAAI,YAAY,YAAY;AAC1B,WAAO,aAAa,YAAY,WAAW,UAAU;AAAA,EACvD;AACA,SAAO;AACT;AA+BA,IAAM,uBAAuB;AAE7B,SAAS,WAAW,MAAiC;AACnD,QAAM,OAAO,KAAK,UAAU,WAAW,IAAI,CAAC;AAC5C,SAAO;AAAA,IACL;AAAA,IACA,MAAM,WAAW,IAAI;AAAA,IACrB,KAAK,YAAY,IAAI,IAAI;AAAA,EAC3B;AACF;AAEA,SAAS,gBAAgB,MAA4C;AACnE,MAAI;AACF,UAAM,UAAU,KAAK,MAAM,KAAK,IAAI;AAMpC,UAAM,YAAY,QAAQ,YAAY;AAAA,MACpC,CAAC,UAAU,MAAM,QAAQ;AAAA,IAC3B;AACA,UAAM,cAAc,WAAW,OAAO;AACtC,QAAI,CAAC,WAAW,SAAS,gBAAgB,QAAW;AAClD,aAAO;AAAA,IACT;AACA,UAAM,UAAU,KAAK,MAAM,WAAW;AACtC,cAAU,MAAM,cAAc;AAAA,MAC5B;AAAA,MACA;AAAA,IACF,EAAE;AACF,UAAM,OAAO,KAAK,UAAU,OAAO;AACnC,WAAO,EAAE,MAAM,MAAM,WAAW,IAAI,EAAE;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,eAAe,MAA2C;AACvE,QAAM,WAAW,kBAAkB,IAAI;AACvC,SAAO,oBAAoB,UAAU,MAAM,WAAW;AACxD;AAEA,SAAS,gBAAgB,OAAsC;AAC7D,QAAM,QAAQ,MAAM;AACpB,QAAM,WAAW,KAAK,UAAU;AAAA,IAC9B,YAAY;AAAA,MACV,MAAM,SAAS;AAAA,IACjB;AAAA,EACF,CAAC;AACD,QAAM,YAAY,KAAK,UAAU;AAAA,IAC/B,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM,WAAW;AAAA,EAC5B,CAAC;AACD,QAAM,OAAO,iCAAiC,QAAQ,2BAA2B,SAAS;AAC1F,QAAM,OAAO;AACb,SAAO,EAAE,MAAM,MAAM,MAAM,WAAW,IAAI,IAAI,WAAW,IAAI,EAAE;AACjE;AAEA,SAAS,cACP,UACA,OACQ;AACR,SACE,SAAS,OAAO,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,KAAK,GAAG,IAAI,SAAS;AAExE;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,eAAW,KAAK;AAAA,EAClB,CAAC;AACH;AAMA,eAAe,aACb,MACA,WACkB;AAClB,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;AAAA,MACxB;AAAA,MACA,IAAI,QAAiB,CAAC,YAAY;AAChC,gBAAQ,WAAW,MAAM,QAAQ,KAAK,GAAG,KAAK,IAAI,GAAG,SAAS,CAAC;AAC/D,mBAAW,KAAK;AAAA,MAClB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,UAAE;AACA,QAAI,OAAO;AACT,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;AAGA,eAAe,mBACb,OACA,OACA,MACc;AACd,QAAM,UAAU,IAAI,MAAS,MAAM,MAAM;AACzC,MAAI,OAAO;AACX,QAAM,UAAU,MAAM;AAAA,IACpB,EAAE,QAAQ,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,MAAM,MAAM,EAAE;AAAA,IACrD,YAAY;AACV,aAAO,OAAO,MAAM,QAAQ;AAC1B,cAAM,QAAQ;AACd,gBAAQ;AACR,gBAAQ,KAAK,IAAI,MAAM,KAAK,MAAM,KAAK,CAAC;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,OAAO;AACzB,SAAO;AACT;AAMA,SAAS,YAAY,OAAyB;AAC5C,SAAO,iBAAiB,iBAAiB,MAAM;AACjD;AAEA,SAAS,YAAY,OAAyB;AAC5C,SAAO,iBAAiB,iBAAiB,MAAM;AACjD;AA2BA,SAAS,gBACP,OACA,SACA,iBACe;AACf,QAAM,YACJ,iBAAiB,gBAAgB,MAAM,eAAe;AAKxD,QAAM,aAAa,kBAAkB;AACrC,MAAI,cAAc,QAAW;AAC3B,WAAO,YAAY,aAAa,YAAY;AAAA,EAC9C;AACA,QAAM,UAAU,KAAK;AAAA,IACnB,0BAA0B,KAAK;AAAA,IAC/B;AAAA,EACF;AACA,QAAM,WAAW,UAAU,IAAI,KAAK,OAAO,KAAK,UAAU;AAC1D,SAAO,WAAW,aAAa,WAAW;AAC5C;AAWO,IAAM,qBAAN,MAAiD;AAAA,EACtD,YACmB,cACA,iBACA,qBACA,mBACA,aAIA,sBAA8B,uBAC/C;AATiB;AACA;AACA;AACA;AACA;AAIA;AAInB;AAAA,SAAQ,iBAAiB;AAAA,EAHtB;AAAA,EAKH,OACE,OACA,gBACM;AACN,SAAK,KAAK,YAAY,KAAK,EAAE;AAAA,MAC3B,CAAC,cAAc;AACb,uBAAe;AAAA,UACb,MAAM,YAAY,iBAAiB,UAAU,iBAAiB;AAAA,QAChE,CAAC;AAAA,MACH;AAAA,MACA,CAAC,UAAU;AACT,uBAAe,EAAE,MAAM,iBAAiB,QAAQ,MAAM,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,OAAyC;AACjE,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,IAAI,UAAU;AAC9B,iBAAW,gBAAgB,MAAM,CAAC,CAAC;AAAA,IACrC,SAAS,OAAO;AACd,eAAS,gDAAgD,KAAK;AAC9D,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,KAAK,oBAAoB,UAAU,OAAO;AAC1D,UAAM,UAAU,MAAM;AAAA,MACpB;AAAA,MACA,KAAK;AAAA,MACL,CAAC,UAAU,KAAK,KAAK,UAAU,KAAK;AAAA,IACtC;AACA,WAAO,QAAQ,MAAM,OAAO;AAAA,EAC9B;AAAA,EAEQ,oBACN,UACA,OACgB;AAChB,UAAM,UAA0B,CAAC;AACjC,QAAI,UAAyB,CAAC;AAC9B,QAAI,OAAO,SAAS;AAEpB,eAAW,QAAQ,OAAO;AACxB,YAAM,WACJ,KAAK,QAAQ,QAAQ,SAAS,IAAI,uBAAuB;AAC3D,UACE,QAAQ,SAAS,MAChB,QAAQ,UAAU,KAAK,uBACtB,OAAO,WAAW,KAAK,kBACzB;AACA,gBAAQ,KAAK,EAAE,OAAO,SAAS,KAAK,CAAC;AACrC,kBAAU,CAAC;AACX,eAAO,SAAS;AAAA,MAClB;AACA,cAAQ,KAAK,IAAI;AACjB,cAAQ,KAAK,QAAQ,QAAQ,SAAS,IAAI,uBAAuB;AAAA,IACnE;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,KAAK,EAAE,OAAO,SAAS,KAAK,CAAC;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,KACZ,UACA,OACkB;AAClB,QAAI;AACF,UAAI,eAAe,MAAM;AACzB,UAAI,kBAAkB,MAAM;AAC5B,UAAI,iBAAiB;AACrB,aAAO,MAAM;AACX,YAAI,mBAAmB,gCAAgC;AACrD,gBAAM,WAAW,MAAM;AAAA,YACrB,cAAc,UAAU,YAAY;AAAA,UACtC;AACA,cAAI,SAAS,aAAa,KAAK,iBAAiB;AAC9C,kBAAM,KAAK,gBAAgB,QAAQ;AAInC,iBAAK,gBAAgB,MAAM,KAAK;AAChC,mBAAO;AAAA,UACT;AAAA,QACF;AAEA,YAAI,MAAM,MAAM,WAAW,GAAG;AAC5B;AAAA,YACE;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AACA,YAAI,gBAAgB;AAClB;AAAA,YACE;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AACA,cAAM,UAAU,gBAAgB,MAAM,MAAM,CAAC,CAAC;AAC9C,YAAI,CAAC,SAAS;AACZ;AAAA,YACE;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AACA,uBAAe,CAAC,OAAO;AACvB,0BAAkB,SAAS,OAAO,QAAQ;AAC1C,yBAAiB;AAAA,MACnB;AAAA,IACF,SAAS,OAAO;AACd,UAAI,YAAY,KAAK,GAAG;AACtB;AAAA,UACE,MAAM,MAAM,WAAW,IACnB,+FACA;AAAA,QACN;AACA,eAAO;AAAA,MACT;AACA,eAAS,gDAAgD,KAAK;AAC9D,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBQ,eAAe,OAAsB;AAC3C,UAAM,YACJ,iBAAiB,gBAAgB,MAAM,eAAe;AACxD,QAAI,cAAc,QAAW;AAC3B,WAAK,iBAAiB,KAAK;AAAA,QACzB,KAAK;AAAA,QACL,KAAK,IAAI,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,cAAc,UAAiC;AAC3D,UAAM,YAAY,KAAK,iBAAiB,KAAK,IAAI;AACjD,QAAI,aAAa,GAAG;AAClB;AAAA,IACF;AAKA,QAAI,cAAc,WAAW,KAAK,IAAI,KAAK,GAAG;AAC5C,YAAM,IAAI;AAAA,QACR,2CAA2C,SAAS;AAAA,MACtD;AAAA,IACF;AACA,UAAM,MAAM,SAAS;AAAA,EACvB;AAAA,EAEA,MAAc,gBAAgB,SAA4C;AAGxE,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,aAAS,UAAU,GAAG,UAAU,mBAAmB,WAAW,GAAG;AAC/D,UAAI;AACF,cAAM,KAAK,cAAc,QAAQ;AACjC,cAAM,KAAK,aAAa,SAAS,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC;AACnE;AAAA,MACF,SAAS,OAAO;AACd,YAAI,YAAY,KAAK,GAAG;AACtB,gBAAM;AAAA,QACR;AACA,aAAK,eAAe,KAAK;AACzB,YAAI,YAAY,oBAAoB,KAAK,CAAC,YAAY,KAAK,GAAG;AAC5D,gBAAM;AAAA,QACR;AACA,cAAM,OAAO,gBAAgB,OAAO,SAAS,WAAW,KAAK,IAAI,CAAC;AAClE,YAAI,SAAS,MAAM;AACjB,gBAAM;AAAA,QACR;AACA,cAAM,MAAM,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAgB,OAA4B;AAClD,QAAI,KAAK,gBAAgB,QAAW;AAClC;AAAA,IACF;AACA,UAAM,OAAO,MACV,IAAI,CAAC,SAAS,KAAK,GAAG,EACtB,OAAO,CAAC,QAA2B,QAAQ,MAAS;AACvD,QAAI,KAAK,WAAW,GAAG;AACrB;AAAA,IACF;AACA,QAAI;AACF,WAAK,YAAY,IAAI;AAAA,IACvB,SAAS,OAAO;AACd,eAAS,6BAA6B,KAAK;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAAA,EAAC;AAAA,EAEjC,MAAM,aAA4B;AAAA,EAAC;AACrC;AAQA,IAAM,2BAAN,MAAuD;AAAA,EAYrD,YAA6B,UAAwB;AAAxB;AAF7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,gBAAgB;AAAA,EAE8B;AAAA,EAEtD,OACE,OACA,gBACM;AACN,QAAI;AACF,WAAK,SAAS,OAAO,OAAO,CAAC,WAAW;AACtC,YAAI,OAAO,SAAS,iBAAiB,SAAS;AAC5C,eAAK,iBAAiB;AAAA,QACxB;AACA,uBAAe,MAAM;AAAA,MACvB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,WAAK,iBAAiB;AACtB,qBAAe,EAAE,MAAM,iBAAiB,QAAQ,MAAsB,CAAC;AAAA,IACzE;AAAA,EACF;AAAA,EAEA,oBAA4B;AAC1B,UAAM,SAAS,KAAK;AACpB,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,WAA0B;AACxB,WAAO,KAAK,SAAS,SAAS;AAAA,EAChC;AAAA,EAEA,aAA4B;AAC1B,WAAO,KAAK,SAAS,aAAa,KAAK,QAAQ,QAAQ;AAAA,EACzD;AACF;AAeO,IAAM,qBAAN,MAAmD;AAAA,EAQxD,YAAY,SAAoC;AAHhD,SAAQ,SAAS;AAIf,UAAM,kBAAkB,QAAQ,mBAAmB;AACnD,UAAM,sBACJ,QAAQ,uBAAuB;AACjC,QAAI,uBAAuB,GAAG;AAC5B,YAAM,IAAI,YAAY,gDAAgD;AAAA,IACxE;AAEA,SAAK,kBAAkB,IAAI;AAAA,MACzB,IAAI;AAAA,QACF,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,qBAAqB;AAAA,QAC7B,QAAQ;AAAA,QACR,QAAQ,uBAAuB;AAAA,MACjC;AAAA,IACF;AAEA,SAAK,YAAY,IAAI,mBAAmB,KAAK,iBAAiB;AAAA,MAC5D,cAAc,QAAQ,gBAAgB;AAAA,MACtC,oBACE,QAAQ,sBAAsB;AAAA,MAChC,sBAAsB;AAAA,MACtB,qBAAqB,QAAQ,uBAAuB;AAAA,IACtD,CAAC;AAKD,SAAK,WAAW,IAAI,oBAAoB;AAAA,MACtC,SAAS,IAAI,gBAAgB;AAAA,MAC7B,UAAU,uBAAuB;AAAA,QAC/B,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,MACrB,CAAC;AAAA,MACD,YAAY;AAAA,QACV,qBAAqB;AAAA,QACrB,2BAA2B,OAAO;AAAA,MACpC;AAAA,MACA,gBAAgB,CAAC,KAAK,SAAS;AAAA,IACjC,CAAC;AACD,SAAK,SAAS,KAAK,SAAS,UAAU,UAAU,WAAW;AAC3D,mBAAe,IAAI,IAAI;AAAA,EACzB;AAAA,EAEA,OACE,WACA,SACA,OAAoB,CAAC,GACf;AACN,QAAI,KAAK,QAAQ;AACf;AAAA,QACE;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI;AAKF,YAAM,EAAE,MAAM,QAAQ,IAAI;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AACA,UAAI,QAAQ,SAAS,GAAG;AACtB;AAAA,UACE;AAAA,UACA,kDAAkD;AAAA,YAChD,GAAG,IAAI,IAAI,OAAO;AAAA,UACpB,EAAE,KAAK,IAAI,CAAC;AAAA,QAEd;AAAA,MACF;AACA,YAAM,OAAO,KAAK,OAAO,UAAU,KAAK,QAAQ,UAAU,SAAS,IAAI;AAAA,QACrE,YAAY;AAAA,UACV,CAAC,mBAAmB,GAAG;AAAA,UACvB,CAAC,iBAAiB,GAAG;AAAA,QACvB;AAAA,QACA,WAAW,KAAK;AAAA,MAClB,CAAC;AACD,UAAI,KAAK,QAAQ,QAAW;AAC1B,oBAAY,IAAI,MAAM,KAAK,GAAG;AAAA,MAChC;AACA,UAAI,KAAK,YAAY,MAAM;AACzB,aAAK,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;AAAA,MAC/C;AACA,cAAQ,MAAM,KAAK,OAAO;AAAA,IAC5B,SAAS,OAAO;AACd,eAAS,yCAAyC,KAAK;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,MAAM,MACJ,YAAoB,8BACF;AAGlB,UAAM,WAAW,KAAK,gBAAgB,QAAQ,QAAQ,IAAI,GAAG;AAAA,MAAK,MAChE,KAAK,eAAe;AAAA,IACtB;AACA,SAAK,eAAe,QAAQ,MAAM,MAAM,KAAK;AAC7C,WAAO,aAAa,SAAS,SAAS;AAAA,EACxC;AAAA,EAEA,MAAc,iBAAmC;AAC/C,QAAI;AACF,YAAM,KAAK,UAAU,WAAW;AAAA,IAClC,SAAS,OAAO;AACd,eAAS,uCAAuC,KAAK;AACrD,WAAK,gBAAgB,kBAAkB;AACvC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,gBAAgB,kBAAkB,MAAM;AAAA,EACtD;AAAA,EAEA,MAAM,SACJ,YAAoB,8BACF;AAClB,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,WAAW,CAAC;AACnD,SAAK,SAAS;AACd,UAAM,UAAU,MAAM,KAAK,MAAM,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC;AACnE,mBAAe,OAAO,IAAI;AAC1B,UAAM,oBAAoB,MAAM;AAAA,MAC9B,KAAK,SACF,SAAS,EACT,KAAK,MAAM,IAAI,EACf,MAAM,CAAC,UAAU;AAChB,iBAAS,mDAAmD,KAAK;AACjE,eAAO;AAAA,MACT,CAAC;AAAA,MACH,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC;AAAA,IACnC;AACA,WAAO,WAAW;AAAA,EACpB;AACF;AAEA,SAAS,QAAQ,MAAY,SAAmC;AAC9D,OAAK,IAAI,OAAO;AAClB;AAEO,SAAS,oBAAoB,SAGb;AACrB,SAAO,IAAI,mBAAmB;AAAA,IAC5B,GAAG;AAAA,IACH,mBAAmB;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAe,qBACb,WACA,KACkB;AAClB,QAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,WAAW,CAAC;AACnD,MAAI,YAAY;AAChB,aAAW,aAAa,CAAC,GAAG,cAAc,GAAG;AAC3C,gBACG,MAAM,IAAI,WAAW,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC,KAAM;AAAA,EAClE;AACA,SAAO;AACT;AAEO,SAAS,oBACd,YAAoB,8BACF;AAClB,SAAO;AAAA,IAAqB;AAAA,IAAW,CAAC,WAAW,cACjD,UAAU,MAAM,SAAS;AAAA,EAC3B;AACF;AAEO,SAAS,uBACd,YAAoB,8BACF;AAClB,SAAO;AAAA,IAAqB;AAAA,IAAW,CAAC,WAAW,cACjD,UAAU,SAAS,SAAS;AAAA,EAC9B;AACF;;;AG93BO,SAAS,qBAAqB,SAGlB;AACjB,SAAO,oBAAoB,OAAO;AACpC;AAEO,SAAS,qBAAqB,WAAsC;AACzE,SAAO,oBAAoB,SAAS;AACtC;AAEO,SAAS,wBAAwB,WAAsC;AAC5E,SAAO,uBAAuB,SAAS;AACzC;;;ACUA,IAAM,sCAAsC;AAC5C,IAAM,qCAAqC;AAC3C,IAAM,uBAAuB;AAS7B,IAAM,qBAAqB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAC5D,IAAM,wBAAwB;AAC9B,IAAMA,gCAA+B;AAIrC,IAAM,uBAAuB,oBAAI,IAAsB;AAahD,SAAS,YAAe,SAAiC;AAC9D,uBAAqB,IAAI,OAAO;AAGhC,OAAK,QACF,QAAQ,MAAM;AACb,yBAAqB,OAAO,OAAO;AAAA,EACrC,CAAC,EACA,MAAM,MAAM;AAAA,EAEb,CAAC;AACH,SAAO;AACT;AAaA,eAAsB,YAAY,YAAoB,KAAwB;AAC5E,QAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,WAAW,CAAC;AACnD,QAAM,kBAAkB,MAAM,qBAAqB,SAAS;AAC5D,QAAM,oBAAoB,MAAM;AAAA,IAC9B,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC;AAAA,EACnC;AACA,SAAO,mBAAmB;AAC5B;AAWA,eAAsB,qBACpB,YAAoBA,+BACF;AAIlB,QAAM,mBAAmB,MAAM,MAAM;AAAA,EAAC,CAAC;AACvC,SAAO,gBAAgB,MAAM,KAAK,oBAAoB,GAAG,SAAS;AACpE;AAQA,eAAe,gBACb,UACA,WACkB;AAClB,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;AAAA,MACxB,QAAQ,WAAW,QAAQ,EAAE,KAAK,MAAM,IAAI;AAAA,MAC5C,IAAI,QAAiB,CAAC,YAAY;AAChC,gBAAQ,WAAW,MAAM,QAAQ,KAAK,GAAG,SAAS;AAClD,mBAAW,KAAK;AAAA,MAClB,CAAC;AAAA,IACH,CAAC;AAAA,EACH,UAAE;AACA,QAAI,OAAO;AACT,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;AAOA,IACE,OAAO,YAAY,eACnB,QAAQ,YAAY,QACpB,QAAQ,SAAS,QAAQ,MACzB;AACA,MAAI,aAAa;AACjB,UAAQ,GAAG,cAAc,MAAM;AAC7B,QAAI,YAAY;AACd;AAAA,IACF;AACA,iBAAa;AAEb,SAAK,QAAQ,WAAW;AAAA,MACtB,GAAG,MAAM,KAAK,oBAAoB,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAC;AAAA,MAChE,wBAAwB,qBAAqB,EAAE,MAAM,MAAM,KAAK;AAAA,IAClE,CAAC,EAAE,KAAK,MAAM;AACZ,mBAAa;AAAA,IACf,CAAC;AAAA,EACH,CAAC;AACH;AAyDA,SAAS,WAAW,UAAoB,MAA6B;AACnE,MAAI;AACF,WAAO,SAAS,SAAS,IAAI,IAAI,KAAK;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,QAA2C;AAI3E,QAAM,QAAQ,QAAQ,KAAK;AAC3B,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,WAAO,WAAW,IAAI,UAAU,MAAQ;AAAA,EAC1C;AACA,QAAM,KAAK,KAAK,MAAM,KAAK;AAC3B,MAAI,OAAO,MAAM,EAAE,GAAG;AACpB,WAAO;AAAA,EACT;AACA,SAAO,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC;AACpC;AAYA,SAAS,YACP,WACA,SACA,KACa;AACb,SAAO;AAAA,IACL;AAAA,IACA,MAAM,YAAY,WAAW,OAAO;AAAA,IACpC,WAAW,iBAAiB,SAAS,YAAY;AAAA,IACjD,SAAS,iBAAiB,SAAS,UAAU;AAAA,IAC7C,SAAS,gBAAgB,OAAO;AAAA,EAClC;AACF;AAEA,SAAS,YACP,WACA,SACQ;AACR,MAAI,cAAc,iBAAiB;AACjC,UAAM,WAAW;AAAA,MACf,gBAAgB,QAAQ,OAAO,GAAG;AAAA,IACpC;AACA,QAAI,OAAO,UAAU,SAAS,UAAU;AACtC,aAAO,SAAS;AAAA,IAClB;AAAA,EACF;AACA,MAAI,OAAO,QAAQ,qBAAqB,UAAU;AAChD,WAAO,QAAQ;AAAA,EACjB;AACA,SAAO,UAAU,SAAS;AAC5B;AAMA,SAAS,iBACP,SACA,OACoB;AACpB,QAAM,UAAU,gBAAgB,QAAQ,OAAO;AAC/C,QAAM,WACJ,gBAAgB,QAAQ,aAAa,KAAK,gBAAgB,QAAQ,QAAQ;AAC5E,QAAM,MAAM,UAAU,KAAK,KAAK,WAAW,KAAK;AAChD,MAAI,OAAO,QAAQ,UAAU;AAC3B,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,SAAO,OAAO,MAAM,MAAM,IAAI,SAAY;AAC5C;AAEA,SAAS,gBAAgB,SAA2C;AAClE,QAAM,WAAW,gBAAgB,gBAAgB,QAAQ,OAAO,GAAG,SAAS;AAC5E,MAAI,UAAU,SAAS,MAAM;AAC3B,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ;AACvB,SAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,SAAS,IAAI,QAAQ,MAAM;AACnE;AAEA,SAAS,gBAAgB,OAAqD;AAC5E,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAyBA,SAAS,WAAW,SAA0D;AAC5E,QAAM,UAAU,gBAAgB,OAAO;AACvC,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,QAAQ;AACxB,MAAI,YAAY,QAAW;AACzB,WAAO,EAAE,QAAQ;AAAA,EACnB;AACA,QAAM,SAAU,SAAqC;AACrD,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,OAAO,WAAW,WAAW,SAAS,cAAc,EAAE,UAAU;AAAA,EAC1E;AACF;AAEA,SAAS,gBAAgB,SAAsD;AAC7E,MAAI,OAAO,QAAQ,kBAAkB,UAAU;AAC7C,WAAO,QAAQ;AAAA,EACjB;AACA,QAAM,WAAY,QAAQ,iBAAiB,QAAQ;AAGnD,QAAM,KAAK,UAAU;AACrB,SAAO,OAAO,OAAO,WAAW,KAAK;AACvC;AAEA,IAAI,aAAa;AAEV,IAAM,aAAN,MAAiB;AAAA,EAgBtB,YAAY,QAA0B;AATtC;AAAA;AAAA,SAAiB,kBAAkB,oBAAI,IAA2B;AAKlE;AAAA;AAAA;AAAA;AAAA,SAAiB,eAAe,oBAAI,IAAsB;AAC1D,SAAQ,SAAS;AAIf,SAAK,SAAS,OAAO;AACrB,SAAK,aAAa,OAAO;AACzB,SAAK,UAAU,OAAO,WAAW;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAoC;AAC1C,WAAO,OAAO,KAAK,WAAW,aAAa,KAAK,OAAO,IAAI,KAAK;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,oBAAgD;AACtD,QAAI,KAAK,QAAQ;AACf;AAAA,QACE;AAAA,QACA;AAAA,MACF;AACA,aAAO;AAAA,IACT;AACA,QAAI,CAAC,KAAK,gBAAgB;AACxB,WAAK,iBAAiB,qBAAqB;AAAA,QACzC,cAAc,CAAC,SAAS,cACtB,KAAK,gBAAgB,SAAS,SAAS;AAAA,QACzC,aAAa,CAAC,SAAS,KAAK,wBAAwB,IAAI;AAAA,MAC1D,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,gBACZ,SACA,WACe;AACf,QAAI;AACJ,QAAI;AAGF,iBAAW,MAAM,KAAK;AAAA,QACpB;AAAA,QACA;AAAA,QACA,EAAE,SAAS,UAAU;AAAA,MACvB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,SAAS,iBAAiB,cAAc,MAAM,SAAS;AAC7D,UAAI,WAAW,QAAW;AAExB,cAAM,IAAI,cAAc,0BAA0B,OAAO,KAAK,CAAC,IAAI;AAAA,UACjE,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,YAAM,IAAI,cAAc,mCAAmC,MAAM,IAAI;AAAA,QACnE,WAAW,mBAAmB,IAAI,MAAM;AAAA,QACxC,WAAW,WAAW;AAAA,QACtB,GAAI,iBAAiB,eAAe,MAAM,iBAAiB,SACvD,EAAE,cAAc,MAAM,aAAa,IACnC,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AAEA,UAAM,iBAAiB,gBAAgB,UAAU,QAAQ;AACzD,QAAI,mBAAmB,QAAW;AAChC,WAAK,qBAAqB,cAAc;AAAA,IAC1C;AAEA,UAAM,iBAAiB,gBAAgB,UAAU,cAAc;AAC/D,UAAM,WAAW,gBAAgB;AACjC,QAAI,aAAa,UAAa,aAAa,OAAO,aAAa,GAAG;AAEhE,YAAM,IAAI;AAAA,QACR,2BAA2B,QAAQ,aACjC,gBAAgB,gBAAgB,oBAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,UAA0B;AAC7C,eAAW,WAAW,UAAU;AAC9B,UAAI,CAAC,KAAK,gBAAgB,IAAI,OAAO,GAAG;AACtC,aAAK,gBAAgB,IAAI,SAAS;AAAA,UAChC,kBAAkB,oBAAI,IAAI;AAAA,UAC1B,cAAc,oBAAI,IAAI;AAAA,UACtB,QAAQ;AAAA,UACR,cAAc;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,SAAqC;AACrD,WAAO,KAAK,gBAAgB,IAAI,OAAO,GAAG;AAAA,EAC5C;AAAA;AAAA,EAGA,oBAAoB,UAA6B;AAC/C,WAAO,SAAS,KAAK,CAAC,YAAY,KAAK,gBAAgB,IAAI,OAAO,GAAG,MAAM;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,oBAAoB,UAAoD;AACtE,UAAM,UAA0C,CAAC;AACjD,eAAW,WAAW,UAAU;AAC9B,YAAM,WAAW,KAAK,gBAAgB,IAAI,OAAO;AACjD,UAAI,aAAa,QAAW;AAC1B;AAAA,MACF;AACA,WAAK,gBAAgB,OAAO,OAAO;AACnC,cAAQ,OAAO,IAAI;AAAA,QACjB,WAAW,SAAS,iBAAiB;AAAA,QACrC,QAAQ,SAAS;AAAA,QACjB,WACE,SAAS,gBACT,CAAC,GAAG,SAAS,gBAAgB,EAAE;AAAA,UAAM,CAAC,WACpC,SAAS,aAAa,IAAI,MAAM;AAAA,QAClC;AAAA,QACF,eAAe,SAAS;AAAA,MAC1B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,aACN,WACA,SACA,KACa;AACb,SAAK,uBAAuB,GAAG;AAC/B,WAAO,YAAY,WAAW,SAAS,GAAG;AAAA,EAC5C;AAAA,EAEQ,uBAAuB,KAAmC;AAChE,QAAI,QAAQ,QAAW;AACrB;AAAA,IACF;AACA,UAAM,WAAW,KAAK,gBAAgB,IAAI,IAAI,OAAO;AACrD,QAAI,aAAa,QAAW;AAC1B;AAAA,IACF;AACA,QAAI,IAAI,WAAW,QAAW;AAC5B,eAAS,SAAS;AAAA,IACpB,OAAO;AACL,eAAS,iBAAiB,IAAI,IAAI,MAAM;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,qBAAqB,KAAoC;AAC/D,eAAW,CAAC,eAAe,aAAa,KAAK,OAAO,QAAQ,GAAG,GAAG;AAChE,UAAI,OAAO,kBAAkB,UAAU;AACrC;AAAA,MACF;AACA,YAAM,WAAW,KAAK,gBAAgB,IAAI,aAAa;AACvD,UAAI,aAAa,QAAW;AAC1B;AAAA,MACF;AACA,eAAS,gBAAgB;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,wBAAwB,MAA0B;AACxD,eAAW,OAAO,MAAM;AACtB,YAAM,WAAW,KAAK,gBAAgB,IAAI,IAAI,OAAO;AACrD,UAAI,aAAa,QAAW;AAC1B;AAAA,MACF;AACA,UAAI,IAAI,WAAW,QAAW;AAC5B,iBAAS,eAAe;AAAA,MAC1B,OAAO;AACL,iBAAS,aAAa,IAAI,IAAI,MAAM;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAiB,SAAiC;AAChD,SAAK,aAAa,IAAI,OAAO;AAC7B,SAAK,QACF,QAAQ,MAAM,KAAK,aAAa,OAAO,OAAO,CAAC,EAC/C,MAAM,MAAM;AAAA,IAAC,CAAC;AACjB,WAAO,YAAY,OAAO;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBACJ,YAAoBA,+BACF;AAClB,UAAM,mBAAmB,MAAM,MAAM;AAAA,IAAC,CAAC;AACvC,WAAO,gBAAgB,MAAM,KAAK,KAAK,YAAY,GAAG,SAAS;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,uBACJ,YAAoBA,+BACF;AAClB,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,WAAW,CAAC;AACnD,UAAM,UAAU,MAAM,KAAK,mBAAmB,SAAS;AACvD,UAAM,UACH,MAAM,KAAK,gBAAgB,MAAM,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC,KACpE;AACF,WAAO,WAAW;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAoBA,+BAAgD;AACxE,QAAI,KAAK,SAAS;AAChB,aAAO,KAAK;AAAA,IACd;AACA,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,IAAI,WAAW,CAAC;AACnD,SAAK,WAAW,YAAY;AAM1B,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC;AAAA,MACnC;AACA,WAAK,SAAS;AACd,YAAM,YAAY,KAAK;AACvB,WAAK,iBAAiB;AACtB,YAAM,aACH,MAAM,WAAW,SAAS,KAAK,IAAI,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC,KAAM;AAGrE,aAAO,WAAW;AAAA,IACpB,GAAG;AACH,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,QACJ,UACA,SACA,SACY;AAMZ,UAAM,EAAE,MAAM,QAAQ,IAAI,qBAAqB,OAAO;AACtD,QAAI,QAAQ,SAAS,GAAG;AACtB,UAAI;AACF,gBAAQ;AAAA,UACN,2BAA2B,QAAQ,SAAS,QAAQ,MAAM,+BAC1B,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,QAIlE;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,WAAO,KAAK,YAAe,UAAU,MAAM,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YACJ,UACA,MACA,SACY;AAGZ,UAAM,WAAW,kBAAkB,IAAI;AACvC,UAAM,UAAU,oBAAoB,UAAU,MAAM,WAAW;AAC/D,WAAO,KAAK,aAAgB,UAAU,SAAS,OAAO;AAAA,EACxD;AAAA,EAEA,MAAc,aACZ,UACA,SACA,SACY;AACZ,UAAM,MAAM,GAAG,KAAK,UAAU,GAAG,QAAQ;AACzC,UAAM,UAAU,SAAS,WAAW,KAAK;AACzC,UAAM,SAAS,SAAS,UAAU;AAElC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,eAAe,UAAU,KAAK,cAAc,KAAK,EAAE;AAAA,IACrD;AACA,QAAI,QAAQ,iBAAiB;AAC3B,cAAQ,kBAAkB,IAAI,QAAQ;AAAA,IACxC;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC;AAAA,QACA;AAAA,QACA,MAAM,QAAQ;AAAA,QACd,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,QAAQ,SAAS,MAAM,KAAK,UAAU,MAAM,GAAG,GAAG,CAAC;AAAA,UACnD;AAAA,UACA,SAAS;AAAA,UACT,kBAAkB,WAAW,UAAU,aAAa,CAAC;AAAA,QACvD;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,SAAS,KAAK;AAGnC,UAAI,OAAO,OAAO;AAChB,YAAI,OAAO,KAAK;AACd,gBAAM,IAAI;AAAA,YACR,GAAG,OAAO,KAAK,qBAAqB,KAAK,UAAU,GAAG,OAAO,GAAG;AAAA,YAChE,OAAO;AAAA,UACT;AAAA,QACF;AACA,cAAM,IAAI,YAAY,OAAO,KAAK;AAAA,MACpC;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAa;AAChC,cAAM;AAAA,MACR;AACA,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,SAAS,cAAc;AAC/B,gBAAM,IAAI,YAAY,2BAA2B,OAAO,IAAI;AAAA,QAC9D;AACA,cAAM,IAAI,YAAY,MAAM,OAAO;AAAA,MACrC;AACA,YAAM,IAAI,YAAY,wBAAwB;AAAA,IAChD,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAkB,MAA0B;AAChD,WAAO,KAAK,QAAW,6BAA6B,EAAE,KAAK,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,mBACJ,kBACA,UACY;AACZ,WAAO,KAAK,QAAW,8BAA8B;AAAA,MACnD;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aACJ,SACA,QAC8B;AAC9B,UAAM,eAAe,IAAI,gBAAgB;AACzC,QAAI,OAAO,OAAO,QAAW;AAC3B,mBAAa,IAAI,MAAM,OAAO,EAAE;AAAA,IAClC,OAAO;AACL,mBAAa,IAAI,QAAQ,OAAO,IAAI;AACpC,mBAAa,IAAI,cAAc,OAAO,OAAO,cAAc,MAAM,CAAC;AAAA,IACpE;AAEA,UAAM,WAAW,mBAAmB,mBAAmB,OAAO,CAAC,SAAS,aAAa,SAAS,CAAC;AAC/F,UAAM,WAAW,MAAM,KAAK,IAAmC,QAAQ;AACvE,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,IAAO,UAA8B;AACzC,UAAM,MAAM,GAAG,KAAK,UAAU,GAAG,QAAQ;AACzC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAEnE,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,UAAU,KAAK,cAAc,KAAK,EAAE,GAAG;AAAA,QACjE,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,QAAQ,SAAS,MAAM,KAAK,UAAU,MAAM,GAAG,GAAG,CAAC;AAAA,UACnD;AAAA,UACA,SAAS;AAAA,UACT,kBAAkB,WAAW,UAAU,aAAa,CAAC;AAAA,QACvD;AAAA,MACF;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAa;AAChC,cAAM;AAAA,MACR;AACA,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,SAAS,cAAc;AAC/B,gBAAM,IAAI,YAAY,2BAA2B,KAAK,OAAO,IAAI;AAAA,QACnE;AACA,cAAM,IAAI,YAAY,MAAM,OAAO;AAAA,MACrC;AACA,YAAM,IAAI,YAAY,wBAAwB;AAAA,IAChD,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBACE,YACA,SACM;AACN,UAAM,OAAO;AAAA,MACX,GAAG;AAAA,MACH;AAAA,MACA,YAAY;AAAA,MACZ,YAAY;AAAA,IACd;AACA,SAAK,kBAAkB,GAAG;AAAA,MACxB;AAAA,MACA;AAAA,MACA,YAAY,kBAAkB,MAAM,MAAS;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,iBAAiB,SAAwC;AACvD,SAAK,kBAAkB,GAAG;AAAA,MACxB;AAAA,MACA,EAAE,GAAG,SAAS,YAAY,YAAY;AAAA,MACtC,KAAK,aAAa,iBAAiB,SAAS,WAAW,OAAO,CAAC;AAAA,IACjE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,kBAAkB,YAA2C;AAC3D,UAAM,UAAU,oCAAoC,UAAU;AAC9D,SAAK,kBAAkB,GAAG;AAAA,MACxB;AAAA,MACA;AAAA,QACE,GAAG;AAAA,QACH,YAAY;AAAA,QACZ,YAAY;AAAA,MACd;AAAA,MACA,KAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA,QAAQ,cAAc,OAAO,WAAW,OAAO,IAAI;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WACJ,SACA,SAMe;AACf,UAAM,WAAW,mBAAmB,mBAAmB,OAAO,CAAC;AAC/D,UAAM,KAAK,QAAQ,UAAU,SAAS,EAAE,QAAQ,QAAQ,CAAC;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YACJ,kBACA,OACA,UACA,MACA,uBACA,iBACA,sBACA,mBACA,YACA,WACA,kBACA,UACA,yBACA,oBAC8B;AAG9B,UAAM,UAAmC,EAAE,iBAAiB;AAC5D,QAAI,UAAU,QAAW;AACvB,cAAQ,QAAQ;AAAA,IAClB;AACA,QAAI,UAAU;AACZ,cAAQ,WAAW;AAAA,IACrB;AACA,QAAI,SAAS,QAAW;AACtB,cAAQ,OAAO;AAAA,IACjB;AACA,QAAI,0BAA0B,QAAW;AACvC,cAAQ,wBAAwB;AAAA,IAClC;AACA,QAAI,oBAAoB,QAAW;AACjC,cAAQ,kBAAkB;AAAA,IAC5B;AACA,QAAI,sBAAsB;AACxB,cAAQ,uBAAuB;AAC/B,cAAQ,oBAAoB;AAAA,IAC9B;AACA,QAAI,sBAAsB,QAAW;AACnC,cAAQ,oBAAoB;AAAA,IAC9B;AACA,QAAI,eAAe,QAAW;AAC5B,UAAI,WAAW,WAAW,GAAG;AAC3B,gBAAQ,YAAY,WAAW,CAAC;AAAA,MAClC,OAAO;AACL,gBAAQ,aAAa;AAAA,MACvB;AAAA,IACF;AACA,QAAI,cAAc,QAAW;AAC3B,cAAQ,YAAY;AAAA,IACtB;AACA,QAAI,qBAAqB,QAAW;AAClC,cAAQ,mBAAmB;AAAA,IAC7B;AACA,QAAI,aAAa,UAAa,WAAW,GAAG;AAC1C,cAAQ,WAAW;AAAA,IACrB;AACA,QAAI,yBAAyB;AAC3B,cAAQ,0BAA0B;AAAA,IACpC;AACA,QAAI,oBAAoB;AACtB,cAAQ,qBAAqB;AAAA,IAC/B;AAUA,UAAM,UAAU,uBACZ,sCACA;AACJ,WAAO,KAAK,QAA6B,yBAAyB,SAAS;AAAA,MACzE;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,gBACJ,QACA,SAC+B;AAC/B,UAAM,QAAQ,SAAS,SAAS,WAAW,iBAAiB;AAC5D,UAAM,MAAM,GAAG,KAAK,UAAU,0BAA0B,MAAM,GAAG,KAAK;AACtE,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,GAAM;AAE7D,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,UAAU,KAAK,cAAc,KAAK,EAAE,GAAG;AAAA,QACjE,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,QAAQ,SAAS,MAAM,KAAK,UAAU,MAAM,GAAG,GAAG,CAAC;AAAA,QACrD;AAAA,MACF;AAEA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAa;AAChC,cAAM;AAAA,MACR;AACA,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,SAAS,cAAc;AAC/B,gBAAM,IAAI,YAAY,iCAAiC;AAAA,QACzD;AACA,cAAM,IAAI,YAAY,MAAM,OAAO;AAAA,MACrC;AACA,YAAM,IAAI,YAAY,wBAAwB;AAAA,IAChD,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,YACJ,gBACA,SAC2B;AAC3B,UAAM,eAAe,IAAI,gBAAgB;AACzC,QAAI,SAAS,mBAAmB,OAAO;AACrC,mBAAa,IAAI,kBAAkB,OAAO;AAAA,IAC5C;AACA,QAAI,SAAS,sBAAsB,OAAO;AACxC,mBAAa,IAAI,qBAAqB,OAAO;AAAA,IAC/C;AACA,UAAM,eAAe,aAAa,SAAS;AAC3C,UAAM,QAAQ,eAAe,IAAI,YAAY,KAAK;AAClD,UAAM,MAAM,GAAG,KAAK,UAAU,4BAA4B,cAAc,GAAG,KAAK;AAChF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,GAAM;AAE7D,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,UAAU,KAAK,cAAc,KAAK,EAAE,GAAG;AAAA,QACjE,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,QAAQ,SAAS,MAAM,KAAK,UAAU,MAAM,GAAG,GAAG,CAAC;AAAA,QACrD;AAAA,MACF;AAEA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAa;AAChC,cAAM;AAAA,MACR;AACA,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,SAAS,cAAc;AAC/B,gBAAM,IAAI,YAAY,iCAAiC;AAAA,QACzD;AACA,cAAM,IAAI,YAAY,MAAM,OAAO;AAAA,MACrC;AACA,YAAM,IAAI,YAAY,wBAAwB;AAAA,IAChD,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,gBACJ,WACA,oBAC+B;AAC/B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,EAAE,WAAW,mBAAmB;AAAA,MAChC,EAAE,SAAS,IAAO;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,WAAoD;AACvE,WAAO,KAAK;AAAA,MACV;AAAA,MACA,EAAE,UAAU;AAAA,MACZ,EAAE,SAAS,mCAAmC;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,qBACJ,WACA,SACA,kBACA,SAMC;AACD,WAAO,KAAK;AAAA,MAMV;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,YAAY,UAAa,UAAU,IAAI,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC5D;AAAA,MACA,EAAE,SAAS,oCAAoC;AAAA,IACjD;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,qBAAqB,cAAqC;AAC9D,UAAM,KAAK;AAAA,MACT;AAAA,MACA,EAAE,aAAa;AAAA,MACf,EAAE,SAAS,IAAO;AAAA,IACpB;AAAA,EACF;AACF;","names":["DEFAULT_LIFECYCLE_TIMEOUT_MS"]}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
replayContextReady,
|
|
6
6
|
runWithReplayContext,
|
|
7
7
|
warnOnce
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6DIR65JW.js";
|
|
9
9
|
|
|
10
10
|
// src/codeChange.ts
|
|
11
11
|
var MAX_FILES = 60;
|
|
@@ -1210,4 +1210,4 @@ export {
|
|
|
1210
1210
|
sleepForReplayPersistence,
|
|
1211
1211
|
replay
|
|
1212
1212
|
};
|
|
1213
|
-
//# sourceMappingURL=chunk-
|
|
1213
|
+
//# sourceMappingURL=chunk-A5UGSRMK.js.map
|
|
@@ -94,7 +94,7 @@ function encodeRequestBody(body) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// src/version.generated.ts
|
|
97
|
-
var __version__ = "0.
|
|
97
|
+
var __version__ = "0.52.1";
|
|
98
98
|
var __packageName__ = "@bitfab/sdk";
|
|
99
99
|
|
|
100
100
|
// src/errors.ts
|
|
@@ -412,6 +412,42 @@ function encodePayloadBody(payload) {
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
+
// src/traceMetadata.ts
|
|
416
|
+
var callerMetadata = /* @__PURE__ */ new Map();
|
|
417
|
+
var derivedMetadata = /* @__PURE__ */ new Map();
|
|
418
|
+
function mergeCallerMetadataIntoTracePayload(payload) {
|
|
419
|
+
const externalTrace = payload.externalTrace;
|
|
420
|
+
if (typeof externalTrace !== "object" || externalTrace === null) {
|
|
421
|
+
return payload;
|
|
422
|
+
}
|
|
423
|
+
const external = externalTrace;
|
|
424
|
+
const traceId = typeof payload.id === "string" && payload.id !== "" ? payload.id : external.id;
|
|
425
|
+
if (typeof traceId !== "string" || traceId === "") {
|
|
426
|
+
return payload;
|
|
427
|
+
}
|
|
428
|
+
const caller = callerMetadata.get(traceId);
|
|
429
|
+
if (!caller) {
|
|
430
|
+
return payload;
|
|
431
|
+
}
|
|
432
|
+
const derived = derivedMetadata.get(traceId) ?? {};
|
|
433
|
+
const exported = external.metadata;
|
|
434
|
+
if (typeof exported === "object" && exported !== null) {
|
|
435
|
+
Object.assign(derived, exported);
|
|
436
|
+
}
|
|
437
|
+
derivedMetadata.set(traceId, derived);
|
|
438
|
+
const shadowed = Object.keys(derived).filter((key) => key in caller && caller[key] !== derived[key]).sort();
|
|
439
|
+
if (shadowed.length > 0) {
|
|
440
|
+
warnOnce(
|
|
441
|
+
`trace-metadata-shadowed:${shadowed.join(",")}`,
|
|
442
|
+
`trace metadata key(s) ${shadowed.join(", ")} were set both by the caller and by an integration's own trace export; the caller's value is the one kept on the trace.`
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
...payload,
|
|
447
|
+
externalTrace: { ...external, metadata: { ...derived, ...caller } }
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
415
451
|
// src/otel.ts
|
|
416
452
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
417
453
|
import {
|
|
@@ -1646,7 +1682,8 @@ var HttpClient = class {
|
|
|
1646
1682
|
* {@link HttpClient.sendExternalSpan}; replay confirms persistence with the
|
|
1647
1683
|
* server-authoritative barrier in `replay.ts`, not by awaiting this call.
|
|
1648
1684
|
*/
|
|
1649
|
-
sendExternalTrace(
|
|
1685
|
+
sendExternalTrace(rawPayload) {
|
|
1686
|
+
const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
|
|
1650
1687
|
this.getTraceTransport()?.submit(
|
|
1651
1688
|
"external_trace",
|
|
1652
1689
|
{
|
|
@@ -1879,4 +1916,4 @@ export {
|
|
|
1879
1916
|
parseRetryAfterMs,
|
|
1880
1917
|
HttpClient
|
|
1881
1918
|
};
|
|
1882
|
-
//# sourceMappingURL=chunk-
|
|
1919
|
+
//# sourceMappingURL=chunk-UQLT25PG.js.map
|