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/node.cjs
CHANGED
|
@@ -85,25 +85,6 @@ var init_asyncStorage = __esm({
|
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
// src/version.generated.ts
|
|
89
|
-
var __version__;
|
|
90
|
-
var init_version_generated = __esm({
|
|
91
|
-
"src/version.generated.ts"() {
|
|
92
|
-
"use strict";
|
|
93
|
-
__version__ = "0.13.8";
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// src/constants.ts
|
|
98
|
-
var DEFAULT_SERVICE_URL;
|
|
99
|
-
var init_constants = __esm({
|
|
100
|
-
"src/constants.ts"() {
|
|
101
|
-
"use strict";
|
|
102
|
-
init_version_generated();
|
|
103
|
-
DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
88
|
// src/errors.ts
|
|
108
89
|
var BitfabError;
|
|
109
90
|
var init_errors = __esm({
|
|
@@ -119,340 +100,6 @@ var init_errors = __esm({
|
|
|
119
100
|
}
|
|
120
101
|
});
|
|
121
102
|
|
|
122
|
-
// src/http.ts
|
|
123
|
-
function awaitOnExit(promise) {
|
|
124
|
-
pendingTracePromises.add(promise);
|
|
125
|
-
void promise.finally(() => {
|
|
126
|
-
pendingTracePromises.delete(promise);
|
|
127
|
-
}).catch(() => {
|
|
128
|
-
});
|
|
129
|
-
return promise;
|
|
130
|
-
}
|
|
131
|
-
async function flushTraces(timeoutMs = 5e3) {
|
|
132
|
-
if (pendingTracePromises.size === 0) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
await Promise.race([
|
|
136
|
-
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
137
|
-
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
138
|
-
]);
|
|
139
|
-
}
|
|
140
|
-
var pendingTracePromises, HttpClient;
|
|
141
|
-
var init_http = __esm({
|
|
142
|
-
"src/http.ts"() {
|
|
143
|
-
"use strict";
|
|
144
|
-
init_constants();
|
|
145
|
-
init_errors();
|
|
146
|
-
pendingTracePromises = /* @__PURE__ */ new Set();
|
|
147
|
-
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
148
|
-
let isFlushing = false;
|
|
149
|
-
process.on("beforeExit", () => {
|
|
150
|
-
if (pendingTracePromises.size > 0 && !isFlushing) {
|
|
151
|
-
isFlushing = true;
|
|
152
|
-
Promise.allSettled(
|
|
153
|
-
Array.from(pendingTracePromises).map(
|
|
154
|
-
(p) => p.catch(() => {
|
|
155
|
-
})
|
|
156
|
-
)
|
|
157
|
-
).then(() => {
|
|
158
|
-
isFlushing = false;
|
|
159
|
-
}).catch(() => {
|
|
160
|
-
isFlushing = false;
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
HttpClient = class {
|
|
166
|
-
constructor(config) {
|
|
167
|
-
this.apiKey = config.apiKey;
|
|
168
|
-
this.serviceUrl = config.serviceUrl;
|
|
169
|
-
this.timeout = config.timeout ?? 12e4;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Make an HTTP request to the Bitfab API. Defaults to POST; pass
|
|
173
|
-
* `options.method` to use a different verb (e.g. "PATCH").
|
|
174
|
-
*
|
|
175
|
-
* @param endpoint - The API endpoint (without base URL)
|
|
176
|
-
* @param payload - The request body
|
|
177
|
-
* @param options - Optional request options
|
|
178
|
-
* @returns The parsed JSON response
|
|
179
|
-
* @throws {BitfabError} If the request fails
|
|
180
|
-
*/
|
|
181
|
-
async request(endpoint, payload, options) {
|
|
182
|
-
const url = `${this.serviceUrl}${endpoint}`;
|
|
183
|
-
const timeout = options?.timeout ?? this.timeout;
|
|
184
|
-
const method = options?.method ?? "POST";
|
|
185
|
-
const controller = new AbortController();
|
|
186
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
187
|
-
let body;
|
|
188
|
-
let serializationError;
|
|
189
|
-
try {
|
|
190
|
-
body = JSON.stringify(payload);
|
|
191
|
-
} catch (error) {
|
|
192
|
-
serializationError = error instanceof Error ? error.message : String(error);
|
|
193
|
-
body = JSON.stringify({
|
|
194
|
-
...Object.fromEntries(
|
|
195
|
-
Object.entries(payload).filter(
|
|
196
|
-
([, v]) => typeof v === "string" || typeof v === "number"
|
|
197
|
-
)
|
|
198
|
-
),
|
|
199
|
-
rawSpan: {},
|
|
200
|
-
errors: [
|
|
201
|
-
{ source: "sdk", step: "json_serialize", error: serializationError }
|
|
202
|
-
]
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
try {
|
|
206
|
-
const response = await fetch(url, {
|
|
207
|
-
method,
|
|
208
|
-
headers: {
|
|
209
|
-
"Content-Type": "application/json",
|
|
210
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
211
|
-
},
|
|
212
|
-
body,
|
|
213
|
-
signal: controller.signal
|
|
214
|
-
});
|
|
215
|
-
if (!response.ok) {
|
|
216
|
-
const errorText = await response.text();
|
|
217
|
-
throw new BitfabError(
|
|
218
|
-
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
const result = await response.json();
|
|
222
|
-
if (result.error) {
|
|
223
|
-
if (result.url) {
|
|
224
|
-
throw new BitfabError(
|
|
225
|
-
`${result.error} Configure it at: ${this.serviceUrl}${result.url}`,
|
|
226
|
-
result.url
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
throw new BitfabError(result.error);
|
|
230
|
-
}
|
|
231
|
-
return result;
|
|
232
|
-
} catch (error) {
|
|
233
|
-
if (error instanceof BitfabError) {
|
|
234
|
-
throw error;
|
|
235
|
-
}
|
|
236
|
-
if (error instanceof Error) {
|
|
237
|
-
if (error.name === "AbortError") {
|
|
238
|
-
throw new BitfabError(`Request timed out after ${timeout}ms`);
|
|
239
|
-
}
|
|
240
|
-
throw new BitfabError(error.message);
|
|
241
|
-
}
|
|
242
|
-
throw new BitfabError("Unknown error occurred");
|
|
243
|
-
} finally {
|
|
244
|
-
clearTimeout(timeoutId);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Look up a function by name.
|
|
249
|
-
* Blocks until complete - needed for function execution.
|
|
250
|
-
*/
|
|
251
|
-
async lookupFunction(name) {
|
|
252
|
-
return this.request("/api/sdk/functions/lookup", { name });
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Send an internal trace (from BAML execution).
|
|
256
|
-
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
257
|
-
*/
|
|
258
|
-
sendInternalTrace(functionId, payload) {
|
|
259
|
-
void awaitOnExit(
|
|
260
|
-
this.request(`/api/sdk/functions/${functionId}/traces`, {
|
|
261
|
-
...payload,
|
|
262
|
-
sdkVersion: __version__
|
|
263
|
-
})
|
|
264
|
-
).catch((error) => {
|
|
265
|
-
try {
|
|
266
|
-
console.error("Bitfab: Failed to create trace:", error);
|
|
267
|
-
} catch {
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Send an external span (from withSpan wrapper or OpenAI tracing).
|
|
273
|
-
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
274
|
-
* Returns the tracked promise so callers can optionally await it.
|
|
275
|
-
*/
|
|
276
|
-
sendExternalSpan(payload) {
|
|
277
|
-
return awaitOnExit(
|
|
278
|
-
this.request("/api/sdk/externalSpans", {
|
|
279
|
-
...payload,
|
|
280
|
-
sdkVersion: __version__
|
|
281
|
-
})
|
|
282
|
-
).catch((error) => {
|
|
283
|
-
try {
|
|
284
|
-
console.error("Bitfab: Failed to create external span:", error);
|
|
285
|
-
} catch {
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Send an external trace (from OpenAI tracing).
|
|
291
|
-
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
292
|
-
*/
|
|
293
|
-
sendExternalTrace(payload) {
|
|
294
|
-
void awaitOnExit(
|
|
295
|
-
this.request("/api/sdk/externalTraces", {
|
|
296
|
-
...payload,
|
|
297
|
-
sdkVersion: __version__
|
|
298
|
-
})
|
|
299
|
-
).catch((error) => {
|
|
300
|
-
try {
|
|
301
|
-
console.error("Bitfab: Failed to create external trace:", error);
|
|
302
|
-
} catch {
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Partial update of an existing external trace identified by sourceTraceId.
|
|
308
|
-
* Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
|
|
309
|
-
* returns a tracked promise that callers may optionally await.
|
|
310
|
-
*/
|
|
311
|
-
patchTrace(sourceTraceId, payload) {
|
|
312
|
-
const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
|
|
313
|
-
return awaitOnExit(
|
|
314
|
-
this.request(endpoint, payload, { method: "PATCH" })
|
|
315
|
-
).catch((error) => {
|
|
316
|
-
try {
|
|
317
|
-
console.error("Bitfab: Failed to patch trace:", error);
|
|
318
|
-
} catch {
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Start a replay session by fetching historical traces.
|
|
324
|
-
* Blocking call — creates a test run and returns lightweight item references.
|
|
325
|
-
*/
|
|
326
|
-
async startReplay(traceFunctionKey, limit, traceIds, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId) {
|
|
327
|
-
const payload = { traceFunctionKey, limit };
|
|
328
|
-
if (traceIds) {
|
|
329
|
-
payload.traceIds = traceIds;
|
|
330
|
-
}
|
|
331
|
-
if (codeChangeDescription !== void 0) {
|
|
332
|
-
payload.codeChangeDescription = codeChangeDescription;
|
|
333
|
-
}
|
|
334
|
-
if (codeChangeFiles !== void 0) {
|
|
335
|
-
payload.codeChangeFiles = codeChangeFiles;
|
|
336
|
-
}
|
|
337
|
-
if (includeDbBranchLease) {
|
|
338
|
-
payload.includeDbBranchLease = true;
|
|
339
|
-
}
|
|
340
|
-
if (experimentGroupId !== void 0) {
|
|
341
|
-
payload.experimentGroupId = experimentGroupId;
|
|
342
|
-
}
|
|
343
|
-
const timeout = includeDbBranchLease ? 18e4 : 3e4;
|
|
344
|
-
return this.request("/api/sdk/replay/start", payload, {
|
|
345
|
-
timeout
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Fetch an external span by ID.
|
|
350
|
-
* Blocking GET request.
|
|
351
|
-
*/
|
|
352
|
-
async getExternalSpan(spanId) {
|
|
353
|
-
const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}`;
|
|
354
|
-
const controller = new AbortController();
|
|
355
|
-
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
356
|
-
try {
|
|
357
|
-
const response = await fetch(url, {
|
|
358
|
-
method: "GET",
|
|
359
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
360
|
-
signal: controller.signal
|
|
361
|
-
});
|
|
362
|
-
if (!response.ok) {
|
|
363
|
-
const errorText = await response.text();
|
|
364
|
-
throw new BitfabError(
|
|
365
|
-
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
return await response.json();
|
|
369
|
-
} catch (error) {
|
|
370
|
-
if (error instanceof BitfabError) {
|
|
371
|
-
throw error;
|
|
372
|
-
}
|
|
373
|
-
if (error instanceof Error) {
|
|
374
|
-
if (error.name === "AbortError") {
|
|
375
|
-
throw new BitfabError("Request timed out after 30000ms");
|
|
376
|
-
}
|
|
377
|
-
throw new BitfabError(error.message);
|
|
378
|
-
}
|
|
379
|
-
throw new BitfabError("Unknown error occurred");
|
|
380
|
-
} finally {
|
|
381
|
-
clearTimeout(timeoutId);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Fetch the span tree for a root span.
|
|
386
|
-
* Blocking GET request.
|
|
387
|
-
*/
|
|
388
|
-
async getSpanTree(externalSpanId) {
|
|
389
|
-
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}`;
|
|
390
|
-
const controller = new AbortController();
|
|
391
|
-
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
392
|
-
try {
|
|
393
|
-
const response = await fetch(url, {
|
|
394
|
-
method: "GET",
|
|
395
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
396
|
-
signal: controller.signal
|
|
397
|
-
});
|
|
398
|
-
if (!response.ok) {
|
|
399
|
-
const errorText = await response.text();
|
|
400
|
-
throw new BitfabError(
|
|
401
|
-
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
return await response.json();
|
|
405
|
-
} catch (error) {
|
|
406
|
-
if (error instanceof BitfabError) {
|
|
407
|
-
throw error;
|
|
408
|
-
}
|
|
409
|
-
if (error instanceof Error) {
|
|
410
|
-
if (error.name === "AbortError") {
|
|
411
|
-
throw new BitfabError("Request timed out after 30000ms");
|
|
412
|
-
}
|
|
413
|
-
throw new BitfabError(error.message);
|
|
414
|
-
}
|
|
415
|
-
throw new BitfabError("Unknown error occurred");
|
|
416
|
-
} finally {
|
|
417
|
-
clearTimeout(timeoutId);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Mark a replay test run as completed.
|
|
422
|
-
* Blocking call.
|
|
423
|
-
*/
|
|
424
|
-
async completeReplay(testRunId) {
|
|
425
|
-
return this.request(
|
|
426
|
-
"/api/sdk/replay/complete",
|
|
427
|
-
{ testRunId },
|
|
428
|
-
{ timeout: 3e4 }
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Ask the server to materialize a per-trace DB branch lease from a
|
|
433
|
-
* captured `dbSnapshotRef`. Blocking — the resolver creates a Neon
|
|
434
|
-
* snapshot + preview branch and polls operations to readiness, which
|
|
435
|
-
* can take seconds.
|
|
436
|
-
*/
|
|
437
|
-
async resolveDbBranchLease(testRunId, traceId, dbSnapshotRef) {
|
|
438
|
-
return this.request(
|
|
439
|
-
"/api/sdk/replay/resolveDbBranchLease",
|
|
440
|
-
{ testRunId, traceId, dbSnapshotRef },
|
|
441
|
-
{ timeout: 9e4 }
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
/** Release a previously-resolved DB branch by deleting its Neon branch. Idempotent server-side. */
|
|
445
|
-
async releaseDbBranchLease(neonBranchId) {
|
|
446
|
-
await this.request(
|
|
447
|
-
"/api/sdk/replay/releaseDbBranchLease",
|
|
448
|
-
{ neonBranchId },
|
|
449
|
-
{ timeout: 3e4 }
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
|
|
456
103
|
// src/replayContext.ts
|
|
457
104
|
function getReplayContext() {
|
|
458
105
|
return replayContextStorage?.getStore() ?? null;
|
|
@@ -594,6 +241,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
594
241
|
let result;
|
|
595
242
|
let error = null;
|
|
596
243
|
const replayedTraceId = crypto.randomUUID();
|
|
244
|
+
const pendingPersistence = [];
|
|
597
245
|
try {
|
|
598
246
|
const span = await httpClient.getExternalSpan(serverItem.externalSpanId);
|
|
599
247
|
const spanData = span.rawData?.span_data ?? {};
|
|
@@ -616,7 +264,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
616
264
|
mockTree,
|
|
617
265
|
callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
|
|
618
266
|
mockStrategy,
|
|
619
|
-
dbBranchLease: lease
|
|
267
|
+
dbBranchLease: lease,
|
|
268
|
+
pendingPersistence
|
|
620
269
|
},
|
|
621
270
|
() => fn(...inputs)
|
|
622
271
|
);
|
|
@@ -624,6 +273,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
|
|
|
624
273
|
} catch (e) {
|
|
625
274
|
error = e instanceof Error ? e.message : String(e);
|
|
626
275
|
} finally {
|
|
276
|
+
await Promise.allSettled(pendingPersistence);
|
|
627
277
|
if (lease) {
|
|
628
278
|
try {
|
|
629
279
|
await httpClient.releaseDbBranchLease(lease.neonBranchId);
|
|
@@ -666,6 +316,21 @@ async function mapWithConcurrency(tasks, maxConcurrency) {
|
|
|
666
316
|
return results;
|
|
667
317
|
}
|
|
668
318
|
async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
319
|
+
if (options?.traceIds !== void 0) {
|
|
320
|
+
if (options.traceIds.length === 0) {
|
|
321
|
+
throw new BitfabError("traceIds must contain at least one trace ID.");
|
|
322
|
+
}
|
|
323
|
+
if (options.traceIds.length > 100) {
|
|
324
|
+
throw new BitfabError(
|
|
325
|
+
`traceIds supports at most 100 trace IDs per replay (got ${options.traceIds.length}).`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (options?.limit !== void 0 && options?.traceIds !== void 0) {
|
|
330
|
+
throw new BitfabError(
|
|
331
|
+
"Pass either limit or traceIds, not both: an explicit trace ID list already determines how many traces replay."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
669
334
|
await replayContextReady;
|
|
670
335
|
const {
|
|
671
336
|
testRunId,
|
|
@@ -673,7 +338,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
673
338
|
items: serverItems
|
|
674
339
|
} = await httpClient.startReplay(
|
|
675
340
|
traceFunctionKey,
|
|
676
|
-
|
|
341
|
+
// limit is meaningless with explicit traceIds (the ID list determines
|
|
342
|
+
// the count), so it's omitted from the request entirely.
|
|
343
|
+
options?.traceIds ? void 0 : options?.limit ?? 5,
|
|
677
344
|
options?.traceIds,
|
|
678
345
|
options?.codeChangeDescription,
|
|
679
346
|
options?.codeChangeFiles,
|
|
@@ -694,20 +361,46 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
694
361
|
)
|
|
695
362
|
);
|
|
696
363
|
const resultItems = await mapWithConcurrency(tasks, maxConcurrency);
|
|
697
|
-
await
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
const completeResult = await httpClient.completeReplay(testRunId);
|
|
701
|
-
serverTraceIds = completeResult.traceIds ?? {};
|
|
702
|
-
} catch (e) {
|
|
364
|
+
const completeResult = await httpClient.completeReplay(testRunId);
|
|
365
|
+
const serverTraceIds = completeResult.traceIds;
|
|
366
|
+
if (serverTraceIds === void 0) {
|
|
703
367
|
try {
|
|
704
|
-
console.
|
|
368
|
+
console.warn(
|
|
369
|
+
"Bitfab: server did not return replay trace IDs; item.traceId will be null (server upgrade required for verdict persistence)"
|
|
370
|
+
);
|
|
705
371
|
} catch {
|
|
706
372
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
373
|
+
for (const item of resultItems) {
|
|
374
|
+
item.traceId = null;
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
const missing = [];
|
|
378
|
+
let completedCount = 0;
|
|
379
|
+
for (const item of resultItems) {
|
|
380
|
+
if (item.traceId) {
|
|
381
|
+
const mapped = serverTraceIds[item.traceId];
|
|
382
|
+
if (item.error === null) {
|
|
383
|
+
completedCount += 1;
|
|
384
|
+
if (mapped === void 0) {
|
|
385
|
+
missing.push(item.traceId);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
item.traceId = mapped ?? null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (missing.length > 0) {
|
|
392
|
+
const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
|
|
393
|
+
if (missing.length === completedCount) {
|
|
394
|
+
throw new BitfabError(
|
|
395
|
+
`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.`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
console.error(
|
|
400
|
+
`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(", ")}`
|
|
401
|
+
);
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
711
404
|
}
|
|
712
405
|
}
|
|
713
406
|
return {
|
|
@@ -719,7 +412,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
|
|
|
719
412
|
var init_replay = __esm({
|
|
720
413
|
"src/replay.ts"() {
|
|
721
414
|
"use strict";
|
|
722
|
-
|
|
415
|
+
init_errors();
|
|
723
416
|
init_replayContext();
|
|
724
417
|
init_serialize();
|
|
725
418
|
}
|
|
@@ -751,9 +444,346 @@ registerAsyncLocalStorageClass(
|
|
|
751
444
|
import_node_async_hooks.AsyncLocalStorage
|
|
752
445
|
);
|
|
753
446
|
|
|
447
|
+
// src/version.generated.ts
|
|
448
|
+
var __version__ = "0.15.0";
|
|
449
|
+
|
|
450
|
+
// src/constants.ts
|
|
451
|
+
var DEFAULT_SERVICE_URL = "https://bitfab.ai";
|
|
452
|
+
|
|
453
|
+
// src/http.ts
|
|
454
|
+
init_errors();
|
|
455
|
+
var pendingTracePromises = /* @__PURE__ */ new Set();
|
|
456
|
+
function awaitOnExit(promise) {
|
|
457
|
+
pendingTracePromises.add(promise);
|
|
458
|
+
void promise.finally(() => {
|
|
459
|
+
pendingTracePromises.delete(promise);
|
|
460
|
+
}).catch(() => {
|
|
461
|
+
});
|
|
462
|
+
return promise;
|
|
463
|
+
}
|
|
464
|
+
async function flushTraces(timeoutMs = 5e3) {
|
|
465
|
+
if (pendingTracePromises.size === 0) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
await Promise.race([
|
|
469
|
+
Promise.allSettled(Array.from(pendingTracePromises)),
|
|
470
|
+
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
471
|
+
]);
|
|
472
|
+
}
|
|
473
|
+
if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
|
|
474
|
+
let isFlushing = false;
|
|
475
|
+
process.on("beforeExit", () => {
|
|
476
|
+
if (pendingTracePromises.size > 0 && !isFlushing) {
|
|
477
|
+
isFlushing = true;
|
|
478
|
+
Promise.allSettled(
|
|
479
|
+
Array.from(pendingTracePromises).map(
|
|
480
|
+
(p) => p.catch(() => {
|
|
481
|
+
})
|
|
482
|
+
)
|
|
483
|
+
).then(() => {
|
|
484
|
+
isFlushing = false;
|
|
485
|
+
}).catch(() => {
|
|
486
|
+
isFlushing = false;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
var HttpClient = class {
|
|
492
|
+
constructor(config) {
|
|
493
|
+
this.apiKey = config.apiKey;
|
|
494
|
+
this.serviceUrl = config.serviceUrl;
|
|
495
|
+
this.timeout = config.timeout ?? 12e4;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Make an HTTP request to the Bitfab API. Defaults to POST; pass
|
|
499
|
+
* `options.method` to use a different verb (e.g. "PATCH").
|
|
500
|
+
*
|
|
501
|
+
* @param endpoint - The API endpoint (without base URL)
|
|
502
|
+
* @param payload - The request body
|
|
503
|
+
* @param options - Optional request options
|
|
504
|
+
* @returns The parsed JSON response
|
|
505
|
+
* @throws {BitfabError} If the request fails
|
|
506
|
+
*/
|
|
507
|
+
async request(endpoint, payload, options) {
|
|
508
|
+
const url = `${this.serviceUrl}${endpoint}`;
|
|
509
|
+
const timeout = options?.timeout ?? this.timeout;
|
|
510
|
+
const method = options?.method ?? "POST";
|
|
511
|
+
const controller = new AbortController();
|
|
512
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
513
|
+
let body;
|
|
514
|
+
let serializationError;
|
|
515
|
+
try {
|
|
516
|
+
body = JSON.stringify(payload);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
serializationError = error instanceof Error ? error.message : String(error);
|
|
519
|
+
body = JSON.stringify({
|
|
520
|
+
...Object.fromEntries(
|
|
521
|
+
Object.entries(payload).filter(
|
|
522
|
+
([, v]) => typeof v === "string" || typeof v === "number"
|
|
523
|
+
)
|
|
524
|
+
),
|
|
525
|
+
rawSpan: {},
|
|
526
|
+
errors: [
|
|
527
|
+
{ source: "sdk", step: "json_serialize", error: serializationError }
|
|
528
|
+
]
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const response = await fetch(url, {
|
|
533
|
+
method,
|
|
534
|
+
headers: {
|
|
535
|
+
"Content-Type": "application/json",
|
|
536
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
537
|
+
},
|
|
538
|
+
body,
|
|
539
|
+
signal: controller.signal
|
|
540
|
+
});
|
|
541
|
+
if (!response.ok) {
|
|
542
|
+
const errorText = await response.text();
|
|
543
|
+
throw new BitfabError(
|
|
544
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
const result = await response.json();
|
|
548
|
+
if (result.error) {
|
|
549
|
+
if (result.url) {
|
|
550
|
+
throw new BitfabError(
|
|
551
|
+
`${result.error} Configure it at: ${this.serviceUrl}${result.url}`,
|
|
552
|
+
result.url
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
throw new BitfabError(result.error);
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
} catch (error) {
|
|
559
|
+
if (error instanceof BitfabError) {
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
if (error instanceof Error) {
|
|
563
|
+
if (error.name === "AbortError") {
|
|
564
|
+
throw new BitfabError(`Request timed out after ${timeout}ms`);
|
|
565
|
+
}
|
|
566
|
+
throw new BitfabError(error.message);
|
|
567
|
+
}
|
|
568
|
+
throw new BitfabError("Unknown error occurred");
|
|
569
|
+
} finally {
|
|
570
|
+
clearTimeout(timeoutId);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Look up a function by name.
|
|
575
|
+
* Blocks until complete - needed for function execution.
|
|
576
|
+
*/
|
|
577
|
+
async lookupFunction(name) {
|
|
578
|
+
return this.request("/api/sdk/functions/lookup", { name });
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Send an internal trace (from BAML execution).
|
|
582
|
+
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
583
|
+
*/
|
|
584
|
+
sendInternalTrace(functionId, payload) {
|
|
585
|
+
void awaitOnExit(
|
|
586
|
+
this.request(`/api/sdk/functions/${functionId}/traces`, {
|
|
587
|
+
...payload,
|
|
588
|
+
sdkVersion: __version__
|
|
589
|
+
})
|
|
590
|
+
).catch((error) => {
|
|
591
|
+
try {
|
|
592
|
+
console.error("Bitfab: Failed to create trace:", error);
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Send an external span (from withSpan wrapper or OpenAI tracing).
|
|
599
|
+
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
600
|
+
* Returns the tracked promise so callers can optionally await it.
|
|
601
|
+
*/
|
|
602
|
+
sendExternalSpan(payload) {
|
|
603
|
+
return awaitOnExit(
|
|
604
|
+
this.request("/api/sdk/externalSpans", {
|
|
605
|
+
...payload,
|
|
606
|
+
sdkVersion: __version__
|
|
607
|
+
})
|
|
608
|
+
).catch((error) => {
|
|
609
|
+
try {
|
|
610
|
+
console.error("Bitfab: Failed to create external span:", error);
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Send an external trace (from OpenAI tracing).
|
|
617
|
+
* Fire-and-forget with awaitOnExit - doesn't block the caller.
|
|
618
|
+
* Returns the tracked promise so callers can optionally await it
|
|
619
|
+
* (the replay path does, so trace completions are persisted before
|
|
620
|
+
* `completeReplay` builds the trace-ID mapping).
|
|
621
|
+
*/
|
|
622
|
+
sendExternalTrace(payload) {
|
|
623
|
+
return awaitOnExit(
|
|
624
|
+
this.request("/api/sdk/externalTraces", {
|
|
625
|
+
...payload,
|
|
626
|
+
sdkVersion: __version__
|
|
627
|
+
})
|
|
628
|
+
).catch((error) => {
|
|
629
|
+
try {
|
|
630
|
+
console.error("Bitfab: Failed to create external trace:", error);
|
|
631
|
+
} catch {
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Partial update of an existing external trace identified by sourceTraceId.
|
|
637
|
+
* Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
|
|
638
|
+
* returns a tracked promise that callers may optionally await.
|
|
639
|
+
*/
|
|
640
|
+
patchTrace(sourceTraceId, payload) {
|
|
641
|
+
const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
|
|
642
|
+
return awaitOnExit(
|
|
643
|
+
this.request(endpoint, payload, { method: "PATCH" })
|
|
644
|
+
).catch((error) => {
|
|
645
|
+
try {
|
|
646
|
+
console.error("Bitfab: Failed to patch trace:", error);
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Start a replay session by fetching historical traces.
|
|
653
|
+
* Blocking call — creates a test run and returns lightweight item references.
|
|
654
|
+
*/
|
|
655
|
+
async startReplay(traceFunctionKey, limit, traceIds, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId) {
|
|
656
|
+
const payload = { traceFunctionKey };
|
|
657
|
+
if (limit !== void 0) {
|
|
658
|
+
payload.limit = limit;
|
|
659
|
+
}
|
|
660
|
+
if (traceIds) {
|
|
661
|
+
payload.traceIds = traceIds;
|
|
662
|
+
}
|
|
663
|
+
if (codeChangeDescription !== void 0) {
|
|
664
|
+
payload.codeChangeDescription = codeChangeDescription;
|
|
665
|
+
}
|
|
666
|
+
if (codeChangeFiles !== void 0) {
|
|
667
|
+
payload.codeChangeFiles = codeChangeFiles;
|
|
668
|
+
}
|
|
669
|
+
if (includeDbBranchLease) {
|
|
670
|
+
payload.includeDbBranchLease = true;
|
|
671
|
+
}
|
|
672
|
+
if (experimentGroupId !== void 0) {
|
|
673
|
+
payload.experimentGroupId = experimentGroupId;
|
|
674
|
+
}
|
|
675
|
+
const timeout = includeDbBranchLease ? 18e4 : 3e4;
|
|
676
|
+
return this.request("/api/sdk/replay/start", payload, {
|
|
677
|
+
timeout
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Fetch an external span by ID.
|
|
682
|
+
* Blocking GET request.
|
|
683
|
+
*/
|
|
684
|
+
async getExternalSpan(spanId) {
|
|
685
|
+
const url = `${this.serviceUrl}/api/sdk/externalSpans/${spanId}`;
|
|
686
|
+
const controller = new AbortController();
|
|
687
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
688
|
+
try {
|
|
689
|
+
const response = await fetch(url, {
|
|
690
|
+
method: "GET",
|
|
691
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
692
|
+
signal: controller.signal
|
|
693
|
+
});
|
|
694
|
+
if (!response.ok) {
|
|
695
|
+
const errorText = await response.text();
|
|
696
|
+
throw new BitfabError(
|
|
697
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
return await response.json();
|
|
701
|
+
} catch (error) {
|
|
702
|
+
if (error instanceof BitfabError) {
|
|
703
|
+
throw error;
|
|
704
|
+
}
|
|
705
|
+
if (error instanceof Error) {
|
|
706
|
+
if (error.name === "AbortError") {
|
|
707
|
+
throw new BitfabError("Request timed out after 30000ms");
|
|
708
|
+
}
|
|
709
|
+
throw new BitfabError(error.message);
|
|
710
|
+
}
|
|
711
|
+
throw new BitfabError("Unknown error occurred");
|
|
712
|
+
} finally {
|
|
713
|
+
clearTimeout(timeoutId);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Fetch the span tree for a root span.
|
|
718
|
+
* Blocking GET request.
|
|
719
|
+
*/
|
|
720
|
+
async getSpanTree(externalSpanId) {
|
|
721
|
+
const url = `${this.serviceUrl}/api/sdk/replay/spanTree/${externalSpanId}`;
|
|
722
|
+
const controller = new AbortController();
|
|
723
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
724
|
+
try {
|
|
725
|
+
const response = await fetch(url, {
|
|
726
|
+
method: "GET",
|
|
727
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
728
|
+
signal: controller.signal
|
|
729
|
+
});
|
|
730
|
+
if (!response.ok) {
|
|
731
|
+
const errorText = await response.text();
|
|
732
|
+
throw new BitfabError(
|
|
733
|
+
`HTTP ${response.status}: ${errorText.slice(0, 500)}`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
return await response.json();
|
|
737
|
+
} catch (error) {
|
|
738
|
+
if (error instanceof BitfabError) {
|
|
739
|
+
throw error;
|
|
740
|
+
}
|
|
741
|
+
if (error instanceof Error) {
|
|
742
|
+
if (error.name === "AbortError") {
|
|
743
|
+
throw new BitfabError("Request timed out after 30000ms");
|
|
744
|
+
}
|
|
745
|
+
throw new BitfabError(error.message);
|
|
746
|
+
}
|
|
747
|
+
throw new BitfabError("Unknown error occurred");
|
|
748
|
+
} finally {
|
|
749
|
+
clearTimeout(timeoutId);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Mark a replay test run as completed.
|
|
754
|
+
* Blocking call.
|
|
755
|
+
*/
|
|
756
|
+
async completeReplay(testRunId) {
|
|
757
|
+
return this.request(
|
|
758
|
+
"/api/sdk/replay/complete",
|
|
759
|
+
{ testRunId },
|
|
760
|
+
{ timeout: 3e4 }
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Ask the server to materialize a per-trace DB branch lease from a
|
|
765
|
+
* captured `dbSnapshotRef`. Blocking — the resolver creates a Neon
|
|
766
|
+
* snapshot + preview branch and polls operations to readiness, which
|
|
767
|
+
* can take seconds.
|
|
768
|
+
*/
|
|
769
|
+
async resolveDbBranchLease(testRunId, traceId, dbSnapshotRef) {
|
|
770
|
+
return this.request(
|
|
771
|
+
"/api/sdk/replay/resolveDbBranchLease",
|
|
772
|
+
{ testRunId, traceId, dbSnapshotRef },
|
|
773
|
+
{ timeout: 9e4 }
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
/** Release a previously-resolved DB branch by deleting its Neon branch. Idempotent server-side. */
|
|
777
|
+
async releaseDbBranchLease(neonBranchId) {
|
|
778
|
+
await this.request(
|
|
779
|
+
"/api/sdk/replay/releaseDbBranchLease",
|
|
780
|
+
{ neonBranchId },
|
|
781
|
+
{ timeout: 3e4 }
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
754
786
|
// src/claudeAgentSdk.ts
|
|
755
|
-
init_constants();
|
|
756
|
-
init_http();
|
|
757
787
|
function nowIso() {
|
|
758
788
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
759
789
|
}
|
|
@@ -1518,9 +1548,6 @@ async function runFunctionWithBaml(bamlSource, inputs, providers, envVars) {
|
|
|
1518
1548
|
};
|
|
1519
1549
|
}
|
|
1520
1550
|
|
|
1521
|
-
// src/client.ts
|
|
1522
|
-
init_constants();
|
|
1523
|
-
|
|
1524
1551
|
// src/dbSnapshot.ts
|
|
1525
1552
|
init_errors();
|
|
1526
1553
|
var SUPPORTED_PROVIDERS = ["neon"];
|
|
@@ -1538,12 +1565,7 @@ function buildSnapshotRef(config, sdkWallClockBeforeFn) {
|
|
|
1538
1565
|
};
|
|
1539
1566
|
}
|
|
1540
1567
|
|
|
1541
|
-
// src/client.ts
|
|
1542
|
-
init_http();
|
|
1543
|
-
|
|
1544
1568
|
// src/langgraph.ts
|
|
1545
|
-
init_constants();
|
|
1546
|
-
init_http();
|
|
1547
1569
|
var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
|
|
1548
1570
|
var LANGGRAPH_METADATA_KEYS = [
|
|
1549
1571
|
"langgraph_step",
|
|
@@ -2091,8 +2113,6 @@ var ReplayEnvironment = class {
|
|
|
2091
2113
|
init_serialize();
|
|
2092
2114
|
|
|
2093
2115
|
// src/tracing.ts
|
|
2094
|
-
init_constants();
|
|
2095
|
-
init_http();
|
|
2096
2116
|
var BitfabOpenAITracingProcessor = class {
|
|
2097
2117
|
/**
|
|
2098
2118
|
* Initialize the tracing processor.
|
|
@@ -2926,9 +2946,18 @@ var Bitfab = class {
|
|
|
2926
2946
|
spanType: options.type ?? "custom"
|
|
2927
2947
|
};
|
|
2928
2948
|
const sendSpan = async (params) => {
|
|
2949
|
+
const replayCtx = getReplayContext();
|
|
2950
|
+
const persistenceCollector = isRootSpan ? replayCtx?.pendingPersistence : void 0;
|
|
2951
|
+
let resolvePersistence;
|
|
2952
|
+
if (persistenceCollector) {
|
|
2953
|
+
persistenceCollector.push(
|
|
2954
|
+
new Promise((resolve) => {
|
|
2955
|
+
resolvePersistence = resolve;
|
|
2956
|
+
})
|
|
2957
|
+
);
|
|
2958
|
+
}
|
|
2929
2959
|
try {
|
|
2930
2960
|
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2931
|
-
const replayCtx = getReplayContext();
|
|
2932
2961
|
const spanPromise = self.sendWrapperSpan({
|
|
2933
2962
|
...baseSpanParams,
|
|
2934
2963
|
...params,
|
|
@@ -2943,13 +2972,17 @@ var Bitfab = class {
|
|
|
2943
2972
|
if (isRootSpan) {
|
|
2944
2973
|
const pending = pendingSpanPromises.get(traceId) ?? [];
|
|
2945
2974
|
pending.push(spanPromise);
|
|
2946
|
-
|
|
2947
|
-
Promise.allSettled(pending)
|
|
2948
|
-
|
|
2949
|
-
|
|
2975
|
+
if (persistenceCollector) {
|
|
2976
|
+
await Promise.allSettled(pending);
|
|
2977
|
+
} else {
|
|
2978
|
+
await Promise.race([
|
|
2979
|
+
Promise.allSettled(pending),
|
|
2980
|
+
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
2981
|
+
]);
|
|
2982
|
+
}
|
|
2950
2983
|
pendingSpanPromises.delete(traceId);
|
|
2951
2984
|
const traceState = activeTraceStates.get(traceId);
|
|
2952
|
-
self.sendTraceCompletion({
|
|
2985
|
+
const completionPromise = self.sendTraceCompletion({
|
|
2953
2986
|
traceFunctionKey,
|
|
2954
2987
|
traceId,
|
|
2955
2988
|
startedAt: traceState?.startedAt ?? startedAt,
|
|
@@ -2962,6 +2995,9 @@ var Bitfab = class {
|
|
|
2962
2995
|
dbSnapshotRef: traceState?.dbSnapshotRef
|
|
2963
2996
|
});
|
|
2964
2997
|
activeTraceStates.delete(traceId);
|
|
2998
|
+
if (persistenceCollector) {
|
|
2999
|
+
await completionPromise;
|
|
3000
|
+
}
|
|
2965
3001
|
} else {
|
|
2966
3002
|
const pending = pendingSpanPromises.get(traceId);
|
|
2967
3003
|
if (pending) {
|
|
@@ -2971,6 +3007,8 @@ var Bitfab = class {
|
|
|
2971
3007
|
}
|
|
2972
3008
|
}
|
|
2973
3009
|
} catch {
|
|
3010
|
+
} finally {
|
|
3011
|
+
resolvePersistence?.();
|
|
2974
3012
|
}
|
|
2975
3013
|
};
|
|
2976
3014
|
const replayCtxForMock = getReplayContext();
|
|
@@ -3123,7 +3161,7 @@ var Bitfab = class {
|
|
|
3123
3161
|
if (params.dbSnapshotRef) {
|
|
3124
3162
|
rawTrace.db_snapshot_ref = params.dbSnapshotRef;
|
|
3125
3163
|
}
|
|
3126
|
-
this.httpClient.sendExternalTrace({
|
|
3164
|
+
return this.httpClient.sendExternalTrace({
|
|
3127
3165
|
type: "sdk-function",
|
|
3128
3166
|
source: "typescript-sdk-function",
|
|
3129
3167
|
traceFunctionKey: params.traceFunctionKey,
|
|
@@ -3197,7 +3235,9 @@ var Bitfab = class {
|
|
|
3197
3235
|
*
|
|
3198
3236
|
* @param traceFunctionKey - The trace function key to replay
|
|
3199
3237
|
* @param fn - The function to replay (must be the return value of `withSpan`)
|
|
3200
|
-
* @param options - Optional replay options
|
|
3238
|
+
* @param options - Optional replay options. `limit` and `traceIds` are
|
|
3239
|
+
* mutually exclusive — an explicit ID list already determines how many
|
|
3240
|
+
* traces replay, so passing both throws a BitfabError.
|
|
3201
3241
|
* @returns ReplayResult with items, testRunId, and testRunUrl
|
|
3202
3242
|
*/
|
|
3203
3243
|
async replay(traceFunctionKey, fn, options) {
|
|
@@ -3266,10 +3306,6 @@ var BitfabFunction = class {
|
|
|
3266
3306
|
}
|
|
3267
3307
|
};
|
|
3268
3308
|
|
|
3269
|
-
// src/index.ts
|
|
3270
|
-
init_constants();
|
|
3271
|
-
init_http();
|
|
3272
|
-
|
|
3273
3309
|
// src/node.ts
|
|
3274
3310
|
init_asyncStorage();
|
|
3275
3311
|
assertAsyncStorageRegistered();
|