bitfab 0.53.0 → 0.53.2

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.
@@ -94,7 +94,7 @@ function encodeRequestBody(body) {
94
94
  }
95
95
 
96
96
  // src/version.generated.ts
97
- var __version__ = "0.53.0";
97
+ var __version__ = "0.53.2";
98
98
  var __packageName__ = "bitfab";
99
99
 
100
100
  // src/errors.ts
@@ -415,6 +415,32 @@ function encodePayloadBody(payload) {
415
415
  }
416
416
  }
417
417
 
418
+ // src/spanOrigin.ts
419
+ var FRAMEWORK_INSTRUMENTATIONS = [
420
+ "openai-agents",
421
+ "langgraph",
422
+ "claude-agent-sdk",
423
+ "vercel-ai"
424
+ ];
425
+ var FRAMEWORK_INSTRUMENTATION_SET = new Set(
426
+ FRAMEWORK_INSTRUMENTATIONS
427
+ );
428
+
429
+ // src/unrefTimer.ts
430
+ function unrefTimer(timer) {
431
+ const handle = timer;
432
+ if (typeof handle.unref === "function") {
433
+ handle.unref();
434
+ }
435
+ }
436
+
437
+ // src/simulationPlan.ts
438
+ var SIMULATION_PLAN_READ_TIMEOUT_MS = 5e3;
439
+ var ROOT_TRACE_FUNCTION_KEY_FIELD = "rootTraceFunctionKey";
440
+ function planWaitSliceMs(timeoutMs) {
441
+ return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
442
+ }
443
+
418
444
  // src/traceMetadata.ts
419
445
  var activeRecords = /* @__PURE__ */ new Map();
420
446
  var completedRecords = /* @__PURE__ */ new Map();
@@ -486,14 +512,6 @@ var DeliveryError = class extends Error {
486
512
  }
487
513
  };
488
514
 
489
- // src/unrefTimer.ts
490
- function unrefTimer(timer) {
491
- const handle = timer;
492
- if (typeof handle.unref === "function") {
493
- handle.unref();
494
- }
495
- }
496
-
497
515
  // src/otel.ts
498
516
  var OPERATION_ATTRIBUTE = "bitfab.operation";
499
517
  var PAYLOAD_ATTRIBUTE = "bitfab.payload";
@@ -1116,6 +1134,18 @@ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
1116
1134
  var EXIT_FLUSH_TIMEOUT_MS = 5e3;
1117
1135
  var DEFAULT_LIFECYCLE_TIMEOUT_MS2 = 3e4;
1118
1136
  var pendingTracePromises = /* @__PURE__ */ new Set();
1137
+ var liveHttpClients = /* @__PURE__ */ new Set();
1138
+ async function releaseHeldExternalSpans(timeoutMs) {
1139
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
1140
+ for (const client of [...liveHttpClients]) {
1141
+ await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
1142
+ }
1143
+ }
1144
+ function stopSimulationPlans() {
1145
+ for (const client of [...liveHttpClients]) {
1146
+ client.stopSimulationPlan?.();
1147
+ }
1148
+ }
1119
1149
  function awaitOnExit(promise) {
1120
1150
  pendingTracePromises.add(promise);
1121
1151
  void promise.finally(() => {
@@ -1126,7 +1156,10 @@ function awaitOnExit(promise) {
1126
1156
  }
1127
1157
  async function flushTraces(timeoutMs = 5e3) {
1128
1158
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1129
- const requestsFlushed = await awaitPendingRequests(timeoutMs);
1159
+ await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
1160
+ const requestsFlushed = await awaitPendingRequests(
1161
+ Math.max(0, deadline - Date.now())
1162
+ );
1130
1163
  const transportsFlushed = await flushTraceTransports(
1131
1164
  Math.max(0, deadline - Date.now())
1132
1165
  );
@@ -1163,12 +1196,27 @@ if (typeof process !== "undefined" && process.versions != null && process.versio
1163
1196
  return;
1164
1197
  }
1165
1198
  isFlushing = true;
1166
- void Promise.allSettled([
1167
- ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1168
- })),
1169
- shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1170
- ]).then(() => {
1199
+ const planWaitMs = SIMULATION_PLAN_READ_TIMEOUT_MS;
1200
+ const stopFlushing = () => {
1171
1201
  isFlushing = false;
1202
+ };
1203
+ const deadline = setTimeout(
1204
+ stopFlushing,
1205
+ EXIT_FLUSH_TIMEOUT_MS + planWaitMs
1206
+ );
1207
+ unrefTimer(deadline);
1208
+ void releaseHeldExternalSpans(planWaitMs).catch(() => {
1209
+ }).then(() => {
1210
+ stopSimulationPlans();
1211
+ }).then(
1212
+ () => Promise.allSettled([
1213
+ ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1214
+ })),
1215
+ shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1216
+ ])
1217
+ ).then(() => {
1218
+ clearTimeout(deadline);
1219
+ stopFlushing();
1172
1220
  });
1173
1221
  });
1174
1222
  }
@@ -1262,6 +1310,21 @@ function sourceTraceIdOf(payload) {
1262
1310
  return typeof id === "string" ? id : void 0;
1263
1311
  }
1264
1312
  var carrierSeq = 0;
1313
+ function describeError(error) {
1314
+ return error instanceof Error ? error.message : String(error);
1315
+ }
1316
+ function warnDroppedExternalSpan(error) {
1317
+ warnOnce(
1318
+ "external-span-dropped",
1319
+ `a span was dropped because the send step failed: ${describeError(error)}`
1320
+ );
1321
+ }
1322
+ function warnDroppedExternalTrace(error) {
1323
+ warnOnce(
1324
+ "external-trace-dropped",
1325
+ `a trace was dropped because the send step failed: ${describeError(error)}`
1326
+ );
1327
+ }
1265
1328
  var HttpClient = class {
1266
1329
  constructor(config) {
1267
1330
  // Only traces a caller asked about are tracked, so ordinary tracing stores
@@ -1276,6 +1339,7 @@ var HttpClient = class {
1276
1339
  this.apiKey = config.apiKey;
1277
1340
  this.serviceUrl = config.serviceUrl;
1278
1341
  this.timeout = config.timeout ?? 12e4;
1342
+ liveHttpClients.add(this);
1279
1343
  }
1280
1344
  /**
1281
1345
  * Resolve the API key at the moment it is needed (request time), invoking
@@ -1473,9 +1537,14 @@ var HttpClient = class {
1473
1537
  * attributing its timeout here would fail a client whose own work succeeded.
1474
1538
  */
1475
1539
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1540
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
1541
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
1476
1542
  await replayContextReady.catch(() => {
1477
1543
  });
1478
- return waitForPromises(Array.from(this.deferredWork), timeoutMs);
1544
+ return waitForPromises(
1545
+ Array.from(this.deferredWork),
1546
+ Math.max(0, deadline - Date.now())
1547
+ );
1479
1548
  }
1480
1549
  /**
1481
1550
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -1483,7 +1552,9 @@ var HttpClient = class {
1483
1552
  */
1484
1553
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1485
1554
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1486
- const settled = await this.settleDeferredWork(timeoutMs);
1555
+ const settled = await this.settleDeferredWork(
1556
+ Math.max(0, deadline - Date.now())
1557
+ );
1487
1558
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1488
1559
  return settled && flushed;
1489
1560
  }
@@ -1498,6 +1569,13 @@ var HttpClient = class {
1498
1569
  }
1499
1570
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1500
1571
  this.closing = (async () => {
1572
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
1573
+ this.stopSimulationPlan?.();
1574
+ this.externalSpanTransform = void 0;
1575
+ this.externalTraceTransform = void 0;
1576
+ this.releaseHeldExternalSpans = void 0;
1577
+ this.stopSimulationPlan = void 0;
1578
+ liveHttpClients.delete(this);
1501
1579
  const settled = await this.settleDeferredWork(
1502
1580
  Math.max(0, deadline - Date.now())
1503
1581
  );
@@ -1609,6 +1687,13 @@ var HttpClient = class {
1609
1687
  protocol
1610
1688
  });
1611
1689
  }
1690
+ async getSimulationPlan() {
1691
+ return this.get(
1692
+ "/api/sdk/sim-plan",
1693
+ SIMULATION_PLAN_READ_TIMEOUT_MS,
1694
+ { Connection: "close" }
1695
+ );
1696
+ }
1612
1697
  async getTraceSpan(traceId, lookup) {
1613
1698
  const searchParams = new URLSearchParams();
1614
1699
  if (lookup.id !== void 0) {
@@ -1625,14 +1710,18 @@ var HttpClient = class {
1625
1710
  * GET a JSON endpoint on the service with the client's API key. Throws a
1626
1711
  * `BitfabError` carrying the status text for any non-2xx response.
1627
1712
  */
1628
- async get(endpoint) {
1713
+ async get(endpoint, timeoutMs, extraHeaders) {
1629
1714
  const url = `${this.serviceUrl}${endpoint}`;
1715
+ const timeout = timeoutMs ?? this.timeout;
1630
1716
  const controller = new AbortController();
1631
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
1717
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1632
1718
  try {
1633
1719
  const response = await fetch(url, {
1634
1720
  method: "GET",
1635
- headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
1721
+ headers: {
1722
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`,
1723
+ ...extraHeaders
1724
+ },
1636
1725
  signal: controller.signal
1637
1726
  });
1638
1727
  if (!response.ok) {
@@ -1651,7 +1740,7 @@ var HttpClient = class {
1651
1740
  }
1652
1741
  if (error instanceof Error) {
1653
1742
  if (error.name === "AbortError") {
1654
- throw new BitfabError(`Request timed out after ${this.timeout}ms`);
1743
+ throw new BitfabError(`Request timed out after ${timeout}ms`);
1655
1744
  }
1656
1745
  throw new BitfabError(error.message);
1657
1746
  }
@@ -1678,16 +1767,27 @@ var HttpClient = class {
1678
1767
  carrierMeta("internal_trace", body, void 0)
1679
1768
  );
1680
1769
  }
1681
- /**
1682
- * Queue an external span (from withSpan wrapper or OpenAI tracing) onto this
1683
- * client's batching transport. Fire-and-forget: the transport owns delivery,
1684
- * so callers await `flushTraces()` or `close()` rather than a per-span
1685
- * promise.
1686
- */
1687
1770
  sendExternalSpan(payload) {
1771
+ const gate = this.externalSpanTransform;
1772
+ if (gate === void 0) {
1773
+ this.submitExternalSpan(payload);
1774
+ return;
1775
+ }
1776
+ try {
1777
+ gate(payload, (ready) => this.submitExternalSpan(ready));
1778
+ } catch (error) {
1779
+ warnDroppedExternalSpan(error);
1780
+ }
1781
+ }
1782
+ submitExternalSpan(payload) {
1783
+ const body = {
1784
+ ...payload,
1785
+ sdkVersion: __version__
1786
+ };
1787
+ delete body[ROOT_TRACE_FUNCTION_KEY_FIELD];
1688
1788
  this.getTraceTransport()?.submit(
1689
1789
  "external_span",
1690
- { ...payload, sdkVersion: __version__ },
1790
+ body,
1691
1791
  this.recordedMeta("external_span", payload, carrierRef(payload))
1692
1792
  );
1693
1793
  }
@@ -1699,6 +1799,18 @@ var HttpClient = class {
1699
1799
  */
1700
1800
  sendExternalTrace(rawPayload) {
1701
1801
  const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
1802
+ const gate = this.externalTraceTransform;
1803
+ if (gate === void 0) {
1804
+ this.submitExternalTrace(payload);
1805
+ return;
1806
+ }
1807
+ try {
1808
+ gate(payload, (ready) => this.submitExternalTrace(ready));
1809
+ } catch (error) {
1810
+ warnDroppedExternalTrace(error);
1811
+ }
1812
+ }
1813
+ submitExternalTrace(payload) {
1702
1814
  this.getTraceTransport()?.submit(
1703
1815
  "external_trace",
1704
1816
  {
@@ -1943,4 +2055,4 @@ export {
1943
2055
  parseRetryAfterMs,
1944
2056
  HttpClient
1945
2057
  };
1946
- //# sourceMappingURL=chunk-CBRO6BP7.js.map
2058
+ //# sourceMappingURL=chunk-S4UK5HNW.js.map