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