@bitfab/sdk 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
@@ -11,7 +11,7 @@ import {
11
11
  getCurrentReplayBranch,
12
12
  getCurrentSpan,
13
13
  getCurrentTrace
14
- } from "./chunk-BXWUPEC4.js";
14
+ } from "./chunk-EDOZ3DP3.js";
15
15
  import {
16
16
  BITFAB_PROGRESS_PREFIX,
17
17
  BitfabError,
@@ -23,7 +23,7 @@ import {
23
23
  flushTraces,
24
24
  reportReplayProgress,
25
25
  serializeReplayResult
26
- } from "./chunk-ENOQ2K2H.js";
26
+ } from "./chunk-CFECTUDU.js";
27
27
  export {
28
28
  BITFAB_PROGRESS_PREFIX,
29
29
  Bitfab,
package/dist/node.cjs CHANGED
@@ -88,7 +88,7 @@ var __version__;
88
88
  var init_version_generated = __esm({
89
89
  "src/version.generated.ts"() {
90
90
  "use strict";
91
- __version__ = "0.36.6";
91
+ __version__ = "0.36.7";
92
92
  }
93
93
  });
94
94
 
@@ -122,33 +122,58 @@ function toArrayBuffer(view) {
122
122
  view.byteOffset + view.byteLength
123
123
  );
124
124
  }
125
+ function compressedRequest(body, rawBytes, compressed) {
126
+ if (compressed.byteLength >= rawBytes) {
127
+ return { body, rawBytes, wireBytes: rawBytes };
128
+ }
129
+ return {
130
+ body: compressed instanceof Uint8Array ? toArrayBuffer(compressed) : compressed,
131
+ contentEncoding: "gzip",
132
+ rawBytes,
133
+ wireBytes: compressed.byteLength
134
+ };
135
+ }
125
136
  async function gzipViaStream(bytes) {
126
137
  const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
127
138
  return await new Response(stream).arrayBuffer();
128
139
  }
129
140
  function encodeRequestBody(body) {
130
141
  if (readEnv(DISABLE_COMPRESSION_ENV)) {
131
- return { body };
142
+ const rawBytes = new TextEncoder().encode(body).byteLength;
143
+ return { body, rawBytes, wireBytes: rawBytes };
132
144
  }
133
145
  const bytes = new TextEncoder().encode(body);
134
146
  if (bytes.byteLength < MIN_COMPRESSED_BYTES) {
135
- return { body };
147
+ return {
148
+ body,
149
+ rawBytes: bytes.byteLength,
150
+ wireBytes: bytes.byteLength
151
+ };
136
152
  }
137
153
  if (gzipNode) {
138
154
  return gzipNode(bytes).then(
139
- (compressed) => ({
140
- body: toArrayBuffer(compressed),
141
- contentEncoding: "gzip"
142
- }),
143
- () => ({ body })
155
+ (compressed) => compressedRequest(body, bytes.byteLength, compressed),
156
+ () => ({
157
+ body,
158
+ rawBytes: bytes.byteLength,
159
+ wireBytes: bytes.byteLength
160
+ })
144
161
  );
145
162
  }
146
163
  if (typeof CompressionStream === "undefined") {
147
- return { body };
164
+ return {
165
+ body,
166
+ rawBytes: bytes.byteLength,
167
+ wireBytes: bytes.byteLength
168
+ };
148
169
  }
149
170
  return gzipViaStream(bytes).then(
150
- (compressed) => ({ body: compressed, contentEncoding: "gzip" }),
151
- () => ({ body })
171
+ (compressed) => compressedRequest(body, bytes.byteLength, compressed),
172
+ () => ({
173
+ body,
174
+ rawBytes: bytes.byteLength,
175
+ wireBytes: bytes.byteLength
176
+ })
152
177
  );
153
178
  }
154
179
  var DISABLE_COMPRESSION_ENV, MIN_COMPRESSED_BYTES, gzipNode, _nodeGzipReady;
@@ -254,15 +279,15 @@ function carrierBytesOf(encoded, body) {
254
279
  }
255
280
  return encoded.length + extra;
256
281
  }
257
- function fitsCarrierBudget(body) {
282
+ function fitsCarrierBudget(body, maxBytes = MAX_SPAN_CARRIER_BYTES) {
258
283
  const units = body.length;
259
- if (units * MAX_BYTES_PER_UNIT + 2 <= MAX_SPAN_CARRIER_BYTES) {
284
+ if (units * MAX_BYTES_PER_UNIT + 2 <= maxBytes) {
260
285
  return true;
261
286
  }
262
- if (units + 2 > MAX_SPAN_CARRIER_BYTES) {
287
+ if (units + 2 > maxBytes) {
263
288
  return false;
264
289
  }
265
- return carrierByteLength(body) <= MAX_SPAN_CARRIER_BYTES;
290
+ return carrierByteLength(body) <= maxBytes;
266
291
  }
267
292
  function asRecord(value) {
268
293
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
@@ -306,7 +331,7 @@ function collectCandidates(containers) {
306
331
  }
307
332
  return candidates.sort((a, b) => b.size - a.size);
308
333
  }
309
- function trimPayloadToBudget(payload, encode) {
334
+ function trimPayloadToBudget(payload, encode, maxBytes = MAX_SPAN_CARRIER_BYTES) {
310
335
  const { copy, containers } = cloneTrimmable(payload);
311
336
  const candidates = collectCandidates(containers);
312
337
  if (candidates.length === 0) {
@@ -322,30 +347,31 @@ function trimPayloadToBudget(payload, encode) {
322
347
  } catch {
323
348
  return void 0;
324
349
  }
325
- if (fitsCarrierBudget(body)) {
350
+ if (fitsCarrierBudget(body, maxBytes)) {
326
351
  return { value: copy, trimmed };
327
352
  }
328
353
  }
329
354
  return void 0;
330
355
  }
331
- function markPayloadTrimmed(value, trimmed) {
356
+ function markPayloadTrimmed(value, trimmed, maxBytes = MAX_SPAN_CARRIER_BYTES) {
332
357
  const existing = Array.isArray(value.errors) ? value.errors : [];
333
358
  value.errors = [
334
359
  ...existing,
335
360
  {
336
361
  source: "sdk",
337
362
  step: "payload_budget",
338
- error: `trimmed oversized field(s) to fit the ${MAX_SPAN_CARRIER_BYTES}-byte span carrier budget: ${[
363
+ error: `trimmed oversized field(s) to fit the ${maxBytes}-byte span carrier budget: ${[
339
364
  ...new Set(trimmed)
340
365
  ].join(", ")}`
341
366
  }
342
367
  ];
343
368
  }
344
- var MAX_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
369
+ var MAX_SPAN_CARRIER_BYTES, MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
345
370
  var init_payloadBudget = __esm({
346
371
  "src/payloadBudget.ts"() {
347
372
  "use strict";
348
373
  MAX_SPAN_CARRIER_BYTES = 28e5;
374
+ MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES = 78e5;
349
375
  textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
350
376
  MAX_BYTES_PER_UNIT = 3;
351
377
  STRUCTURAL_SPAN_KEYS = /* @__PURE__ */ new Set([
@@ -377,30 +403,31 @@ var init_warnOnce = __esm({
377
403
  });
378
404
 
379
405
  // src/serializePayload.ts
380
- function serializePayloadBody(payload) {
406
+ function serializePayloadBody(payload, maxCarrierBytes = MAX_SPAN_CARRIER_BYTES) {
381
407
  const encoded = encodePayloadBody(payload);
382
- if (fitsCarrierBudget(encoded.body)) {
408
+ if (fitsCarrierBudget(encoded.body, maxCarrierBytes)) {
383
409
  return { body: encoded.body, dropped: encoded.dropped };
384
410
  }
385
- return applyPayloadBudget(encoded);
411
+ return applyPayloadBudget(encoded, maxCarrierBytes);
386
412
  }
387
- function applyPayloadBudget(encoded) {
413
+ function applyPayloadBudget(encoded, maxCarrierBytes) {
388
414
  const result = encoded.value ? trimPayloadToBudget(
389
415
  encoded.value,
390
- (value) => encodePayloadBody(value).body
416
+ (value) => encodePayloadBody(value).body,
417
+ maxCarrierBytes
391
418
  ) : void 0;
392
419
  if (!result) {
393
420
  return { body: encoded.body, dropped: encoded.dropped };
394
421
  }
395
422
  warnOnce(
396
423
  "payload:over-budget",
397
- `a span payload exceeded the ${MAX_SPAN_CARRIER_BYTES}-byte carrier budget; its largest field(s) (${[
424
+ `a span payload exceeded the ${maxCarrierBytes}-byte carrier budget; its largest field(s) (${[
398
425
  ...new Set(result.trimmed)
399
426
  ].join(
400
427
  ", "
401
428
  )}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`
402
429
  );
403
- markPayloadTrimmed(result.value, result.trimmed);
430
+ markPayloadTrimmed(result.value, result.trimmed, maxCarrierBytes);
404
431
  return {
405
432
  body: encodePayloadBody(result.value).body,
406
433
  dropped: encoded.dropped
@@ -657,6 +684,31 @@ function encodeSpan(span) {
657
684
  const json = JSON.stringify(spanToOtlp(span));
658
685
  return { json, size: byteLength(json) };
659
686
  }
687
+ function trimEncodedSpan(span) {
688
+ try {
689
+ const carrier = JSON.parse(span.json);
690
+ const attribute = carrier.attributes?.find(
691
+ (entry) => entry.key === PAYLOAD_ATTRIBUTE
692
+ );
693
+ const payloadBody = attribute?.value?.stringValue;
694
+ if (!attribute?.value || payloadBody === void 0) {
695
+ return void 0;
696
+ }
697
+ const payload = JSON.parse(payloadBody);
698
+ attribute.value.stringValue = serializePayloadBody(
699
+ payload,
700
+ MAX_SPAN_CARRIER_BYTES
701
+ ).body;
702
+ const json = JSON.stringify(carrier);
703
+ return { json, size: byteLength(json) };
704
+ } catch {
705
+ return void 0;
706
+ }
707
+ }
708
+ async function prepareRequest(body) {
709
+ const prepared = encodeRequestBody(body);
710
+ return prepared instanceof Promise ? await prepared : prepared;
711
+ }
660
712
  function requestEnvelope(first) {
661
713
  const scope = first.instrumentationScope;
662
714
  const resource = JSON.stringify({
@@ -793,7 +845,7 @@ function shutdownOtelTransports(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS) {
793
845
  (transport, remaining) => transport.shutdown(remaining)
794
846
  );
795
847
  }
796
- 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;
848
+ 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;
797
849
  var init_otel = __esm({
798
850
  "src/otel.ts"() {
799
851
  "use strict";
@@ -801,6 +853,7 @@ var init_otel = __esm({
801
853
  import_core = require("@opentelemetry/core");
802
854
  import_resources = require("@opentelemetry/resources");
803
855
  import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
856
+ init_compress();
804
857
  init_constants();
805
858
  init_errors();
806
859
  init_payloadBudget();
@@ -812,6 +865,7 @@ var init_otel = __esm({
812
865
  PAYLOAD_ATTRIBUTE = "bitfab.payload";
813
866
  OTLP_TRACES_ENDPOINT = "/api/sdk/otel/v1/traces";
814
867
  MAX_EXPORT_REQUEST_BYTES = 3e6;
868
+ MAX_DECOMPRESSED_REQUEST_BYTES = 8e6;
815
869
  MAX_REQUEST_BYTES_ENV = "BITFAB_OTEL_MAX_REQUEST_BYTES";
816
870
  EXPORT_CONCURRENCY_ENV = "BITFAB_OTEL_EXPORT_CONCURRENCY";
817
871
  MAX_QUEUE_SIZE = 8192;
@@ -894,15 +948,43 @@ var init_otel = __esm({
894
948
  return batches;
895
949
  }
896
950
  async send(envelope, batch) {
897
- if (batch.size > this.maxRequestBytes) {
898
- logError(
899
- "a single OpenTelemetry span exceeded the configured request-size target and could not be exported"
900
- );
901
- return false;
902
- }
903
951
  try {
904
- await this.sendWithRetries(encodeRequest(envelope, batch.spans));
905
- return true;
952
+ let requestSpans = batch.spans;
953
+ let requestRawBytes = batch.size;
954
+ let alreadyTrimmed = false;
955
+ while (true) {
956
+ if (requestRawBytes <= MAX_DECOMPRESSED_REQUEST_BYTES) {
957
+ const prepared = await prepareRequest(
958
+ encodeRequest(envelope, requestSpans)
959
+ );
960
+ if (prepared.wireBytes <= this.maxRequestBytes) {
961
+ await this.sendWithRetries(prepared);
962
+ return true;
963
+ }
964
+ }
965
+ if (batch.spans.length !== 1) {
966
+ logError(
967
+ "an OpenTelemetry span batch exceeded the configured request-size target and could not be exported"
968
+ );
969
+ return false;
970
+ }
971
+ if (alreadyTrimmed) {
972
+ logError(
973
+ "a single OpenTelemetry span exceeded the configured request-size target after trimming"
974
+ );
975
+ return false;
976
+ }
977
+ const trimmed = trimEncodedSpan(batch.spans[0]);
978
+ if (!trimmed) {
979
+ logError(
980
+ "a single OpenTelemetry span exceeded the configured request-size target and could not be trimmed"
981
+ );
982
+ return false;
983
+ }
984
+ requestSpans = [trimmed];
985
+ requestRawBytes = envelope.size + trimmed.size;
986
+ alreadyTrimmed = true;
987
+ }
906
988
  } catch (error) {
907
989
  if (error instanceof OtlpPayloadTooLargeError) {
908
990
  logError(
@@ -930,12 +1012,12 @@ var init_otel = __esm({
930
1012
  * the server does not yet understand. The fix is a client-supplied
931
1013
  * idempotency key that ingestion dedupes on.
932
1014
  */
933
- async sendWithRetries(body) {
1015
+ async sendWithRetries(request) {
934
1016
  for (let attempt = 0; attempt < MAX_SEND_ATTEMPTS; attempt += 1) {
935
1017
  try {
936
1018
  const response = await this.directSender(
937
1019
  OTLP_TRACES_ENDPOINT,
938
- body,
1020
+ request,
939
1021
  EXPORT_TIMEOUT_MILLIS
940
1022
  );
941
1023
  const partialSuccess = asRecord2(response?.partialSuccess);
@@ -1052,7 +1134,10 @@ var init_otel = __esm({
1052
1134
  return;
1053
1135
  }
1054
1136
  try {
1055
- const { body, dropped } = serializePayloadBody(payload);
1137
+ const { body, dropped } = serializePayloadBody(
1138
+ payload,
1139
+ MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES
1140
+ );
1056
1141
  if (dropped.length > 0) {
1057
1142
  warnOnce(
1058
1143
  "otel-carrier-payload-stubbed",
@@ -1241,7 +1326,7 @@ var init_http = __esm({
1241
1326
  }
1242
1327
  if (!this.traceTransport) {
1243
1328
  this.traceTransport = createTraceTransport({
1244
- directSender: (endpoint, body, timeoutMs) => this.sendEncoded(endpoint, body, {
1329
+ directSender: (endpoint, request, timeoutMs) => this.sendPrepared(endpoint, request, {
1245
1330
  timeout: timeoutMs
1246
1331
  })
1247
1332
  });
@@ -1328,13 +1413,16 @@ var init_http = __esm({
1328
1413
  * same data twice.
1329
1414
  */
1330
1415
  async sendEncoded(endpoint, body, options) {
1416
+ const prepared = encodeRequestBody(body);
1417
+ const encoded = prepared instanceof Promise ? await prepared : prepared;
1418
+ return this.sendPrepared(endpoint, encoded, options);
1419
+ }
1420
+ async sendPrepared(endpoint, encoded, options) {
1331
1421
  const url = `${this.serviceUrl}${endpoint}`;
1332
1422
  const timeout = options?.timeout ?? this.timeout;
1333
1423
  const method = options?.method ?? "POST";
1334
1424
  const controller = new AbortController();
1335
1425
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1336
- const prepared = encodeRequestBody(body);
1337
- const encoded = prepared instanceof Promise ? await prepared : prepared;
1338
1426
  const headers = {
1339
1427
  "Content-Type": "application/json",
1340
1428
  Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
@@ -1808,8 +1896,8 @@ var init_serialize = __esm({
1808
1896
  import_superjson = __toESM(require("superjson"), 1);
1809
1897
  init_payloadBudget();
1810
1898
  init_warnOnce();
1811
- MAX_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
1812
- MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
1899
+ MAX_SERIALIZED_BYTES = MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES;
1900
+ MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_COMPRESSIBLE_SPAN_CARRIER_BYTES;
1813
1901
  MAX_SAFE_DEPTH = 6;
1814
1902
  }
1815
1903
  });