@bitfab/sdk 0.44.0 → 0.44.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-AX6EZPGH.js → chunk-AYEJRU7Z.js} +2 -2
- package/dist/{chunk-AX6EZPGH.js.map → chunk-AYEJRU7Z.js.map} +1 -1
- package/dist/{chunk-XYMG2FH7.js → chunk-RZF5XSM6.js} +6 -6
- package/dist/{chunk-3WIN3VU3.js → chunk-SMWYCJH6.js} +13 -5
- package/dist/chunk-SMWYCJH6.js.map +1 -0
- package/dist/{chunk-6JTVCM2L.js → chunk-XTX66R4I.js} +2 -2
- package/dist/{chunk-6JTVCM2L.js.map → chunk-XTX66R4I.js.map} +1 -1
- package/dist/{http-Q4TSO32V.js → http-CL4DNNRP.js} +2 -2
- package/dist/{http-QWUWDTFN.js → http-GHKRM3L7.js} +2 -2
- package/dist/index.cjs +12 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +3 -3
- package/dist/node.cjs +12 -4
- 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-RPB6CT6M.js → replay-IPATGCIY.js} +3 -3
- package/dist/replayCli.js +2 -2
- package/dist/replayCli.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3WIN3VU3.js.map +0 -1
- /package/dist/{chunk-XYMG2FH7.js.map → chunk-RZF5XSM6.js.map} +0 -0
- /package/dist/{http-Q4TSO32V.js.map → http-CL4DNNRP.js.map} +0 -0
- /package/dist/{http-QWUWDTFN.js.map → http-GHKRM3L7.js.map} +0 -0
- /package/dist/{replay-RPB6CT6M.js.map → replay-IPATGCIY.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/codeChange.ts","../src/mockOverride.ts","../src/randomUuid.ts","../src/serialize.ts","../src/replay.ts"],"sourcesContent":["import type { CodeChangeFile } from \"./http\"\n\n/**\n * Auto-capture the code change to attach to a replay, when the caller passed\n * none explicitly.\n *\n * This lives in the SDK (not a wrapper) on purpose: `replay()` is the only point\n * guaranteed to run on every replay, so capturing here works no matter how the\n * replay was launched (plugin wrapper, a hand-run script, CI). Precedence:\n *\n * 1. `BITFAB_CODE_CHANGE_PATH` file — an override a caller/tool can inject.\n * 2. `git diff` vs trunk — the default fallback.\n *\n * Both are best-effort and browser-safe: any failure (no git, no fs, not a repo,\n * bad JSON) yields `null` and the replay proceeds with no code change. The diff\n * is cumulative (whole branch vs trunk), not per-experiment; an explicit\n * `codeChangeFiles` on `replay()` always wins over this and is what carries a\n * precise per-experiment before/after.\n */\n\nexport interface ResolvedCodeChange {\n description?: string\n files?: CodeChangeFile[]\n}\n\n// Bounds so a large delta never bloats the experiment payload.\nconst MAX_FILES = 60\nconst MAX_FILE_BYTES = 500_000\nconst MAX_TOTAL_BYTES = 2_000_000\n\n// Candidate trunk refs, tried in order, when no explicit base is supplied.\nconst TRUNK_CANDIDATES = [\n \"origin/HEAD\",\n \"origin/main\",\n \"origin/master\",\n \"main\",\n \"master\",\n]\n\nconst NUL = String.fromCharCode(0)\n\n/**\n * Resolve an auto code change: the `BITFAB_CODE_CHANGE_PATH` override first,\n * then the git-vs-trunk diff. Returns null when neither yields anything.\n */\nexport async function resolveAutoCodeChange(\n label?: string,\n): Promise<ResolvedCodeChange | null> {\n if (typeof process === \"undefined\") {\n return null\n }\n if (process.env?.BITFAB_DISABLE_CODE_CHANGE_CAPTURE) {\n return null\n }\n const fromEnv = await readCodeChangeFile()\n if (fromEnv) {\n return fromEnv\n }\n return captureCodeChangeFromGit(process.cwd?.() ?? \".\", label)\n}\n\nasync function readCodeChangeFile(): Promise<ResolvedCodeChange | null> {\n const path = process.env?.BITFAB_CODE_CHANGE_PATH\n if (!path) {\n return null\n }\n try {\n const { readFile } = await import(\"node:fs/promises\")\n const parsed = JSON.parse(await readFile(path, \"utf8\"))\n // A malformed payload (non-array, or entries that aren't objects) must yield\n // no code change, never forward a bad shape to the start-replay request.\n const files =\n Array.isArray(parsed?.files) &&\n parsed.files.every(\n (f: unknown) =>\n typeof f === \"object\" && f !== null && !Array.isArray(f),\n )\n ? parsed.files\n : undefined\n const description =\n typeof parsed?.description === \"string\" ? parsed.description : undefined\n if (!files && description === undefined) {\n return null\n }\n return { description, files }\n } catch {\n return null\n }\n}\n\nasync function captureCodeChangeFromGit(\n cwd: string,\n label?: string,\n): Promise<ResolvedCodeChange | null> {\n let execFile: typeof import(\"node:child_process\").execFile\n let readFile: typeof import(\"node:fs/promises\").readFile\n try {\n ;({ execFile } = await import(\"node:child_process\"))\n ;({ readFile } = await import(\"node:fs/promises\"))\n } catch {\n // No child_process / fs (e.g. a browser bundle): capture is a no-op.\n return null\n }\n\n const git = (dir: string, args: string[]): Promise<string | null> =>\n new Promise((resolve) => {\n execFile(\n \"git\",\n args,\n // 30s timeout so a hung git (e.g. a network-touching ref op) can't\n // block the whole replay indefinitely.\n { cwd: dir, maxBuffer: 64 * 1024 * 1024, timeout: 30_000 },\n (err, stdout) => resolve(err ? null : stdout),\n )\n })\n\n try {\n const root = (await git(cwd, [\"rev-parse\", \"--show-toplevel\"]))?.trim()\n if (!root) {\n return null\n }\n\n const resolved = await resolveBase(git, root)\n if (!resolved) {\n return null\n }\n const { base, fromTrunk } = resolved\n\n // Size of a path on either side WITHOUT reading its contents: the git blob\n // size for `before`, a stat for the working `after`. Lets us skip an\n // oversized file before loading it into memory.\n const blobBytes = async (ref: string, path: string): Promise<number> => {\n const out = await git(root, [\"cat-file\", \"-s\", `${ref}:${path}`])\n const n = out ? Number.parseInt(out.trim(), 10) : Number.NaN\n return Number.isFinite(n) ? n : 0\n }\n const workingBytes = async (path: string): Promise<number> => {\n try {\n const { stat } = await import(\"node:fs/promises\")\n const { join } = await import(\"node:path\")\n return (await stat(join(root, path))).size\n } catch {\n return 0\n }\n }\n\n // Tracked changes vs base. `:!.bitfab` keeps replay artifacts out.\n const tracked = await git(root, [\n \"diff\",\n \"--name-status\",\n \"--find-renames\",\n \"-z\",\n base,\n \"--\",\n \":!.bitfab\",\n ])\n // Untracked-but-not-ignored files (the diff above omits these).\n const untracked = await git(root, [\n \"ls-files\",\n \"--others\",\n \"--exclude-standard\",\n \"-z\",\n \"--\",\n \":!.bitfab\",\n ])\n\n const entries: GitChange[] = [\n ...parseNameStatusZ(tracked ?? \"\"),\n ...(untracked ?? \"\")\n .split(NUL)\n .filter((p) => p.length > 0)\n .map((path) => ({ status: \"A\", beforePath: path, path })),\n ]\n if (entries.length === 0) {\n return null\n }\n\n const files: CodeChangeFile[] = []\n let totalBytes = 0\n for (const { status, beforePath, path } of entries) {\n if (files.length >= MAX_FILES) {\n break\n }\n // Skip oversized files by size BEFORE reading their full contents, so a\n // huge changed file can't OOM/stall the replay just to be discarded.\n const beforeBytes = status === \"A\" ? 0 : await blobBytes(base, beforePath)\n const afterBytes = status === \"D\" ? 0 : await workingBytes(path)\n if (beforeBytes > MAX_FILE_BYTES || afterBytes > MAX_FILE_BYTES) {\n continue\n }\n // Normalize CRLF -> LF on both sides: git's blob is already LF-normalized\n // (autocrlf clean), so a raw CRLF working file would otherwise skew every\n // line as changed. Comparing/storing LF keeps the diff meaningful.\n const before = (\n status === \"A\"\n ? \"\"\n : ((await git(root, [\"show\", `${base}:${beforePath}`])) ?? \"\")\n ).replace(/\\r\\n/g, \"\\n\")\n const after = (\n status === \"D\" ? \"\" : await readWorkingFile(readFile, root, path)\n ).replace(/\\r\\n/g, \"\\n\")\n if (before === after) {\n continue\n }\n // Bytes, not chars: a multi-byte source file must count against the byte\n // cap the same way Ruby's `bytesize` does.\n const size =\n Buffer.byteLength(before, \"utf8\") + Buffer.byteLength(after, \"utf8\")\n if (\n totalBytes + size > MAX_TOTAL_BYTES ||\n looksBinary(before) ||\n looksBinary(after)\n ) {\n continue\n }\n totalBytes += size\n files.push({ path, before, after })\n }\n\n if (files.length === 0) {\n return null\n }\n\n const subject = (\n await git(root, [\"log\", \"-1\", \"--format=%s\", \"HEAD\"])\n )?.trim()\n const fileWord = files.length === 1 ? \"file\" : \"files\"\n const head = label?.trim() || subject || \"Working-tree change\"\n // \"vs trunk\" only when we actually diffed against a trunk merge-base; the\n // HEAD fallback captures uncommitted work only, so label it honestly.\n const against = fromTrunk ? \"vs trunk\" : \"uncommitted (vs HEAD)\"\n return {\n description: `${head} (${files.length} ${fileWord} changed ${against})`,\n files,\n }\n } catch {\n return null\n }\n}\n\nasync function resolveBase(\n git: (dir: string, args: string[]) => Promise<string | null>,\n root: string,\n): Promise<{ base: string; fromTrunk: boolean } | null> {\n const forced = process.env?.BITFAB_CODE_CHANGE_BASE\n if (forced && (await refExists(git, root, forced))) {\n const base =\n (await git(root, [\"merge-base\", \"HEAD\", forced]))?.trim() ||\n (await git(root, [\"rev-parse\", \"--verify\", forced]))?.trim() ||\n null\n return base ? { base, fromTrunk: true } : null\n }\n for (const candidate of TRUNK_CANDIDATES) {\n if (!(await refExists(git, root, candidate))) {\n continue\n }\n const mb = (await git(root, [\"merge-base\", \"HEAD\", candidate]))?.trim()\n if (mb) {\n return { base: mb, fromTrunk: true }\n }\n }\n return (await refExists(git, root, \"HEAD\"))\n ? { base: \"HEAD\", fromTrunk: false }\n : null\n}\n\nasync function refExists(\n git: (dir: string, args: string[]) => Promise<string | null>,\n root: string,\n ref: string,\n): Promise<boolean> {\n return (\n (await git(root, [\"rev-parse\", \"--verify\", `${ref}^{object}`])) !== null\n )\n}\n\nasync function readWorkingFile(\n readFile: typeof import(\"node:fs/promises\").readFile,\n root: string,\n path: string,\n): Promise<string> {\n try {\n const { join } = await import(\"node:path\")\n return await readFile(join(root, path), \"utf8\")\n } catch {\n return \"\"\n }\n}\n\ninterface GitChange {\n status: string\n beforePath: string\n path: string\n}\n\nfunction parseNameStatusZ(raw: string): GitChange[] {\n const parts = raw.split(NUL).filter((p) => p.length > 0)\n const out: GitChange[] = []\n let i = 0\n while (i + 1 < parts.length) {\n const status = parts[i].charAt(0)\n i += 1\n const beforePath = parts[i]\n i += 1\n if ((status === \"R\" || status === \"C\") && i < parts.length) {\n out.push({ status, beforePath, path: parts[i] })\n i += 1\n } else {\n out.push({ status, beforePath, path: beforePath })\n }\n }\n return out\n}\n\nfunction looksBinary(s: string): boolean {\n return s.slice(0, 8000).includes(NUL)\n}\n","/**\n * Selective mock overrides for replay.\n *\n * A mock override injects a custom value into a specific span (node) during\n * replay: the matched span short-circuits its real execution and returns the\n * value you supply, so downstream real code runs against the substituted\n * output. This is a third mock mode alongside \"run real code\" and \"replay\n * recorded output\" (see {@link MockStrategy}).\n *\n * The matcher and value are deliberately separate so matching stays cheap\n * (structural metadata only, no output fetch) and the recorded output is\n * fetched lazily - and only if the value function actually asks for it via\n * {@link MockOverrideCtx.getOriginalOutput}.\n */\n\n/**\n * Structural identity of a span during replay, passed to a {@link NodeMatcher}.\n * Carries no output payload - matching must not depend on the recorded output,\n * so the output fetch stays lazy and gated.\n */\nexport interface SpanNodeMeta {\n /** The `withSpan` key of the executing span (its code-side identity). */\n traceFunctionKey: string\n /** Resolved span name: `options.name ?? fn.name ?? traceFunctionKey`. */\n spanName: string\n /** Span type, e.g. \"llm\", \"agent\", \"tool\", \"custom\". */\n type: string\n /**\n * The id of this span in the original (replayed) trace, when it exists in\n * that trace's tree. Undefined when the live span has no recorded\n * counterpart (e.g. a span the changed code newly introduced).\n */\n originalSpanId?: string\n}\n\n/** Context passed to a {@link MockValue} function for a matched span. */\nexport interface MockOverrideCtx {\n /** The matched span's structural metadata. */\n node: SpanNodeMeta\n /** The live replay args passed to the wrapped function this run. */\n inputs: unknown[]\n /**\n * Lazily fetch this span's ORIGINAL recorded output (deserialized). The fetch\n * happens only when called and is memoized per replay item, so a purely flat\n * override that never calls it triggers zero output round trips. Rejects if\n * the span has no recorded counterpart. Being async, it is only usable for\n * spans wrapping async functions.\n */\n getOriginalOutput: () => Promise<unknown>\n}\n\n/**\n * Return this from a mock override resolver to decline the override for the\n * current span. Resolution continues with the next override, then the replay's\n * base mock strategy.\n */\nexport const NO_MOCK_OVERRIDE = Symbol(\"bitfab.noMockOverride\")\n\n/** Selects which spans an override applies to. Runs on structural metadata. */\nexport type NodeMatcher = (node: SpanNodeMeta) => boolean\n\n/** The function form of {@link MockValue}, receiving the override context. */\nexport type MockValueFn = (ctx: MockOverrideCtx) => unknown | Promise<unknown>\n\n/**\n * A client-wide, keyed, or per-replay resolver. A global resolver can route on\n * `ctx.node.traceFunctionKey`; a keyed resolver is only invoked for spans with\n * its registered key. Return {@link NO_MOCK_OVERRIDE} for spans the resolver\n * does not want to override.\n */\nexport type MockOverrideResolver = (\n ctx: MockOverrideCtx,\n) => unknown | Promise<unknown>\n\n/**\n * The value injected for a matched span: either a flat value used as-is, or a\n * function of the {@link MockOverrideCtx} that returns one (or a Promise of\n * one). Full replacement - the value IS the span's output, no merge with the\n * recorded output.\n *\n * The flat side is spelled out (rather than `unknown`) so an inline function\n * still gets a typed `ctx`: `unknown | Fn` would collapse to `unknown` and drop\n * the contextual type. To inject a value that is itself a function, wrap it:\n * `value: () => theFunction`.\n */\nexport type MockValue =\n | MockValueFn\n | string\n | number\n | boolean\n | bigint\n | symbol\n | object\n | null\n | undefined\n\n/** One (match, value) pair. */\nexport interface MockOverride {\n match: NodeMatcher\n value: MockValue\n}\n\n/** Accepted shape for one mock override declaration. */\nexport type MockOverrideInput = MockOverride | MockOverrideResolver\n\n/**\n * Resolve an override's `value`: call it with `ctx` when it's a function, else\n * use it as-is (a flat value).\n */\nexport function resolveMockValue(\n value: MockValue,\n ctx: MockOverrideCtx,\n): unknown | Promise<unknown> {\n return typeof value === \"function\"\n ? (value as (c: MockOverrideCtx) => unknown | Promise<unknown>)(ctx)\n : value\n}\n\n/**\n * Normalize the per-call `mockOverride` option (an override pair, a global\n * resolver, an array, or nothing) into an ordered internal override array.\n */\nexport function normalizeMockOverrides(\n mockOverride?: MockOverrideInput | MockOverrideInput[],\n): MockOverride[] {\n if (mockOverride === undefined) {\n return []\n }\n const overrides = Array.isArray(mockOverride) ? mockOverride : [mockOverride]\n return overrides.map((override) =>\n typeof override === \"function\"\n ? { match: () => true, value: override }\n : override,\n )\n}\n\n/**\n * What a replay mock replaced on a span, and where the replacement came from.\n * Reported alongside `mocked` so a trace can say how a span was produced rather\n * than only that it was not re-run.\n *\n * `target` is `\"output\"` for every mock today: both the recorded-output path\n * and an override short-circuit the call. `\"input\"` is reserved for\n * substituting a span's arguments and letting the real code run.\n */\nexport type MockTarget = \"output\" | \"input\"\n\n/**\n * `recorded` is the original trace's own value; `override` is one the caller\n * supplied via `mockOverride` / `registerMockOverride`. Deliberately not\n * \"dynamic\": an override value may be a flat constant.\n */\nexport type MockSource = \"recorded\" | \"override\"\n","/**\n * Generate a UUID v4 without assuming a global `crypto`.\n *\n * `crypto.randomUUID()` is the fast path and exists in every mainstream modern\n * runtime (Node >= 19, all browsers, Deno, Vercel edge, Cloudflare Workers).\n * But the SDK must NEVER crash a host app, and a few runtimes it can legitimately\n * run in lack a global `crypto` or `crypto.randomUUID` (Node < 19 without a\n * polyfill, older React Native, some sandboxes). There, an unguarded\n * `crypto.randomUUID()` throws on the user's call path. This helper falls back\n * to a `Math.random()`-based v4 string instead.\n *\n * The fallback is NOT cryptographically secure. That is fine: trace and span\n * IDs only need to be unique enough to correlate the spans of one trace; they\n * are never security-sensitive.\n */\nimport { warnOnce } from \"./warnOnce.js\"\n\nexport function randomUuid(): string {\n const globalCrypto = (\n globalThis as { crypto?: { randomUUID?: () => string } }\n ).crypto\n if (typeof globalCrypto?.randomUUID === \"function\") {\n try {\n return globalCrypto.randomUUID()\n } catch {\n // Fall through to the manual generator below.\n }\n }\n warnOnce(\n \"crypto-unavailable\",\n \"global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive).\",\n )\n return fallbackUuidV4()\n}\n\n/**\n * RFC 4122 version 4 layout filled from `Math.random()`. Used only when a usable\n * global `crypto.randomUUID` is unavailable.\n */\nfunction fallbackUuidV4(): string {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (char) => {\n const rand = (Math.random() * 16) | 0\n const value = char === \"x\" ? rand : (rand & 0x3) | 0x8\n return value.toString(16)\n })\n}\n","/**\n * Serialization utilities for Bitfab SDK.\n *\n * This module provides serialization with type metadata preservation,\n * using superjson for handling special JavaScript types like Date, Map,\n * Set, BigInt, undefined, etc.\n */\n\nimport superjson from \"superjson\"\nimport { MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES } from \"./payloadBudget.js\"\nimport { warnOnce } from \"./warnOnce.js\"\n\n/**\n * Serialized value with JSON data and optional superjson meta for type preservation.\n *\n * The json field contains the JSON-serializable data.\n * The meta field (if present) contains superjson type information for deserializing\n * special types like Date, Map, Set, BigInt, etc.\n */\nexport interface SerializedValue {\n json: unknown\n meta?: unknown\n}\n\n// Cap on a single serialized value. superjson can succeed on values like SDK\n// client instances (OpenAI, etc.) and produce hundreds of KB to MB of useless\n// internal state, so this is the cheap early-out that stops the walk before a\n// pathological object is carried any further. It is deliberately the same\n// number as the whole-span budget: one legitimately large value may use the\n// entire budget, and `serializePayloadBody` is what enforces the total once\n// every field is in. Anything past it is replaced with a stub so the span\n// still ships and the trace isn't dropped server-side.\nconst MAX_SERIALIZED_BYTES = MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES\n\n// The framework-capture path (toJsonSafe) shares that ceiling. Agent/graph\n// states (LangGraph, Claude Agent SDK) are legitimately larger than a single\n// function's args/return, and capping them lower than ordinary values only\n// discarded detail the span had room for.\nconst MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES\n\nfunction describeValue(value: unknown): string {\n try {\n const ctorName = (value as { constructor?: { name?: string } })?.constructor\n ?.name\n if (ctorName && ctorName !== \"Object\") {\n return ctorName\n }\n } catch {\n // Property access on `value` can throw (Proxy, poisoned getter).\n }\n return typeof value\n}\n\nfunction unserializableStub(value: unknown, reason: string): SerializedValue {\n // Normalize the byte count out of the key so a too_large warning dedups\n // across differently-sized payloads instead of warning once per size.\n warnOnce(\n `serialize:${reason.replace(/\\d+/g, \"N\")}`,\n `a value could not be fully serialized for a span (${reason}); it was replaced with a placeholder. The span still ships, but its captured input/output is incomplete.`,\n )\n let summary: string\n try {\n summary = `<unserializable: ${describeValue(value)} (${reason})>`\n } catch {\n summary = `<unserializable (${reason})>`\n }\n return { json: summary }\n}\n\n/**\n * Serialize a value using superjson for trace storage.\n *\n * Handles arbitrary JavaScript values including:\n * - Date, RegExp, Error\n * - Map, Set\n * - BigInt\n * - undefined (in objects/arrays)\n * - Circular references\n *\n * Guarantees:\n * - Never throws. Pathological inputs (SDK clients, proxies, poisoned\n * getters, circular graphs that defeat superjson) return a stub string.\n * - Never returns a payload larger than MAX_SERIALIZED_BYTES; oversized\n * inputs are replaced with a stub. Without this the wire-side\n * `JSON.stringify` in http.ts can produce a request that times out or\n * gets rejected, leaving a trace with zero spans.\n *\n * @param value - Any JavaScript value to serialize\n * @returns SerializedValue with 'json' field containing the data.\n * If type metadata is needed for reconstruction, includes 'meta' field.\n *\n * @example\n * ```typescript\n * const result = serializeValue(new Date('2024-01-15T10:30:00Z'))\n * // result.json contains the ISO string\n * // result.meta contains type info for Date reconstruction\n * ```\n */\nexport function serializeValue(value: unknown): SerializedValue {\n try {\n const { json, meta } = superjson.serialize(value)\n\n let size: number\n try {\n size = JSON.stringify(json).length\n } catch {\n return unserializableStub(value, \"stringify_failed_after_superjson\")\n }\n if (size > MAX_SERIALIZED_BYTES) {\n return unserializableStub(value, `too_large_${size}_bytes`)\n }\n\n return meta ? { json, meta } : { json }\n } catch {\n try {\n return { json: JSON.parse(JSON.stringify(value)) }\n } catch {\n return unserializableStub(value, \"json_stringify_failed\")\n }\n }\n}\n\n/**\n * Deserialize a value that was serialized with serializeValue.\n *\n * @param serialized - A SerializedValue object with 'json' and optional 'meta'\n * @returns The reconstructed JavaScript value\n *\n * @example\n * ```typescript\n * const serialized = serializeValue(new Date('2024-01-15'))\n * const date = deserializeValue(serialized)\n * // date is a Date object\n * ```\n */\nexport function deserializeValue(serialized: SerializedValue): unknown {\n if (serialized.meta === undefined) {\n // No metadata, return as-is\n return serialized.json\n }\n\n // Use superjson to deserialize with type reconstruction\n // Cast json to the expected superjson type\n type SuperJSONResult = Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize({\n json: serialized.json as SuperJSONResult[\"json\"],\n meta: serialized.meta as SuperJSONResult[\"meta\"],\n })\n}\n\nconst MAX_SAFE_DEPTH = 6\n\n/**\n * Convert any value to JSON-safe primitives, never throwing.\n *\n * Produces plain objects/arrays/scalars, recursing through `toJSON()` and\n * own-enumerable properties so no raw non-serializable value (a class, a\n * BigInt-bearing object) survives into a span payload. Cycles collapse to a\n * `<cycle ...>` marker; depth is capped.\n *\n * This is the single shared \"safe serialize\" used by the framework\n * integrations that capture raw objects (LangGraph, Claude Agent SDK). Keeping\n * the recurse-the-dump logic here, in one place, is what stops a new\n * integration from reintroducing the \"dump without recursing\" bug - see\n * `serializationInvariant.test.ts`.\n */\nexport function toJsonSafe(value: unknown): unknown {\n return toJsonSafeReport(value).safe\n}\n\n/**\n * Like {@link toJsonSafe}, but also reports what could not be faithfully\n * captured.\n *\n * Returns `{ safe, dropped }` where `dropped` lists the type name behind every\n * placeholder the walker had to emit: a cycle, a max-depth cut, an oversized\n * payload, or a value that could only be stringified (a function/symbol) or\n * stubbed after a throw. A non-empty `dropped` means the captured input/output\n * is lossy. Framework handlers carry it to the send boundary so a degraded\n * capture is marked non-replayable (`serialization_degraded`) instead of being\n * shipped as if it round-trips - mirrors the Python SDK's `to_json_safe_report`\n * + `finalize_span_payload`.\n */\nexport function toJsonSafeReport(value: unknown): {\n safe: unknown\n dropped: string[]\n} {\n const dropped: string[] = []\n const safe = toJsonSafeInner(value, 0, new WeakSet(), dropped)\n // Cap output size for parity with serializeValue. A multi-MB framework\n // payload (a large LangGraph state, a long message history) would otherwise\n // be JSON.stringify'd synchronously on the user's thread in http.ts and may\n // be rejected server-side, leaving a trace with zero spans. Stub it instead\n // so the span still ships. toJsonSafeInner produces only plain\n // objects/arrays/scalars/strings, so JSON.stringify here cannot throw; the\n // try is belt-and-suspenders.\n try {\n const size = JSON.stringify(safe)?.length ?? 0\n if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {\n warnOnce(\n \"toJsonSafe:too_large\",\n `a framework payload exceeded ${MAX_FRAMEWORK_SERIALIZED_BYTES} bytes and was replaced with a placeholder so the span still ships. The captured state for this span is incomplete.`,\n )\n // Keep any drops already accumulated by the walk (cycles, functions,\n // depth cuts); a payload can be both lossy AND oversized, and the\n // non-replayable marking needs the real types, not just the size stub.\n return {\n safe: `<unserializable: too_large_${size}_bytes>`,\n dropped: [...dropped, `too_large_${size}_bytes`],\n }\n }\n } catch {\n // Keep the recursed value; the http-layer sanitizer is the final backstop.\n }\n return { safe, dropped }\n}\n\nfunction toJsonSafeInner(\n value: unknown,\n depth: number,\n seen: WeakSet<object>,\n dropped: string[],\n): unknown {\n if (value === null || value === undefined) {\n return value\n }\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value\n }\n\n const className =\n (value as { constructor?: { name?: string } })?.constructor?.name ??\n typeof value\n if (depth > MAX_SAFE_DEPTH) {\n dropped.push(className)\n return `<${className}>`\n }\n\n // Non-object composites (bigint, function, symbol) stringify directly. A\n // bigint stringifies faithfully; a function/symbol becomes a lossy summary,\n // so those are reported as dropped.\n if (typeof value !== \"object\") {\n if (typeof value === \"function\" || typeof value === \"symbol\") {\n dropped.push(className)\n }\n try {\n return String(value)\n } catch {\n dropped.push(className)\n return `<${className}>`\n }\n }\n\n if (seen.has(value as object)) {\n dropped.push(className)\n return `<cycle ${className}>`\n }\n seen.add(value as object)\n\n let result: unknown\n if (Array.isArray(value)) {\n result = value.map((item) =>\n toJsonSafeInner(item, depth + 1, seen, dropped),\n )\n } else if (typeof (value as Record<string, unknown>).toJSON === \"function\") {\n // Recurse toJSON() output: it can still hold non-serializable values (e.g.\n // a LangChain tool whose schema is a class) that would otherwise survive\n // into the span payload and crash the wire-side JSON.stringify.\n try {\n result = toJsonSafeInner(\n (value as { toJSON(): unknown }).toJSON(),\n depth + 1,\n seen,\n dropped,\n )\n } catch {\n dropped.push(className)\n result = `<${className}>`\n }\n } else {\n try {\n const obj: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n if (!k.startsWith(\"_\")) {\n obj[k] = toJsonSafeInner(v, depth + 1, seen, dropped)\n }\n }\n result = obj\n } catch {\n dropped.push(className)\n result = `<${className}>`\n }\n }\n\n // Backtrack: keep only ancestors on the current path in `seen`, so a shared\n // (DAG) reference under sibling keys is serialized again rather than stubbed\n // as a false cycle. Real cycles (an ancestor referencing itself) are still\n // caught above.\n seen.delete(value as object)\n return result\n}\n","/**\n * Replay historical traces through a function and create a test run.\n *\n * The replay flow has three phases:\n * 1. Start: fetches historical traces from the server and creates a test run\n * 2. Execute: re-runs each trace's inputs through the provided function locally\n * 3. Complete: marks the test run as completed on the server\n */\n\nimport { resolveAutoCodeChange } from \"./codeChange.js\"\nimport type { DbSnapshotRef } from \"./dbSnapshot.js\"\nimport { BitfabError } from \"./errors.js\"\nimport {\n type CodeChangeFile,\n flushTraces,\n type HttpClient,\n type SpanTreeNode,\n type TokenUsage,\n} from \"./http.js\"\nimport type { MockOverride, MockOverrideInput } from \"./mockOverride.js\"\nimport { normalizeMockOverrides } from \"./mockOverride.js\"\nimport { randomUuid } from \"./randomUuid.js\"\nimport type {\n DbBranchLease,\n DbBranchSettings,\n DbBranchTimings,\n MockSpan,\n MockTree,\n} from \"./replayContext.js\"\nimport { replayContextReady, runWithReplayContext } from \"./replayContext.js\"\nimport { deserializeValue } from \"./serialize.js\"\n\nexport type MockStrategy = \"none\" | \"all\" | \"marked\"\n\nexport type TraceIngestionType = \"captured\" | \"seeded\"\n\nconst REPLAY_PERSISTENCE_TIMEOUT_MS = 30_000\nconst MAX_REPLAY_ATTEMPTS = 100\n\nfunction expandReplayWork<T>(\n serverItems: T[],\n attempts: number,\n): Array<{ serverItem: T; attempt: number }> {\n return Array.from({ length: attempts }, (_, attempt) =>\n serverItems.map((serverItem) => ({ serverItem, attempt })),\n ).flat()\n}\n\nfunction resolveAttempts(attempts: number | undefined): number {\n if (attempts === undefined) {\n return 1\n }\n if (\n !Number.isInteger(attempts) ||\n attempts < 1 ||\n attempts > MAX_REPLAY_ATTEMPTS\n ) {\n throw new BitfabError(\n `attempts must be an integer from 1 to ${MAX_REPLAY_ATTEMPTS} (got ${attempts}).`,\n )\n }\n return attempts\n}\n\n/**\n * How the DB-snapshot branch each replay item runs against is sized and warmed.\n *\n * Every field is optional, so this object is only worth passing when you want\n * to override the mirror project's own sizing. To branch with those defaults,\n * pass `dbBranch: true` instead of an empty object.\n */\nexport interface DbBranchOptions {\n /**\n * Autoscaling floor for the branch's compute, in Neon Compute Units (0.25 to\n * 56). Omit to keep the mirror project's own default. Raise it when the\n * mirror is provisioned smaller than the database it stands in for, so\n * replay latency reflects your code rather than a cold, undersized branch.\n */\n minCu?: number\n /**\n * Autoscaling ceiling for the branch's compute, in Neon Compute Units.\n * Setting it equal to `minCu` pins the size, which keeps items comparable:\n * otherwise a later item can run against an endpoint that has already\n * scaled up and post a better number for the same code.\n */\n maxCu?: number\n /**\n * SQL that warms the branch's cache. The server appends it to the branch's\n * readiness check, so it runs BEFORE your function sees the branch and its\n * time is not charged to the replayed call. Invalid SQL fails the branch\n * rather than silently leaving it cold.\n */\n warmupSql?: string\n}\n\n/**\n * Whether the caller asked for database branching. `true` and a settings object\n * both turn it on; `false` and omission leave it off. Kept separate from\n * `resolveDbBranchSettings` because `dbBranch: true` enables branching while\n * contributing no settings, so presence of settings can't stand in for the\n * switch.\n */\nfunction dbBranchEnabled(\n dbBranch: DbBranchOptions | boolean | undefined,\n): boolean {\n return dbBranch !== undefined && dbBranch !== false\n}\n\n/**\n * Narrow the caller's branch options to the wire shape, dropping the object\n * entirely when nothing was set so the request stays byte-identical to what\n * older SDKs send. Booleans carry no settings: they only move the switch.\n */\nfunction resolveDbBranchSettings(\n dbBranch: DbBranchOptions | boolean | undefined,\n): DbBranchSettings | undefined {\n if (!dbBranch || dbBranch === true) {\n return undefined\n }\n const { minCu, maxCu, warmupSql } = dbBranch\n const settings: DbBranchSettings = {\n ...(minCu === undefined ? {} : { minCu }),\n ...(maxCu === undefined ? {} : { maxCu }),\n ...(warmupSql === undefined ? {} : { warmupSql }),\n }\n return Object.keys(settings).length === 0 ? undefined : settings\n}\n\nexport interface ReplayOptions {\n /**\n * Maximum number of traces to replay (1-5,000, default 5). Ignored when\n * `traceIds` is passed (with a warning), or when `datasetId` is passed,\n * because either source already determines how many traces replay.\n */\n limit?: number\n attempts?: number\n /** Optional list of specific trace IDs to replay (max 100). Mutually exclusive with `datasetId`. */\n traceIds?: string[]\n /** Optional display name for the resulting experiment/test run. */\n name?: string\n /** Maximum number of items to process in parallel. Set to 1 for sequential. Default 10. */\n maxConcurrency?: number\n /**\n * Description of the code change being tested in this replay. Stored on\n * the resulting experiment so the change can be reviewed alongside results.\n * Supplying a description without `codeChangeFiles` preserves this text\n * while the SDK automatically captures the files.\n * Pass `null` when the replay should have no description; use\n * `codeChangeFiles: null` to suppress automatic file capture.\n */\n codeChangeDescription?: string | null\n /**\n * Files edited as part of this code change. Each entry holds the file path\n * and the full `before`/`after` contents. The agent reads each file before\n * and after editing and passes the two strings. Use `\"\"` for newly created\n * files (`before`) or deleted files (`after`). Omit this field to capture\n * automatically, or pass `null` to suppress file capture for this replay.\n */\n codeChangeFiles?: CodeChangeFile[] | null\n /**\n * Mock strategy for child spans during replay.\n * - \"marked\": only spans tagged with { mockOnReplay: true } in SpanOptions are mocked (default)\n * - \"none\": everything runs real code\n * - \"all\": every matched recorded child withSpan returns historical output;\n * a missing occurrence fails the replay item closed\n */\n mock?: MockStrategy\n /**\n * Selective mock overrides: inject custom values into specific spans during\n * replay, so downstream real code runs against the substituted output. Each\n * Pass `{ match, value }` pairs, or one resolver invoked for every child span.\n * A resolver can route on `ctx.node.traceFunctionKey` and return\n * `NO_MOCK_OVERRIDE` to continue to the next override and base strategy.\n * Per-call overrides take precedence over registrations on the client.\n */\n mockOverride?: MockOverrideInput | MockOverrideInput[]\n /**\n * Run each item against a database branch restored to the state its source\n * trace saw. Pass `true` to branch with the mirror project's own sizing, or a\n * {@link DbBranchOptions} object to tune how each branch is sized and warmed.\n * `false` and omission leave branching off. Inside `fn`, read the resolved\n * branch with `getCurrentReplayBranch()`.\n *\n * Items whose source trace carried no DB snapshot reference get no branch and\n * use the app's normal database path. Unsafe calls on that path still require\n * replay mocking. An item whose branch was requested but could not be resolved\n * fails instead of running, so replay never silently reports a result that did\n * not use the historical data you asked for.\n */\n dbBranch?: DbBranchOptions | boolean\n /** Group ID to associate this replay with an experiment group for live streaming in Studio. */\n experimentGroupId?: string\n /**\n * Dataset this replay runs against. Mutually exclusive with `traceIds`. When\n * set, the resulting experiment is\n * durably attributed to the dataset (stored on the test run), so it appears\n * under the dataset's experiments even if the trace lineage can't be\n * reconstructed. Validated server-side against the org.\n */\n datasetId?: string\n /**\n * Graders to attach directly to this experiment (test run), independent of any\n * graders already on the dataset. The resulting experiment is graded by the\n * union of these and the dataset's runnable graders at completion, so use this\n * to grade a single run with a check you don't want to add to the dataset\n * permanently. Each id must be an active/live grader belonging to the same\n * organization and trace function, otherwise the server rejects the replay.\n */\n graderIds?: string[]\n /**\n * Reshape recorded inputs before they are spread into `fn`.\n *\n * Replay pulls each trace's inputs exactly as they were captured against the\n * function's signature AT TRACE TIME. When the function's shape has since\n * changed (params renamed, reordered, collapsed into an options object, etc.),\n * the recorded inputs no longer line up and `fn(...inputs)` throws. This hook\n * lets a caller map the recorded inputs onto the current signature so replay\n * can still run.\n *\n * Receives the deserialized recorded inputs and a per-trace {@link AdaptContext}\n * (so a table-driven adapter can look up the adapted inputs by `traceId`), and\n * returns the array actually spread into `fn`. The returned array is also what\n * `ReplayItem.input` reports, so the experiment shows what was really run.\n *\n * Runs per item, inside the same try/catch as `fn`: if the adapter throws, the\n * failure is surfaced on that item's `error` rather than crashing the run.\n * Omit it to spread the recorded inputs unchanged.\n */\n adaptInputs?: (inputs: unknown[], ctx: AdaptContext) => unknown[]\n /**\n * Resolve every item's inputs and stop, without calling the function.\n *\n * Selection, span fetch, deserialization, and `adaptInputs` all run, so each\n * item reports the exact arguments the function would have received. Nothing\n * executes and no replay traces are produced, which is the cheap way to check\n * that recorded inputs still fit the current signature.\n */\n dryRun?: boolean\n /**\n * Called once per item as it finishes, in completion order (not input\n * order), with running totals for the whole run. Use it to render replay\n * progress, for example a terminal progress bar. Replay does not know\n * pass/fail at this point (verdicts are assigned later), so the totals only\n * distinguish items whose function ran (`succeeded`) from items that threw\n * (`errored`).\n *\n * Invoked synchronously right after each item finishes, so keep it cheap. A\n * throwing callback never crashes the run: the error is swallowed so progress\n * UI can't break replay.\n */\n onItemFinish?: (progress: ReplayItemFinishProgress) => void\n /**\n * @deprecated Use {@link onItemFinish}. This compatibility callback receives\n * the same per-item finish events plus the legacy whole-run `\"complete\"`\n * event. It is ignored when `onItemFinish` is also provided.\n */\n onProgress?: (progress: ReplayProgress) => void\n /**\n * Called once when each replay item begins processing, before input loading,\n * mock preparation, database branching, or the customer function runs. Use\n * it with {@link onItemFinish} to distinguish queued items from items that\n * are still in flight. A throwing callback never crashes the run.\n */\n onItemStart?: (progress: ReplayItemStartProgress) => void\n}\n\n/** Emitted through {@link ReplayOptions.onItemStart} when a worker starts an item. */\nexport interface ReplayItemStartProgress {\n type: \"started\"\n /** Test run ID created for this replay. */\n testRunId: string\n /** Items whose processing has started so far. */\n started: number\n /** Items that have finished so far, whether they succeeded or errored. */\n completed: number\n /** Total number of items in this replay run. */\n total: number\n /** Of the completed items, how many finished without a code or replay error. */\n succeeded: number\n /** Of the completed items, how many finished with an error. */\n errored: number\n /** The historical trace and span whose replay processing just started. */\n item: {\n originalTraceId: string\n originalSpanId: string\n /** @deprecated alias for `originalTraceId`. */\n sourceTraceId: string\n /** @deprecated alias for `originalSpanId`. */\n sourceSpanId: string\n attempt: number\n }\n}\n\n/** Running totals reported to {@link ReplayOptions.onItemFinish} as replay proceeds. */\nexport interface ReplayItemFinishProgress {\n /** Event kind, omitted for backward-compatible per-item events. */\n type?: \"item\"\n /** Test run ID created for this replay. */\n testRunId: string\n /** Items that have finished so far, whether they succeeded or errored. */\n completed: number\n /** Total number of items in this replay run. */\n total: number\n /** Of the completed items, how many finished without a code or replay error. */\n succeeded: number\n /** Of the completed items, how many have `item.error` set. */\n errored: number\n /**\n * The single item that just finished to produce this event. Field-for-field\n * the same shape and meaning as {@link ReplayItem}, so a progress UI and a\n * final-result UI read one contract. `traceId` is null at this stage (the\n * server replay id arrives at completion), and `tokens` is null for the same\n * reason: the replayed run's usage is aggregated server-side by\n * completeReplay. `durationMs` is how long this item took to replay; the\n * `original*` fields describe the trace it replayed.\n */\n item: ReplayProgressItem\n}\n\n/** See {@link ReplayItemFinishProgress.item}. Mirrors {@link ReplayItem}. */\nexport interface ReplayProgressItem {\n /** Trace ID of the new replay trace (null during the run; the server id arrives at completion). */\n traceId?: string | null\n /** Bitfab trace ID of the original (historical) trace being replayed. */\n originalTraceId: string | null\n /** External span ID the recorded inputs were read from (the original root span). */\n originalSpanId?: string | null\n /** @deprecated alias for `originalTraceId`. */\n sourceTraceId: string | null\n /** @deprecated alias for `originalSpanId`. */\n sourceSpanId?: string | null\n attempt: number\n /** Deserialized inputs from the original trace. */\n input?: unknown[]\n /** The result returned by the replayed function, or undefined on error. */\n result?: unknown\n /** The original output from the historical trace. */\n originalOutput?: unknown\n /** Backward-compatible message for either error kind. */\n error: string | null\n /** The actual value thrown by the replayed customer function. */\n traceError?: unknown | null\n /** The actual value thrown while Bitfab prepared this replay item. */\n replayError?: unknown | null\n /** How long the replayed function took on this run. */\n durationMs?: number | null\n /** The original trace's duration, tokens, and model. */\n originalDurationMs?: number | null\n originalTokens?: TokenUsage | null\n originalModel?: string | null\n /** Always null here: the replayed run's usage is only known at completion. */\n tokens?: TokenUsage | null\n /** @deprecated renamed to `originalModel`. */\n model?: string | null\n dbSnapshotRef?: DbSnapshotRef | null\n dbBranchTimings?: DbBranchTimings | null\n}\n\n/**\n * @deprecated Use {@link ReplayItemFinishProgress}. This legacy shape also\n * represents the item-less terminal `\"complete\"` event emitted by\n * {@link ReplayOptions.onProgress}.\n */\nexport interface ReplayProgress {\n type?: \"item\" | \"complete\"\n result?: ReplayResult<unknown>\n testRunId?: string\n completed: number\n total: number\n succeeded: number\n errored: number\n item?: ReplayItemFinishProgress[\"item\"]\n}\n\n/**\n * Wire prefix the Bitfab plugin scans for. Each line {@link reportReplayProgress}\n * writes is this prefix followed by the JSON of the running totals (plus the\n * finished `item`). The plugin polls these lines to report live progress while a\n * replay runs in the background; keep the SDK emitter and the plugin parser in\n * sync.\n */\nexport const BITFAB_PROGRESS_PREFIX = \"@@bitfab:progress \"\n\n/**\n * A ready-made replay lifecycle callback for replay scripts.\n * Pass it straight in:\n *\n * ```ts\n * await bitfab.replay(\"my-fn\", fn, {\n * limit,\n * onItemStart: reportReplayProgress,\n * onItemFinish: reportReplayProgress,\n * })\n * ```\n *\n * It writes `@@bitfab:progress` lines to stderr, which the Bitfab plugin polls\n * to report in-flight and finished items while replay runs in the background,\n * so scripts never hand-format the wire protocol.\n * stdout is left untouched for direct-run ReplayResult JSON. Outside Node (no\n * `process.stderr`, e.g. a browser) it is a no-op, and a write failure is\n * swallowed so progress can never crash a run.\n */\nexport function reportReplayProgress(\n progress: ReplayItemFinishProgress | ReplayItemStartProgress | ReplayProgress,\n): void {\n const stderr = typeof process !== \"undefined\" ? process.stderr : undefined\n if (!stderr) {\n return\n }\n try {\n stderr.write(\n `${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress, replayJsonReplacer)}\\n`,\n )\n } catch {\n // A progress reporter must never crash the replay run.\n }\n}\n\n/** Per-trace context passed to {@link ReplayOptions.adaptInputs}. */\nexport interface AdaptContext {\n /** Bitfab trace ID of the original (historical) trace being replayed. */\n originalTraceId: string\n /** External span ID the recorded inputs were read from. */\n originalSpanId: string\n /** @deprecated alias for {@link AdaptContext.originalTraceId}. */\n sourceTraceId: string\n /** @deprecated alias for {@link AdaptContext.originalSpanId}. */\n sourceSpanId: string\n metadata: Record<string, unknown>\n}\n\n/**\n * The shape an adapter module must export as `adaptInputs`.\n *\n * Author an adapter in its own file (e.g. `scripts/replay-adapters/<name>.ts`),\n * import it in your replay script, and pass it to `replay({ adaptInputs })`:\n *\n * ```ts\n * import type { AdaptInputsFn } from \"@bitfab/sdk\"\n * export const adaptInputs: AdaptInputsFn = (inputs, ctx) => [reshape(inputs)]\n * ```\n */\nexport type AdaptInputsFn = (inputs: unknown[], ctx: AdaptContext) => unknown[]\n\nexport interface ReplayItem<T> {\n /**\n * Server trace ID of the new replay trace this item produced. Written in by\n * `replay()` from the complete-replay response once the server has minted the\n * trace row; the client-side id used to correlate spans during the run is\n * never surfaced here. Null until completion, on older servers that omit the\n * mapping, or if the item produced no trace. Not the verdict-persistence key:\n * that is the original-trace lineage (`originalTraceId` + `testRunId`).\n */\n traceId: string | null\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 {@link ReplayItem.originalTraceId}. */\n sourceTraceId: string\n /** @deprecated alias for {@link ReplayItem.originalSpanId}. */\n sourceSpanId: string\n attempt: number\n /** Deserialized inputs from the original trace. */\n input: unknown[]\n /** The result returned by the function during replay, or undefined on error. */\n result: T | undefined\n originalOutput: unknown\n /**\n * How the source trace came to exist. Absent on servers that predate the\n * field, which only ever served captured traces.\n */\n ingestionType?: TraceIngestionType\n /**\n * Backward-compatible message for either error kind. Prefer `traceError` and\n * `replayError` when callers need the original exception and its source.\n */\n error: string | null\n /** The actual value thrown by the replayed customer function. */\n traceError: unknown | null\n /**\n * The actual value thrown by replay setup before the customer function ran,\n * such as database warmup, input hydration, or mock preparation.\n */\n replayError: unknown | null\n /**\n * How long the replayed function took on this run, in ms. Null if the item\n * failed before it ran. Compare against\n * {@link ReplayItem.originalDurationMs} for the before/after.\n */\n durationMs: number | null\n /** The original trace's duration in ms, or null if its timestamps are missing. */\n originalDurationMs: number | null\n /**\n * Token usage recorded on the original trace, or null if it captured none.\n * The \"old\" side of a token delta; {@link ReplayItem.tokens} is the new one.\n */\n originalTokens: TokenUsage | null\n /** Model name from the original trace, or null if not captured. */\n originalModel: string | null\n /**\n * Token usage from the REPLAYED run (this item's new execution), aggregated\n * server-side from the spans it produced, or null if the run captured no\n * token data. Compare against {@link ReplayItem.originalTokens} to see how\n * the code change moved cost. Matches what Studio's experiments view shows.\n *\n * Unprefixed because the replay is this item's subject: anything describing\n * the trace being replayed carries `original`.\n */\n tokens: TokenUsage | null\n /** @deprecated renamed to {@link ReplayItem.originalModel}. */\n model: string | null\n /**\n * The DB snapshot ref the SDK captured at trace open. Useful for debugging\n * (\"what state was this trace pinned to?\") and for customers building\n * their own resolvers. Undefined when the source trace was captured\n * without `dbSnapshot` configured.\n */\n dbSnapshotRef: DbSnapshotRef | null\n /**\n * How long this item's DB branch took to provision, per phase, measured\n * server-side. Present whenever a branch was attempted, on both outcomes:\n * complete on success, partial up to the failing phase when the resolve\n * failed. Null when no branch was asked for, when the source trace carried\n * no snapshot ref, or against a server that predates timings.\n */\n dbBranchTimings: DbBranchTimings | null\n}\n\nexport type { CodeChangeFile, TokenUsage }\n\nexport interface ReplayResult<T> {\n /** Individual replay items with inputs, results, and comparison data. */\n items: ReplayItem<T>[]\n /** The test run ID created on the server. */\n testRunId: string\n /** Full URL to view the test run in the dashboard. */\n testRunUrl: string\n attempts: number\n}\n\n/**\n * Whole-run replay failure that preserves every item collected before the run\n * failed. The original whole-run exception is available as `cause`.\n */\nexport class ReplayError<T = unknown> extends BitfabError {\n constructor(\n message: string,\n public readonly items: ReplayItem<T>[],\n public readonly testRunId: string,\n public readonly testRunUrl: string,\n public readonly cause: unknown,\n ) {\n super(message, testRunUrl)\n this.name = \"ReplayError\"\n }\n}\n\n/**\n * A database branch requested for one replay item could not be resolved.\n *\n * The server's resolver code and message are retained as structured fields so\n * callers can handle failures such as `branch_create_failed` and\n * `snapshot_from_replaced_origin` without parsing the compatible `item.error`\n * string.\n */\nexport class DbBranchReplayError extends BitfabError {\n constructor(\n public readonly code: string,\n message: string,\n public readonly originalTraceId: string,\n public readonly cause?: unknown,\n ) {\n super(message)\n this.name = \"DbBranchReplayError\"\n }\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nfunction replayItemErrorMessage(error: unknown): string {\n if (error instanceof DbBranchReplayError) {\n return `Replay requested a database branch for trace ${error.originalTraceId} but it could not be resolved (${error.code}): ${error.message}. The function was not run, because replaying it against the live database would produce a result that looks valid but did not use the historical data you asked for.`\n }\n return errorMessage(error)\n}\n\nfunction replayJsonReplacer(_key: string, value: unknown): unknown {\n if (value instanceof Error) {\n const serialized: Record<string, unknown> = {\n name: value.name,\n message: value.message,\n stack: value.stack,\n }\n if (value instanceof DbBranchReplayError) {\n serialized.code = value.code\n serialized.originalTraceId = value.originalTraceId\n if (value.cause !== undefined) {\n serialized.cause = value.cause\n }\n }\n return serialized\n }\n return value\n}\n\n/**\n * Serialize a replay result as JSON while retaining structured trace and replay\n * errors. Use this for direct-run stdout instead of raw `JSON.stringify`, which\n * drops the useful fields on JavaScript `Error` objects.\n */\nexport function serializeReplayResult<T>(result: ReplayResult<T>): string {\n return JSON.stringify(result, replayJsonReplacer, 2)\n}\n\nasync function preserveReplayFailure<T, TItem>(\n operation: () => Promise<T>,\n items: ReplayItem<TItem>[],\n testRunId: string,\n testRunUrl: string,\n): Promise<T> {\n try {\n return await operation()\n } catch (cause) {\n if (cause instanceof ReplayError) {\n throw cause\n }\n throw new ReplayError(\n errorMessage(cause),\n items,\n testRunId,\n testRunUrl,\n cause,\n )\n }\n}\n\n/**\n * Deserialize inputs from a historical span's rawData.\n *\n * Prefers superjson-serialized `input_meta` for type preservation,\n * falls back to the raw `input` field.\n */\nfunction deserializeInputs(spanData: Record<string, unknown>): unknown[] {\n const inputMeta = spanData.input_meta as unknown\n const rawInput = spanData.input\n\n // If superjson meta is available, deserialize with type reconstruction\n if (inputMeta !== undefined && inputMeta !== null) {\n const deserialized = deserializeValue({ json: rawInput, meta: inputMeta })\n if (Array.isArray(deserialized)) {\n return deserialized\n }\n return deserialized !== undefined && deserialized !== null\n ? [deserialized]\n : []\n }\n\n // Fall back to raw input\n if (Array.isArray(rawInput)) {\n return rawInput\n }\n return rawInput !== undefined && rawInput !== null ? [rawInput] : []\n}\n\n/**\n * Deserialize the original output from a historical span's rawData.\n */\nfunction deserializeOutput(spanData: Record<string, unknown>): unknown {\n const outputMeta = spanData.output_meta as unknown\n const rawOutput = spanData.output\n\n if (outputMeta !== undefined && outputMeta !== null) {\n return deserializeValue({ json: rawOutput, meta: outputMeta })\n }\n\n return rawOutput\n}\n\n/**\n * Walk the children of a root span tree node in depth-first order and build\n * a MockTree keyed by `${traceFunctionKey}:${spanName}:${callIndex}`.\n *\n * The historical root itself is NOT walked. At replay time the runtime root\n * span has `isRootSpan === true` and never queries the mockTree (mock\n * interception is skipped for root spans by design), so the root has nothing\n * to look up. Walking it would just leave an unreachable entry in the table.\n *\n * The (key, name) compound match is what disambiguates same-key spans:\n * - A wrapped function's children commonly share its traceFunctionKey via\n * the fluent `getFunction(key).withSpan({ name }, ...)` pattern. They\n * disambiguate by `name`, never colliding with each other.\n * - Recursion: same (key, name) at every depth. callIndex per (key, name)\n * orders them correctly without leaking the historical root into the\n * nested call's slot.\n * - Outer-wrapper replay scripts: the outer wrapper's `name` is distinct\n * from anything in the historical tree (it only exists at replay), so\n * its presence never disturbs counters or lookups for spans that do\n * exist in the historical tree.\n */\nfunction buildMockTree(rootNode: SpanTreeNode): MockTree {\n const spans = new Map<string, MockSpan>()\n const counters = new Map<string, number>()\n\n function walk(node: SpanTreeNode): void {\n const key = node.traceFunctionKey\n if (key) {\n const name = node.spanName || key\n const counterKey = `${key}:${name}`\n const index = counters.get(counterKey) ?? 0\n counters.set(counterKey, index + 1)\n // `output`/`outputMeta` are undefined on a payload-free (lazy) tree;\n // `externalSpanId` is what the lazy path fetches them by. Both are copied\n // verbatim so eager and lazy trees build through the same walk.\n spans.set(`${counterKey}:${index}`, {\n sourceSpanId: node.sourceSpanId,\n externalSpanId: node.externalSpanId,\n output: node.output,\n outputMeta: node.outputMeta,\n })\n }\n for (const child of node.children) {\n walk(child)\n }\n }\n\n for (const child of rootNode.children) {\n walk(child)\n }\n\n return { spans }\n}\n\n/**\n * Execute a single replay item: fetch span data, deserialize inputs, call\n * the function within a replay context that injects testRunId into new spans.\n */\nasync function processItem<TReturn>(\n httpClient: HttpClient,\n serverItem: {\n originalTraceId?: string\n originalSpanId?: string\n /** @deprecated alias emitted by servers that predate the originalTraceId rename. */\n sourceTraceId: string\n /** @deprecated alias emitted by servers that predate the originalSpanId rename. */\n sourceSpanId: string\n originalDurationMs?: number | null\n originalTokens?: TokenUsage | null\n originalModel?: string | null\n originalMetadata?: Record<string, unknown>\n ingestionType?: TraceIngestionType\n /** @deprecated unprefixed aliases emitted by servers that predate the rename.\n * `tokens` here is the ORIGINAL trace's usage: the start item has never\n * carried the replay's, which only exists after completeReplay. */\n durationMs: number | null\n tokens?: TokenUsage | null\n model: string | null\n dbSnapshotRef?: DbSnapshotRef\n dbBranchLease?: DbBranchLease\n dbBranchLeaseError?: { code: string; message: string }\n dbBranchTimings?: DbBranchTimings\n },\n // biome-ignore lint/suspicious/noExplicitAny: replay deserializes inputs from historical data\n fn: (...args: any[]) => TReturn | Promise<TReturn>,\n testRunId: string,\n mockStrategy: MockStrategy,\n resolvedOverrides: MockOverride[],\n replayedTraceId: string,\n attempt: number,\n includeDbBranchLease: boolean,\n dbBranchSettings: DbBranchSettings | undefined,\n adaptInputs:\n | ((inputs: unknown[], ctx: AdaptContext) => unknown[])\n | undefined,\n dryRun: boolean,\n): Promise<ReplayItem<TReturn>> {\n // The server-side resolver materializes a Neon preview branch per item\n // during `/api/sdk/replay/start` (when the customer passed `dbBranch`,\n // which triggers `includeDbBranchLease: true`). The lease arrives on the\n // server item; we just attach it to the replay context and release the\n // branch in `finally` so any throw (in fetch, mock-tree build, or the\n // customer fn) frees the Neon resource. Items whose source trace had no\n // snapshot ref arrive without a lease; `getCurrentReplayBranch()` returns\n // null for those, and the app uses its normal DB path. Unsafe calls on that\n // path still require replay mocking.\n let lease = includeDbBranchLease ? serverItem.dbBranchLease : undefined\n // A resolve that was attempted and FAILED is different: the caller asked for\n // a branch, so running their function against live data would produce a\n // result that looks valid and isn't. Fail this item instead, loudly.\n let leaseError = includeDbBranchLease\n ? serverItem.dbBranchLeaseError\n : undefined\n let dbSnapshotRef = serverItem.dbSnapshotRef\n // Reported whichever way the resolve went, so it is tracked separately from\n // both the lease and the error rather than hanging off either.\n let dbBranchTimings = includeDbBranchLease\n ? serverItem.dbBranchTimings\n : undefined\n\n let inputs: unknown[] = []\n let originalOutput: unknown\n let result: TReturn | undefined\n let error: string | null = null\n let traceError: unknown | null = null\n let replayError: unknown | null = null\n // Timed around the replayed function only, so it is comparable to\n // originalDurationMs (that same function's wall time on the original run)\n // rather than to the span fetches and persistence around it.\n let replayDurationMs: number | null = null\n // Null until the replayed function actually starts, so an item that fails in\n // the span fetch or mock-tree build reports no replay duration rather than\n // timing work that was not the function.\n let replayStarted: number | null = null\n\n // The ORIGINAL (historical) trace/span this item replays. Canonical server\n // keys are originalTraceId/originalSpanId; older servers only send the\n // deprecated sourceTraceId/sourceSpanId aliases.\n const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId\n const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId\n\n try {\n if (includeDbBranchLease && !lease && !leaseError) {\n let resolved: Awaited<ReturnType<HttpClient[\"resolveDbBranchLease\"]>>\n try {\n resolved = await httpClient.resolveDbBranchLease(\n testRunId,\n originalTraceId,\n dbBranchSettings,\n attempt,\n )\n } catch (cause) {\n throw new DbBranchReplayError(\n \"lease_request_failed\",\n `Bitfab could not request the database branch: ${errorMessage(cause)}`,\n originalTraceId,\n cause,\n )\n }\n lease = resolved.lease ?? undefined\n leaseError = resolved.leaseError ?? undefined\n dbSnapshotRef = resolved.dbSnapshotRef ?? dbSnapshotRef\n dbBranchTimings = resolved.timings ?? dbBranchTimings\n }\n\n if (leaseError) {\n throw new DbBranchReplayError(\n leaseError.code,\n leaseError.message,\n originalTraceId,\n )\n }\n\n const span = await httpClient.getExternalSpan(originalSpanId, {\n view: \"replay\",\n })\n const spanData = (span.rawData?.span_data ?? {}) as Record<string, unknown>\n\n inputs = deserializeInputs(spanData)\n originalOutput = deserializeOutput(spanData)\n\n // Reshape the recorded inputs onto the current signature when an adapter\n // is supplied. Runs before the mock tree / fn call so the adapted array is\n // what fn receives and what `item.input` reports.\n if (adaptInputs) {\n const originalMetadata = serverItem.originalMetadata\n inputs = adaptInputs(inputs, {\n originalTraceId,\n originalSpanId,\n // Deprecated aliases for originalTraceId/originalSpanId.\n sourceTraceId: originalTraceId,\n sourceSpanId: originalSpanId,\n metadata: isPlainRecord(originalMetadata)\n ? { ...originalMetadata }\n : {},\n })\n }\n\n // Build the mock tree whenever mocking could apply: any non-\"none\" strategy,\n // OR overrides are present (overrides substitute even under \"none\" - the\n // \"run real code but replace node X\" case). Only \"all\" needs outputs inline;\n // Non-\"all\" runs that need a tree (\"marked\", or \"none\" with overrides)\n // keep it payload-free and pull outputs lazily, so we never drag down every\n // span's recorded output when only a few get mocked.\n const hasOverrides = resolvedOverrides.length > 0\n const needTree =\n mockStrategy === \"all\" || mockStrategy === \"marked\" || hasOverrides\n const includeOutputs = mockStrategy === \"all\"\n\n let mockTree: MockTree | undefined\n if (needTree) {\n try {\n const treeResponse = await httpClient.getSpanTree(originalSpanId, {\n includeOutputs,\n includeRootOutput: false,\n })\n if (!treeResponse.root) {\n throw new BitfabError(\n `Replay mock strategy \"${mockStrategy}\"${hasOverrides ? \" with overrides\" : \"\"} requires a span tree root for original span ${originalSpanId}.`,\n )\n }\n mockTree = buildMockTree(treeResponse.root)\n } catch (error) {\n if (mockStrategy !== \"marked\" || hasOverrides) {\n throw error\n }\n // The root still runs under \"marked\". An empty active tree lets an\n // unmarked-only root proceed, while any selected child fails closed at\n // its call site instead of executing the real span.\n mockTree = { spans: new Map() }\n }\n }\n\n // Lazy per-span output fetch, memoized per item so a span read twice (once\n // as a marked mock, once via an override's getOriginalOutput) fetches once.\n // Present only when outputs were NOT fetched inline (not the eager \"all\"\n // path); its presence signals the mock path to fetch on demand.\n const outputCache = new Map<string, Promise<unknown>>()\n const fetchSpanOutput =\n mockTree && !includeOutputs\n ? (externalSpanId: string): Promise<unknown> => {\n let pending = outputCache.get(externalSpanId)\n if (!pending) {\n pending = httpClient\n .getExternalSpan(externalSpanId, { view: \"replay\" })\n .then((s) =>\n deserializeOutput(\n (s.rawData?.span_data ?? {}) as Record<string, unknown>,\n ),\n )\n outputCache.set(externalSpanId, pending)\n }\n return pending\n }\n : undefined\n\n if (dryRun) {\n return {\n traceId: null,\n originalTraceId,\n originalSpanId,\n sourceTraceId: originalTraceId,\n sourceSpanId: originalSpanId,\n attempt,\n input: inputs,\n result: undefined,\n originalOutput,\n ...(serverItem.ingestionType && {\n ingestionType: serverItem.ingestionType,\n }),\n error: null,\n traceError: null,\n replayError: null,\n durationMs: null,\n originalDurationMs:\n serverItem.originalDurationMs ?? serverItem.durationMs ?? null,\n originalTokens: serverItem.originalTokens ?? serverItem.tokens ?? null,\n originalModel: serverItem.originalModel ?? serverItem.model ?? null,\n tokens: null,\n model: serverItem.originalModel ?? serverItem.model ?? null,\n dbSnapshotRef: dbSnapshotRef ?? null,\n dbBranchTimings: dbBranchTimings ?? null,\n }\n }\n\n try {\n replayStarted = performance.now()\n const maybePromise = runWithReplayContext(\n {\n testRunId,\n traceId: replayedTraceId,\n inputSourceSpanId: span.id,\n inputSourceTraceId: span.externalTraceId,\n sourceBitfabTraceId: originalTraceId,\n replayAttempt: attempt,\n mockTree,\n callCounters: mockTree ? new Map() : undefined,\n mockStrategy,\n mockOverrides: hasOverrides ? resolvedOverrides : undefined,\n fetchSpanOutput,\n dbBranchLease: lease,\n dbBranchTimings,\n },\n () => fn(...inputs),\n )\n result =\n maybePromise instanceof Promise ? await maybePromise : maybePromise\n replayDurationMs = Math.round(performance.now() - replayStarted)\n } catch (e) {\n // The function ran and threw, so it still has a duration worth reporting.\n if (replayStarted !== null) {\n replayDurationMs = Math.round(performance.now() - replayStarted)\n }\n traceError = e\n error = errorMessage(e)\n }\n } catch (e) {\n // Replay setup failed. replayStarted is null unless the customer function\n // had already begun, so this leaves durationMs null for a genuine\n // setup failure rather than timing work that was not the function.\n if (replayStarted !== null) {\n replayDurationMs = Math.round(performance.now() - replayStarted)\n }\n replayError = e\n error = replayItemErrorMessage(e)\n } finally {\n if (lease) {\n try {\n await httpClient.releaseDbBranchLease(lease.neonBranchId)\n } catch (e) {\n try {\n console.warn(\n `Bitfab: failed to release DB branch ${lease.neonBranchId} (TTL janitor will catch it): ${\n e instanceof Error ? e.message : String(e)\n }`,\n )\n } catch {\n // Never crash the host\n }\n }\n }\n }\n\n // Canonical names first, falling back to the unprefixed ones a server that\n // predates the rename sends.\n const originalDurationMs =\n serverItem.originalDurationMs ?? serverItem.durationMs ?? null\n const originalModel = serverItem.originalModel ?? serverItem.model ?? null\n return {\n // Written in by replay() from the complete-replay response once the server\n // has minted this replay trace's row. Null until then: the client-side\n // correlation id (replayedTraceId) is never surfaced as the item's traceId.\n traceId: null,\n originalTraceId,\n originalSpanId,\n // Deprecated aliases for originalTraceId/originalSpanId.\n sourceTraceId: originalTraceId,\n sourceSpanId: originalSpanId,\n attempt,\n input: inputs,\n result,\n originalOutput,\n ...(serverItem.ingestionType && {\n ingestionType: serverItem.ingestionType,\n }),\n error,\n traceError,\n replayError,\n durationMs: replayDurationMs,\n originalDurationMs,\n originalTokens: serverItem.originalTokens ?? serverItem.tokens ?? null,\n originalModel,\n // Filled in by replay() from the complete-replay response once the\n // replay traces are persisted and their spans aggregated server-side.\n // Null here (and on older servers) means \"replay tokens not known\".\n tokens: null,\n model: originalModel,\n dbSnapshotRef: dbSnapshotRef ?? null,\n dbBranchTimings: dbBranchTimings ?? null,\n }\n}\n\n/**\n * Block until the server has fully persisted every replay trace this run\n * submitted, or fail loudly.\n *\n * `completeReplay` reads at a single instant: it mints the trace-ID mapping and\n * aggregates each trace's tokens from its spans. Run it while spans are still\n * in flight and `item.traceId` comes back null with undercounted tokens.\n *\n * In the normal case the verdict is local: ingestion commits a request's\n * carriers before it answers, so a flush that delivered every carrier has\n * already proven the traces are whole and polling only asked the server to\n * repeat itself. Acks cannot settle a request that timed out client-side after\n * the server committed, so anything short of fully delivered falls through to\n * the server poll, which stays the authority there.\n *\n * Returns the server-assigned `traces.id` for each replay trace, read back off\n * the OTLP ingest response during the flush and keyed by the client-side replay\n * trace id. Empty when the server predates that field or nothing was delivered.\n */\nexport async function waitForReplayPersistence(\n httpClient: HttpClient,\n testRunId: string,\n replayedTraceIds: string[],\n): Promise<Record<string, string>> {\n // Settle deferred submissions before counting. A `finalize` root span only\n // reaches the transport once its finalize chain resolves, so tallying first\n // would under-count and let the barrier pass on a trace that is still short.\n // This waits for that work WITHOUT flushing: a run that submitted nothing\n // must not emit an export request just to discover it has nothing to wait on.\n const deferredSettled = await httpClient.settleDeferredWork(\n REPLAY_PERSISTENCE_TIMEOUT_MS,\n )\n\n // Checked BEFORE the tally: deferred work that never landed means no\n // completion may have registered at all, and an empty tally would then look\n // like \"nothing to wait for\" and finalize the run with root spans pending.\n if (!deferredSettled) {\n // Freed before throwing: every other exit releases these, and a retained\n // record would carry this run's submitted-but-unacked spans into the next\n // replay of the same trace ids.\n httpClient.takeTraceDeliveries(replayedTraceIds)\n throw new BitfabError(\n `Replay could not settle deferred span work before the deadline, so the ` +\n `expected span counts are incomplete (testRunId ${testRunId}).`,\n )\n }\n\n // Peeked before flushing: a run whose traces never closed submitted nothing\n // to wait on, and must not emit an export request just to discover that.\n if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {\n httpClient.takeTraceDeliveries(replayedTraceIds)\n return {}\n }\n\n // A failed flush is a hint, not a verdict. It means delivery was not\n // CONFIRMED within the deadline, which is not the same as lost: the export\n // timeout can fire while requests are still in flight and the server goes on\n // to persist every one of them. Throwing here failed runs whose data had\n // fully landed.\n const flushed = await flushTraces(REPLAY_PERSISTENCE_TIMEOUT_MS)\n\n const deliveries = httpClient.takeTraceDeliveries(replayedTraceIds)\n const expectedSpanCounts: Record<string, number> = {}\n const readBackTraceIds: Record<string, string> = {}\n let allDelivered = true\n for (const [traceId, delivery] of Object.entries(deliveries)) {\n if (delivery.serverTraceId !== undefined) {\n readBackTraceIds[traceId] = delivery.serverTraceId\n }\n if (!delivery.closed) {\n continue\n }\n expectedSpanCounts[traceId] = delivery.spanCount\n allDelivered = allDelivered && delivery.delivered\n }\n if (allDelivered) {\n return readBackTraceIds\n }\n\n // Unacked is not unpersisted: a request that timed out client-side may have\n // committed anyway, and an export the flush deadline abandoned may still be\n // in flight. Neither is knowable here, so from this point the server is the\n // only authority and the run polls it until it answers or the budget ends.\n //\n // The poll gets its own budget. Sharing one deadline with the flush meant a\n // flush that used it all left zero polling window, so the barrier failed\n // after a single not-ready answer - exactly the case where the export is\n // still in flight and about to persist.\n const deadline = Date.now() + REPLAY_PERSISTENCE_TIMEOUT_MS\n\n let missing = Object.keys(expectedSpanCounts).length\n while (true) {\n const status = await httpClient.getReplayStatus(\n testRunId,\n expectedSpanCounts,\n )\n const ready = status.traceIds ?? {}\n missing = Object.keys(expectedSpanCounts).filter(\n (traceId) => ready[traceId] === undefined,\n ).length\n if (missing === 0) {\n return readBackTraceIds\n }\n if (Date.now() >= deadline) {\n break\n }\n await sleepForReplayPersistence(\n Math.min(100, Math.max(0, deadline - Date.now())),\n )\n }\n\n // Only now is it a real failure: the server itself never confirmed these.\n // Surface the flush result as the likely cause rather than as the verdict.\n const cause = flushed\n ? \"\"\n : \" Delivery was also not confirmed before the flush deadline, so the \" +\n \"spans likely never reached the server.\"\n throw new BitfabError(\n `Replay traces were not fully persisted before the delivery deadline ` +\n `(testRunId ${testRunId}, missing ${missing} of ` +\n `${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`,\n )\n}\n\n/** @internal Wait between replay persistence polls without releasing Node. */\nexport function sleepForReplayPersistence(ms: number): Promise<void> {\n return new Promise((resolve) => {\n // This timer is part of an awaited persistence barrier. It must keep a CLI\n // process alive until the server confirms the replay or the deadline fires.\n setTimeout(resolve, ms)\n })\n}\n\n/**\n * Run async tasks with a concurrency limit.\n * Each task factory is called when a slot opens; results preserve input order.\n */\nasync function mapWithConcurrency<T>(\n tasks: Array<() => Promise<T>>,\n maxConcurrency: number,\n onSettled?: (result: T, index: number) => void | Promise<void>,\n onStarted?: (index: number) => void,\n): Promise<T[]> {\n const results: T[] = new Array(tasks.length)\n let nextIndex = 0\n\n async function worker(): Promise<void> {\n while (nextIndex < tasks.length) {\n const index = nextIndex++\n onStarted?.(index)\n const result = await tasks[index]()\n results[index] = result\n await onSettled?.(result, index)\n }\n }\n\n const workers = Array.from(\n { length: Math.min(maxConcurrency, tasks.length) },\n () => worker(),\n )\n await Promise.all(workers)\n return results\n}\n\n/**\n * Replay historical traces through a function and create a test run.\n *\n * @internal Called by Bitfab.replay, not part of the public API.\n */\nexport async function replay<TReturn>(\n httpClient: HttpClient,\n serviceUrl: string,\n traceFunctionKey: string,\n // biome-ignore lint/suspicious/noExplicitAny: replay deserializes inputs from historical data\n fn: (...args: any[]) => TReturn | Promise<TReturn>,\n options?: ReplayOptions,\n registeredOverrides: MockOverride[] = [],\n): Promise<ReplayResult<TReturn>> {\n if (options?.traceIds !== undefined) {\n if (options.traceIds.length === 0) {\n throw new BitfabError(\"traceIds must contain at least one trace ID.\")\n }\n if (options.traceIds.length > 100) {\n throw new BitfabError(\n `traceIds supports at most 100 trace IDs per replay (got ${options.traceIds.length}).`,\n )\n }\n }\n if (options?.traceIds !== undefined && options?.datasetId !== undefined) {\n throw new BitfabError(\n \"traceIds and datasetId select different replay sources and cannot be used together.\",\n )\n }\n if (options?.limit !== undefined && options?.traceIds !== undefined) {\n try {\n console.warn(\n \"Bitfab: limit is ignored when traceIds is passed: the explicit trace ID list already determines how many traces replay.\",\n )\n } catch {\n // Never crash the host app\n }\n }\n const attempts = resolveAttempts(options?.attempts)\n await replayContextReady\n\n // Resolve the code change to attach to this experiment. codeChangeFiles is\n // the capture control: omitted auto-captures, an array wins, and null opts\n // out. A caller-supplied description is preserved when files are captured.\n let codeChangeDescription = options?.codeChangeDescription\n let codeChangeFiles = options?.codeChangeFiles\n if (codeChangeFiles === undefined) {\n const captured = await resolveAutoCodeChange(options?.name)\n if (captured) {\n codeChangeFiles = captured.files\n if (codeChangeDescription === undefined) {\n codeChangeDescription = captured.description\n }\n }\n }\n\n const {\n testRunId,\n testRunUrl,\n items: serverItems,\n } = await httpClient.startReplay(\n traceFunctionKey,\n // limit is meaningless with explicit traceIds (the ID list determines\n // the count), so it's omitted from the request entirely.\n options?.traceIds ? undefined : (options?.limit ?? 5),\n options?.traceIds,\n options?.name,\n codeChangeDescription,\n codeChangeFiles,\n // A dry run executes nothing, so a branch would be provisioned (and billed)\n // for code that never runs, and a seeded source's refusal would fail the item\n // before it could report its resolved inputs.\n dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true,\n options?.experimentGroupId,\n options?.datasetId,\n options?.graderIds,\n resolveDbBranchSettings(options?.dbBranch),\n attempts,\n options?.adaptInputs !== undefined,\n )\n\n // An empty selection is the common shape of \"the corpus was never seeded\".\n // The result still reports it faithfully (zero items), but a caller reading\n // only the exit path would otherwise see a clean run that did nothing.\n if (serverItems.length === 0) {\n try {\n console.warn(\n `Bitfab: no traces matched \"${traceFunctionKey}\", so this replay ran nothing. Capture a trace, or seed one with seedTrace, before replaying.`,\n )\n } catch {\n // Never crash the host app\n }\n }\n\n const mockStrategy: MockStrategy = options?.mock ?? \"marked\"\n const maxConcurrency = options?.maxConcurrency ?? 10\n const fullTestRunUrl = `${serviceUrl}${testRunUrl}`\n\n // Per-call overrides take precedence over registered ones: concatenating them\n // first means the first-match-wins scan hits per-call overrides before the\n // client-registered chain, and both before the base mock strategy.\n const resolvedOverrides = [\n ...normalizeMockOverrides(options?.mockOverride),\n ...registeredOverrides,\n ]\n\n // One client-side replay trace id per item. It tags that item's replay spans\n // during the run so the server can echo back the row id it minted for them\n // (resolved in the complete-replay mapping below). Kept out of the public\n // ReplayItem: it's a correlation handle, never an id callers use.\n const workItems = expandReplayWork(serverItems, attempts)\n const replayedTraceIds = workItems.map(() => randomUuid())\n // Declared before the first span is submitted: the transport records delivery\n // only for traces someone asked about, so anything submitted before this\n // would go untracked.\n httpClient.trackTraceDeliveries(replayedTraceIds)\n const tasks = workItems.map(\n ({ serverItem, attempt }, index) =>\n () =>\n processItem(\n httpClient,\n serverItem,\n fn,\n testRunId,\n mockStrategy,\n resolvedOverrides,\n replayedTraceIds[index],\n attempt,\n dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true,\n resolveDbBranchSettings(options?.dbBranch),\n options?.adaptInputs,\n options?.dryRun === true,\n ),\n )\n const total = tasks.length\n const onItemFinish = options?.onItemFinish ?? options?.onProgress\n let completed = 0\n let started = 0\n let succeeded = 0\n let errored = 0\n\n // Flush an item's trace the moment it finishes, so its server traces.id is\n // read back off the ingest response and surfaced on the item as it settles,\n // instead of waiting for the end-of-run barrier. Waiting until the end is\n // worst at low concurrency: each trace is closed and ready when its item\n // finishes, but nothing fills a batch to force an export, so ready ids sit in\n // the queue for the whole run.\n //\n // Coalesced so a burst of concurrent finishes shares one flush: while a flush\n // runs, later finishers mark `pending` and await the same promise, which loops\n // once more to cover them. Every caller therefore waits for a flush that ran\n // after it asked, and low concurrency degrades to a clean flush per item.\n let flushInFlight: Promise<void> | null = null\n let flushPending = false\n const flushFinishedItemTrace = (): Promise<void> => {\n // Mark work waiting first, so a finisher that arrives mid-flush is always\n // picked up by another loop iteration. The runner clears `flushInFlight`\n // synchronously with the loop exit (no await between the `while` check and\n // the reset), so no caller can observe a stale in-flight promise.\n flushPending = true\n if (!flushInFlight) {\n flushInFlight = (async () => {\n try {\n while (flushPending) {\n flushPending = false\n await httpClient.settleDeferredWork(REPLAY_PERSISTENCE_TIMEOUT_MS)\n await flushTraces(REPLAY_PERSISTENCE_TIMEOUT_MS)\n }\n } finally {\n flushInFlight = null\n }\n })()\n }\n return flushInFlight\n }\n\n const resultItems = await mapWithConcurrency(\n tasks,\n maxConcurrency,\n async (item, index) => {\n // Deliver this item's trace now, then read its server id back off the\n // ingest response. A flush failure never crashes the run: the id degrades\n // to null and the end-of-run barrier stays the authority on persistence.\n // Done BEFORE touching the counters so the increment and emit below carry\n // no await between them: with items flushing concurrently, that keeps each\n // event's running totals a consistent snapshot.\n let serverTraceId: string | null = null\n try {\n await flushFinishedItemTrace()\n serverTraceId =\n httpClient.peekServerTraceId(replayedTraceIds[index]) ?? null\n } catch {\n // Best-effort: fall through with null; the barrier settles persistence.\n }\n item.traceId = serverTraceId\n completed += 1\n if (item.error === null) {\n succeeded += 1\n } else {\n errored += 1\n }\n try {\n onItemFinish?.({\n testRunId,\n completed,\n total,\n succeeded,\n errored,\n item: {\n // The server's assigned traces.id, read back off the ingest\n // response and surfaced as this item finishes. Null only if the\n // per-item flush could not confirm delivery in time; the end-of-run\n // barrier then fills the returned ReplayItem. The client-side\n // placeholder is never surfaced.\n traceId: serverTraceId,\n originalTraceId: item.originalTraceId ?? null,\n originalSpanId: item.originalSpanId ?? null,\n // Deprecated aliases for originalTraceId/originalSpanId.\n sourceTraceId: item.originalTraceId ?? null,\n sourceSpanId: item.originalSpanId ?? null,\n attempt: item.attempt,\n input: item.input,\n result: item.result,\n originalOutput: item.originalOutput,\n error: item.error,\n traceError: item.traceError,\n replayError: item.replayError,\n durationMs: item.durationMs,\n originalDurationMs: item.originalDurationMs,\n originalTokens: item.originalTokens,\n originalModel: item.originalModel,\n tokens: item.tokens,\n model: item.model,\n dbSnapshotRef: item.dbSnapshotRef,\n dbBranchTimings: item.dbBranchTimings,\n },\n })\n } catch {\n // Progress UI must never crash the replay run.\n }\n },\n options?.onItemStart\n ? (index) => {\n started += 1\n const { serverItem, attempt } = workItems[index]\n const originalTraceId =\n serverItem.originalTraceId ?? serverItem.sourceTraceId\n const originalSpanId =\n serverItem.originalSpanId ?? serverItem.sourceSpanId\n try {\n options.onItemStart?.({\n type: \"started\",\n testRunId,\n started,\n completed,\n total,\n succeeded,\n errored,\n item: {\n originalTraceId,\n originalSpanId,\n sourceTraceId: originalTraceId,\n sourceSpanId: originalSpanId,\n attempt,\n },\n })\n } catch {\n // Progress UI must never crash the replay run.\n }\n }\n : undefined,\n )\n\n // Items submit their spans through the same batching transport live traffic\n // uses, so nothing here has waited on the network yet. Flush the transports,\n // then let the SERVER confirm each trace reached a final status with all of\n // its spans before finalizing: a drained queue is not proof that Bitfab\n // committed the data.\n // A dry run executed nothing, so it produced no spans and no traces. The\n // persistence barrier asserts the opposite (every completed item has a\n // persisted trace) and would fail the whole run, so it is skipped along with\n // the trace-id mapping it feeds. The test run is still finalized so it does\n // not sit unfinished in the experiments list.\n if (options?.dryRun === true) {\n await httpClient.completeReplay(testRunId).catch(() => undefined)\n const dryResult: ReplayResult<TReturn> = {\n items: resultItems,\n testRunId,\n testRunUrl: fullTestRunUrl,\n attempts,\n }\n await writeReplayResultFile(dryResult)\n return dryResult\n }\n\n const deliveredTraceIds = await preserveReplayFailure(\n () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),\n resultItems,\n testRunId,\n fullTestRunUrl,\n )\n\n // Primary source for item.traceId: the server's assigned traces.id, read back\n // per trace off the ingest response during the flush above (server-sourced, no\n // polling, already in hand before completeReplay). The completeReplay map below\n // remains the fallback for servers that don't return ids on ingest.\n for (let index = 0; index < resultItems.length; index += 1) {\n const localId = replayedTraceIds[index]\n const readBack = localId ? deliveredTraceIds[localId] : undefined\n if (readBack !== undefined) {\n resultItems[index].traceId = readBack\n }\n }\n\n // completeReplay finalizes the test run and returns the token/diagnostic\n // mapping; its failures propagate loudly because a run that never completed\n // can't be finalized and callers must hear about it.\n const completeResult = await preserveReplayFailure(\n () => httpClient.completeReplay(testRunId),\n resultItems,\n testRunId,\n fullTestRunUrl,\n )\n const serverTraceIds = completeResult.traceIds\n // Per-replay-trace token usage, keyed by server trace id. The REPLAYED run's\n // tokens (span-aggregated server-side), used below to fill each item.tokens.\n const replayTokens = completeResult.tokens\n\n // `serverTraceIds` maps each item's client-side replay trace id (its entry in\n // `replayedTraceIds`, which tagged that item's spans during the run) to the\n // server's trace row id. We use it to (a) write the real server replay trace\n // id into `item.traceId` now that the row exists, (b) attach each item's\n // server-aggregated token usage, and (c) detect a systemic upload failure\n // early. Verdict persistence does NOT use this map: it is keyed by the\n // original-trace lineage (`originalTraceId` + `testRunId`), which needs no\n // client-held server id. Older servers that omit the map yield no replay\n // tokens and leave `item.traceId` null.\n if (serverTraceIds !== undefined) {\n const missing: string[] = []\n let completedCount = 0\n for (let index = 0; index < resultItems.length; index += 1) {\n const item = resultItems[index]\n const localId = replayedTraceIds[index]\n const mapped = localId ? serverTraceIds[localId] : undefined\n // Fallback fill for servers that did not return ids on the ingest\n // response; the read-back loop above already set it when they did, so\n // never overwrite a resolved id with null here.\n item.traceId = item.traceId ?? mapped ?? null\n if (item.error === null) {\n completedCount += 1\n if (mapped === undefined) {\n missing.push(localId ?? item.originalTraceId)\n }\n }\n if (mapped !== undefined) {\n item.tokens = replayTokens?.[mapped] ?? null\n }\n }\n // ALL completed items missing → systemic (the replayed function isn't\n // wrapped with withSpan, or uploads are wholesale broken). Throw; the run's\n // traces never persisted, so nothing can be labeled and silence here is the\n // exact bug this guarantee exists to prevent.\n if (completedCount > 0 && missing.length === completedCount) {\n const serverCount =\n completeResult.traceCount !== undefined\n ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.`\n : \"\"\n const cause = new BitfabError(\n `Replay completed but the server has no persisted trace for any of the ${completedCount} completed item(s) (testRunId ${testRunId}).${serverCount} ` +\n `Trace uploads were awaited, so either the uploads failed (check for \"Bitfab: Failed to create\" errors above) or the replayed function is not wrapped with withSpan.`,\n )\n throw new ReplayError(\n cause.message,\n resultItems,\n testRunId,\n fullTestRunUrl,\n cause,\n )\n }\n // SOME completed items missing → per-item upload failure (transient blip,\n // one oversized payload). Log it, but return the run; the items that landed\n // can still be labeled via their lineage.\n if (missing.length > 0) {\n try {\n console.error(\n `Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId}). Their replay token usage is unavailable and they cannot be labeled.`,\n )\n } catch {\n // Never crash the host app\n }\n }\n }\n\n const result: ReplayResult<TReturn> = {\n items: resultItems,\n testRunId,\n testRunUrl: fullTestRunUrl,\n attempts,\n }\n // Persist the enriched result so the Bitfab plugin never has to parse the\n // replay's stdout, which a dependency's logging can corrupt.\n await writeReplayResultFile(result)\n // Preserve the legacy terminal event only for onProgress. onItemFinish is\n // strictly item-scoped, so every invocation always includes an item.\n if (!options?.onItemFinish) {\n try {\n options?.onProgress?.({\n type: \"complete\",\n testRunId,\n completed: total,\n total,\n succeeded,\n errored,\n result,\n })\n } catch {\n // A progress reporter must never crash the replay run.\n }\n }\n return result\n}\n\nasync function writeReplayResultFile(\n result: ReplayResult<unknown>,\n): Promise<void> {\n const resultPath =\n typeof process !== \"undefined\"\n ? process.env?.BITFAB_REPLAY_RESULT_PATH\n : undefined\n if (!resultPath) {\n return\n }\n\n try {\n const [{ dirname }, { mkdir, writeFile }] = await Promise.all([\n import(\"node:path\"),\n import(\"node:fs/promises\"),\n ])\n await mkdir(dirname(resultPath), { recursive: true })\n await writeFile(resultPath, `${serializeReplayResult(result)}\\n`)\n } catch (err) {\n try {\n console.warn(\n `Bitfab: failed to write replay result to BITFAB_REPLAY_RESULT_PATH (${resultPath}): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n } catch {\n // Never crash the host app.\n }\n }\n}\n"],"mappings":"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","names":["error"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|