bitfab 0.36.6 → 0.36.7

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/index.d.cts CHANGED
@@ -408,6 +408,7 @@ declare class HttpClient {
408
408
  timeout?: number;
409
409
  method?: "POST" | "PATCH" | "PUT";
410
410
  }): Promise<T>;
411
+ private sendPrepared;
411
412
  /**
412
413
  * Look up a function by name.
413
414
  * Blocks until complete - needed for function execution.
@@ -2508,7 +2509,7 @@ declare class BitfabFunction {
2508
2509
  /**
2509
2510
  * SDK version from package.json (injected at build time)
2510
2511
  */
2511
- declare const __version__ = "0.36.6";
2512
+ declare const __version__ = "0.36.7";
2512
2513
 
2513
2514
  /**
2514
2515
  * Constants for the Bitfab SDK.
package/dist/index.d.ts CHANGED
@@ -408,6 +408,7 @@ declare class HttpClient {
408
408
  timeout?: number;
409
409
  method?: "POST" | "PATCH" | "PUT";
410
410
  }): Promise<T>;
411
+ private sendPrepared;
411
412
  /**
412
413
  * Look up a function by name.
413
414
  * Blocks until complete - needed for function execution.
@@ -2508,7 +2509,7 @@ declare class BitfabFunction {
2508
2509
  /**
2509
2510
  * SDK version from package.json (injected at build time)
2510
2511
  */
2511
- declare const __version__ = "0.36.6";
2512
+ declare const __version__ = "0.36.7";
2512
2513
 
2513
2514
  /**
2514
2515
  * Constants for the Bitfab SDK.
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  getCurrentReplayBranch,
21
21
  getCurrentSpan,
22
22
  getCurrentTrace
23
- } from "./chunk-BXWUPEC4.js";
23
+ } from "./chunk-EDOZ3DP3.js";
24
24
  import {
25
25
  BITFAB_PROGRESS_PREFIX,
26
26
  BitfabError,
@@ -32,7 +32,7 @@ import {
32
32
  flushTraces,
33
33
  reportReplayProgress,
34
34
  serializeReplayResult
35
- } from "./chunk-ENOQ2K2H.js";
35
+ } from "./chunk-CFECTUDU.js";
36
36
  export {
37
37
  BITFAB_PROGRESS_PREFIX,
38
38
  Bitfab,
package/dist/node.cjs CHANGED
@@ -97,7 +97,7 @@ var __version__;
97
97
  var init_version_generated = __esm({
98
98
  "src/version.generated.ts"() {
99
99
  "use strict";
100
- __version__ = "0.36.6";
100
+ __version__ = "0.36.7";
101
101
  }
102
102
  });
103
103
 
@@ -131,33 +131,58 @@ function toArrayBuffer(view) {
131
131
  view.byteOffset + view.byteLength
132
132
  );
133
133
  }
134
+ function compressedRequest(body, rawBytes, compressed) {
135
+ if (compressed.byteLength >= rawBytes) {
136
+ return { body, rawBytes, wireBytes: rawBytes };
137
+ }
138
+ return {
139
+ body: compressed instanceof Uint8Array ? toArrayBuffer(compressed) : compressed,
140
+ contentEncoding: "gzip",
141
+ rawBytes,
142
+ wireBytes: compressed.byteLength
143
+ };
144
+ }
134
145
  async function gzipViaStream(bytes) {
135
146
  const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
136
147
  return await new Response(stream).arrayBuffer();
137
148
  }
138
149
  function encodeRequestBody(body) {
139
150
  if (readEnv(DISABLE_COMPRESSION_ENV)) {
140
- return { body };
151
+ const rawBytes = new TextEncoder().encode(body).byteLength;
152
+ return { body, rawBytes, wireBytes: rawBytes };
141
153
  }
142
154
  const bytes = new TextEncoder().encode(body);
143
155
  if (bytes.byteLength < MIN_COMPRESSED_BYTES) {
144
- return { body };
156
+ return {
157
+ body,
158
+ rawBytes: bytes.byteLength,
159
+ wireBytes: bytes.byteLength
160
+ };
145
161
  }
146
162
  if (gzipNode) {
147
163
  return gzipNode(bytes).then(
148
- (compressed) => ({
149
- body: toArrayBuffer(compressed),
150
- contentEncoding: "gzip"
151
- }),
152
- () => ({ body })
164
+ (compressed) => compressedRequest(body, bytes.byteLength, compressed),
165
+ () => ({
166
+ body,
167
+ rawBytes: bytes.byteLength,
168
+ wireBytes: bytes.byteLength
169
+ })
153
170
  );
154
171
  }
155
172
  if (typeof CompressionStream === "undefined") {
156
- return { body };
173
+ return {
174
+ body,
175
+ rawBytes: bytes.byteLength,
176
+ wireBytes: bytes.byteLength
177
+ };
157
178
  }
158
179
  return gzipViaStream(bytes).then(
159
- (compressed) => ({ body: compressed, contentEncoding: "gzip" }),
160
- () => ({ body })
180
+ (compressed) => compressedRequest(body, bytes.byteLength, compressed),
181
+ () => ({
182
+ body,
183
+ rawBytes: bytes.byteLength,
184
+ wireBytes: bytes.byteLength
185
+ })
161
186
  );
162
187
  }
163
188
  var DISABLE_COMPRESSION_ENV, MIN_COMPRESSED_BYTES, gzipNode, _nodeGzipReady;
@@ -263,15 +288,15 @@ function carrierBytesOf(encoded, body) {
263
288
  }
264
289
  return encoded.length + extra;
265
290
  }
266
- function fitsCarrierBudget(body) {
291
+ function fitsCarrierBudget(body, maxBytes = MAX_SPAN_CARRIER_BYTES) {
267
292
  const units = body.length;
268
- if (units * MAX_BYTES_PER_UNIT + 2 <= MAX_SPAN_CARRIER_BYTES) {
293
+ if (units * MAX_BYTES_PER_UNIT + 2 <= maxBytes) {
269
294
  return true;
270
295
  }
271
- if (units + 2 > MAX_SPAN_CARRIER_BYTES) {
296
+ if (units + 2 > maxBytes) {
272
297
  return false;
273
298
  }
274
- return carrierByteLength(body) <= MAX_SPAN_CARRIER_BYTES;
299
+ return carrierByteLength(body) <= maxBytes;
275
300
  }
276
301
  function asRecord(value) {
277
302
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
@@ -315,7 +340,7 @@ function collectCandidates(containers) {
315
340
  }
316
341
  return candidates.sort((a, b) => b.size - a.size);
317
342
  }
318
- function trimPayloadToBudget(payload, encode) {
343
+ function trimPayloadToBudget(payload, encode, maxBytes = MAX_SPAN_CARRIER_BYTES) {
319
344
  const { copy, containers } = cloneTrimmable(payload);
320
345
  const candidates = collectCandidates(containers);
321
346
  if (candidates.length === 0) {
@@ -331,30 +356,31 @@ function trimPayloadToBudget(payload, encode) {
331
356
  } catch {
332
357
  return void 0;
333
358
  }
334
- if (fitsCarrierBudget(body)) {
359
+ if (fitsCarrierBudget(body, maxBytes)) {
335
360
  return { value: copy, trimmed };
336
361
  }
337
362
  }
338
363
  return void 0;
339
364
  }
340
- function markPayloadTrimmed(value, trimmed) {
365
+ function markPayloadTrimmed(value, trimmed, maxBytes = MAX_SPAN_CARRIER_BYTES) {
341
366
  const existing = Array.isArray(value.errors) ? value.errors : [];
342
367
  value.errors = [
343
368
  ...existing,
344
369
  {
345
370
  source: "sdk",
346
371
  step: "payload_budget",
347
- error: `trimmed oversized field(s) to fit the ${MAX_SPAN_CARRIER_BYTES}-byte span carrier budget: ${[
372
+ error: `trimmed oversized field(s) to fit the ${maxBytes}-byte span carrier budget: ${[
348
373
  ...new Set(trimmed)
349
374
  ].join(", ")}`
350
375
  }
351
376
  ];
352
377
  }
353
- var MAX_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
378
+ var MAX_SPAN_CARRIER_BYTES, MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
354
379
  var init_payloadBudget = __esm({
355
380
  "src/payloadBudget.ts"() {
356
381
  "use strict";
357
382
  MAX_SPAN_CARRIER_BYTES = 28e5;
383
+ MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES = 78e5;
358
384
  textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
359
385
  MAX_BYTES_PER_UNIT = 3;
360
386
  STRUCTURAL_SPAN_KEYS = /* @__PURE__ */ new Set([
@@ -386,30 +412,31 @@ var init_warnOnce = __esm({
386
412
  });
387
413
 
388
414
  // src/serializePayload.ts
389
- function serializePayloadBody(payload) {
415
+ function serializePayloadBody(payload, maxCarrierBytes = MAX_SPAN_CARRIER_BYTES) {
390
416
  const encoded = encodePayloadBody(payload);
391
- if (fitsCarrierBudget(encoded.body)) {
417
+ if (fitsCarrierBudget(encoded.body, maxCarrierBytes)) {
392
418
  return { body: encoded.body, dropped: encoded.dropped };
393
419
  }
394
- return applyPayloadBudget(encoded);
420
+ return applyPayloadBudget(encoded, maxCarrierBytes);
395
421
  }
396
- function applyPayloadBudget(encoded) {
422
+ function applyPayloadBudget(encoded, maxCarrierBytes) {
397
423
  const result = encoded.value ? trimPayloadToBudget(
398
424
  encoded.value,
399
- (value) => encodePayloadBody(value).body
425
+ (value) => encodePayloadBody(value).body,
426
+ maxCarrierBytes
400
427
  ) : void 0;
401
428
  if (!result) {
402
429
  return { body: encoded.body, dropped: encoded.dropped };
403
430
  }
404
431
  warnOnce(
405
432
  "payload:over-budget",
406
- `a span payload exceeded the ${MAX_SPAN_CARRIER_BYTES}-byte carrier budget; its largest field(s) (${[
433
+ `a span payload exceeded the ${maxCarrierBytes}-byte carrier budget; its largest field(s) (${[
407
434
  ...new Set(result.trimmed)
408
435
  ].join(
409
436
  ", "
410
437
  )}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`
411
438
  );
412
- markPayloadTrimmed(result.value, result.trimmed);
439
+ markPayloadTrimmed(result.value, result.trimmed, maxCarrierBytes);
413
440
  return {
414
441
  body: encodePayloadBody(result.value).body,
415
442
  dropped: encoded.dropped
@@ -666,6 +693,31 @@ function encodeSpan(span) {
666
693
  const json = JSON.stringify(spanToOtlp(span));
667
694
  return { json, size: byteLength(json) };
668
695
  }
696
+ function trimEncodedSpan(span) {
697
+ try {
698
+ const carrier = JSON.parse(span.json);
699
+ const attribute = carrier.attributes?.find(
700
+ (entry) => entry.key === PAYLOAD_ATTRIBUTE
701
+ );
702
+ const payloadBody = attribute?.value?.stringValue;
703
+ if (!attribute?.value || payloadBody === void 0) {
704
+ return void 0;
705
+ }
706
+ const payload = JSON.parse(payloadBody);
707
+ attribute.value.stringValue = serializePayloadBody(
708
+ payload,
709
+ MAX_SPAN_CARRIER_BYTES
710
+ ).body;
711
+ const json = JSON.stringify(carrier);
712
+ return { json, size: byteLength(json) };
713
+ } catch {
714
+ return void 0;
715
+ }
716
+ }
717
+ async function prepareRequest(body) {
718
+ const prepared = encodeRequestBody(body);
719
+ return prepared instanceof Promise ? await prepared : prepared;
720
+ }
669
721
  function requestEnvelope(first) {
670
722
  const scope = first.instrumentationScope;
671
723
  const resource = JSON.stringify({
@@ -802,7 +854,7 @@ function shutdownOtelTransports(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS) {
802
854
  (transport, remaining) => transport.shutdown(remaining)
803
855
  );
804
856
  }
805
- var import_api, import_core, import_resources, import_sdk_trace_base, OPERATION_ATTRIBUTE, PAYLOAD_ATTRIBUTE, OTLP_TRACES_ENDPOINT, MAX_EXPORT_REQUEST_BYTES, MAX_REQUEST_BYTES_ENV, EXPORT_CONCURRENCY_ENV, MAX_QUEUE_SIZE, DIRECT_MAX_EXPORT_BATCH_SIZE, DIRECT_MAX_REQUEST_BATCH_SIZE, DEFAULT_EXPORT_CONCURRENCY, MAX_EXPORT_CONCURRENCY, SCHEDULE_DELAY_MILLIS, EXPORT_TIMEOUT_MILLIS, RETRY_DELAY_MILLIS, MAX_SEND_ATTEMPTS, DEFAULT_LIFECYCLE_TIMEOUT_MS, RETRYABLE_STATUSES, liveTransports, traceSubmissionSpanIds, replayTraceSubmissions, submissionCounter, SPAN_SEPARATOR_BYTES, OtlpPayloadTooLargeError, OtlpPartialSuccessError, BitfabSpanExporter, DeliveryTrackingExporter, OtelBatchTransport;
857
+ var import_api, import_core, import_resources, import_sdk_trace_base, OPERATION_ATTRIBUTE, PAYLOAD_ATTRIBUTE, OTLP_TRACES_ENDPOINT, MAX_EXPORT_REQUEST_BYTES, MAX_DECOMPRESSED_REQUEST_BYTES, MAX_REQUEST_BYTES_ENV, EXPORT_CONCURRENCY_ENV, MAX_QUEUE_SIZE, DIRECT_MAX_EXPORT_BATCH_SIZE, DIRECT_MAX_REQUEST_BATCH_SIZE, DEFAULT_EXPORT_CONCURRENCY, MAX_EXPORT_CONCURRENCY, SCHEDULE_DELAY_MILLIS, EXPORT_TIMEOUT_MILLIS, RETRY_DELAY_MILLIS, MAX_SEND_ATTEMPTS, DEFAULT_LIFECYCLE_TIMEOUT_MS, RETRYABLE_STATUSES, liveTransports, traceSubmissionSpanIds, replayTraceSubmissions, submissionCounter, SPAN_SEPARATOR_BYTES, OtlpPayloadTooLargeError, OtlpPartialSuccessError, BitfabSpanExporter, DeliveryTrackingExporter, OtelBatchTransport;
806
858
  var init_otel = __esm({
807
859
  "src/otel.ts"() {
808
860
  "use strict";
@@ -810,6 +862,7 @@ var init_otel = __esm({
810
862
  import_core = require("@opentelemetry/core");
811
863
  import_resources = require("@opentelemetry/resources");
812
864
  import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
865
+ init_compress();
813
866
  init_constants();
814
867
  init_errors();
815
868
  init_payloadBudget();
@@ -821,6 +874,7 @@ var init_otel = __esm({
821
874
  PAYLOAD_ATTRIBUTE = "bitfab.payload";
822
875
  OTLP_TRACES_ENDPOINT = "/api/sdk/otel/v1/traces";
823
876
  MAX_EXPORT_REQUEST_BYTES = 3e6;
877
+ MAX_DECOMPRESSED_REQUEST_BYTES = 8e6;
824
878
  MAX_REQUEST_BYTES_ENV = "BITFAB_OTEL_MAX_REQUEST_BYTES";
825
879
  EXPORT_CONCURRENCY_ENV = "BITFAB_OTEL_EXPORT_CONCURRENCY";
826
880
  MAX_QUEUE_SIZE = 8192;
@@ -903,15 +957,43 @@ var init_otel = __esm({
903
957
  return batches;
904
958
  }
905
959
  async send(envelope, batch) {
906
- if (batch.size > this.maxRequestBytes) {
907
- logError(
908
- "a single OpenTelemetry span exceeded the configured request-size target and could not be exported"
909
- );
910
- return false;
911
- }
912
960
  try {
913
- await this.sendWithRetries(encodeRequest(envelope, batch.spans));
914
- return true;
961
+ let requestSpans = batch.spans;
962
+ let requestRawBytes = batch.size;
963
+ let alreadyTrimmed = false;
964
+ while (true) {
965
+ if (requestRawBytes <= MAX_DECOMPRESSED_REQUEST_BYTES) {
966
+ const prepared = await prepareRequest(
967
+ encodeRequest(envelope, requestSpans)
968
+ );
969
+ if (prepared.wireBytes <= this.maxRequestBytes) {
970
+ await this.sendWithRetries(prepared);
971
+ return true;
972
+ }
973
+ }
974
+ if (batch.spans.length !== 1) {
975
+ logError(
976
+ "an OpenTelemetry span batch exceeded the configured request-size target and could not be exported"
977
+ );
978
+ return false;
979
+ }
980
+ if (alreadyTrimmed) {
981
+ logError(
982
+ "a single OpenTelemetry span exceeded the configured request-size target after trimming"
983
+ );
984
+ return false;
985
+ }
986
+ const trimmed = trimEncodedSpan(batch.spans[0]);
987
+ if (!trimmed) {
988
+ logError(
989
+ "a single OpenTelemetry span exceeded the configured request-size target and could not be trimmed"
990
+ );
991
+ return false;
992
+ }
993
+ requestSpans = [trimmed];
994
+ requestRawBytes = envelope.size + trimmed.size;
995
+ alreadyTrimmed = true;
996
+ }
915
997
  } catch (error) {
916
998
  if (error instanceof OtlpPayloadTooLargeError) {
917
999
  logError(
@@ -939,12 +1021,12 @@ var init_otel = __esm({
939
1021
  * the server does not yet understand. The fix is a client-supplied
940
1022
  * idempotency key that ingestion dedupes on.
941
1023
  */
942
- async sendWithRetries(body) {
1024
+ async sendWithRetries(request) {
943
1025
  for (let attempt = 0; attempt < MAX_SEND_ATTEMPTS; attempt += 1) {
944
1026
  try {
945
1027
  const response = await this.directSender(
946
1028
  OTLP_TRACES_ENDPOINT,
947
- body,
1029
+ request,
948
1030
  EXPORT_TIMEOUT_MILLIS
949
1031
  );
950
1032
  const partialSuccess = asRecord2(response?.partialSuccess);
@@ -1061,7 +1143,10 @@ var init_otel = __esm({
1061
1143
  return;
1062
1144
  }
1063
1145
  try {
1064
- const { body, dropped } = serializePayloadBody(payload);
1146
+ const { body, dropped } = serializePayloadBody(
1147
+ payload,
1148
+ MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES
1149
+ );
1065
1150
  if (dropped.length > 0) {
1066
1151
  warnOnce(
1067
1152
  "otel-carrier-payload-stubbed",
@@ -1250,7 +1335,7 @@ var init_http = __esm({
1250
1335
  }
1251
1336
  if (!this.traceTransport) {
1252
1337
  this.traceTransport = createTraceTransport({
1253
- directSender: (endpoint, body, timeoutMs) => this.sendEncoded(endpoint, body, {
1338
+ directSender: (endpoint, request, timeoutMs) => this.sendPrepared(endpoint, request, {
1254
1339
  timeout: timeoutMs
1255
1340
  })
1256
1341
  });
@@ -1337,13 +1422,16 @@ var init_http = __esm({
1337
1422
  * same data twice.
1338
1423
  */
1339
1424
  async sendEncoded(endpoint, body, options) {
1425
+ const prepared = encodeRequestBody(body);
1426
+ const encoded = prepared instanceof Promise ? await prepared : prepared;
1427
+ return this.sendPrepared(endpoint, encoded, options);
1428
+ }
1429
+ async sendPrepared(endpoint, encoded, options) {
1340
1430
  const url = `${this.serviceUrl}${endpoint}`;
1341
1431
  const timeout = options?.timeout ?? this.timeout;
1342
1432
  const method = options?.method ?? "POST";
1343
1433
  const controller = new AbortController();
1344
1434
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1345
- const prepared = encodeRequestBody(body);
1346
- const encoded = prepared instanceof Promise ? await prepared : prepared;
1347
1435
  const headers = {
1348
1436
  "Content-Type": "application/json",
1349
1437
  Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
@@ -1817,8 +1905,8 @@ var init_serialize = __esm({
1817
1905
  import_superjson = __toESM(require("superjson"), 1);
1818
1906
  init_payloadBudget();
1819
1907
  init_warnOnce();
1820
- MAX_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
1821
- MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
1908
+ MAX_SERIALIZED_BYTES = MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES;
1909
+ MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES;
1822
1910
  MAX_SAFE_DEPTH = 6;
1823
1911
  }
1824
1912
  });