@bitfab/sdk 0.34.1 → 0.36.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/README.md +0 -17
- package/dist/{chunk-5ZMEY5NX.js → chunk-4YTIVUYX.js} +307 -208
- package/dist/chunk-4YTIVUYX.js.map +1 -0
- package/dist/{chunk-KO373TDH.js → chunk-JECWMVJG.js} +3 -3
- package/dist/index.cjs +328 -213
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +2 -2
- package/dist/node.cjs +328 -213
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +2 -2
- package/dist/{replay-YI2BUPCV.js → replay-CVMULH7T.js} +2 -2
- package/package.json +1 -2
- package/dist/chunk-5ZMEY5NX.js.map +0 -1
- /package/dist/{chunk-KO373TDH.js.map → chunk-JECWMVJG.js.map} +0 -0
- /package/dist/{replay-YI2BUPCV.js.map → replay-CVMULH7T.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -42,7 +42,7 @@ var __version__;
|
|
|
42
42
|
var init_version_generated = __esm({
|
|
43
43
|
"src/version.generated.ts"() {
|
|
44
44
|
"use strict";
|
|
45
|
-
__version__ = "0.
|
|
45
|
+
__version__ = "0.36.0";
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
@@ -56,6 +56,87 @@ var init_constants = __esm({
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
// src/readEnv.ts
|
|
60
|
+
function readEnv(name) {
|
|
61
|
+
if (typeof process !== "undefined" && process.env) {
|
|
62
|
+
return process.env[name];
|
|
63
|
+
}
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
var init_readEnv = __esm({
|
|
67
|
+
"src/readEnv.ts"() {
|
|
68
|
+
"use strict";
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/compress.ts
|
|
73
|
+
function toArrayBuffer(view) {
|
|
74
|
+
return view.buffer.slice(
|
|
75
|
+
view.byteOffset,
|
|
76
|
+
view.byteOffset + view.byteLength
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
async function gzipViaStream(bytes) {
|
|
80
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
|
|
81
|
+
return await new Response(stream).arrayBuffer();
|
|
82
|
+
}
|
|
83
|
+
function encodeRequestBody(body) {
|
|
84
|
+
if (readEnv(DISABLE_COMPRESSION_ENV)) {
|
|
85
|
+
return { body };
|
|
86
|
+
}
|
|
87
|
+
const bytes = new TextEncoder().encode(body);
|
|
88
|
+
if (bytes.byteLength < MIN_COMPRESSED_BYTES) {
|
|
89
|
+
return { body };
|
|
90
|
+
}
|
|
91
|
+
if (gzipNode) {
|
|
92
|
+
return gzipNode(bytes).then(
|
|
93
|
+
(compressed) => ({
|
|
94
|
+
body: toArrayBuffer(compressed),
|
|
95
|
+
contentEncoding: "gzip"
|
|
96
|
+
}),
|
|
97
|
+
() => ({ body })
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (typeof CompressionStream === "undefined") {
|
|
101
|
+
return { body };
|
|
102
|
+
}
|
|
103
|
+
return gzipViaStream(bytes).then(
|
|
104
|
+
(compressed) => ({ body: compressed, contentEncoding: "gzip" }),
|
|
105
|
+
() => ({ body })
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
var DISABLE_COMPRESSION_ENV, MIN_COMPRESSED_BYTES, gzipNode, _nodeGzipReady;
|
|
109
|
+
var init_compress = __esm({
|
|
110
|
+
"src/compress.ts"() {
|
|
111
|
+
"use strict";
|
|
112
|
+
init_readEnv();
|
|
113
|
+
DISABLE_COMPRESSION_ENV = "BITFAB_DISABLE_COMPRESSION";
|
|
114
|
+
MIN_COMPRESSED_BYTES = 8192;
|
|
115
|
+
_nodeGzipReady = (typeof process !== "undefined" && process.versions?.node ? (
|
|
116
|
+
// The join trick hides "node:zlib" from static analysis so bundlers that
|
|
117
|
+
// ban Node.js built-ins don't fail at build time. webpackIgnore tells
|
|
118
|
+
// webpack/turbopack to emit a native import() so Node.js can resolve the
|
|
119
|
+
// module at runtime. Same pattern as `asyncStorage.ts`.
|
|
120
|
+
import(
|
|
121
|
+
/* webpackIgnore: true */
|
|
122
|
+
["node", "zlib"].join(":")
|
|
123
|
+
).then(({ gzip }) => {
|
|
124
|
+
gzipNode = (data) => new Promise((resolve, reject) => {
|
|
125
|
+
gzip(data, (error, result) => {
|
|
126
|
+
if (error) {
|
|
127
|
+
reject(error);
|
|
128
|
+
} else {
|
|
129
|
+
resolve(result);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}).catch(() => {
|
|
134
|
+
})
|
|
135
|
+
) : Promise.resolve()).then(() => {
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
59
140
|
// src/errors.ts
|
|
60
141
|
var BitfabError;
|
|
61
142
|
var init_errors = __esm({
|
|
@@ -144,6 +225,131 @@ var init_replayContext = __esm({
|
|
|
144
225
|
}
|
|
145
226
|
});
|
|
146
227
|
|
|
228
|
+
// src/payloadBudget.ts
|
|
229
|
+
function byteLength(value) {
|
|
230
|
+
return textEncoder ? textEncoder.encode(value).length : value.length;
|
|
231
|
+
}
|
|
232
|
+
function carrierByteLength(body) {
|
|
233
|
+
return carrierBytesOf(textEncoder ? textEncoder.encode(body) : null, body);
|
|
234
|
+
}
|
|
235
|
+
function carrierBytesOf(encoded, body) {
|
|
236
|
+
if (!encoded) {
|
|
237
|
+
return body.length + 2;
|
|
238
|
+
}
|
|
239
|
+
let extra = 2;
|
|
240
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
241
|
+
const byte = encoded[i];
|
|
242
|
+
if (byte === 34 || byte === 92) {
|
|
243
|
+
extra += 1;
|
|
244
|
+
} else if (byte < 32) {
|
|
245
|
+
extra += byte === 8 || byte === 9 || byte === 10 || byte === 12 || byte === 13 ? 1 : 5;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return encoded.length + extra;
|
|
249
|
+
}
|
|
250
|
+
function fitsCarrierBudget(body) {
|
|
251
|
+
const units = body.length;
|
|
252
|
+
if (units * MAX_BYTES_PER_UNIT + 2 <= MAX_SPAN_CARRIER_BYTES) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
if (units + 2 > MAX_SPAN_CARRIER_BYTES) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
return carrierByteLength(body) <= MAX_SPAN_CARRIER_BYTES;
|
|
259
|
+
}
|
|
260
|
+
function asRecord(value) {
|
|
261
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
262
|
+
}
|
|
263
|
+
function cloneTrimmable(payload) {
|
|
264
|
+
const copy = { ...payload };
|
|
265
|
+
const containers = [];
|
|
266
|
+
const spanData = asRecord(copy.span_data);
|
|
267
|
+
if (spanData) {
|
|
268
|
+
const clone = { ...spanData };
|
|
269
|
+
copy.span_data = clone;
|
|
270
|
+
containers.push(clone);
|
|
271
|
+
}
|
|
272
|
+
const rawSpan = asRecord(copy.rawSpan);
|
|
273
|
+
const rawSpanData = rawSpan && asRecord(rawSpan.span_data);
|
|
274
|
+
if (rawSpan && rawSpanData) {
|
|
275
|
+
const clone = { ...rawSpanData };
|
|
276
|
+
copy.rawSpan = { ...rawSpan, span_data: clone };
|
|
277
|
+
containers.push(clone);
|
|
278
|
+
}
|
|
279
|
+
if (containers.length === 0) {
|
|
280
|
+
containers.push(copy);
|
|
281
|
+
}
|
|
282
|
+
return { copy, containers };
|
|
283
|
+
}
|
|
284
|
+
function collectCandidates(containers) {
|
|
285
|
+
const candidates = [];
|
|
286
|
+
for (const container of containers) {
|
|
287
|
+
for (const [key, value] of Object.entries(container)) {
|
|
288
|
+
if (STRUCTURAL_SPAN_KEYS.has(key) || value == null) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
let size;
|
|
292
|
+
try {
|
|
293
|
+
size = byteLength(JSON.stringify(value) ?? "");
|
|
294
|
+
} catch {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
candidates.push({ container, key, size });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return candidates.sort((a, b) => b.size - a.size);
|
|
301
|
+
}
|
|
302
|
+
function trimPayloadToBudget(payload, encode) {
|
|
303
|
+
const { copy, containers } = cloneTrimmable(payload);
|
|
304
|
+
const candidates = collectCandidates(containers);
|
|
305
|
+
if (candidates.length === 0) {
|
|
306
|
+
return void 0;
|
|
307
|
+
}
|
|
308
|
+
const trimmed = [];
|
|
309
|
+
for (const candidate of candidates) {
|
|
310
|
+
candidate.container[candidate.key] = `<unserializable: too_large_${candidate.size}_bytes>`;
|
|
311
|
+
trimmed.push(candidate.key);
|
|
312
|
+
let body;
|
|
313
|
+
try {
|
|
314
|
+
body = encode(copy);
|
|
315
|
+
} catch {
|
|
316
|
+
return void 0;
|
|
317
|
+
}
|
|
318
|
+
if (fitsCarrierBudget(body)) {
|
|
319
|
+
return { value: copy, trimmed };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function markPayloadTrimmed(value, trimmed) {
|
|
325
|
+
const existing = Array.isArray(value.errors) ? value.errors : [];
|
|
326
|
+
value.errors = [
|
|
327
|
+
...existing,
|
|
328
|
+
{
|
|
329
|
+
source: "sdk",
|
|
330
|
+
step: "payload_budget",
|
|
331
|
+
error: `trimmed oversized field(s) to fit the ${MAX_SPAN_CARRIER_BYTES}-byte span carrier budget: ${[
|
|
332
|
+
...new Set(trimmed)
|
|
333
|
+
].join(", ")}`
|
|
334
|
+
}
|
|
335
|
+
];
|
|
336
|
+
}
|
|
337
|
+
var MAX_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
|
|
338
|
+
var init_payloadBudget = __esm({
|
|
339
|
+
"src/payloadBudget.ts"() {
|
|
340
|
+
"use strict";
|
|
341
|
+
MAX_SPAN_CARRIER_BYTES = 28e5;
|
|
342
|
+
textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
|
|
343
|
+
MAX_BYTES_PER_UNIT = 3;
|
|
344
|
+
STRUCTURAL_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
345
|
+
"name",
|
|
346
|
+
"type",
|
|
347
|
+
"function_name",
|
|
348
|
+
"error_source"
|
|
349
|
+
]);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
147
353
|
// src/warnOnce.ts
|
|
148
354
|
function warnOnce(key, message) {
|
|
149
355
|
if (warned.has(key)) {
|
|
@@ -165,8 +371,37 @@ var init_warnOnce = __esm({
|
|
|
165
371
|
|
|
166
372
|
// src/serializePayload.ts
|
|
167
373
|
function serializePayloadBody(payload) {
|
|
374
|
+
const encoded = encodePayloadBody(payload);
|
|
375
|
+
if (fitsCarrierBudget(encoded.body)) {
|
|
376
|
+
return { body: encoded.body, dropped: encoded.dropped };
|
|
377
|
+
}
|
|
378
|
+
return applyPayloadBudget(encoded);
|
|
379
|
+
}
|
|
380
|
+
function applyPayloadBudget(encoded) {
|
|
381
|
+
const result = encoded.value ? trimPayloadToBudget(
|
|
382
|
+
encoded.value,
|
|
383
|
+
(value) => encodePayloadBody(value).body
|
|
384
|
+
) : void 0;
|
|
385
|
+
if (!result) {
|
|
386
|
+
return { body: encoded.body, dropped: encoded.dropped };
|
|
387
|
+
}
|
|
388
|
+
warnOnce(
|
|
389
|
+
"payload:over-budget",
|
|
390
|
+
`a span payload exceeded the ${MAX_SPAN_CARRIER_BYTES}-byte carrier budget; its largest field(s) (${[
|
|
391
|
+
...new Set(result.trimmed)
|
|
392
|
+
].join(
|
|
393
|
+
", "
|
|
394
|
+
)}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`
|
|
395
|
+
);
|
|
396
|
+
markPayloadTrimmed(result.value, result.trimmed);
|
|
397
|
+
return {
|
|
398
|
+
body: encodePayloadBody(result.value).body,
|
|
399
|
+
dropped: encoded.dropped
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
function encodePayloadBody(payload) {
|
|
168
403
|
try {
|
|
169
|
-
return { body: JSON.stringify(payload), dropped: [] };
|
|
404
|
+
return { body: JSON.stringify(payload), dropped: [], value: payload };
|
|
170
405
|
} catch {
|
|
171
406
|
const dropped = [];
|
|
172
407
|
const sanitize = (value, seen) => {
|
|
@@ -231,12 +466,11 @@ function serializePayloadBody(payload) {
|
|
|
231
466
|
sanitized = sanitize(payload, /* @__PURE__ */ new WeakSet());
|
|
232
467
|
} catch (error) {
|
|
233
468
|
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
dropped
|
|
237
|
-
};
|
|
469
|
+
const marker = { error: `payload_serialize_failed: ${message}` };
|
|
470
|
+
return { body: JSON.stringify(marker), dropped, value: marker };
|
|
238
471
|
}
|
|
239
|
-
|
|
472
|
+
const isRecord = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
|
|
473
|
+
if (dropped.length > 0 && isRecord) {
|
|
240
474
|
const obj = sanitized;
|
|
241
475
|
const existing = Array.isArray(obj.errors) ? obj.errors : [];
|
|
242
476
|
obj.errors = [
|
|
@@ -250,29 +484,21 @@ function serializePayloadBody(payload) {
|
|
|
250
484
|
}
|
|
251
485
|
];
|
|
252
486
|
}
|
|
253
|
-
return {
|
|
487
|
+
return {
|
|
488
|
+
body: JSON.stringify(sanitized),
|
|
489
|
+
dropped,
|
|
490
|
+
value: isRecord ? sanitized : void 0
|
|
491
|
+
};
|
|
254
492
|
}
|
|
255
493
|
}
|
|
256
494
|
var init_serializePayload = __esm({
|
|
257
495
|
"src/serializePayload.ts"() {
|
|
258
496
|
"use strict";
|
|
497
|
+
init_payloadBudget();
|
|
259
498
|
init_warnOnce();
|
|
260
499
|
}
|
|
261
500
|
});
|
|
262
501
|
|
|
263
|
-
// src/readEnv.ts
|
|
264
|
-
function readEnv(name) {
|
|
265
|
-
if (typeof process !== "undefined" && process.env) {
|
|
266
|
-
return process.env[name];
|
|
267
|
-
}
|
|
268
|
-
return void 0;
|
|
269
|
-
}
|
|
270
|
-
var init_readEnv = __esm({
|
|
271
|
-
"src/readEnv.ts"() {
|
|
272
|
-
"use strict";
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
502
|
// src/unrefTimer.ts
|
|
277
503
|
function unrefTimer(timer) {
|
|
278
504
|
const handle = timer;
|
|
@@ -318,7 +544,7 @@ function recordTraceSubmission(operation, payload) {
|
|
|
318
544
|
return;
|
|
319
545
|
}
|
|
320
546
|
if (operation === "external_span") {
|
|
321
|
-
const rawSpan =
|
|
547
|
+
const rawSpan = asRecord2(payload.rawSpan);
|
|
322
548
|
if (typeof rawSpan?.id !== "string") {
|
|
323
549
|
submissionCounter += 1;
|
|
324
550
|
}
|
|
@@ -355,14 +581,14 @@ function takeReplaySpanCounts(traceIds) {
|
|
|
355
581
|
}
|
|
356
582
|
return counts;
|
|
357
583
|
}
|
|
358
|
-
function
|
|
584
|
+
function asRecord2(value) {
|
|
359
585
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
360
586
|
}
|
|
361
587
|
function resolveSourceTraceId(payload) {
|
|
362
588
|
if (typeof payload.sourceTraceId === "string") {
|
|
363
589
|
return payload.sourceTraceId;
|
|
364
590
|
}
|
|
365
|
-
const rawTrace =
|
|
591
|
+
const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
|
|
366
592
|
return typeof rawTrace?.id === "string" ? rawTrace.id : void 0;
|
|
367
593
|
}
|
|
368
594
|
function otlpValue(value) {
|
|
@@ -420,32 +646,27 @@ function spanToOtlp(span) {
|
|
|
420
646
|
}
|
|
421
647
|
return result;
|
|
422
648
|
}
|
|
423
|
-
function
|
|
649
|
+
function encodeSpan(span) {
|
|
650
|
+
const json = JSON.stringify(spanToOtlp(span));
|
|
651
|
+
return { json, size: byteLength(json) };
|
|
652
|
+
}
|
|
653
|
+
function requestEnvelope(first) {
|
|
424
654
|
const scope = first.instrumentationScope;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
]
|
|
439
|
-
}
|
|
440
|
-
]
|
|
441
|
-
};
|
|
655
|
+
const resource = JSON.stringify({
|
|
656
|
+
attributes: otlpAttributes(
|
|
657
|
+
first.resource.attributes
|
|
658
|
+
)
|
|
659
|
+
});
|
|
660
|
+
const scopeJson = JSON.stringify({
|
|
661
|
+
name: scope.name,
|
|
662
|
+
version: scope.version ?? ""
|
|
663
|
+
});
|
|
664
|
+
const head = `{"resourceSpans":[{"resource":${resource},"scopeSpans":[{"scope":${scopeJson},"spans":[`;
|
|
665
|
+
const tail = "]}]}]}";
|
|
666
|
+
return { head, tail, size: byteLength(head) + byteLength(tail) };
|
|
442
667
|
}
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
if (typeof TextEncoder !== "undefined") {
|
|
446
|
-
return new TextEncoder().encode(json).length;
|
|
447
|
-
}
|
|
448
|
-
return json.length;
|
|
668
|
+
function encodeRequest(envelope, spans) {
|
|
669
|
+
return envelope.head + spans.map((span) => span.json).join(",") + envelope.tail;
|
|
449
670
|
}
|
|
450
671
|
function delay(ms) {
|
|
451
672
|
return new Promise((resolve) => {
|
|
@@ -495,16 +716,12 @@ function isRetryable(error) {
|
|
|
495
716
|
}
|
|
496
717
|
return RETRYABLE_STATUSES.has(status) || status >= 500;
|
|
497
718
|
}
|
|
498
|
-
function normalizeCollectorEndpoint(endpoint) {
|
|
499
|
-
const trimmed = endpoint.replace(/\/+$/, "");
|
|
500
|
-
return trimmed.endsWith("/v1/traces") ? trimmed : `${trimmed}/v1/traces`;
|
|
501
|
-
}
|
|
502
719
|
function endSpan(span, endTime) {
|
|
503
720
|
span.end(endTime);
|
|
504
721
|
}
|
|
505
722
|
function spanName(operation, payload) {
|
|
506
723
|
if (operation === "external_span") {
|
|
507
|
-
const spanData =
|
|
724
|
+
const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
|
|
508
725
|
if (typeof spanData?.name === "string") {
|
|
509
726
|
return spanData.name;
|
|
510
727
|
}
|
|
@@ -515,8 +732,8 @@ function spanName(operation, payload) {
|
|
|
515
732
|
return `bitfab.${operation}`;
|
|
516
733
|
}
|
|
517
734
|
function payloadTimestamp(payload, field) {
|
|
518
|
-
const rawSpan =
|
|
519
|
-
const rawTrace =
|
|
735
|
+
const rawSpan = asRecord2(payload.rawSpan);
|
|
736
|
+
const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
|
|
520
737
|
const raw = rawSpan?.[field] ?? rawTrace?.[field];
|
|
521
738
|
if (typeof raw !== "string") {
|
|
522
739
|
return void 0;
|
|
@@ -525,7 +742,7 @@ function payloadTimestamp(payload, field) {
|
|
|
525
742
|
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
526
743
|
}
|
|
527
744
|
function hasError(payload) {
|
|
528
|
-
const spanData =
|
|
745
|
+
const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
|
|
529
746
|
if (spanData?.error != null) {
|
|
530
747
|
return true;
|
|
531
748
|
}
|
|
@@ -535,7 +752,6 @@ function hasError(payload) {
|
|
|
535
752
|
function createOtelTransport(options) {
|
|
536
753
|
return new OtelBatchTransport({
|
|
537
754
|
...options,
|
|
538
|
-
collectorEndpoint: readEnv(COLLECTOR_ENDPOINT_ENV) || void 0,
|
|
539
755
|
exportConcurrency: readBoundedIntEnv(
|
|
540
756
|
EXPORT_CONCURRENCY_ENV,
|
|
541
757
|
MAX_EXPORT_CONCURRENCY,
|
|
@@ -570,7 +786,7 @@ function shutdownOtelTransports(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS) {
|
|
|
570
786
|
(transport, remaining) => transport.shutdown(remaining)
|
|
571
787
|
);
|
|
572
788
|
}
|
|
573
|
-
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,
|
|
789
|
+
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;
|
|
574
790
|
var init_otel = __esm({
|
|
575
791
|
"src/otel.ts"() {
|
|
576
792
|
"use strict";
|
|
@@ -580,6 +796,7 @@ var init_otel = __esm({
|
|
|
580
796
|
import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
581
797
|
init_constants();
|
|
582
798
|
init_errors();
|
|
799
|
+
init_payloadBudget();
|
|
583
800
|
init_readEnv();
|
|
584
801
|
init_serializePayload();
|
|
585
802
|
init_unrefTimer();
|
|
@@ -590,10 +807,8 @@ var init_otel = __esm({
|
|
|
590
807
|
MAX_EXPORT_REQUEST_BYTES = 3e6;
|
|
591
808
|
MAX_REQUEST_BYTES_ENV = "BITFAB_OTEL_MAX_REQUEST_BYTES";
|
|
592
809
|
EXPORT_CONCURRENCY_ENV = "BITFAB_OTEL_EXPORT_CONCURRENCY";
|
|
593
|
-
COLLECTOR_ENDPOINT_ENV = "BITFAB_OTEL_EXPORTER_ENDPOINT";
|
|
594
810
|
MAX_QUEUE_SIZE = 8192;
|
|
595
811
|
DIRECT_MAX_EXPORT_BATCH_SIZE = 512;
|
|
596
|
-
COLLECTOR_MAX_EXPORT_BATCH_SIZE = 32;
|
|
597
812
|
DIRECT_MAX_REQUEST_BATCH_SIZE = 8;
|
|
598
813
|
DEFAULT_EXPORT_CONCURRENCY = 32;
|
|
599
814
|
MAX_EXPORT_CONCURRENCY = 64;
|
|
@@ -607,6 +822,7 @@ var init_otel = __esm({
|
|
|
607
822
|
traceSubmissionSpanIds = /* @__PURE__ */ new Map();
|
|
608
823
|
replayTraceSubmissions = /* @__PURE__ */ new Set();
|
|
609
824
|
submissionCounter = 0;
|
|
825
|
+
SPAN_SEPARATOR_BYTES = 1;
|
|
610
826
|
OtlpPayloadTooLargeError = class extends Error {
|
|
611
827
|
};
|
|
612
828
|
OtlpPartialSuccessError = class extends Error {
|
|
@@ -635,57 +851,55 @@ var init_otel = __esm({
|
|
|
635
851
|
return true;
|
|
636
852
|
}
|
|
637
853
|
let encoded;
|
|
854
|
+
let envelope;
|
|
638
855
|
try {
|
|
639
|
-
encoded = spans.map(
|
|
856
|
+
encoded = spans.map(encodeSpan);
|
|
857
|
+
envelope = requestEnvelope(spans[0]);
|
|
640
858
|
} catch (error) {
|
|
641
859
|
logError("failed to encode an OpenTelemetry span batch", error);
|
|
642
860
|
return false;
|
|
643
861
|
}
|
|
644
|
-
const
|
|
645
|
-
const batches = this.buildRequestBatches(first, encoded);
|
|
862
|
+
const batches = this.buildRequestBatches(envelope, encoded);
|
|
646
863
|
const results = await mapWithConcurrency(
|
|
647
864
|
batches,
|
|
648
865
|
this.exportConcurrency,
|
|
649
|
-
(batch) => this.send(
|
|
866
|
+
(batch) => this.send(envelope, batch)
|
|
650
867
|
);
|
|
651
868
|
return results.every(Boolean);
|
|
652
869
|
}
|
|
653
|
-
buildRequestBatches(
|
|
870
|
+
buildRequestBatches(envelope, spans) {
|
|
654
871
|
const batches = [];
|
|
655
872
|
let current = [];
|
|
873
|
+
let size = envelope.size;
|
|
656
874
|
for (const span of spans) {
|
|
657
|
-
|
|
658
|
-
|
|
875
|
+
const addition = span.size + (current.length > 0 ? SPAN_SEPARATOR_BYTES : 0);
|
|
876
|
+
if (current.length > 0 && (current.length >= this.maxRequestBatchSize || size + addition > this.maxRequestBytes)) {
|
|
877
|
+
batches.push({ spans: current, size });
|
|
659
878
|
current = [];
|
|
879
|
+
size = envelope.size;
|
|
660
880
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
batches.push(current);
|
|
664
|
-
current = [span];
|
|
665
|
-
} else {
|
|
666
|
-
current = candidate;
|
|
667
|
-
}
|
|
881
|
+
current.push(span);
|
|
882
|
+
size += span.size + (current.length > 1 ? SPAN_SEPARATOR_BYTES : 0);
|
|
668
883
|
}
|
|
669
884
|
if (current.length > 0) {
|
|
670
|
-
batches.push(current);
|
|
885
|
+
batches.push({ spans: current, size });
|
|
671
886
|
}
|
|
672
887
|
return batches;
|
|
673
888
|
}
|
|
674
|
-
async send(
|
|
675
|
-
|
|
676
|
-
if (encodedSize(payload) > this.maxRequestBytes) {
|
|
889
|
+
async send(envelope, batch) {
|
|
890
|
+
if (batch.size > this.maxRequestBytes) {
|
|
677
891
|
logError(
|
|
678
892
|
"a single OpenTelemetry span exceeded the configured request-size target and could not be exported"
|
|
679
893
|
);
|
|
680
894
|
return false;
|
|
681
895
|
}
|
|
682
896
|
try {
|
|
683
|
-
await this.sendWithRetries(
|
|
897
|
+
await this.sendWithRetries(encodeRequest(envelope, batch.spans));
|
|
684
898
|
return true;
|
|
685
899
|
} catch (error) {
|
|
686
900
|
if (error instanceof OtlpPayloadTooLargeError) {
|
|
687
901
|
logError(
|
|
688
|
-
spans.length === 1 ? "a single OpenTelemetry span exceeded the ingestion request limit and could not be exported" : "an OpenTelemetry span batch exceeded the ingestion request limit and could not be exported"
|
|
902
|
+
batch.spans.length === 1 ? "a single OpenTelemetry span exceeded the ingestion request limit and could not be exported" : "an OpenTelemetry span batch exceeded the ingestion request limit and could not be exported"
|
|
689
903
|
);
|
|
690
904
|
return false;
|
|
691
905
|
}
|
|
@@ -709,15 +923,15 @@ var init_otel = __esm({
|
|
|
709
923
|
* the server does not yet understand. The fix is a client-supplied
|
|
710
924
|
* idempotency key that ingestion dedupes on.
|
|
711
925
|
*/
|
|
712
|
-
async sendWithRetries(
|
|
926
|
+
async sendWithRetries(body) {
|
|
713
927
|
for (let attempt = 0; attempt < MAX_SEND_ATTEMPTS; attempt += 1) {
|
|
714
928
|
try {
|
|
715
929
|
const response = await this.directSender(
|
|
716
930
|
OTLP_TRACES_ENDPOINT,
|
|
717
|
-
|
|
931
|
+
body,
|
|
718
932
|
EXPORT_TIMEOUT_MILLIS
|
|
719
933
|
);
|
|
720
|
-
const partialSuccess =
|
|
934
|
+
const partialSuccess = asRecord2(response?.partialSuccess);
|
|
721
935
|
const rejected = partialSuccess?.rejectedSpans;
|
|
722
936
|
if (rejected !== void 0 && rejected !== "0" && rejected !== 0) {
|
|
723
937
|
logError(
|
|
@@ -745,115 +959,6 @@ var init_otel = __esm({
|
|
|
745
959
|
async forceFlush() {
|
|
746
960
|
}
|
|
747
961
|
};
|
|
748
|
-
CollectorSpanExporter = class {
|
|
749
|
-
constructor(endpoint, apiKey, maxRequestBytes) {
|
|
750
|
-
this.endpoint = endpoint;
|
|
751
|
-
this.apiKey = apiKey;
|
|
752
|
-
this.maxRequestBytes = maxRequestBytes;
|
|
753
|
-
}
|
|
754
|
-
/**
|
|
755
|
-
* Loaded through a dynamic import rather than a top-level one so bundlers
|
|
756
|
-
* code-split it: Collector delivery is opt-in, and a consumer who never sets
|
|
757
|
-
* an endpoint should not pay for the exporter in their initial bundle. It is
|
|
758
|
-
* a hard dependency, so this cannot fail for want of the package.
|
|
759
|
-
*/
|
|
760
|
-
loadExporterModule() {
|
|
761
|
-
if (!this.pendingModule) {
|
|
762
|
-
this.pendingModule = import("@opentelemetry/exporter-trace-otlp-proto");
|
|
763
|
-
}
|
|
764
|
-
return this.pendingModule;
|
|
765
|
-
}
|
|
766
|
-
export(spans, resultCallback) {
|
|
767
|
-
void this.exportAsync(spans).then(
|
|
768
|
-
(succeeded) => {
|
|
769
|
-
resultCallback({
|
|
770
|
-
code: succeeded ? import_core.ExportResultCode.SUCCESS : import_core.ExportResultCode.FAILED
|
|
771
|
-
});
|
|
772
|
-
},
|
|
773
|
-
(error) => {
|
|
774
|
-
resultCallback({ code: import_core.ExportResultCode.FAILED, error });
|
|
775
|
-
}
|
|
776
|
-
);
|
|
777
|
-
}
|
|
778
|
-
async exportAsync(spans) {
|
|
779
|
-
if (spans.length === 0) {
|
|
780
|
-
return true;
|
|
781
|
-
}
|
|
782
|
-
let delegate;
|
|
783
|
-
try {
|
|
784
|
-
delegate = await this.resolveDelegate();
|
|
785
|
-
} catch (error) {
|
|
786
|
-
logError("failed to build the OTLP Collector exporter", error);
|
|
787
|
-
return false;
|
|
788
|
-
}
|
|
789
|
-
const results = await Promise.all(
|
|
790
|
-
this.partition(spans).map(
|
|
791
|
-
(batch) => new Promise((resolve) => {
|
|
792
|
-
try {
|
|
793
|
-
delegate.export(batch, (result) => {
|
|
794
|
-
resolve(result.code === import_core.ExportResultCode.SUCCESS);
|
|
795
|
-
});
|
|
796
|
-
} catch (error) {
|
|
797
|
-
logError("Collector export threw", error);
|
|
798
|
-
resolve(false);
|
|
799
|
-
}
|
|
800
|
-
})
|
|
801
|
-
)
|
|
802
|
-
);
|
|
803
|
-
return results.every(Boolean);
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* Partition by the encoded JSON size of each carrier rather than its encoded
|
|
807
|
-
* protobuf size. Protobuf is strictly smaller than the equivalent JSON for
|
|
808
|
-
* these payloads, so the JSON figure is a conservative bound that keeps every
|
|
809
|
-
* request under the target without pulling `@opentelemetry/otlp-transformer`
|
|
810
|
-
* into the dependency set purely to measure bytes.
|
|
811
|
-
*/
|
|
812
|
-
partition(spans) {
|
|
813
|
-
const batches = [];
|
|
814
|
-
let current = [];
|
|
815
|
-
let currentSize = 0;
|
|
816
|
-
for (const span of spans) {
|
|
817
|
-
const size = encodedSize(spanToOtlp(span));
|
|
818
|
-
if (current.length > 0 && currentSize + size > this.maxRequestBytes) {
|
|
819
|
-
batches.push(current);
|
|
820
|
-
current = [];
|
|
821
|
-
currentSize = 0;
|
|
822
|
-
}
|
|
823
|
-
current.push(span);
|
|
824
|
-
currentSize += size;
|
|
825
|
-
}
|
|
826
|
-
if (current.length > 0) {
|
|
827
|
-
batches.push(current);
|
|
828
|
-
}
|
|
829
|
-
return batches;
|
|
830
|
-
}
|
|
831
|
-
async resolveDelegate() {
|
|
832
|
-
const apiKey = this.apiKey() ?? "";
|
|
833
|
-
if (this.delegate && this.delegateApiKey === apiKey) {
|
|
834
|
-
return this.delegate;
|
|
835
|
-
}
|
|
836
|
-
const { OTLPTraceExporter } = await this.loadExporterModule();
|
|
837
|
-
const previous = this.delegate;
|
|
838
|
-
this.delegate = new OTLPTraceExporter({
|
|
839
|
-
url: this.endpoint,
|
|
840
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
841
|
-
timeoutMillis: EXPORT_TIMEOUT_MILLIS
|
|
842
|
-
});
|
|
843
|
-
this.delegateApiKey = apiKey;
|
|
844
|
-
if (previous) {
|
|
845
|
-
void previous.shutdown().catch(() => {
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
return this.delegate;
|
|
849
|
-
}
|
|
850
|
-
async shutdown() {
|
|
851
|
-
await this.delegate?.shutdown();
|
|
852
|
-
}
|
|
853
|
-
async forceFlush() {
|
|
854
|
-
await this.delegate?.forceFlush?.();
|
|
855
|
-
}
|
|
856
|
-
};
|
|
857
962
|
DeliveryTrackingExporter = class {
|
|
858
963
|
constructor(exporter) {
|
|
859
964
|
this.exporter = exporter;
|
|
@@ -896,27 +1001,22 @@ var init_otel = __esm({
|
|
|
896
1001
|
OtelBatchTransport = class {
|
|
897
1002
|
constructor(options) {
|
|
898
1003
|
this.closed = false;
|
|
899
|
-
const collectorEndpoint = options.collectorEndpoint;
|
|
900
1004
|
const maxRequestBytes = options.maxRequestBytes ?? MAX_EXPORT_REQUEST_BYTES;
|
|
901
1005
|
const maxRequestBatchSize = options.maxRequestBatchSize ?? DIRECT_MAX_REQUEST_BATCH_SIZE;
|
|
902
1006
|
if (maxRequestBatchSize <= 0) {
|
|
903
1007
|
throw new BitfabError("maxRequestBatchSize must be a positive integer");
|
|
904
1008
|
}
|
|
905
1009
|
this.deliveryTracker = new DeliveryTrackingExporter(
|
|
906
|
-
|
|
1010
|
+
new BitfabSpanExporter(
|
|
907
1011
|
options.directSender,
|
|
908
1012
|
maxRequestBytes,
|
|
909
1013
|
maxRequestBatchSize,
|
|
910
1014
|
options.exportConcurrency ?? DEFAULT_EXPORT_CONCURRENCY
|
|
911
|
-
) : new CollectorSpanExporter(
|
|
912
|
-
normalizeCollectorEndpoint(collectorEndpoint),
|
|
913
|
-
options.apiKey,
|
|
914
|
-
maxRequestBytes
|
|
915
1015
|
)
|
|
916
1016
|
);
|
|
917
1017
|
this.processor = new import_sdk_trace_base.BatchSpanProcessor(this.deliveryTracker, {
|
|
918
1018
|
maxQueueSize: options.maxQueueSize ?? MAX_QUEUE_SIZE,
|
|
919
|
-
maxExportBatchSize: options.maxExportBatchSize ??
|
|
1019
|
+
maxExportBatchSize: options.maxExportBatchSize ?? DIRECT_MAX_EXPORT_BATCH_SIZE,
|
|
920
1020
|
scheduledDelayMillis: SCHEDULE_DELAY_MILLIS,
|
|
921
1021
|
exportTimeoutMillis: options.exportTimeoutMillis ?? EXPORT_TIMEOUT_MILLIS
|
|
922
1022
|
});
|
|
@@ -1069,6 +1169,7 @@ var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCL
|
|
|
1069
1169
|
var init_http = __esm({
|
|
1070
1170
|
"src/http.ts"() {
|
|
1071
1171
|
"use strict";
|
|
1172
|
+
init_compress();
|
|
1072
1173
|
init_constants();
|
|
1073
1174
|
init_errors();
|
|
1074
1175
|
init_replayContext();
|
|
@@ -1133,8 +1234,7 @@ var init_http = __esm({
|
|
|
1133
1234
|
}
|
|
1134
1235
|
if (!this.traceTransport) {
|
|
1135
1236
|
this.traceTransport = createTraceTransport({
|
|
1136
|
-
|
|
1137
|
-
directSender: (endpoint, payload, timeoutMs) => this.request(endpoint, payload, {
|
|
1237
|
+
directSender: (endpoint, body, timeoutMs) => this.sendEncoded(endpoint, body, {
|
|
1138
1238
|
timeout: timeoutMs
|
|
1139
1239
|
})
|
|
1140
1240
|
});
|
|
@@ -1204,11 +1304,6 @@ var init_http = __esm({
|
|
|
1204
1304
|
* @throws {BitfabError} If the request fails
|
|
1205
1305
|
*/
|
|
1206
1306
|
async request(endpoint, payload, options) {
|
|
1207
|
-
const url = `${this.serviceUrl}${endpoint}`;
|
|
1208
|
-
const timeout = options?.timeout ?? this.timeout;
|
|
1209
|
-
const method = options?.method ?? "POST";
|
|
1210
|
-
const controller = new AbortController();
|
|
1211
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1212
1307
|
const { body, dropped } = serializePayloadBody(payload);
|
|
1213
1308
|
if (dropped.length > 0) {
|
|
1214
1309
|
try {
|
|
@@ -1218,14 +1313,33 @@ var init_http = __esm({
|
|
|
1218
1313
|
} catch {
|
|
1219
1314
|
}
|
|
1220
1315
|
}
|
|
1316
|
+
return this.sendEncoded(endpoint, body, options);
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* POST an already-encoded body. The span transport encodes its own batches,
|
|
1320
|
+
* so routing them back through {@link HttpClient.request} would encode the
|
|
1321
|
+
* same data twice.
|
|
1322
|
+
*/
|
|
1323
|
+
async sendEncoded(endpoint, body, options) {
|
|
1324
|
+
const url = `${this.serviceUrl}${endpoint}`;
|
|
1325
|
+
const timeout = options?.timeout ?? this.timeout;
|
|
1326
|
+
const method = options?.method ?? "POST";
|
|
1327
|
+
const controller = new AbortController();
|
|
1328
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1329
|
+
const prepared = encodeRequestBody(body);
|
|
1330
|
+
const encoded = prepared instanceof Promise ? await prepared : prepared;
|
|
1331
|
+
const headers = {
|
|
1332
|
+
"Content-Type": "application/json",
|
|
1333
|
+
Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
|
|
1334
|
+
};
|
|
1335
|
+
if (encoded.contentEncoding) {
|
|
1336
|
+
headers["Content-Encoding"] = encoded.contentEncoding;
|
|
1337
|
+
}
|
|
1221
1338
|
try {
|
|
1222
1339
|
const response = await fetch(url, {
|
|
1223
1340
|
method,
|
|
1224
|
-
headers
|
|
1225
|
-
|
|
1226
|
-
Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
|
|
1227
|
-
},
|
|
1228
|
-
body,
|
|
1341
|
+
headers,
|
|
1342
|
+
body: encoded.body,
|
|
1229
1343
|
signal: controller.signal
|
|
1230
1344
|
});
|
|
1231
1345
|
if (!response.ok) {
|
|
@@ -1674,9 +1788,10 @@ var init_serialize = __esm({
|
|
|
1674
1788
|
"src/serialize.ts"() {
|
|
1675
1789
|
"use strict";
|
|
1676
1790
|
import_superjson = __toESM(require("superjson"), 1);
|
|
1791
|
+
init_payloadBudget();
|
|
1677
1792
|
init_warnOnce();
|
|
1678
|
-
MAX_SERIALIZED_BYTES =
|
|
1679
|
-
MAX_FRAMEWORK_SERIALIZED_BYTES =
|
|
1793
|
+
MAX_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
|
|
1794
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
|
|
1680
1795
|
MAX_SAFE_DEPTH = 6;
|
|
1681
1796
|
}
|
|
1682
1797
|
});
|