bitfab 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PY6V4FE3.js → chunk-EQI6ZJC3.js} +113 -57
- package/dist/chunk-EQI6ZJC3.js.map +1 -0
- package/dist/{chunk-UTVFKV2Z.js → chunk-FA6DBCAT.js} +96 -96
- package/dist/chunk-FA6DBCAT.js.map +1 -0
- package/dist/index.cjs +207 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/node.cjs +171 -115
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +2 -2
- package/dist/{replay-P3QXRLOC.js → replay-QAWGVRCZ.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-PY6V4FE3.js.map +0 -1
- package/dist/chunk-UTVFKV2Z.js.map +0 -1
- /package/dist/{replay-P3QXRLOC.js.map → replay-QAWGVRCZ.js.map} +0 -0
|
@@ -7,6 +7,114 @@ var BitfabError = class extends Error {
|
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
// src/serialize.ts
|
|
11
|
+
import superjson from "superjson";
|
|
12
|
+
var MAX_SERIALIZED_BYTES = 512e3;
|
|
13
|
+
function describeValue(value) {
|
|
14
|
+
try {
|
|
15
|
+
const ctorName = value?.constructor?.name;
|
|
16
|
+
if (ctorName && ctorName !== "Object") {
|
|
17
|
+
return ctorName;
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
return typeof value;
|
|
22
|
+
}
|
|
23
|
+
function unserializableStub(value, reason) {
|
|
24
|
+
let summary;
|
|
25
|
+
try {
|
|
26
|
+
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
27
|
+
} catch {
|
|
28
|
+
summary = `<unserializable (${reason})>`;
|
|
29
|
+
}
|
|
30
|
+
return { json: summary };
|
|
31
|
+
}
|
|
32
|
+
function serializeValue(value) {
|
|
33
|
+
try {
|
|
34
|
+
const { json, meta } = superjson.serialize(value);
|
|
35
|
+
let size;
|
|
36
|
+
try {
|
|
37
|
+
size = JSON.stringify(json).length;
|
|
38
|
+
} catch {
|
|
39
|
+
return unserializableStub(value, "stringify_failed_after_superjson");
|
|
40
|
+
}
|
|
41
|
+
if (size > MAX_SERIALIZED_BYTES) {
|
|
42
|
+
return unserializableStub(value, `too_large_${size}_bytes`);
|
|
43
|
+
}
|
|
44
|
+
return meta ? { json, meta } : { json };
|
|
45
|
+
} catch {
|
|
46
|
+
try {
|
|
47
|
+
return { json: JSON.parse(JSON.stringify(value)) };
|
|
48
|
+
} catch {
|
|
49
|
+
return unserializableStub(value, "json_stringify_failed");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function deserializeValue(serialized) {
|
|
54
|
+
if (serialized.meta === void 0) {
|
|
55
|
+
return serialized.json;
|
|
56
|
+
}
|
|
57
|
+
return superjson.deserialize({
|
|
58
|
+
json: serialized.json,
|
|
59
|
+
meta: serialized.meta
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
var MAX_SAFE_DEPTH = 6;
|
|
63
|
+
function toJsonSafe(value) {
|
|
64
|
+
return toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
65
|
+
}
|
|
66
|
+
function toJsonSafeInner(value, depth, seen) {
|
|
67
|
+
if (value === null || value === void 0) {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
const className = value?.constructor?.name ?? typeof value;
|
|
74
|
+
if (depth > MAX_SAFE_DEPTH) {
|
|
75
|
+
return `<${className}>`;
|
|
76
|
+
}
|
|
77
|
+
if (typeof value !== "object") {
|
|
78
|
+
try {
|
|
79
|
+
return String(value);
|
|
80
|
+
} catch {
|
|
81
|
+
return `<${className}>`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (seen.has(value)) {
|
|
85
|
+
return `<cycle ${className}>`;
|
|
86
|
+
}
|
|
87
|
+
seen.add(value);
|
|
88
|
+
let result;
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
result = value.map((item) => toJsonSafeInner(item, depth + 1, seen));
|
|
91
|
+
} else if (typeof value.toJSON === "function") {
|
|
92
|
+
try {
|
|
93
|
+
result = toJsonSafeInner(
|
|
94
|
+
value.toJSON(),
|
|
95
|
+
depth + 1,
|
|
96
|
+
seen
|
|
97
|
+
);
|
|
98
|
+
} catch {
|
|
99
|
+
result = `<${className}>`;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
try {
|
|
103
|
+
const obj = {};
|
|
104
|
+
for (const [k, v] of Object.entries(value)) {
|
|
105
|
+
if (!k.startsWith("_")) {
|
|
106
|
+
obj[k] = toJsonSafeInner(v, depth + 1, seen);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
result = obj;
|
|
110
|
+
} catch {
|
|
111
|
+
result = `<${className}>`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
seen.delete(value);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
10
118
|
// src/asyncStorage.ts
|
|
11
119
|
var AsyncLocalStorageClass = null;
|
|
12
120
|
var initDone = false;
|
|
@@ -62,61 +170,11 @@ function runWithReplayContext(ctx, fn) {
|
|
|
62
170
|
return fn();
|
|
63
171
|
}
|
|
64
172
|
|
|
65
|
-
// src/serialize.ts
|
|
66
|
-
import superjson from "superjson";
|
|
67
|
-
var MAX_SERIALIZED_BYTES = 512e3;
|
|
68
|
-
function describeValue(value) {
|
|
69
|
-
try {
|
|
70
|
-
const ctorName = value?.constructor?.name;
|
|
71
|
-
if (ctorName && ctorName !== "Object") {
|
|
72
|
-
return ctorName;
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
return typeof value;
|
|
77
|
-
}
|
|
78
|
-
function unserializableStub(value, reason) {
|
|
79
|
-
let summary;
|
|
80
|
-
try {
|
|
81
|
-
summary = `<unserializable: ${describeValue(value)} (${reason})>`;
|
|
82
|
-
} catch {
|
|
83
|
-
summary = `<unserializable (${reason})>`;
|
|
84
|
-
}
|
|
85
|
-
return { json: summary };
|
|
86
|
-
}
|
|
87
|
-
function serializeValue(value) {
|
|
88
|
-
try {
|
|
89
|
-
const { json, meta } = superjson.serialize(value);
|
|
90
|
-
let size;
|
|
91
|
-
try {
|
|
92
|
-
size = JSON.stringify(json).length;
|
|
93
|
-
} catch {
|
|
94
|
-
return unserializableStub(value, "stringify_failed_after_superjson");
|
|
95
|
-
}
|
|
96
|
-
if (size > MAX_SERIALIZED_BYTES) {
|
|
97
|
-
return unserializableStub(value, `too_large_${size}_bytes`);
|
|
98
|
-
}
|
|
99
|
-
return meta ? { json, meta } : { json };
|
|
100
|
-
} catch {
|
|
101
|
-
try {
|
|
102
|
-
return { json: JSON.parse(JSON.stringify(value)) };
|
|
103
|
-
} catch {
|
|
104
|
-
return unserializableStub(value, "json_stringify_failed");
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function deserializeValue(serialized) {
|
|
109
|
-
if (serialized.meta === void 0) {
|
|
110
|
-
return serialized.json;
|
|
111
|
-
}
|
|
112
|
-
return superjson.deserialize({
|
|
113
|
-
json: serialized.json,
|
|
114
|
-
meta: serialized.meta
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
173
|
export {
|
|
119
174
|
BitfabError,
|
|
175
|
+
serializeValue,
|
|
176
|
+
deserializeValue,
|
|
177
|
+
toJsonSafe,
|
|
120
178
|
registerAsyncLocalStorageClass,
|
|
121
179
|
assertAsyncStorageRegistered,
|
|
122
180
|
asyncStorageReady,
|
|
@@ -124,8 +182,6 @@ export {
|
|
|
124
182
|
createAsyncLocalStorage,
|
|
125
183
|
replayContextReady,
|
|
126
184
|
getReplayContext,
|
|
127
|
-
runWithReplayContext
|
|
128
|
-
serializeValue,
|
|
129
|
-
deserializeValue
|
|
185
|
+
runWithReplayContext
|
|
130
186
|
};
|
|
131
|
-
//# sourceMappingURL=chunk-
|
|
187
|
+
//# sourceMappingURL=chunk-EQI6ZJC3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/serialize.ts","../src/asyncStorage.ts","../src/replayContext.ts"],"sourcesContent":["/**\n * Shared error type for Bitfab SDK runtime errors. Lives in its own\n * module to avoid import cycles between `http.ts` and modules that need\n * to throw structured errors (e.g. `dbSnapshot.ts` validation).\n */\n\nexport class BitfabError extends Error {\n constructor(\n message: string,\n public readonly url?: string,\n ) {\n super(message)\n this.name = \"BitfabError\"\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\"\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 serialized payload size. superjson can succeed on values like SDK\n// client instances (OpenAI, etc.) and produce hundreds of KB to MB of useless\n// internal state. Anything beyond this is replaced with a stub so the span\n// still ships and the trace isn't dropped server-side.\nconst MAX_SERIALIZED_BYTES = 512_000\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 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 toJsonSafeInner(value, 0, new WeakSet())\n}\n\nfunction toJsonSafeInner(\n value: unknown,\n depth: number,\n seen: WeakSet<object>,\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 return `<${className}>`\n }\n\n // Non-object composites (bigint, function, symbol) stringify directly.\n if (typeof value !== \"object\") {\n try {\n return String(value)\n } catch {\n return `<${className}>`\n }\n }\n\n if (seen.has(value as object)) {\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) => toJsonSafeInner(item, depth + 1, seen))\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 )\n } catch {\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)\n }\n }\n result = obj\n } catch {\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 * Shared AsyncLocalStorage loader.\n *\n * Provides two ways to initialize AsyncLocalStorage:\n *\n * 1. **Synchronous registration** (preferred for Node.js):\n * `asyncStorageNode.ts` calls `registerAsyncLocalStorageClass()` at module\n * evaluation time, so the class is available immediately — no async gap.\n * The `node.ts` entry point imports it before anything else.\n *\n * 2. **Async dynamic import** (fallback for the default entry point):\n * Loads `node:async_hooks` via a bundler-safe dynamic import. This is used\n * by the default `index.ts` entry point so the SDK works in browsers\n * (where the import silently fails) and in Node.js when imported via the\n * default entry point.\n *\n * ## Why the dynamic import looks like this\n *\n * We need to handle three environments:\n *\n * 1. **Pure Node.js** — `import(\"node:async_hooks\")` works natively.\n * 2. **Webpack/Turbopack (Next.js server)** — The bundler processes\n * `import()` calls at build time. The `webpackIgnore` magic comment tells\n * webpack (and turbopack) to emit a native `import()` call instead of\n * trying to resolve it, so Node.js handles it at runtime.\n * 3. **Browsers / Edge** — The `process.versions?.node` guard prevents\n * execution entirely. If it somehow runs, `.catch(() => {})` swallows\n * the failure.\n */\n\nexport interface AsyncLocalStorageLike<T> {\n getStore(): T | undefined\n run<R>(store: T, fn: () => R): R\n}\n\nlet AsyncLocalStorageClass: (new () => AsyncLocalStorageLike<unknown>) | null =\n null\nlet initDone = false\n\n/**\n * Register the AsyncLocalStorage class synchronously.\n *\n * Called by `asyncStorageNode.ts` at module evaluation time so the class\n * is available before any span is created — no async gap, no race condition.\n *\n * Safe to call multiple times; subsequent calls are no-ops.\n */\nexport function registerAsyncLocalStorageClass(\n cls: new () => AsyncLocalStorageLike<unknown>,\n): void {\n if (!AsyncLocalStorageClass) {\n AsyncLocalStorageClass = cls\n }\n initDone = true\n}\n\n/**\n * Assert that AsyncLocalStorage was registered successfully.\n *\n * Called by `node.ts` after importing `asyncStorageNode.ts` to catch\n * import-order bugs at startup rather than silently degrading to the\n * browser fallback (flat spans with no nesting).\n *\n * This should ONLY be called from the Node.js entry point where we\n * know `node:async_hooks` must be available.\n */\nexport function assertAsyncStorageRegistered(): void {\n if (!AsyncLocalStorageClass) {\n console.warn(\n \"Bitfab: AsyncLocalStorage not available — nested span context will not propagate.\",\n )\n }\n}\n\nexport const asyncStorageReady: Promise<void> = (\n typeof process !== \"undefined\" && process.versions?.node\n ? // The join trick hides \"node:async_hooks\" from static analysis so\n // bundlers that ban Node.js built-ins don't fail at build time.\n // webpackIgnore tells webpack/turbopack to emit a native import()\n // so Node.js can resolve the module at runtime.\n import(\n /* webpackIgnore: true */\n [\"node\", \"async_hooks\"].join(\":\")\n )\n .then(\n (mod: {\n AsyncLocalStorage: new () => AsyncLocalStorageLike<unknown>\n }) => {\n registerAsyncLocalStorageClass(mod.AsyncLocalStorage)\n },\n )\n .catch(() => {})\n : Promise.resolve()\n).then(() => {\n initDone = true\n})\n\nexport function isAsyncStorageInitDone(): boolean {\n return initDone\n}\n\nexport function createAsyncLocalStorage<T>(): AsyncLocalStorageLike<T> | null {\n return AsyncLocalStorageClass\n ? (new AsyncLocalStorageClass() as AsyncLocalStorageLike<T>)\n : null\n}\n","/**\n * Replay context propagation via AsyncLocalStorage.\n *\n * When set, the withSpan wrapper injects testRunId into the span payload\n * so that new spans created during replay are linked to the test run.\n * Optionally carries a mock tree so child spans can return historical\n * outputs instead of executing.\n */\n\nimport {\n type AsyncLocalStorageLike,\n asyncStorageReady,\n createAsyncLocalStorage,\n} from \"./asyncStorage.js\"\n\n/** A single span entry in the mock tree with its historical output. */\nexport interface MockSpan {\n sourceSpanId: string\n output: unknown\n outputMeta?: unknown\n}\n\n/**\n * Per-item DB branch resolved by the Bitfab service from the source\n * trace's `dbSnapshotRef`. Carried on the replay context so that\n * customer code reads `databaseUrl` through `ReplayEnvironment`, and so\n * the process-isolated replay runner can materialize it into a `.env`\n * overlay file before customer code initializes its DB client.\n *\n * `neonBranchId` is the literal Neon branch id; passing it to\n * `releaseDbBranchLease` deletes that branch.\n */\nexport interface DbBranchLease {\n neonBranchId: string\n /** Env var name the customer's app reads, e.g. \"DATABASE_URL\". */\n envKey: string\n databaseUrl: string\n expiresAt: string\n /**\n * The instant the branch was pinned to (the source trace's wall clock).\n * Echoed back in `db_snapshot_usage` on the replayed trace's completion.\n */\n snapshotTimestamp?: string\n providerConsoleUrl?: string\n readOnly?: boolean\n}\n\n/**\n * Pre-built lookup table of historical span outputs.\n * Keys are `${traceFunctionKey}:${spanName}:${callIndex}` so that repeated\n * calls with the same (key, name) are matched by call order, but spans\n * sharing only the traceFunctionKey (different name) do not collide.\n */\nexport interface MockTree {\n spans: Map<string, MockSpan>\n}\n\nexport interface ReplayContext {\n testRunId: string\n traceId?: string\n inputSourceSpanId?: string\n /**\n * External trace ID from `external_traces.id`. Used for span-chain\n * lookup against the source platform's trace tree (Braintrust, etc.).\n * NOT the same as the Bitfab `traceId` — see `sourceBitfabTraceId`.\n */\n inputSourceTraceId?: string\n /**\n * The Bitfab `traces.id` of the historical trace that produced this\n * replay item's input. This is what customer-facing surfaces (e.g.\n * `ReplayEnvironment.traceId`) should expose, since it's the ID the\n * customer sees in the Bitfab dashboard.\n */\n sourceBitfabTraceId?: string\n mockTree?: MockTree\n callCounters?: Map<string, number>\n mockStrategy?: \"none\" | \"all\" | \"marked\"\n dbBranchLease?: DbBranchLease\n /**\n * Set to true by `ReplayEnvironment` the first time customer code\n * actually obtains `databaseUrl` for this item (via the getter or\n * `snapshot()`). Reported on the trace completion inside\n * `db_snapshot_usage` so the server can distinguish \"branch was\n * provisioned and exposed\" from \"branch URL was actually consumed\".\n * Any future consumption path that hands the URL to customer code by\n * other means (e.g. a process-isolated runner writing an env overlay)\n * must also set this.\n */\n dbSnapshotAccessed?: boolean\n /**\n * Collector for the replay item's trace-persistence work. When present,\n * the root span's send path pushes a promise that resolves only after\n * every span upload AND the trace completion have been sent (registered\n * synchronously at send time, so the replay runner can await it after\n * the wrapped fn resolves). This is what lets replay guarantee traces\n * are persisted server-side before `completeReplay` builds the\n * trace-ID mapping. Absent outside replay, where sends stay\n * fire-and-forget.\n */\n pendingPersistence?: Promise<unknown>[]\n}\n\nlet replayContextStorage: AsyncLocalStorageLike<ReplayContext | null> | null =\n null\n\nexport const replayContextReady: Promise<void> = asyncStorageReady.then(() => {\n replayContextStorage = createAsyncLocalStorage<ReplayContext | null>()\n})\n\n/** Get the current replay context, if any. */\nexport function getReplayContext(): ReplayContext | null {\n return replayContextStorage?.getStore() ?? null\n}\n\n/** Run a function within a replay context. */\nexport function runWithReplayContext<T>(ctx: ReplayContext, fn: () => T): T {\n if (replayContextStorage) {\n return replayContextStorage.run(ctx, fn)\n }\n return fn()\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,16 +5,97 @@ import {
|
|
|
5
5
|
deserializeValue,
|
|
6
6
|
getReplayContext,
|
|
7
7
|
isAsyncStorageInitDone,
|
|
8
|
-
serializeValue
|
|
9
|
-
|
|
8
|
+
serializeValue,
|
|
9
|
+
toJsonSafe
|
|
10
|
+
} from "./chunk-EQI6ZJC3.js";
|
|
10
11
|
|
|
11
12
|
// src/version.generated.ts
|
|
12
|
-
var __version__ = "0.
|
|
13
|
+
var __version__ = "0.19.0";
|
|
13
14
|
|
|
14
15
|
// src/constants.ts
|
|
15
16
|
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
16
17
|
|
|
17
18
|
// src/http.ts
|
|
19
|
+
function serializePayloadBody(payload) {
|
|
20
|
+
try {
|
|
21
|
+
return { body: JSON.stringify(payload), dropped: [] };
|
|
22
|
+
} catch {
|
|
23
|
+
const dropped = [];
|
|
24
|
+
const sanitize = (value, seen) => {
|
|
25
|
+
const t = typeof value;
|
|
26
|
+
if (value === null || t === "string" || t === "number" || t === "boolean") {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
if (t === "bigint") {
|
|
30
|
+
dropped.push("BigInt");
|
|
31
|
+
return "<unserializable: BigInt>";
|
|
32
|
+
}
|
|
33
|
+
if (t === "function") {
|
|
34
|
+
const name = value.name || "Function";
|
|
35
|
+
dropped.push(name);
|
|
36
|
+
return `<unserializable: ${name}>`;
|
|
37
|
+
}
|
|
38
|
+
if (t === "symbol") {
|
|
39
|
+
dropped.push("Symbol");
|
|
40
|
+
return "<unserializable: Symbol>";
|
|
41
|
+
}
|
|
42
|
+
if (t !== "object") {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
const obj = value;
|
|
46
|
+
const className = obj.constructor?.name || "object";
|
|
47
|
+
if (seen.has(obj)) {
|
|
48
|
+
dropped.push(className);
|
|
49
|
+
return `<cycle: ${className}>`;
|
|
50
|
+
}
|
|
51
|
+
seen.add(obj);
|
|
52
|
+
let result;
|
|
53
|
+
if (Array.isArray(obj)) {
|
|
54
|
+
result = obj.map((item) => sanitize(item, seen));
|
|
55
|
+
} else if (typeof obj.toJSON === "function") {
|
|
56
|
+
try {
|
|
57
|
+
result = sanitize(obj.toJSON(), seen);
|
|
58
|
+
} catch {
|
|
59
|
+
dropped.push(className);
|
|
60
|
+
result = `<unserializable: ${className}>`;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
65
|
+
out[k] = sanitize(v, seen);
|
|
66
|
+
}
|
|
67
|
+
result = out;
|
|
68
|
+
}
|
|
69
|
+
seen.delete(obj);
|
|
70
|
+
return result;
|
|
71
|
+
};
|
|
72
|
+
let sanitized;
|
|
73
|
+
try {
|
|
74
|
+
sanitized = sanitize(payload, /* @__PURE__ */ new WeakSet());
|
|
75
|
+
} catch (error) {
|
|
76
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
77
|
+
return {
|
|
78
|
+
body: JSON.stringify({ error: `payload_serialize_failed: ${message}` }),
|
|
79
|
+
dropped
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (dropped.length > 0 && typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized)) {
|
|
83
|
+
const obj = sanitized;
|
|
84
|
+
const existing = Array.isArray(obj.errors) ? obj.errors : [];
|
|
85
|
+
obj.errors = [
|
|
86
|
+
...existing,
|
|
87
|
+
{
|
|
88
|
+
source: "sdk",
|
|
89
|
+
step: "json_serialize",
|
|
90
|
+
error: `stubbed non-serializable value(s): ${[
|
|
91
|
+
...new Set(dropped)
|
|
92
|
+
].join(", ")}`
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
return { body: JSON.stringify(sanitized), dropped };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
18
99
|
var pendingTracePromises = /* @__PURE__ */ new Set();
|
|
19
100
|
function awaitOnExit(promise) {
|
|
20
101
|
pendingTracePromises.add(promise);
|
|
@@ -73,23 +154,14 @@ var HttpClient = class {
|
|
|
73
154
|
const method = options?.method ?? "POST";
|
|
74
155
|
const controller = new AbortController();
|
|
75
156
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Object.entries(payload).filter(
|
|
85
|
-
([, v]) => typeof v === "string" || typeof v === "number"
|
|
86
|
-
)
|
|
87
|
-
),
|
|
88
|
-
rawSpan: {},
|
|
89
|
-
errors: [
|
|
90
|
-
{ source: "sdk", step: "json_serialize", error: serializationError }
|
|
91
|
-
]
|
|
92
|
-
});
|
|
157
|
+
const { body, dropped } = serializePayloadBody(payload);
|
|
158
|
+
if (dropped.length > 0) {
|
|
159
|
+
try {
|
|
160
|
+
console.warn(
|
|
161
|
+
`Bitfab: request body to ${endpoint} held ${dropped.length} non-serializable value(s) (${[...new Set(dropped)].join(", ")}); they were stubbed so the span still sends, but the trace may be incomplete or not replayable. Capture a JSON-safe projection of this input to make it replayable.`
|
|
162
|
+
);
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
93
165
|
}
|
|
94
166
|
try {
|
|
95
167
|
const response = await fetch(url, {
|
|
@@ -350,30 +422,7 @@ var HttpClient = class {
|
|
|
350
422
|
function nowIso() {
|
|
351
423
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
352
424
|
}
|
|
353
|
-
|
|
354
|
-
if (value === null || value === void 0) {
|
|
355
|
-
return value;
|
|
356
|
-
}
|
|
357
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
358
|
-
return value;
|
|
359
|
-
}
|
|
360
|
-
if (Array.isArray(value)) {
|
|
361
|
-
return value.map(safeSerialize);
|
|
362
|
-
}
|
|
363
|
-
if (typeof value === "object") {
|
|
364
|
-
if (typeof value.toJSON === "function") {
|
|
365
|
-
return value.toJSON();
|
|
366
|
-
}
|
|
367
|
-
const result = {};
|
|
368
|
-
for (const [k, v] of Object.entries(value)) {
|
|
369
|
-
if (!k.startsWith("_")) {
|
|
370
|
-
result[k] = safeSerialize(v);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return result;
|
|
374
|
-
}
|
|
375
|
-
return String(value);
|
|
376
|
-
}
|
|
425
|
+
var safeSerialize = toJsonSafe;
|
|
377
426
|
function extractContentBlocks(content) {
|
|
378
427
|
if (!Array.isArray(content)) {
|
|
379
428
|
return [];
|
|
@@ -1136,56 +1185,7 @@ var LANGGRAPH_METADATA_KEYS = [
|
|
|
1136
1185
|
function nowIso2() {
|
|
1137
1186
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1138
1187
|
}
|
|
1139
|
-
var
|
|
1140
|
-
function safeSerialize2(value) {
|
|
1141
|
-
return safeSerializeInner(value, 0, /* @__PURE__ */ new WeakSet());
|
|
1142
|
-
}
|
|
1143
|
-
function safeSerializeInner(value, depth, seen) {
|
|
1144
|
-
if (value === null || value === void 0) {
|
|
1145
|
-
return value;
|
|
1146
|
-
}
|
|
1147
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1148
|
-
return value;
|
|
1149
|
-
}
|
|
1150
|
-
const className = value?.constructor?.name ?? typeof value;
|
|
1151
|
-
if (depth > MAX_SERIALIZE_DEPTH) {
|
|
1152
|
-
return `<${className}>`;
|
|
1153
|
-
}
|
|
1154
|
-
if (typeof value === "object") {
|
|
1155
|
-
if (seen.has(value)) {
|
|
1156
|
-
return `<cycle ${className}>`;
|
|
1157
|
-
}
|
|
1158
|
-
seen.add(value);
|
|
1159
|
-
}
|
|
1160
|
-
if (Array.isArray(value)) {
|
|
1161
|
-
return value.map((item) => safeSerializeInner(item, depth + 1, seen));
|
|
1162
|
-
}
|
|
1163
|
-
if (typeof value === "object") {
|
|
1164
|
-
if (typeof value.toJSON === "function") {
|
|
1165
|
-
try {
|
|
1166
|
-
return value.toJSON();
|
|
1167
|
-
} catch {
|
|
1168
|
-
return `<${className}>`;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
try {
|
|
1172
|
-
const result = {};
|
|
1173
|
-
for (const [k, v] of Object.entries(value)) {
|
|
1174
|
-
if (!k.startsWith("_")) {
|
|
1175
|
-
result[k] = safeSerializeInner(v, depth + 1, seen);
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
return result;
|
|
1179
|
-
} catch {
|
|
1180
|
-
return `<${className}>`;
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
try {
|
|
1184
|
-
return String(value);
|
|
1185
|
-
} catch {
|
|
1186
|
-
return `<${className}>`;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1188
|
+
var safeSerialize2 = toJsonSafe;
|
|
1189
1189
|
function convertMessage(message) {
|
|
1190
1190
|
if (typeof message !== "object" || message === null) {
|
|
1191
1191
|
return { role: "unknown", content: String(message) };
|
|
@@ -2984,7 +2984,7 @@ var Bitfab = class {
|
|
|
2984
2984
|
`Function is wrapped with trace function key '${wrappedKey}' but replay was called with '${traceFunctionKey}'. Pass matching keys, or pass the unwrapped function to replay it under the explicit key.`
|
|
2985
2985
|
);
|
|
2986
2986
|
}
|
|
2987
|
-
const { replay: doReplay } = await import("./replay-
|
|
2987
|
+
const { replay: doReplay } = await import("./replay-QAWGVRCZ.js");
|
|
2988
2988
|
return doReplay(
|
|
2989
2989
|
this.httpClient,
|
|
2990
2990
|
this.serviceUrl,
|
|
@@ -3063,4 +3063,4 @@ export {
|
|
|
3063
3063
|
Bitfab,
|
|
3064
3064
|
BitfabFunction
|
|
3065
3065
|
};
|
|
3066
|
-
//# sourceMappingURL=chunk-
|
|
3066
|
+
//# sourceMappingURL=chunk-FA6DBCAT.js.map
|