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