@bitfab/sdk 0.34.2 → 0.36.1

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.
@@ -207,8 +207,78 @@ var BitfabError = class extends Error {
207
207
  }
208
208
  };
209
209
 
210
+ // src/readEnv.ts
211
+ function readEnv(name) {
212
+ if (typeof process !== "undefined" && process.env) {
213
+ return process.env[name];
214
+ }
215
+ return void 0;
216
+ }
217
+
218
+ // src/compress.ts
219
+ var DISABLE_COMPRESSION_ENV = "BITFAB_DISABLE_COMPRESSION";
220
+ var MIN_COMPRESSED_BYTES = 8192;
221
+ var gzipNode;
222
+ var _nodeGzipReady = (typeof process !== "undefined" && process.versions?.node ? (
223
+ // The join trick hides "node:zlib" from static analysis so bundlers that
224
+ // ban Node.js built-ins don't fail at build time. webpackIgnore tells
225
+ // webpack/turbopack to emit a native import() so Node.js can resolve the
226
+ // module at runtime. Same pattern as `asyncStorage.ts`.
227
+ import(
228
+ /* webpackIgnore: true */
229
+ ["node", "zlib"].join(":")
230
+ ).then(({ gzip }) => {
231
+ gzipNode = (data) => new Promise((resolve, reject) => {
232
+ gzip(data, (error, result) => {
233
+ if (error) {
234
+ reject(error);
235
+ } else {
236
+ resolve(result);
237
+ }
238
+ });
239
+ });
240
+ }).catch(() => {
241
+ })
242
+ ) : Promise.resolve()).then(() => {
243
+ });
244
+ function toArrayBuffer(view) {
245
+ return view.buffer.slice(
246
+ view.byteOffset,
247
+ view.byteOffset + view.byteLength
248
+ );
249
+ }
250
+ async function gzipViaStream(bytes) {
251
+ const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
252
+ return await new Response(stream).arrayBuffer();
253
+ }
254
+ function encodeRequestBody(body) {
255
+ if (readEnv(DISABLE_COMPRESSION_ENV)) {
256
+ return { body };
257
+ }
258
+ const bytes = new TextEncoder().encode(body);
259
+ if (bytes.byteLength < MIN_COMPRESSED_BYTES) {
260
+ return { body };
261
+ }
262
+ if (gzipNode) {
263
+ return gzipNode(bytes).then(
264
+ (compressed) => ({
265
+ body: toArrayBuffer(compressed),
266
+ contentEncoding: "gzip"
267
+ }),
268
+ () => ({ body })
269
+ );
270
+ }
271
+ if (typeof CompressionStream === "undefined") {
272
+ return { body };
273
+ }
274
+ return gzipViaStream(bytes).then(
275
+ (compressed) => ({ body: compressed, contentEncoding: "gzip" }),
276
+ () => ({ body })
277
+ );
278
+ }
279
+
210
280
  // src/version.generated.ts
211
- var __version__ = "0.34.2";
281
+ var __version__ = "0.36.1";
212
282
 
213
283
  // src/constants.ts
214
284
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -279,6 +349,125 @@ function runWithReplayContext(ctx, fn) {
279
349
  return fn();
280
350
  }
281
351
 
352
+ // src/payloadBudget.ts
353
+ var MAX_SPAN_CARRIER_BYTES = 28e5;
354
+ var textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
355
+ function byteLength(value) {
356
+ return textEncoder ? textEncoder.encode(value).length : value.length;
357
+ }
358
+ function carrierByteLength(body) {
359
+ return carrierBytesOf(textEncoder ? textEncoder.encode(body) : null, body);
360
+ }
361
+ function carrierBytesOf(encoded, body) {
362
+ if (!encoded) {
363
+ return body.length + 2;
364
+ }
365
+ let extra = 2;
366
+ for (let i = 0; i < encoded.length; i++) {
367
+ const byte = encoded[i];
368
+ if (byte === 34 || byte === 92) {
369
+ extra += 1;
370
+ } else if (byte < 32) {
371
+ extra += byte === 8 || byte === 9 || byte === 10 || byte === 12 || byte === 13 ? 1 : 5;
372
+ }
373
+ }
374
+ return encoded.length + extra;
375
+ }
376
+ var MAX_BYTES_PER_UNIT = 3;
377
+ function fitsCarrierBudget(body) {
378
+ const units = body.length;
379
+ if (units * MAX_BYTES_PER_UNIT + 2 <= MAX_SPAN_CARRIER_BYTES) {
380
+ return true;
381
+ }
382
+ if (units + 2 > MAX_SPAN_CARRIER_BYTES) {
383
+ return false;
384
+ }
385
+ return carrierByteLength(body) <= MAX_SPAN_CARRIER_BYTES;
386
+ }
387
+ var STRUCTURAL_SPAN_KEYS = /* @__PURE__ */ new Set([
388
+ "name",
389
+ "type",
390
+ "function_name",
391
+ "error_source"
392
+ ]);
393
+ function asRecord(value) {
394
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
395
+ }
396
+ function cloneTrimmable(payload) {
397
+ const copy = { ...payload };
398
+ const containers = [];
399
+ const spanData = asRecord(copy.span_data);
400
+ if (spanData) {
401
+ const clone = { ...spanData };
402
+ copy.span_data = clone;
403
+ containers.push(clone);
404
+ }
405
+ const rawSpan = asRecord(copy.rawSpan);
406
+ const rawSpanData = rawSpan && asRecord(rawSpan.span_data);
407
+ if (rawSpan && rawSpanData) {
408
+ const clone = { ...rawSpanData };
409
+ copy.rawSpan = { ...rawSpan, span_data: clone };
410
+ containers.push(clone);
411
+ }
412
+ if (containers.length === 0) {
413
+ containers.push(copy);
414
+ }
415
+ return { copy, containers };
416
+ }
417
+ function collectCandidates(containers) {
418
+ const candidates = [];
419
+ for (const container of containers) {
420
+ for (const [key, value] of Object.entries(container)) {
421
+ if (STRUCTURAL_SPAN_KEYS.has(key) || value == null) {
422
+ continue;
423
+ }
424
+ let size;
425
+ try {
426
+ size = byteLength(JSON.stringify(value) ?? "");
427
+ } catch {
428
+ continue;
429
+ }
430
+ candidates.push({ container, key, size });
431
+ }
432
+ }
433
+ return candidates.sort((a, b) => b.size - a.size);
434
+ }
435
+ function trimPayloadToBudget(payload, encode) {
436
+ const { copy, containers } = cloneTrimmable(payload);
437
+ const candidates = collectCandidates(containers);
438
+ if (candidates.length === 0) {
439
+ return void 0;
440
+ }
441
+ const trimmed = [];
442
+ for (const candidate of candidates) {
443
+ candidate.container[candidate.key] = `<unserializable: too_large_${candidate.size}_bytes>`;
444
+ trimmed.push(candidate.key);
445
+ let body;
446
+ try {
447
+ body = encode(copy);
448
+ } catch {
449
+ return void 0;
450
+ }
451
+ if (fitsCarrierBudget(body)) {
452
+ return { value: copy, trimmed };
453
+ }
454
+ }
455
+ return void 0;
456
+ }
457
+ function markPayloadTrimmed(value, trimmed) {
458
+ const existing = Array.isArray(value.errors) ? value.errors : [];
459
+ value.errors = [
460
+ ...existing,
461
+ {
462
+ source: "sdk",
463
+ step: "payload_budget",
464
+ error: `trimmed oversized field(s) to fit the ${MAX_SPAN_CARRIER_BYTES}-byte span carrier budget: ${[
465
+ ...new Set(trimmed)
466
+ ].join(", ")}`
467
+ }
468
+ ];
469
+ }
470
+
282
471
  // src/warnOnce.ts
283
472
  var warned = /* @__PURE__ */ new Set();
284
473
  function warnOnce(key, message) {
@@ -294,8 +483,37 @@ function warnOnce(key, message) {
294
483
 
295
484
  // src/serializePayload.ts
296
485
  function serializePayloadBody(payload) {
486
+ const encoded = encodePayloadBody(payload);
487
+ if (fitsCarrierBudget(encoded.body)) {
488
+ return { body: encoded.body, dropped: encoded.dropped };
489
+ }
490
+ return applyPayloadBudget(encoded);
491
+ }
492
+ function applyPayloadBudget(encoded) {
493
+ const result = encoded.value ? trimPayloadToBudget(
494
+ encoded.value,
495
+ (value) => encodePayloadBody(value).body
496
+ ) : void 0;
497
+ if (!result) {
498
+ return { body: encoded.body, dropped: encoded.dropped };
499
+ }
500
+ warnOnce(
501
+ "payload:over-budget",
502
+ `a span payload exceeded the ${MAX_SPAN_CARRIER_BYTES}-byte carrier budget; its largest field(s) (${[
503
+ ...new Set(result.trimmed)
504
+ ].join(
505
+ ", "
506
+ )}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`
507
+ );
508
+ markPayloadTrimmed(result.value, result.trimmed);
509
+ return {
510
+ body: encodePayloadBody(result.value).body,
511
+ dropped: encoded.dropped
512
+ };
513
+ }
514
+ function encodePayloadBody(payload) {
297
515
  try {
298
- return { body: JSON.stringify(payload), dropped: [] };
516
+ return { body: JSON.stringify(payload), dropped: [], value: payload };
299
517
  } catch {
300
518
  const dropped = [];
301
519
  const sanitize = (value, seen) => {
@@ -360,12 +578,11 @@ function serializePayloadBody(payload) {
360
578
  sanitized = sanitize(payload, /* @__PURE__ */ new WeakSet());
361
579
  } catch (error) {
362
580
  const message = error instanceof Error ? error.message : String(error);
363
- return {
364
- body: JSON.stringify({ error: `payload_serialize_failed: ${message}` }),
365
- dropped
366
- };
581
+ const marker = { error: `payload_serialize_failed: ${message}` };
582
+ return { body: JSON.stringify(marker), dropped, value: marker };
367
583
  }
368
- if (dropped.length > 0 && typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized)) {
584
+ const isRecord = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
585
+ if (dropped.length > 0 && isRecord) {
369
586
  const obj = sanitized;
370
587
  const existing = Array.isArray(obj.errors) ? obj.errors : [];
371
588
  obj.errors = [
@@ -379,7 +596,11 @@ function serializePayloadBody(payload) {
379
596
  }
380
597
  ];
381
598
  }
382
- return { body: JSON.stringify(sanitized), dropped };
599
+ return {
600
+ body: JSON.stringify(sanitized),
601
+ dropped,
602
+ value: isRecord ? sanitized : void 0
603
+ };
383
604
  }
384
605
  }
385
606
 
@@ -395,14 +616,6 @@ import {
395
616
  BatchSpanProcessor
396
617
  } from "@opentelemetry/sdk-trace-base";
397
618
 
398
- // src/readEnv.ts
399
- function readEnv(name) {
400
- if (typeof process !== "undefined" && process.env) {
401
- return process.env[name];
402
- }
403
- return void 0;
404
- }
405
-
406
619
  // src/unrefTimer.ts
407
620
  function unrefTimer(timer) {
408
621
  const handle = timer;
@@ -464,7 +677,7 @@ function recordTraceSubmission(operation, payload) {
464
677
  return;
465
678
  }
466
679
  if (operation === "external_span") {
467
- const rawSpan = asRecord(payload.rawSpan);
680
+ const rawSpan = asRecord2(payload.rawSpan);
468
681
  if (typeof rawSpan?.id !== "string") {
469
682
  submissionCounter += 1;
470
683
  }
@@ -501,14 +714,14 @@ function takeReplaySpanCounts(traceIds) {
501
714
  }
502
715
  return counts;
503
716
  }
504
- function asRecord(value) {
717
+ function asRecord2(value) {
505
718
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
506
719
  }
507
720
  function resolveSourceTraceId(payload) {
508
721
  if (typeof payload.sourceTraceId === "string") {
509
722
  return payload.sourceTraceId;
510
723
  }
511
- const rawTrace = asRecord(payload.externalTrace) ?? asRecord(payload.rawTrace);
724
+ const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
512
725
  return typeof rawTrace?.id === "string" ? rawTrace.id : void 0;
513
726
  }
514
727
  function otlpValue(value) {
@@ -566,10 +779,6 @@ function spanToOtlp(span) {
566
779
  }
567
780
  return result;
568
781
  }
569
- var textEncoder = typeof TextEncoder === "undefined" ? void 0 : new TextEncoder();
570
- function byteLength(value) {
571
- return textEncoder ? textEncoder.encode(value).length : value.length;
572
- }
573
782
  var SPAN_SEPARATOR_BYTES = 1;
574
783
  function encodeSpan(span) {
575
784
  const json = JSON.stringify(spanToOtlp(span));
@@ -749,7 +958,7 @@ var BitfabSpanExporter = class {
749
958
  body,
750
959
  EXPORT_TIMEOUT_MILLIS
751
960
  );
752
- const partialSuccess = asRecord(response?.partialSuccess);
961
+ const partialSuccess = asRecord2(response?.partialSuccess);
753
962
  const rejected = partialSuccess?.rejectedSpans;
754
963
  if (rejected !== void 0 && rejected !== "0" && rejected !== 0) {
755
964
  logError(
@@ -924,7 +1133,7 @@ function endSpan(span, endTime) {
924
1133
  }
925
1134
  function spanName(operation, payload) {
926
1135
  if (operation === "external_span") {
927
- const spanData = asRecord(asRecord(payload.rawSpan)?.span_data);
1136
+ const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
928
1137
  if (typeof spanData?.name === "string") {
929
1138
  return spanData.name;
930
1139
  }
@@ -935,8 +1144,8 @@ function spanName(operation, payload) {
935
1144
  return `bitfab.${operation}`;
936
1145
  }
937
1146
  function payloadTimestamp(payload, field) {
938
- const rawSpan = asRecord(payload.rawSpan);
939
- const rawTrace = asRecord(payload.externalTrace) ?? asRecord(payload.rawTrace);
1147
+ const rawSpan = asRecord2(payload.rawSpan);
1148
+ const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
940
1149
  const raw = rawSpan?.[field] ?? rawTrace?.[field];
941
1150
  if (typeof raw !== "string") {
942
1151
  return void 0;
@@ -945,7 +1154,7 @@ function payloadTimestamp(payload, field) {
945
1154
  return Number.isNaN(parsed) ? void 0 : parsed;
946
1155
  }
947
1156
  function hasError(payload) {
948
- const spanData = asRecord(asRecord(payload.rawSpan)?.span_data);
1157
+ const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
949
1158
  if (spanData?.error != null) {
950
1159
  return true;
951
1160
  }
@@ -1194,14 +1403,20 @@ var HttpClient = class {
1194
1403
  const method = options?.method ?? "POST";
1195
1404
  const controller = new AbortController();
1196
1405
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1406
+ const prepared = encodeRequestBody(body);
1407
+ const encoded = prepared instanceof Promise ? await prepared : prepared;
1408
+ const headers = {
1409
+ "Content-Type": "application/json",
1410
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
1411
+ };
1412
+ if (encoded.contentEncoding) {
1413
+ headers["Content-Encoding"] = encoded.contentEncoding;
1414
+ }
1197
1415
  try {
1198
1416
  const response = await fetch(url, {
1199
1417
  method,
1200
- headers: {
1201
- "Content-Type": "application/json",
1202
- Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
1203
- },
1204
- body,
1418
+ headers,
1419
+ body: encoded.body,
1205
1420
  signal: controller.signal
1206
1421
  });
1207
1422
  if (!response.ok) {
@@ -1541,8 +1756,8 @@ function fallbackUuidV4() {
1541
1756
 
1542
1757
  // src/serialize.ts
1543
1758
  import superjson from "superjson";
1544
- var MAX_SERIALIZED_BYTES = 512e3;
1545
- var MAX_FRAMEWORK_SERIALIZED_BYTES = 2e6;
1759
+ var MAX_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
1760
+ var MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
1546
1761
  function describeValue(value) {
1547
1762
  try {
1548
1763
  const ctorName = value?.constructor?.name;
@@ -2180,4 +2395,4 @@ export {
2180
2395
  reportReplayProgress,
2181
2396
  replay
2182
2397
  };
2183
- //# sourceMappingURL=chunk-SBQQOFA5.js.map
2398
+ //# sourceMappingURL=chunk-6ZBZBR5K.js.map