bitfab 0.13.8 → 0.15.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-QT7HWOKU.js +131 -0
- package/dist/chunk-QT7HWOKU.js.map +1 -0
- package/dist/{chunk-4ANYHNQJ.js → chunk-YPG3XIG4.js} +372 -13
- package/dist/chunk-YPG3XIG4.js.map +1 -0
- package/dist/index.cjs +427 -391
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -13
- package/dist/index.d.ts +11 -13
- package/dist/index.js +6 -6
- package/dist/node.cjs +427 -391
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +5 -5
- package/dist/{replay-F7K2JQCZ.js → replay-3MQS22GS.js} +62 -16
- package/dist/replay-3MQS22GS.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-4ANYHNQJ.js.map +0 -1
- package/dist/chunk-VFGUZWAV.js +0 -467
- package/dist/chunk-VFGUZWAV.js.map +0 -1
- package/dist/replay-F7K2JQCZ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -39,25 +39,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
));
|
|
40
40
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
41
41
|
|
|
42
|
-
// src/version.generated.ts
|
|
43
|
-
var __version__;
|
|
44
|
-
var init_version_generated = __esm({
|
|
45
|
-
"src/version.generated.ts"() {
|
|
46
|
-
"use strict";
|
|
47
|
-
__version__ = "0.13.8";
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// src/constants.ts
|
|
52
|
-
var DEFAULT_SERVICE_URL;
|
|
53
|
-
var init_constants = __esm({
|
|
54
|
-
"src/constants.ts"() {
|
|
55
|
-
"use strict";
|
|
56
|
-
init_version_generated();
|
|
57
|
-
DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
42
|
// src/errors.ts
|
|
62
43
|
var BitfabError;
|
|
63
44
|
var init_errors = __esm({
|
|
@@ -73,340 +54,6 @@ var init_errors = __esm({
|
|
|
73
54
|
}
|
|
74
55
|
});
|
|
75
56
|
|
|
76
|
-
// src/http.ts
|
|
77
|
-
function awaitOnExit(promise) {
|
|
78
|
-
pendingTracePromises.add(promise);
|
|
79
|
-
void promise.finally(() => {
|
|
80
|
-
pendingTracePromises.delete(promise);
|
|
81
|
-
}).catch(() => {
|
|
82
|
-
});
|
|
83
|
-
return promise;
|
|
84
|
-
}
|
|
85
|
-
async function flushTraces(timeoutMs = 5e3) {
|
|
86
|
-
if (pendingTracePromises.size === 0) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
await Promise.race([
|
|
90
|
-
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
91
|
-
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
92
|
-
]);
|
|
93
|
-
}
|
|
94
|
-
var pendingTracePromises, HttpClient;
|
|
95
|
-
var init_http = __esm({
|
|
96
|
-
"src/http.ts"() {
|
|
97
|
-
"use strict";
|
|
98
|
-
init_constants();
|
|
99
|
-
init_errors();
|
|
100
|
-
pendingTracePromises = /* @__PURE__ */ new Set();
|
|
101
|
-
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
102
|
-
let isFlushing = false;
|
|
103
|
-
process.on("beforeExit", () => {
|
|
104
|
-
if (pendingTracePromises.size > 0 && !isFlushing) {
|
|
105
|
-
isFlushing = true;
|
|
106
|
-
Promise.allSettled(
|
|
107
|
-
Array.from(pendingTracePromises).map(
|
|
108
|
-
(p) => p.catch(() => {
|
|
109
|
-
})
|
|
110
|
-
)
|
|
111
|
-
).then(() => {
|
|
112
|
-
isFlushing = false;
|
|
113
|
-
}).catch(() => {
|
|
114
|
-
isFlushing = false;
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
HttpClient = class {
|
|
120
|
-
constructor(config) {
|
|
121
|
-
this.apiKey = config.apiKey;
|
|
122
|
-
this.serviceUrl = config.serviceUrl;
|
|
123
|
-
this.timeout = config.timeout ?? 12e4;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Make an HTTP request to the Bitfab API. Defaults to POST; pass
|
|
127
|
-
* `options.method` to use a different verb (e.g. "PATCH").
|
|
128
|
-
*
|
|
129
|
-
* @param endpoint - The API endpoint (without base URL)
|
|
130
|
-
* @param payload - The request body
|
|
131
|
-
* @param options - Optional request options
|
|
132
|
-
* @returns The parsed JSON response
|
|
133
|
-
* @throws {BitfabError} If the request fails
|
|
134
|
-
*/
|
|
135
|
-
async request(endpoint, payload, options) {
|
|
136
|
-
const url = `${this.serviceUrl}${endpoint}`;
|
|
137
|
-
const timeout = options?.timeout ?? this.timeout;
|
|
138
|
-
const method = options?.method ?? "POST";
|
|
139
|
-
const controller = new AbortController();
|
|
140
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
141
|
-
let body;
|
|
142
|
-
let serializationError;
|
|
143
|
-
try {
|
|
144
|
-
body = JSON.stringify(payload);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
serializationError = error instanceof Error ? error.message : String(error);
|
|
147
|
-
body = JSON.stringify({
|
|
148
|
-
...Object.fromEntries(
|
|
149
|
-
Object.entries(payload).filter(
|
|
150
|
-
([, v]) => typeof v === "string" || typeof v === "number"
|
|
151
|
-
)
|
|
152
|
-
),
|
|
153
|
-
rawSpan: {},
|
|
154
|
-
errors: [
|
|
155
|
-
{ source: "sdk", step: "json_serialize", error: serializationError }
|
|
156
|
-
]
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
const response = await fetch(url, {
|
|
161
|
-
method,
|
|
162
|
-
headers: {
|
|
163
|
-
"Content-Type": "application/json",
|
|
164
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
165
|
-
},
|
|
166
|
-
body,
|
|
167
|
-
signal: controller.signal
|
|
168
|
-
});
|
|
169
|
-
if (!response.ok) {
|
|
170
|
-
const errorText = await response.text();
|
|
171
|
-
throw new BitfabError(
|
|
172
|
-
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
const result = await response.json();
|
|
176
|
-
if (result.error) {
|
|
177
|
-
if (result.url) {
|
|
178
|
-
throw new BitfabError(
|
|
179
|
-
`${result.error} Configure it at: ${this.serviceUrl}${result.url}`,
|
|
180
|
-
result.url
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
throw new BitfabError(result.error);
|
|
184
|
-
}
|
|
185
|
-
return result;
|
|
186
|
-
} catch (error) {
|
|
187
|
-
if (error instanceof BitfabError) {
|
|
188
|
-
throw error;
|
|
189
|
-
}
|
|
190
|
-
if (error instanceof Error) {
|
|
191
|
-
if (error.name === "AbortError") {
|
|
192
|
-
throw new BitfabError(`Request timed out after ${timeout}ms`);
|
|
193
|
-
}
|
|
194
|
-
throw new BitfabError(error.message);
|
|
195
|
-
}
|
|
196
|
-
throw new BitfabError("Unknown error occurred");
|
|
197
|
-
} finally {
|
|
198
|
-
clearTimeout(timeoutId);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Look up a function by name.
|
|
203
|
-
* Blocks until complete - needed for function execution.
|
|
204
|
-
*/
|
|
205
|
-
async lookupFunction(name) {
|
|
206
|
-
return this.request("/api/sdk/functions/lookup", { name });
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Send an internal trace (from BAML execution).
|
|
210
|
-
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
211
|
-
*/
|
|
212
|
-
sendInternalTrace(functionId, payload) {
|
|
213
|
-
void awaitOnExit(
|
|
214
|
-
this.request(`/api/sdk/functions/${functionId}/traces`, {
|
|
215
|
-
...payload,
|
|
216
|
-
sdkVersion: __version__
|
|
217
|
-
})
|
|
218
|
-
).catch((error) => {
|
|
219
|
-
try {
|
|
220
|
-
console.error("Bitfab: Failed to create trace:", error);
|
|
221
|
-
} catch {
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Send an external span (from withSpan wrapper or OpenAI tracing).
|
|
227
|
-
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
228
|
-
* Returns the tracked promise so callers can optionally await it.
|
|
229
|
-
*/
|
|
230
|
-
sendExternalSpan(payload) {
|
|
231
|
-
return awaitOnExit(
|
|
232
|
-
this.request("/api/sdk/externalSpans", {
|
|
233
|
-
...payload,
|
|
234
|
-
sdkVersion: __version__
|
|
235
|
-
})
|
|
236
|
-
).catch((error) => {
|
|
237
|
-
try {
|
|
238
|
-
console.error("Bitfab: Failed to create external span:", error);
|
|
239
|
-
} catch {
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Send an external trace (from OpenAI tracing).
|
|
245
|
-
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
246
|
-
*/
|
|
247
|
-
sendExternalTrace(payload) {
|
|
248
|
-
void awaitOnExit(
|
|
249
|
-
this.request("/api/sdk/externalTraces", {
|
|
250
|
-
...payload,
|
|
251
|
-
sdkVersion: __version__
|
|
252
|
-
})
|
|
253
|
-
).catch((error) => {
|
|
254
|
-
try {
|
|
255
|
-
console.error("Bitfab: Failed to create external trace:", error);
|
|
256
|
-
} catch {
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Partial update of an existing external trace identified by sourceTraceId.
|
|
262
|
-
* Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
|
|
263
|
-
* returns a tracked promise that callers may optionally await.
|
|
264
|
-
*/
|
|
265
|
-
patchTrace(sourceTraceId, payload) {
|
|
266
|
-
const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
|
|
267
|
-
return awaitOnExit(
|
|
268
|
-
this.request(endpoint, payload, { method: "PATCH" })
|
|
269
|
-
).catch((error) => {
|
|
270
|
-
try {
|
|
271
|
-
console.error("Bitfab: Failed to patch trace:", error);
|
|
272
|
-
} catch {
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Start a replay session by fetching historical traces.
|
|
278
|
-
* Blocking call — creates a test run and returns lightweight item references.
|
|
279
|
-
*/
|
|
280
|
-
async startReplay(traceFunctionKey, limit, traceIds, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId) {
|
|
281
|
-
const payload = { traceFunctionKey, limit };
|
|
282
|
-
if (traceIds) {
|
|
283
|
-
payload.traceIds = traceIds;
|
|
284
|
-
}
|
|
285
|
-
if (codeChangeDescription !== void 0) {
|
|
286
|
-
payload.codeChangeDescription = codeChangeDescription;
|
|
287
|
-
}
|
|
288
|
-
if (codeChangeFiles !== void 0) {
|
|
289
|
-
payload.codeChangeFiles = codeChangeFiles;
|
|
290
|
-
}
|
|
291
|
-
if (includeDbBranchLease) {
|
|
292
|
-
payload.includeDbBranchLease = true;
|
|
293
|
-
}
|
|
294
|
-
if (experimentGroupId !== void 0) {
|
|
295
|
-
payload.experimentGroupId = experimentGroupId;
|
|
296
|
-
}
|
|
297
|
-
const timeout = includeDbBranchLease ? 18e4 : 3e4;
|
|
298
|
-
return this.request("/api/sdk/replay/start", payload, {
|
|
299
|
-
timeout
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Fetch an external span by ID.
|
|
304
|
-
* Blocking GET request.
|
|
305
|
-
*/
|
|
306
|
-
async getExternalSpan(spanId) {
|
|
307
|
-
const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}`;
|
|
308
|
-
const controller = new AbortController();
|
|
309
|
-
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
310
|
-
try {
|
|
311
|
-
const response = await fetch(url, {
|
|
312
|
-
method: "GET",
|
|
313
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
314
|
-
signal: controller.signal
|
|
315
|
-
});
|
|
316
|
-
if (!response.ok) {
|
|
317
|
-
const errorText = await response.text();
|
|
318
|
-
throw new BitfabError(
|
|
319
|
-
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
return await response.json();
|
|
323
|
-
} catch (error) {
|
|
324
|
-
if (error instanceof BitfabError) {
|
|
325
|
-
throw error;
|
|
326
|
-
}
|
|
327
|
-
if (error instanceof Error) {
|
|
328
|
-
if (error.name === "AbortError") {
|
|
329
|
-
throw new BitfabError("Request timed out after 30000ms");
|
|
330
|
-
}
|
|
331
|
-
throw new BitfabError(error.message);
|
|
332
|
-
}
|
|
333
|
-
throw new BitfabError("Unknown error occurred");
|
|
334
|
-
} finally {
|
|
335
|
-
clearTimeout(timeoutId);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Fetch the span tree for a root span.
|
|
340
|
-
* Blocking GET request.
|
|
341
|
-
*/
|
|
342
|
-
async getSpanTree(externalSpanId) {
|
|
343
|
-
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}`;
|
|
344
|
-
const controller = new AbortController();
|
|
345
|
-
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
346
|
-
try {
|
|
347
|
-
const response = await fetch(url, {
|
|
348
|
-
method: "GET",
|
|
349
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
350
|
-
signal: controller.signal
|
|
351
|
-
});
|
|
352
|
-
if (!response.ok) {
|
|
353
|
-
const errorText = await response.text();
|
|
354
|
-
throw new BitfabError(
|
|
355
|
-
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
return await response.json();
|
|
359
|
-
} catch (error) {
|
|
360
|
-
if (error instanceof BitfabError) {
|
|
361
|
-
throw error;
|
|
362
|
-
}
|
|
363
|
-
if (error instanceof Error) {
|
|
364
|
-
if (error.name === "AbortError") {
|
|
365
|
-
throw new BitfabError("Request timed out after 30000ms");
|
|
366
|
-
}
|
|
367
|
-
throw new BitfabError(error.message);
|
|
368
|
-
}
|
|
369
|
-
throw new BitfabError("Unknown error occurred");
|
|
370
|
-
} finally {
|
|
371
|
-
clearTimeout(timeoutId);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Mark a replay test run as completed.
|
|
376
|
-
* Blocking call.
|
|
377
|
-
*/
|
|
378
|
-
async completeReplay(testRunId) {
|
|
379
|
-
return this.request(
|
|
380
|
-
"/api/sdk/replay/complete",
|
|
381
|
-
{ testRunId },
|
|
382
|
-
{ timeout: 3e4 }
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Ask the server to materialize a per-trace DB branch lease from a
|
|
387
|
-
* captured `dbSnapshotRef`. Blocking — the resolver creates a Neon
|
|
388
|
-
* snapshot + preview branch and polls operations to readiness, which
|
|
389
|
-
* can take seconds.
|
|
390
|
-
*/
|
|
391
|
-
async resolveDbBranchLease(testRunId, traceId, dbSnapshotRef) {
|
|
392
|
-
return this.request(
|
|
393
|
-
"/api/sdk/replay/resolveDbBranchLease",
|
|
394
|
-
{ testRunId, traceId, dbSnapshotRef },
|
|
395
|
-
{ timeout: 9e4 }
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
/** Release a previously-resolved DB branch by deleting its Neon branch. Idempotent server-side. */
|
|
399
|
-
async releaseDbBranchLease(neonBranchId) {
|
|
400
|
-
await this.request(
|
|
401
|
-
"/api/sdk/replay/releaseDbBranchLease",
|
|
402
|
-
{ neonBranchId },
|
|
403
|
-
{ timeout: 3e4 }
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
|
|
410
57
|
// src/asyncStorage.ts
|
|
411
58
|
function registerAsyncLocalStorageClass(cls) {
|
|
412
59
|
if (!AsyncLocalStorageClass) {
|
|
@@ -587,6 +234,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
587
234
|
let result;
|
|
588
235
|
let error = null;
|
|
589
236
|
const replayedTraceId = crypto.randomUUID();
|
|
237
|
+
const pendingPersistence = [];
|
|
590
238
|
try {
|
|
591
239
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
592
240
|
const spanData = span.rawData?.span_data ?? {};
|
|
@@ -609,7 +257,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
609
257
|
mockTree,
|
|
610
258
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
611
259
|
mockStrategy,
|
|
612
|
-
dbBranchLease: lease
|
|
260
|
+
dbBranchLease: lease,
|
|
261
|
+
pendingPersistence
|
|
613
262
|
},
|
|
614
263
|
() => fn(...inputs)
|
|
615
264
|
);
|
|
@@ -617,6 +266,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
617
266
|
} catch (e) {
|
|
618
267
|
error = e instanceof Error ? e.message : String(e);
|
|
619
268
|
} finally {
|
|
269
|
+
await Promise.allSettled(pendingPersistence);
|
|
620
270
|
if (lease) {
|
|
621
271
|
try {
|
|
622
272
|
await httpClient.releaseDbBranchLease(lease.neonBranchId);
|
|
@@ -659,6 +309,21 @@ async function mapWithConcurrency(tasks, maxConcurrency) {
|
|
|
659
309
|
return results;
|
|
660
310
|
}
|
|
661
311
|
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
312
|
+
if (options?.traceIds !== void 0) {
|
|
313
|
+
if (options.traceIds.length === 0) {
|
|
314
|
+
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
315
|
+
}
|
|
316
|
+
if (options.traceIds.length > 100) {
|
|
317
|
+
throw new BitfabError(
|
|
318
|
+
`traceIds supports at most 100 trace IDs per replay (got ${options.traceIds.length}).`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (options?.limit !== void 0 && options?.traceIds !== void 0) {
|
|
323
|
+
throw new BitfabError(
|
|
324
|
+
"Pass either limit or traceIds, not both: an explicit trace ID list already determines how many traces replay."
|
|
325
|
+
);
|
|
326
|
+
}
|
|
662
327
|
await replayContextReady;
|
|
663
328
|
const {
|
|
664
329
|
testRunId,
|
|
@@ -666,7 +331,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
666
331
|
items: serverItems
|
|
667
332
|
} = await httpClient.startReplay(
|
|
668
333
|
traceFunctionKey,
|
|
669
|
-
|
|
334
|
+
// limit is meaningless with explicit traceIds (the ID list determines
|
|
335
|
+
// the count), so it's omitted from the request entirely.
|
|
336
|
+
options?.traceIds ? void 0 : options?.limit ?? 5,
|
|
670
337
|
options?.traceIds,
|
|
671
338
|
options?.codeChangeDescription,
|
|
672
339
|
options?.codeChangeFiles,
|
|
@@ -687,20 +354,46 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
687
354
|
)
|
|
688
355
|
);
|
|
689
356
|
const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
|
|
690
|
-
await
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const completeResult = await httpClient.completeReplay(testRunId);
|
|
694
|
-
serverTraceIds = completeResult.traceIds ?? {};
|
|
695
|
-
} catch (e) {
|
|
357
|
+
const completeResult = await httpClient.completeReplay(testRunId);
|
|
358
|
+
const serverTraceIds = completeResult.traceIds;
|
|
359
|
+
if (serverTraceIds === void 0) {
|
|
696
360
|
try {
|
|
697
|
-
console.
|
|
361
|
+
console.warn(
|
|
362
|
+
"Bitfab: server did not return replay trace IDs; item.traceId will be null (server upgrade required for verdict persistence)"
|
|
363
|
+
);
|
|
698
364
|
} catch {
|
|
699
365
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
366
|
+
for (const item of resultItems) {
|
|
367
|
+
item.traceId = null;
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
const missing = [];
|
|
371
|
+
let completedCount = 0;
|
|
372
|
+
for (const item of resultItems) {
|
|
373
|
+
if (item.traceId) {
|
|
374
|
+
const mapped = serverTraceIds[item.traceId];
|
|
375
|
+
if (item.error === null) {
|
|
376
|
+
completedCount += 1;
|
|
377
|
+
if (mapped === void 0) {
|
|
378
|
+
missing.push(item.traceId);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
item.traceId = mapped ?? null;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (missing.length > 0) {
|
|
385
|
+
const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
|
|
386
|
+
if (missing.length === completedCount) {
|
|
387
|
+
throw new BitfabError(
|
|
388
|
+
`Replay completed but the server has no persisted trace for any of the ${completedCount} completed item(s) (testRunId ${testRunId}).${serverCount} 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.`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
console.error(
|
|
393
|
+
`Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId}).${serverCount} Their traceId is null and verdicts cannot be persisted for them. Missing: ${missing.join(", ")}`
|
|
394
|
+
);
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
704
397
|
}
|
|
705
398
|
}
|
|
706
399
|
return {
|
|
@@ -712,7 +405,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
712
405
|
var init_replay = __esm({
|
|
713
406
|
"src/replay.ts"() {
|
|
714
407
|
"use strict";
|
|
715
|
-
|
|
408
|
+
init_errors();
|
|
716
409
|
init_replayContext();
|
|
717
410
|
init_serialize();
|
|
718
411
|
}
|
|
@@ -737,9 +430,346 @@ __export(index_exports, {
|
|
|
737
430
|
});
|
|
738
431
|
module.exports = __toCommonJS(index_exports);
|
|
739
432
|
|
|
433
|
+
// src/version.generated.ts
|
|
434
|
+
var __version__ = "0.15.0";
|
|
435
|
+
|
|
436
|
+
// src/constants.ts
|
|
437
|
+
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
438
|
+
|
|
439
|
+
// src/http.ts
|
|
440
|
+
init_errors();
|
|
441
|
+
var pendingTracePromises = /* @__PURE__ */ new Set();
|
|
442
|
+
function awaitOnExit(promise) {
|
|
443
|
+
pendingTracePromises.add(promise);
|
|
444
|
+
void promise.finally(() => {
|
|
445
|
+
pendingTracePromises.delete(promise);
|
|
446
|
+
}).catch(() => {
|
|
447
|
+
});
|
|
448
|
+
return promise;
|
|
449
|
+
}
|
|
450
|
+
async function flushTraces(timeoutMs = 5e3) {
|
|
451
|
+
if (pendingTracePromises.size === 0) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
await Promise.race([
|
|
455
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
456
|
+
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
457
|
+
]);
|
|
458
|
+
}
|
|
459
|
+
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
460
|
+
let isFlushing = false;
|
|
461
|
+
process.on("beforeExit", () => {
|
|
462
|
+
if (pendingTracePromises.size > 0 && !isFlushing) {
|
|
463
|
+
isFlushing = true;
|
|
464
|
+
Promise.allSettled(
|
|
465
|
+
Array.from(pendingTracePromises).map(
|
|
466
|
+
(p) => p.catch(() => {
|
|
467
|
+
})
|
|
468
|
+
)
|
|
469
|
+
).then(() => {
|
|
470
|
+
isFlushing = false;
|
|
471
|
+
}).catch(() => {
|
|
472
|
+
isFlushing = false;
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
var HttpClient = class {
|
|
478
|
+
constructor(config) {
|
|
479
|
+
this.apiKey = config.apiKey;
|
|
480
|
+
this.serviceUrl = config.serviceUrl;
|
|
481
|
+
this.timeout = config.timeout ?? 12e4;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Make an HTTP request to the Bitfab API. Defaults to POST; pass
|
|
485
|
+
* `options.method` to use a different verb (e.g. "PATCH").
|
|
486
|
+
*
|
|
487
|
+
* @param endpoint - The API endpoint (without base URL)
|
|
488
|
+
* @param payload - The request body
|
|
489
|
+
* @param options - Optional request options
|
|
490
|
+
* @returns The parsed JSON response
|
|
491
|
+
* @throws {BitfabError} If the request fails
|
|
492
|
+
*/
|
|
493
|
+
async request(endpoint, payload, options) {
|
|
494
|
+
const url = `${this.serviceUrl}${endpoint}`;
|
|
495
|
+
const timeout = options?.timeout ?? this.timeout;
|
|
496
|
+
const method = options?.method ?? "POST";
|
|
497
|
+
const controller = new AbortController();
|
|
498
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
499
|
+
let body;
|
|
500
|
+
let serializationError;
|
|
501
|
+
try {
|
|
502
|
+
body = JSON.stringify(payload);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
serializationError = error instanceof Error ? error.message : String(error);
|
|
505
|
+
body = JSON.stringify({
|
|
506
|
+
...Object.fromEntries(
|
|
507
|
+
Object.entries(payload).filter(
|
|
508
|
+
([, v]) => typeof v === "string" || typeof v === "number"
|
|
509
|
+
)
|
|
510
|
+
),
|
|
511
|
+
rawSpan: {},
|
|
512
|
+
errors: [
|
|
513
|
+
{ source: "sdk", step: "json_serialize", error: serializationError }
|
|
514
|
+
]
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
const response = await fetch(url, {
|
|
519
|
+
method,
|
|
520
|
+
headers: {
|
|
521
|
+
"Content-Type": "application/json",
|
|
522
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
523
|
+
},
|
|
524
|
+
body,
|
|
525
|
+
signal: controller.signal
|
|
526
|
+
});
|
|
527
|
+
if (!response.ok) {
|
|
528
|
+
const errorText = await response.text();
|
|
529
|
+
throw new BitfabError(
|
|
530
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
const result = await response.json();
|
|
534
|
+
if (result.error) {
|
|
535
|
+
if (result.url) {
|
|
536
|
+
throw new BitfabError(
|
|
537
|
+
`${result.error} Configure it at: ${this.serviceUrl}${result.url}`,
|
|
538
|
+
result.url
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
throw new BitfabError(result.error);
|
|
542
|
+
}
|
|
543
|
+
return result;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
if (error instanceof BitfabError) {
|
|
546
|
+
throw error;
|
|
547
|
+
}
|
|
548
|
+
if (error instanceof Error) {
|
|
549
|
+
if (error.name === "AbortError") {
|
|
550
|
+
throw new BitfabError(`Request timed out after ${timeout}ms`);
|
|
551
|
+
}
|
|
552
|
+
throw new BitfabError(error.message);
|
|
553
|
+
}
|
|
554
|
+
throw new BitfabError("Unknown error occurred");
|
|
555
|
+
} finally {
|
|
556
|
+
clearTimeout(timeoutId);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Look up a function by name.
|
|
561
|
+
* Blocks until complete - needed for function execution.
|
|
562
|
+
*/
|
|
563
|
+
async lookupFunction(name) {
|
|
564
|
+
return this.request("/api/sdk/functions/lookup", { name });
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Send an internal trace (from BAML execution).
|
|
568
|
+
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
569
|
+
*/
|
|
570
|
+
sendInternalTrace(functionId, payload) {
|
|
571
|
+
void awaitOnExit(
|
|
572
|
+
this.request(`/api/sdk/functions/${functionId}/traces`, {
|
|
573
|
+
...payload,
|
|
574
|
+
sdkVersion: __version__
|
|
575
|
+
})
|
|
576
|
+
).catch((error) => {
|
|
577
|
+
try {
|
|
578
|
+
console.error("Bitfab: Failed to create trace:", error);
|
|
579
|
+
} catch {
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Send an external span (from withSpan wrapper or OpenAI tracing).
|
|
585
|
+
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
586
|
+
* Returns the tracked promise so callers can optionally await it.
|
|
587
|
+
*/
|
|
588
|
+
sendExternalSpan(payload) {
|
|
589
|
+
return awaitOnExit(
|
|
590
|
+
this.request("/api/sdk/externalSpans", {
|
|
591
|
+
...payload,
|
|
592
|
+
sdkVersion: __version__
|
|
593
|
+
})
|
|
594
|
+
).catch((error) => {
|
|
595
|
+
try {
|
|
596
|
+
console.error("Bitfab: Failed to create external span:", error);
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Send an external trace (from OpenAI tracing).
|
|
603
|
+
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
604
|
+
* Returns the tracked promise so callers can optionally await it
|
|
605
|
+
* (the replay path does, so trace completions are persisted before
|
|
606
|
+
* `completeReplay` builds the trace-ID mapping).
|
|
607
|
+
*/
|
|
608
|
+
sendExternalTrace(payload) {
|
|
609
|
+
return awaitOnExit(
|
|
610
|
+
this.request("/api/sdk/externalTraces", {
|
|
611
|
+
...payload,
|
|
612
|
+
sdkVersion: __version__
|
|
613
|
+
})
|
|
614
|
+
).catch((error) => {
|
|
615
|
+
try {
|
|
616
|
+
console.error("Bitfab: Failed to create external trace:", error);
|
|
617
|
+
} catch {
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Partial update of an existing external trace identified by sourceTraceId.
|
|
623
|
+
* Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
|
|
624
|
+
* returns a tracked promise that callers may optionally await.
|
|
625
|
+
*/
|
|
626
|
+
patchTrace(sourceTraceId, payload) {
|
|
627
|
+
const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
|
|
628
|
+
return awaitOnExit(
|
|
629
|
+
this.request(endpoint, payload, { method: "PATCH" })
|
|
630
|
+
).catch((error) => {
|
|
631
|
+
try {
|
|
632
|
+
console.error("Bitfab: Failed to patch trace:", error);
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Start a replay session by fetching historical traces.
|
|
639
|
+
* Blocking call — creates a test run and returns lightweight item references.
|
|
640
|
+
*/
|
|
641
|
+
async startReplay(traceFunctionKey, limit, traceIds, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId) {
|
|
642
|
+
const payload = { traceFunctionKey };
|
|
643
|
+
if (limit !== void 0) {
|
|
644
|
+
payload.limit = limit;
|
|
645
|
+
}
|
|
646
|
+
if (traceIds) {
|
|
647
|
+
payload.traceIds = traceIds;
|
|
648
|
+
}
|
|
649
|
+
if (codeChangeDescription !== void 0) {
|
|
650
|
+
payload.codeChangeDescription = codeChangeDescription;
|
|
651
|
+
}
|
|
652
|
+
if (codeChangeFiles !== void 0) {
|
|
653
|
+
payload.codeChangeFiles = codeChangeFiles;
|
|
654
|
+
}
|
|
655
|
+
if (includeDbBranchLease) {
|
|
656
|
+
payload.includeDbBranchLease = true;
|
|
657
|
+
}
|
|
658
|
+
if (experimentGroupId !== void 0) {
|
|
659
|
+
payload.experimentGroupId = experimentGroupId;
|
|
660
|
+
}
|
|
661
|
+
const timeout = includeDbBranchLease ? 18e4 : 3e4;
|
|
662
|
+
return this.request("/api/sdk/replay/start", payload, {
|
|
663
|
+
timeout
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Fetch an external span by ID.
|
|
668
|
+
* Blocking GET request.
|
|
669
|
+
*/
|
|
670
|
+
async getExternalSpan(spanId) {
|
|
671
|
+
const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}`;
|
|
672
|
+
const controller = new AbortController();
|
|
673
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
674
|
+
try {
|
|
675
|
+
const response = await fetch(url, {
|
|
676
|
+
method: "GET",
|
|
677
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
678
|
+
signal: controller.signal
|
|
679
|
+
});
|
|
680
|
+
if (!response.ok) {
|
|
681
|
+
const errorText = await response.text();
|
|
682
|
+
throw new BitfabError(
|
|
683
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
return await response.json();
|
|
687
|
+
} catch (error) {
|
|
688
|
+
if (error instanceof BitfabError) {
|
|
689
|
+
throw error;
|
|
690
|
+
}
|
|
691
|
+
if (error instanceof Error) {
|
|
692
|
+
if (error.name === "AbortError") {
|
|
693
|
+
throw new BitfabError("Request timed out after 30000ms");
|
|
694
|
+
}
|
|
695
|
+
throw new BitfabError(error.message);
|
|
696
|
+
}
|
|
697
|
+
throw new BitfabError("Unknown error occurred");
|
|
698
|
+
} finally {
|
|
699
|
+
clearTimeout(timeoutId);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Fetch the span tree for a root span.
|
|
704
|
+
* Blocking GET request.
|
|
705
|
+
*/
|
|
706
|
+
async getSpanTree(externalSpanId) {
|
|
707
|
+
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}`;
|
|
708
|
+
const controller = new AbortController();
|
|
709
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
710
|
+
try {
|
|
711
|
+
const response = await fetch(url, {
|
|
712
|
+
method: "GET",
|
|
713
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
714
|
+
signal: controller.signal
|
|
715
|
+
});
|
|
716
|
+
if (!response.ok) {
|
|
717
|
+
const errorText = await response.text();
|
|
718
|
+
throw new BitfabError(
|
|
719
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
return await response.json();
|
|
723
|
+
} catch (error) {
|
|
724
|
+
if (error instanceof BitfabError) {
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
if (error instanceof Error) {
|
|
728
|
+
if (error.name === "AbortError") {
|
|
729
|
+
throw new BitfabError("Request timed out after 30000ms");
|
|
730
|
+
}
|
|
731
|
+
throw new BitfabError(error.message);
|
|
732
|
+
}
|
|
733
|
+
throw new BitfabError("Unknown error occurred");
|
|
734
|
+
} finally {
|
|
735
|
+
clearTimeout(timeoutId);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Mark a replay test run as completed.
|
|
740
|
+
* Blocking call.
|
|
741
|
+
*/
|
|
742
|
+
async completeReplay(testRunId) {
|
|
743
|
+
return this.request(
|
|
744
|
+
"/api/sdk/replay/complete",
|
|
745
|
+
{ testRunId },
|
|
746
|
+
{ timeout: 3e4 }
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Ask the server to materialize a per-trace DB branch lease from a
|
|
751
|
+
* captured `dbSnapshotRef`. Blocking — the resolver creates a Neon
|
|
752
|
+
* snapshot + preview branch and polls operations to readiness, which
|
|
753
|
+
* can take seconds.
|
|
754
|
+
*/
|
|
755
|
+
async resolveDbBranchLease(testRunId, traceId, dbSnapshotRef) {
|
|
756
|
+
return this.request(
|
|
757
|
+
"/api/sdk/replay/resolveDbBranchLease",
|
|
758
|
+
{ testRunId, traceId, dbSnapshotRef },
|
|
759
|
+
{ timeout: 9e4 }
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
/** Release a previously-resolved DB branch by deleting its Neon branch. Idempotent server-side. */
|
|
763
|
+
async releaseDbBranchLease(neonBranchId) {
|
|
764
|
+
await this.request(
|
|
765
|
+
"/api/sdk/replay/releaseDbBranchLease",
|
|
766
|
+
{ neonBranchId },
|
|
767
|
+
{ timeout: 3e4 }
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
|
|
740
772
|
// src/claudeAgentSdk.ts
|
|
741
|
-
init_constants();
|
|
742
|
-
init_http();
|
|
743
773
|
function nowIso() {
|
|
744
774
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
745
775
|
}
|
|
@@ -1504,9 +1534,6 @@ async function runFunctionWithBaml(bamlSource, inputs, providers, envVars) {
|
|
|
1504
1534
|
};
|
|
1505
1535
|
}
|
|
1506
1536
|
|
|
1507
|
-
// src/client.ts
|
|
1508
|
-
init_constants();
|
|
1509
|
-
|
|
1510
1537
|
// src/dbSnapshot.ts
|
|
1511
1538
|
init_errors();
|
|
1512
1539
|
var SUPPORTED_PROVIDERS = ["neon"];
|
|
@@ -1524,12 +1551,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1524
1551
|
};
|
|
1525
1552
|
}
|
|
1526
1553
|
|
|
1527
|
-
// src/client.ts
|
|
1528
|
-
init_http();
|
|
1529
|
-
|
|
1530
1554
|
// src/langgraph.ts
|
|
1531
|
-
init_constants();
|
|
1532
|
-
init_http();
|
|
1533
1555
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1534
1556
|
var LANGGRAPH_METADATA_KEYS = [
|
|
1535
1557
|
"langgraph_step",
|
|
@@ -2077,8 +2099,6 @@ var ReplayEnvironment = class {
|
|
|
2077
2099
|
init_serialize();
|
|
2078
2100
|
|
|
2079
2101
|
// src/tracing.ts
|
|
2080
|
-
init_constants();
|
|
2081
|
-
init_http();
|
|
2082
2102
|
var BitfabOpenAITracingProcessor = class {
|
|
2083
2103
|
/**
|
|
2084
2104
|
* Initialize the tracing processor.
|
|
@@ -2912,9 +2932,18 @@ var Bitfab = class {
|
|
|
2912
2932
|
spanType: options.type ?? "custom"
|
|
2913
2933
|
};
|
|
2914
2934
|
const sendSpan = async (params) => {
|
|
2935
|
+
const replayCtx = getReplayContext();
|
|
2936
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
2937
|
+
let resolvePersistence;
|
|
2938
|
+
if (persistenceCollector) {
|
|
2939
|
+
persistenceCollector.push(
|
|
2940
|
+
new Promise((resolve) => {
|
|
2941
|
+
resolvePersistence = resolve;
|
|
2942
|
+
})
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2915
2945
|
try {
|
|
2916
2946
|
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2917
|
-
const replayCtx = getReplayContext();
|
|
2918
2947
|
const spanPromise = self.sendWrapperSpan({
|
|
2919
2948
|
...baseSpanParams,
|
|
2920
2949
|
...params,
|
|
@@ -2929,13 +2958,17 @@ var Bitfab = class {
|
|
|
2929
2958
|
if (isRootSpan) {
|
|
2930
2959
|
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
2931
2960
|
pending.push(spanPromise);
|
|
2932
|
-
|
|
2933
|
-
Promise.allSettled(pending)
|
|
2934
|
-
|
|
2935
|
-
|
|
2961
|
+
if (persistenceCollector) {
|
|
2962
|
+
await Promise.allSettled(pending);
|
|
2963
|
+
} else {
|
|
2964
|
+
await Promise.race([
|
|
2965
|
+
Promise.allSettled(pending),
|
|
2966
|
+
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
2967
|
+
]);
|
|
2968
|
+
}
|
|
2936
2969
|
pendingSpanPromises.delete(traceId);
|
|
2937
2970
|
const traceState = activeTraceStates.get(traceId);
|
|
2938
|
-
self.sendTraceCompletion({
|
|
2971
|
+
const completionPromise = self.sendTraceCompletion({
|
|
2939
2972
|
traceFunctionKey,
|
|
2940
2973
|
traceId,
|
|
2941
2974
|
startedAt: traceState?.startedAt ?? startedAt,
|
|
@@ -2948,6 +2981,9 @@ var Bitfab = class {
|
|
|
2948
2981
|
dbSnapshotRef: traceState?.dbSnapshotRef
|
|
2949
2982
|
});
|
|
2950
2983
|
activeTraceStates.delete(traceId);
|
|
2984
|
+
if (persistenceCollector) {
|
|
2985
|
+
await completionPromise;
|
|
2986
|
+
}
|
|
2951
2987
|
} else {
|
|
2952
2988
|
const pending = pendingSpanPromises.get(traceId);
|
|
2953
2989
|
if (pending) {
|
|
@@ -2957,6 +2993,8 @@ var Bitfab = class {
|
|
|
2957
2993
|
}
|
|
2958
2994
|
}
|
|
2959
2995
|
} catch {
|
|
2996
|
+
} finally {
|
|
2997
|
+
resolvePersistence?.();
|
|
2960
2998
|
}
|
|
2961
2999
|
};
|
|
2962
3000
|
const replayCtxForMock = getReplayContext();
|
|
@@ -3109,7 +3147,7 @@ var Bitfab = class {
|
|
|
3109
3147
|
if (params.dbSnapshotRef) {
|
|
3110
3148
|
rawTrace.db_snapshot_ref = params.dbSnapshotRef;
|
|
3111
3149
|
}
|
|
3112
|
-
this.httpClient.sendExternalTrace({
|
|
3150
|
+
return this.httpClient.sendExternalTrace({
|
|
3113
3151
|
type: "sdk-function",
|
|
3114
3152
|
source: "typescript-sdk-function",
|
|
3115
3153
|
traceFunctionKey: params.traceFunctionKey,
|
|
@@ -3183,7 +3221,9 @@ var Bitfab = class {
|
|
|
3183
3221
|
*
|
|
3184
3222
|
* @param traceFunctionKey - The trace function key to replay
|
|
3185
3223
|
* @param fn - The function to replay (must be the return value of `withSpan`)
|
|
3186
|
-
* @param options - Optional replay options
|
|
3224
|
+
* @param options - Optional replay options. `limit` and `traceIds` are
|
|
3225
|
+
* mutually exclusive — an explicit ID list already determines how many
|
|
3226
|
+
* traces replay, so passing both throws a BitfabError.
|
|
3187
3227
|
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
3188
3228
|
*/
|
|
3189
3229
|
async replay(traceFunctionKey, fn, options) {
|
|
@@ -3251,10 +3291,6 @@ var BitfabFunction = class {
|
|
|
3251
3291
|
);
|
|
3252
3292
|
}
|
|
3253
3293
|
};
|
|
3254
|
-
|
|
3255
|
-
// src/index.ts
|
|
3256
|
-
init_constants();
|
|
3257
|
-
init_http();
|
|
3258
3294
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3259
3295
|
0 && (module.exports = {
|
|
3260
3296
|
Bitfab,
|