bitfab 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
|
@@ -51,7 +51,7 @@ var __version__;
|
|
|
51
51
|
var init_version_generated = __esm({
|
|
52
52
|
"src/version.generated.ts"() {
|
|
53
53
|
"use strict";
|
|
54
|
-
__version__ = "0.
|
|
54
|
+
__version__ = "0.36.0";
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -65,6 +65,87 @@ var init_constants = __esm({
|
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
// src/readEnv.ts
|
|
69
|
+
function readEnv(name) {
|
|
70
|
+
if (typeof process !== "undefined" && process.env) {
|
|
71
|
+
return process.env[name];
|
|
72
|
+
}
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
var init_readEnv = __esm({
|
|
76
|
+
"src/readEnv.ts"() {
|
|
77
|
+
"use strict";
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// src/compress.ts
|
|
82
|
+
function toArrayBuffer(view) {
|
|
83
|
+
return view.buffer.slice(
|
|
84
|
+
view.byteOffset,
|
|
85
|
+
view.byteOffset + view.byteLength
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
async function gzipViaStream(bytes) {
|
|
89
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
|
|
90
|
+
return await new Response(stream).arrayBuffer();
|
|
91
|
+
}
|
|
92
|
+
function encodeRequestBody(body) {
|
|
93
|
+
if (readEnv(DISABLE_COMPRESSION_ENV)) {
|
|
94
|
+
return { body };
|
|
95
|
+
}
|
|
96
|
+
const bytes = new TextEncoder().encode(body);
|
|
97
|
+
if (bytes.byteLength < MIN_COMPRESSED_BYTES) {
|
|
98
|
+
return { body };
|
|
99
|
+
}
|
|
100
|
+
if (gzipNode) {
|
|
101
|
+
return gzipNode(bytes).then(
|
|
102
|
+
(compressed) => ({
|
|
103
|
+
body: toArrayBuffer(compressed),
|
|
104
|
+
contentEncoding: "gzip"
|
|
105
|
+
}),
|
|
106
|
+
() => ({ body })
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (typeof CompressionStream === "undefined") {
|
|
110
|
+
return { body };
|
|
111
|
+
}
|
|
112
|
+
return gzipViaStream(bytes).then(
|
|
113
|
+
(compressed) => ({ body: compressed, contentEncoding: "gzip" }),
|
|
114
|
+
() => ({ body })
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
var DISABLE_COMPRESSION_ENV, MIN_COMPRESSED_BYTES, gzipNode, _nodeGzipReady;
|
|
118
|
+
var init_compress = __esm({
|
|
119
|
+
"src/compress.ts"() {
|
|
120
|
+
"use strict";
|
|
121
|
+
init_readEnv();
|
|
122
|
+
DISABLE_COMPRESSION_ENV = "BITFAB_DISABLE_COMPRESSION";
|
|
123
|
+
MIN_COMPRESSED_BYTES = 8192;
|
|
124
|
+
_nodeGzipReady = (typeof process !== "undefined" && process.versions?.node ? (
|
|
125
|
+
// The join trick hides "node:zlib" from static analysis so bundlers that
|
|
126
|
+
// ban Node.js built-ins don't fail at build time. webpackIgnore tells
|
|
127
|
+
// webpack/turbopack to emit a native import() so Node.js can resolve the
|
|
128
|
+
// module at runtime. Same pattern as `asyncStorage.ts`.
|
|
129
|
+
import(
|
|
130
|
+
/* webpackIgnore: true */
|
|
131
|
+
["node", "zlib"].join(":")
|
|
132
|
+
).then(({ gzip }) => {
|
|
133
|
+
gzipNode = (data) => new Promise((resolve, reject) => {
|
|
134
|
+
gzip(data, (error, result) => {
|
|
135
|
+
if (error) {
|
|
136
|
+
reject(error);
|
|
137
|
+
} else {
|
|
138
|
+
resolve(result);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}).catch(() => {
|
|
143
|
+
})
|
|
144
|
+
) : Promise.resolve()).then(() => {
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
68
149
|
// src/errors.ts
|
|
69
150
|
var BitfabError;
|
|
70
151
|
var init_errors = __esm({
|
|
@@ -153,6 +234,131 @@ var init_replayContext = __esm({
|
|
|
153
234
|
}
|
|
154
235
|
});
|
|
155
236
|
|
|
237
|
+
// src/payloadBudget.ts
|
|
238
|
+
function byteLength(value) {
|
|
239
|
+
return textEncoder ? textEncoder.encode(value).length : value.length;
|
|
240
|
+
}
|
|
241
|
+
function carrierByteLength(body) {
|
|
242
|
+
return carrierBytesOf(textEncoder ? textEncoder.encode(body) : null, body);
|
|
243
|
+
}
|
|
244
|
+
function carrierBytesOf(encoded, body) {
|
|
245
|
+
if (!encoded) {
|
|
246
|
+
return body.length + 2;
|
|
247
|
+
}
|
|
248
|
+
let extra = 2;
|
|
249
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
250
|
+
const byte = encoded[i];
|
|
251
|
+
if (byte === 34 || byte === 92) {
|
|
252
|
+
extra += 1;
|
|
253
|
+
} else if (byte < 32) {
|
|
254
|
+
extra += byte === 8 || byte === 9 || byte === 10 || byte === 12 || byte === 13 ? 1 : 5;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return encoded.length + extra;
|
|
258
|
+
}
|
|
259
|
+
function fitsCarrierBudget(body) {
|
|
260
|
+
const units = body.length;
|
|
261
|
+
if (units * MAX_BYTES_PER_UNIT + 2 <= MAX_SPAN_CARRIER_BYTES) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
if (units + 2 > MAX_SPAN_CARRIER_BYTES) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return carrierByteLength(body) <= MAX_SPAN_CARRIER_BYTES;
|
|
268
|
+
}
|
|
269
|
+
function asRecord(value) {
|
|
270
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
271
|
+
}
|
|
272
|
+
function cloneTrimmable(payload) {
|
|
273
|
+
const copy = { ...payload };
|
|
274
|
+
const containers = [];
|
|
275
|
+
const spanData = asRecord(copy.span_data);
|
|
276
|
+
if (spanData) {
|
|
277
|
+
const clone = { ...spanData };
|
|
278
|
+
copy.span_data = clone;
|
|
279
|
+
containers.push(clone);
|
|
280
|
+
}
|
|
281
|
+
const rawSpan = asRecord(copy.rawSpan);
|
|
282
|
+
const rawSpanData = rawSpan && asRecord(rawSpan.span_data);
|
|
283
|
+
if (rawSpan && rawSpanData) {
|
|
284
|
+
const clone = { ...rawSpanData };
|
|
285
|
+
copy.rawSpan = { ...rawSpan, span_data: clone };
|
|
286
|
+
containers.push(clone);
|
|
287
|
+
}
|
|
288
|
+
if (containers.length === 0) {
|
|
289
|
+
containers.push(copy);
|
|
290
|
+
}
|
|
291
|
+
return { copy, containers };
|
|
292
|
+
}
|
|
293
|
+
function collectCandidates(containers) {
|
|
294
|
+
const candidates = [];
|
|
295
|
+
for (const container of containers) {
|
|
296
|
+
for (const [key, value] of Object.entries(container)) {
|
|
297
|
+
if (STRUCTURAL_SPAN_KEYS.has(key) || value == null) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
let size;
|
|
301
|
+
try {
|
|
302
|
+
size = byteLength(JSON.stringify(value) ?? "");
|
|
303
|
+
} catch {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
candidates.push({ container, key, size });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return candidates.sort((a, b) => b.size - a.size);
|
|
310
|
+
}
|
|
311
|
+
function trimPayloadToBudget(payload, encode) {
|
|
312
|
+
const { copy, containers } = cloneTrimmable(payload);
|
|
313
|
+
const candidates = collectCandidates(containers);
|
|
314
|
+
if (candidates.length === 0) {
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
const trimmed = [];
|
|
318
|
+
for (const candidate of candidates) {
|
|
319
|
+
candidate.container[candidate.key] = `<unserializable: too_large_${candidate.size}_bytes>`;
|
|
320
|
+
trimmed.push(candidate.key);
|
|
321
|
+
let body;
|
|
322
|
+
try {
|
|
323
|
+
body = encode(copy);
|
|
324
|
+
} catch {
|
|
325
|
+
return void 0;
|
|
326
|
+
}
|
|
327
|
+
if (fitsCarrierBudget(body)) {
|
|
328
|
+
return { value: copy, trimmed };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return void 0;
|
|
332
|
+
}
|
|
333
|
+
function markPayloadTrimmed(value, trimmed) {
|
|
334
|
+
const existing = Array.isArray(value.errors) ? value.errors : [];
|
|
335
|
+
value.errors = [
|
|
336
|
+
...existing,
|
|
337
|
+
{
|
|
338
|
+
source: "sdk",
|
|
339
|
+
step: "payload_budget",
|
|
340
|
+
error: `trimmed oversized field(s) to fit the ${MAX_SPAN_CARRIER_BYTES}-byte span carrier budget: ${[
|
|
341
|
+
...new Set(trimmed)
|
|
342
|
+
].join(", ")}`
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
}
|
|
346
|
+
var MAX_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
|
|
347
|
+
var init_payloadBudget = __esm({
|
|
348
|
+
"src/payloadBudget.ts"() {
|
|
349
|
+
"use strict";
|
|
350
|
+
MAX_SPAN_CARRIER_BYTES = 28e5;
|
|
351
|
+
textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
|
|
352
|
+
MAX_BYTES_PER_UNIT = 3;
|
|
353
|
+
STRUCTURAL_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
354
|
+
"name",
|
|
355
|
+
"type",
|
|
356
|
+
"function_name",
|
|
357
|
+
"error_source"
|
|
358
|
+
]);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
156
362
|
// src/warnOnce.ts
|
|
157
363
|
function warnOnce(key, message) {
|
|
158
364
|
if (warned.has(key)) {
|
|
@@ -174,8 +380,37 @@ var init_warnOnce = __esm({
|
|
|
174
380
|
|
|
175
381
|
// src/serializePayload.ts
|
|
176
382
|
function serializePayloadBody(payload) {
|
|
383
|
+
const encoded = encodePayloadBody(payload);
|
|
384
|
+
if (fitsCarrierBudget(encoded.body)) {
|
|
385
|
+
return { body: encoded.body, dropped: encoded.dropped };
|
|
386
|
+
}
|
|
387
|
+
return applyPayloadBudget(encoded);
|
|
388
|
+
}
|
|
389
|
+
function applyPayloadBudget(encoded) {
|
|
390
|
+
const result = encoded.value ? trimPayloadToBudget(
|
|
391
|
+
encoded.value,
|
|
392
|
+
(value) => encodePayloadBody(value).body
|
|
393
|
+
) : void 0;
|
|
394
|
+
if (!result) {
|
|
395
|
+
return { body: encoded.body, dropped: encoded.dropped };
|
|
396
|
+
}
|
|
397
|
+
warnOnce(
|
|
398
|
+
"payload:over-budget",
|
|
399
|
+
`a span payload exceeded the ${MAX_SPAN_CARRIER_BYTES}-byte carrier budget; its largest field(s) (${[
|
|
400
|
+
...new Set(result.trimmed)
|
|
401
|
+
].join(
|
|
402
|
+
", "
|
|
403
|
+
)}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`
|
|
404
|
+
);
|
|
405
|
+
markPayloadTrimmed(result.value, result.trimmed);
|
|
406
|
+
return {
|
|
407
|
+
body: encodePayloadBody(result.value).body,
|
|
408
|
+
dropped: encoded.dropped
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function encodePayloadBody(payload) {
|
|
177
412
|
try {
|
|
178
|
-
return { body: JSON.stringify(payload), dropped: [] };
|
|
413
|
+
return { body: JSON.stringify(payload), dropped: [], value: payload };
|
|
179
414
|
} catch {
|
|
180
415
|
const dropped = [];
|
|
181
416
|
const sanitize = (value, seen) => {
|
|
@@ -240,12 +475,11 @@ function serializePayloadBody(payload) {
|
|
|
240
475
|
sanitized = sanitize(payload, /* @__PURE__ */ new WeakSet());
|
|
241
476
|
} catch (error) {
|
|
242
477
|
const message = error instanceof Error ? error.message : String(error);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
dropped
|
|
246
|
-
};
|
|
478
|
+
const marker = { error: `payload_serialize_failed: ${message}` };
|
|
479
|
+
return { body: JSON.stringify(marker), dropped, value: marker };
|
|
247
480
|
}
|
|
248
|
-
|
|
481
|
+
const isRecord = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
|
|
482
|
+
if (dropped.length > 0 && isRecord) {
|
|
249
483
|
const obj = sanitized;
|
|
250
484
|
const existing = Array.isArray(obj.errors) ? obj.errors : [];
|
|
251
485
|
obj.errors = [
|
|
@@ -259,29 +493,21 @@ function serializePayloadBody(payload) {
|
|
|
259
493
|
}
|
|
260
494
|
];
|
|
261
495
|
}
|
|
262
|
-
return {
|
|
496
|
+
return {
|
|
497
|
+
body: JSON.stringify(sanitized),
|
|
498
|
+
dropped,
|
|
499
|
+
value: isRecord ? sanitized : void 0
|
|
500
|
+
};
|
|
263
501
|
}
|
|
264
502
|
}
|
|
265
503
|
var init_serializePayload = __esm({
|
|
266
504
|
"src/serializePayload.ts"() {
|
|
267
505
|
"use strict";
|
|
506
|
+
init_payloadBudget();
|
|
268
507
|
init_warnOnce();
|
|
269
508
|
}
|
|
270
509
|
});
|
|
271
510
|
|
|
272
|
-
// src/readEnv.ts
|
|
273
|
-
function readEnv(name) {
|
|
274
|
-
if (typeof process !== "undefined" && process.env) {
|
|
275
|
-
return process.env[name];
|
|
276
|
-
}
|
|
277
|
-
return void 0;
|
|
278
|
-
}
|
|
279
|
-
var init_readEnv = __esm({
|
|
280
|
-
"src/readEnv.ts"() {
|
|
281
|
-
"use strict";
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
511
|
// src/unrefTimer.ts
|
|
286
512
|
function unrefTimer(timer) {
|
|
287
513
|
const handle = timer;
|
|
@@ -327,7 +553,7 @@ function recordTraceSubmission(operation, payload) {
|
|
|
327
553
|
return;
|
|
328
554
|
}
|
|
329
555
|
if (operation === "external_span") {
|
|
330
|
-
const rawSpan =
|
|
556
|
+
const rawSpan = asRecord2(payload.rawSpan);
|
|
331
557
|
if (typeof rawSpan?.id !== "string") {
|
|
332
558
|
submissionCounter += 1;
|
|
333
559
|
}
|
|
@@ -364,14 +590,14 @@ function takeReplaySpanCounts(traceIds) {
|
|
|
364
590
|
}
|
|
365
591
|
return counts;
|
|
366
592
|
}
|
|
367
|
-
function
|
|
593
|
+
function asRecord2(value) {
|
|
368
594
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
369
595
|
}
|
|
370
596
|
function resolveSourceTraceId(payload) {
|
|
371
597
|
if (typeof payload.sourceTraceId === "string") {
|
|
372
598
|
return payload.sourceTraceId;
|
|
373
599
|
}
|
|
374
|
-
const rawTrace =
|
|
600
|
+
const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
|
|
375
601
|
return typeof rawTrace?.id === "string" ? rawTrace.id : void 0;
|
|
376
602
|
}
|
|
377
603
|
function otlpValue(value) {
|
|
@@ -429,32 +655,27 @@ function spanToOtlp(span) {
|
|
|
429
655
|
}
|
|
430
656
|
return result;
|
|
431
657
|
}
|
|
432
|
-
function
|
|
658
|
+
function encodeSpan(span) {
|
|
659
|
+
const json = JSON.stringify(spanToOtlp(span));
|
|
660
|
+
return { json, size: byteLength(json) };
|
|
661
|
+
}
|
|
662
|
+
function requestEnvelope(first) {
|
|
433
663
|
const scope = first.instrumentationScope;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
]
|
|
448
|
-
}
|
|
449
|
-
]
|
|
450
|
-
};
|
|
664
|
+
const resource = JSON.stringify({
|
|
665
|
+
attributes: otlpAttributes(
|
|
666
|
+
first.resource.attributes
|
|
667
|
+
)
|
|
668
|
+
});
|
|
669
|
+
const scopeJson = JSON.stringify({
|
|
670
|
+
name: scope.name,
|
|
671
|
+
version: scope.version ?? ""
|
|
672
|
+
});
|
|
673
|
+
const head = `{"resourceSpans":[{"resource":${resource},"scopeSpans":[{"scope":${scopeJson},"spans":[`;
|
|
674
|
+
const tail = "]}]}]}";
|
|
675
|
+
return { head, tail, size: byteLength(head) + byteLength(tail) };
|
|
451
676
|
}
|
|
452
|
-
function
|
|
453
|
-
|
|
454
|
-
if (typeof TextEncoder !== "undefined") {
|
|
455
|
-
return new TextEncoder().encode(json).length;
|
|
456
|
-
}
|
|
457
|
-
return json.length;
|
|
677
|
+
function encodeRequest(envelope, spans) {
|
|
678
|
+
return envelope.head + spans.map((span) => span.json).join(",") + envelope.tail;
|
|
458
679
|
}
|
|
459
680
|
function delay(ms) {
|
|
460
681
|
return new Promise((resolve) => {
|
|
@@ -504,16 +725,12 @@ function isRetryable(error) {
|
|
|
504
725
|
}
|
|
505
726
|
return RETRYABLE_STATUSES.has(status) || status >= 500;
|
|
506
727
|
}
|
|
507
|
-
function normalizeCollectorEndpoint(endpoint) {
|
|
508
|
-
const trimmed = endpoint.replace(/\/+$/, "");
|
|
509
|
-
return trimmed.endsWith("/v1/traces") ? trimmed : `${trimmed}/v1/traces`;
|
|
510
|
-
}
|
|
511
728
|
function endSpan(span, endTime) {
|
|
512
729
|
span.end(endTime);
|
|
513
730
|
}
|
|
514
731
|
function spanName(operation, payload) {
|
|
515
732
|
if (operation === "external_span") {
|
|
516
|
-
const spanData =
|
|
733
|
+
const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
|
|
517
734
|
if (typeof spanData?.name === "string") {
|
|
518
735
|
return spanData.name;
|
|
519
736
|
}
|
|
@@ -524,8 +741,8 @@ function spanName(operation, payload) {
|
|
|
524
741
|
return `bitfab.${operation}`;
|
|
525
742
|
}
|
|
526
743
|
function payloadTimestamp(payload, field) {
|
|
527
|
-
const rawSpan =
|
|
528
|
-
const rawTrace =
|
|
744
|
+
const rawSpan = asRecord2(payload.rawSpan);
|
|
745
|
+
const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
|
|
529
746
|
const raw = rawSpan?.[field] ?? rawTrace?.[field];
|
|
530
747
|
if (typeof raw !== "string") {
|
|
531
748
|
return void 0;
|
|
@@ -534,7 +751,7 @@ function payloadTimestamp(payload, field) {
|
|
|
534
751
|
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
535
752
|
}
|
|
536
753
|
function hasError(payload) {
|
|
537
|
-
const spanData =
|
|
754
|
+
const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
|
|
538
755
|
if (spanData?.error != null) {
|
|
539
756
|
return true;
|
|
540
757
|
}
|
|
@@ -544,7 +761,6 @@ function hasError(payload) {
|
|
|
544
761
|
function createOtelTransport(options) {
|
|
545
762
|
return new OtelBatchTransport({
|
|
546
763
|
...options,
|
|
547
|
-
collectorEndpoint: readEnv(COLLECTOR_ENDPOINT_ENV) || void 0,
|
|
548
764
|
exportConcurrency: readBoundedIntEnv(
|
|
549
765
|
EXPORT_CONCURRENCY_ENV,
|
|
550
766
|
MAX_EXPORT_CONCURRENCY,
|
|
@@ -579,7 +795,7 @@ function shutdownOtelTransports(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS) {
|
|
|
579
795
|
(transport, remaining) => transport.shutdown(remaining)
|
|
580
796
|
);
|
|
581
797
|
}
|
|
582
|
-
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,
|
|
798
|
+
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;
|
|
583
799
|
var init_otel = __esm({
|
|
584
800
|
"src/otel.ts"() {
|
|
585
801
|
"use strict";
|
|
@@ -589,6 +805,7 @@ var init_otel = __esm({
|
|
|
589
805
|
import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
590
806
|
init_constants();
|
|
591
807
|
init_errors();
|
|
808
|
+
init_payloadBudget();
|
|
592
809
|
init_readEnv();
|
|
593
810
|
init_serializePayload();
|
|
594
811
|
init_unrefTimer();
|
|
@@ -599,10 +816,8 @@ var init_otel = __esm({
|
|
|
599
816
|
MAX_EXPORT_REQUEST_BYTES = 3e6;
|
|
600
817
|
MAX_REQUEST_BYTES_ENV = "BITFAB_OTEL_MAX_REQUEST_BYTES";
|
|
601
818
|
EXPORT_CONCURRENCY_ENV = "BITFAB_OTEL_EXPORT_CONCURRENCY";
|
|
602
|
-
COLLECTOR_ENDPOINT_ENV = "BITFAB_OTEL_EXPORTER_ENDPOINT";
|
|
603
819
|
MAX_QUEUE_SIZE = 8192;
|
|
604
820
|
DIRECT_MAX_EXPORT_BATCH_SIZE = 512;
|
|
605
|
-
COLLECTOR_MAX_EXPORT_BATCH_SIZE = 32;
|
|
606
821
|
DIRECT_MAX_REQUEST_BATCH_SIZE = 8;
|
|
607
822
|
DEFAULT_EXPORT_CONCURRENCY = 32;
|
|
608
823
|
MAX_EXPORT_CONCURRENCY = 64;
|
|
@@ -616,6 +831,7 @@ var init_otel = __esm({
|
|
|
616
831
|
traceSubmissionSpanIds = /* @__PURE__ */ new Map();
|
|
617
832
|
replayTraceSubmissions = /* @__PURE__ */ new Set();
|
|
618
833
|
submissionCounter = 0;
|
|
834
|
+
SPAN_SEPARATOR_BYTES = 1;
|
|
619
835
|
OtlpPayloadTooLargeError = class extends Error {
|
|
620
836
|
};
|
|
621
837
|
OtlpPartialSuccessError = class extends Error {
|
|
@@ -644,57 +860,55 @@ var init_otel = __esm({
|
|
|
644
860
|
return true;
|
|
645
861
|
}
|
|
646
862
|
let encoded;
|
|
863
|
+
let envelope;
|
|
647
864
|
try {
|
|
648
|
-
encoded = spans.map(
|
|
865
|
+
encoded = spans.map(encodeSpan);
|
|
866
|
+
envelope = requestEnvelope(spans[0]);
|
|
649
867
|
} catch (error) {
|
|
650
868
|
logError("failed to encode an OpenTelemetry span batch", error);
|
|
651
869
|
return false;
|
|
652
870
|
}
|
|
653
|
-
const
|
|
654
|
-
const batches = this.buildRequestBatches(first, encoded);
|
|
871
|
+
const batches = this.buildRequestBatches(envelope, encoded);
|
|
655
872
|
const results = await mapWithConcurrency(
|
|
656
873
|
batches,
|
|
657
874
|
this.exportConcurrency,
|
|
658
|
-
(batch) => this.send(
|
|
875
|
+
(batch) => this.send(envelope, batch)
|
|
659
876
|
);
|
|
660
877
|
return results.every(Boolean);
|
|
661
878
|
}
|
|
662
|
-
buildRequestBatches(
|
|
879
|
+
buildRequestBatches(envelope, spans) {
|
|
663
880
|
const batches = [];
|
|
664
881
|
let current = [];
|
|
882
|
+
let size = envelope.size;
|
|
665
883
|
for (const span of spans) {
|
|
666
|
-
|
|
667
|
-
|
|
884
|
+
const addition = span.size + (current.length > 0 ? SPAN_SEPARATOR_BYTES : 0);
|
|
885
|
+
if (current.length > 0 && (current.length >= this.maxRequestBatchSize || size + addition > this.maxRequestBytes)) {
|
|
886
|
+
batches.push({ spans: current, size });
|
|
668
887
|
current = [];
|
|
888
|
+
size = envelope.size;
|
|
669
889
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
batches.push(current);
|
|
673
|
-
current = [span];
|
|
674
|
-
} else {
|
|
675
|
-
current = candidate;
|
|
676
|
-
}
|
|
890
|
+
current.push(span);
|
|
891
|
+
size += span.size + (current.length > 1 ? SPAN_SEPARATOR_BYTES : 0);
|
|
677
892
|
}
|
|
678
893
|
if (current.length > 0) {
|
|
679
|
-
batches.push(current);
|
|
894
|
+
batches.push({ spans: current, size });
|
|
680
895
|
}
|
|
681
896
|
return batches;
|
|
682
897
|
}
|
|
683
|
-
async send(
|
|
684
|
-
|
|
685
|
-
if (encodedSize(payload) > this.maxRequestBytes) {
|
|
898
|
+
async send(envelope, batch) {
|
|
899
|
+
if (batch.size > this.maxRequestBytes) {
|
|
686
900
|
logError(
|
|
687
901
|
"a single OpenTelemetry span exceeded the configured request-size target and could not be exported"
|
|
688
902
|
);
|
|
689
903
|
return false;
|
|
690
904
|
}
|
|
691
905
|
try {
|
|
692
|
-
await this.sendWithRetries(
|
|
906
|
+
await this.sendWithRetries(encodeRequest(envelope, batch.spans));
|
|
693
907
|
return true;
|
|
694
908
|
} catch (error) {
|
|
695
909
|
if (error instanceof OtlpPayloadTooLargeError) {
|
|
696
910
|
logError(
|
|
697
|
-
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"
|
|
911
|
+
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"
|
|
698
912
|
);
|
|
699
913
|
return false;
|
|
700
914
|
}
|
|
@@ -718,15 +932,15 @@ var init_otel = __esm({
|
|
|
718
932
|
* the server does not yet understand. The fix is a client-supplied
|
|
719
933
|
* idempotency key that ingestion dedupes on.
|
|
720
934
|
*/
|
|
721
|
-
async sendWithRetries(
|
|
935
|
+
async sendWithRetries(body) {
|
|
722
936
|
for (let attempt = 0; attempt < MAX_SEND_ATTEMPTS; attempt += 1) {
|
|
723
937
|
try {
|
|
724
938
|
const response = await this.directSender(
|
|
725
939
|
OTLP_TRACES_ENDPOINT,
|
|
726
|
-
|
|
940
|
+
body,
|
|
727
941
|
EXPORT_TIMEOUT_MILLIS
|
|
728
942
|
);
|
|
729
|
-
const partialSuccess =
|
|
943
|
+
const partialSuccess = asRecord2(response?.partialSuccess);
|
|
730
944
|
const rejected = partialSuccess?.rejectedSpans;
|
|
731
945
|
if (rejected !== void 0 && rejected !== "0" && rejected !== 0) {
|
|
732
946
|
logError(
|
|
@@ -754,115 +968,6 @@ var init_otel = __esm({
|
|
|
754
968
|
async forceFlush() {
|
|
755
969
|
}
|
|
756
970
|
};
|
|
757
|
-
CollectorSpanExporter = class {
|
|
758
|
-
constructor(endpoint, apiKey, maxRequestBytes) {
|
|
759
|
-
this.endpoint = endpoint;
|
|
760
|
-
this.apiKey = apiKey;
|
|
761
|
-
this.maxRequestBytes = maxRequestBytes;
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Loaded through a dynamic import rather than a top-level one so bundlers
|
|
765
|
-
* code-split it: Collector delivery is opt-in, and a consumer who never sets
|
|
766
|
-
* an endpoint should not pay for the exporter in their initial bundle. It is
|
|
767
|
-
* a hard dependency, so this cannot fail for want of the package.
|
|
768
|
-
*/
|
|
769
|
-
loadExporterModule() {
|
|
770
|
-
if (!this.pendingModule) {
|
|
771
|
-
this.pendingModule = import("@opentelemetry/exporter-trace-otlp-proto");
|
|
772
|
-
}
|
|
773
|
-
return this.pendingModule;
|
|
774
|
-
}
|
|
775
|
-
export(spans, resultCallback) {
|
|
776
|
-
void this.exportAsync(spans).then(
|
|
777
|
-
(succeeded) => {
|
|
778
|
-
resultCallback({
|
|
779
|
-
code: succeeded ? import_core.ExportResultCode.SUCCESS : import_core.ExportResultCode.FAILED
|
|
780
|
-
});
|
|
781
|
-
},
|
|
782
|
-
(error) => {
|
|
783
|
-
resultCallback({ code: import_core.ExportResultCode.FAILED, error });
|
|
784
|
-
}
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
async exportAsync(spans) {
|
|
788
|
-
if (spans.length === 0) {
|
|
789
|
-
return true;
|
|
790
|
-
}
|
|
791
|
-
let delegate;
|
|
792
|
-
try {
|
|
793
|
-
delegate = await this.resolveDelegate();
|
|
794
|
-
} catch (error) {
|
|
795
|
-
logError("failed to build the OTLP Collector exporter", error);
|
|
796
|
-
return false;
|
|
797
|
-
}
|
|
798
|
-
const results = await Promise.all(
|
|
799
|
-
this.partition(spans).map(
|
|
800
|
-
(batch) => new Promise((resolve) => {
|
|
801
|
-
try {
|
|
802
|
-
delegate.export(batch, (result) => {
|
|
803
|
-
resolve(result.code === import_core.ExportResultCode.SUCCESS);
|
|
804
|
-
});
|
|
805
|
-
} catch (error) {
|
|
806
|
-
logError("Collector export threw", error);
|
|
807
|
-
resolve(false);
|
|
808
|
-
}
|
|
809
|
-
})
|
|
810
|
-
)
|
|
811
|
-
);
|
|
812
|
-
return results.every(Boolean);
|
|
813
|
-
}
|
|
814
|
-
/**
|
|
815
|
-
* Partition by the encoded JSON size of each carrier rather than its encoded
|
|
816
|
-
* protobuf size. Protobuf is strictly smaller than the equivalent JSON for
|
|
817
|
-
* these payloads, so the JSON figure is a conservative bound that keeps every
|
|
818
|
-
* request under the target without pulling `@opentelemetry/otlp-transformer`
|
|
819
|
-
* into the dependency set purely to measure bytes.
|
|
820
|
-
*/
|
|
821
|
-
partition(spans) {
|
|
822
|
-
const batches = [];
|
|
823
|
-
let current = [];
|
|
824
|
-
let currentSize = 0;
|
|
825
|
-
for (const span of spans) {
|
|
826
|
-
const size = encodedSize(spanToOtlp(span));
|
|
827
|
-
if (current.length > 0 && currentSize + size > this.maxRequestBytes) {
|
|
828
|
-
batches.push(current);
|
|
829
|
-
current = [];
|
|
830
|
-
currentSize = 0;
|
|
831
|
-
}
|
|
832
|
-
current.push(span);
|
|
833
|
-
currentSize += size;
|
|
834
|
-
}
|
|
835
|
-
if (current.length > 0) {
|
|
836
|
-
batches.push(current);
|
|
837
|
-
}
|
|
838
|
-
return batches;
|
|
839
|
-
}
|
|
840
|
-
async resolveDelegate() {
|
|
841
|
-
const apiKey = this.apiKey() ?? "";
|
|
842
|
-
if (this.delegate && this.delegateApiKey === apiKey) {
|
|
843
|
-
return this.delegate;
|
|
844
|
-
}
|
|
845
|
-
const { OTLPTraceExporter } = await this.loadExporterModule();
|
|
846
|
-
const previous = this.delegate;
|
|
847
|
-
this.delegate = new OTLPTraceExporter({
|
|
848
|
-
url: this.endpoint,
|
|
849
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
850
|
-
timeoutMillis: EXPORT_TIMEOUT_MILLIS
|
|
851
|
-
});
|
|
852
|
-
this.delegateApiKey = apiKey;
|
|
853
|
-
if (previous) {
|
|
854
|
-
void previous.shutdown().catch(() => {
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
return this.delegate;
|
|
858
|
-
}
|
|
859
|
-
async shutdown() {
|
|
860
|
-
await this.delegate?.shutdown();
|
|
861
|
-
}
|
|
862
|
-
async forceFlush() {
|
|
863
|
-
await this.delegate?.forceFlush?.();
|
|
864
|
-
}
|
|
865
|
-
};
|
|
866
971
|
DeliveryTrackingExporter = class {
|
|
867
972
|
constructor(exporter) {
|
|
868
973
|
this.exporter = exporter;
|
|
@@ -905,27 +1010,22 @@ var init_otel = __esm({
|
|
|
905
1010
|
OtelBatchTransport = class {
|
|
906
1011
|
constructor(options) {
|
|
907
1012
|
this.closed = false;
|
|
908
|
-
const collectorEndpoint = options.collectorEndpoint;
|
|
909
1013
|
const maxRequestBytes = options.maxRequestBytes ?? MAX_EXPORT_REQUEST_BYTES;
|
|
910
1014
|
const maxRequestBatchSize = options.maxRequestBatchSize ?? DIRECT_MAX_REQUEST_BATCH_SIZE;
|
|
911
1015
|
if (maxRequestBatchSize <= 0) {
|
|
912
1016
|
throw new BitfabError("maxRequestBatchSize must be a positive integer");
|
|
913
1017
|
}
|
|
914
1018
|
this.deliveryTracker = new DeliveryTrackingExporter(
|
|
915
|
-
|
|
1019
|
+
new BitfabSpanExporter(
|
|
916
1020
|
options.directSender,
|
|
917
1021
|
maxRequestBytes,
|
|
918
1022
|
maxRequestBatchSize,
|
|
919
1023
|
options.exportConcurrency ?? DEFAULT_EXPORT_CONCURRENCY
|
|
920
|
-
) : new CollectorSpanExporter(
|
|
921
|
-
normalizeCollectorEndpoint(collectorEndpoint),
|
|
922
|
-
options.apiKey,
|
|
923
|
-
maxRequestBytes
|
|
924
1024
|
)
|
|
925
1025
|
);
|
|
926
1026
|
this.processor = new import_sdk_trace_base.BatchSpanProcessor(this.deliveryTracker, {
|
|
927
1027
|
maxQueueSize: options.maxQueueSize ?? MAX_QUEUE_SIZE,
|
|
928
|
-
maxExportBatchSize: options.maxExportBatchSize ??
|
|
1028
|
+
maxExportBatchSize: options.maxExportBatchSize ?? DIRECT_MAX_EXPORT_BATCH_SIZE,
|
|
929
1029
|
scheduledDelayMillis: SCHEDULE_DELAY_MILLIS,
|
|
930
1030
|
exportTimeoutMillis: options.exportTimeoutMillis ?? EXPORT_TIMEOUT_MILLIS
|
|
931
1031
|
});
|
|
@@ -1078,6 +1178,7 @@ var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCL
|
|
|
1078
1178
|
var init_http = __esm({
|
|
1079
1179
|
"src/http.ts"() {
|
|
1080
1180
|
"use strict";
|
|
1181
|
+
init_compress();
|
|
1081
1182
|
init_constants();
|
|
1082
1183
|
init_errors();
|
|
1083
1184
|
init_replayContext();
|
|
@@ -1142,8 +1243,7 @@ var init_http = __esm({
|
|
|
1142
1243
|
}
|
|
1143
1244
|
if (!this.traceTransport) {
|
|
1144
1245
|
this.traceTransport = createTraceTransport({
|
|
1145
|
-
|
|
1146
|
-
directSender: (endpoint, payload, timeoutMs) => this.request(endpoint, payload, {
|
|
1246
|
+
directSender: (endpoint, body, timeoutMs) => this.sendEncoded(endpoint, body, {
|
|
1147
1247
|
timeout: timeoutMs
|
|
1148
1248
|
})
|
|
1149
1249
|
});
|
|
@@ -1213,11 +1313,6 @@ var init_http = __esm({
|
|
|
1213
1313
|
* @throws {BitfabError} If the request fails
|
|
1214
1314
|
*/
|
|
1215
1315
|
async request(endpoint, payload, options) {
|
|
1216
|
-
const url = `${this.serviceUrl}${endpoint}`;
|
|
1217
|
-
const timeout = options?.timeout ?? this.timeout;
|
|
1218
|
-
const method = options?.method ?? "POST";
|
|
1219
|
-
const controller = new AbortController();
|
|
1220
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1221
1316
|
const { body, dropped } = serializePayloadBody(payload);
|
|
1222
1317
|
if (dropped.length > 0) {
|
|
1223
1318
|
try {
|
|
@@ -1227,14 +1322,33 @@ var init_http = __esm({
|
|
|
1227
1322
|
} catch {
|
|
1228
1323
|
}
|
|
1229
1324
|
}
|
|
1325
|
+
return this.sendEncoded(endpoint, body, options);
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* POST an already-encoded body. The span transport encodes its own batches,
|
|
1329
|
+
* so routing them back through {@link HttpClient.request} would encode the
|
|
1330
|
+
* same data twice.
|
|
1331
|
+
*/
|
|
1332
|
+
async sendEncoded(endpoint, body, options) {
|
|
1333
|
+
const url = `${this.serviceUrl}${endpoint}`;
|
|
1334
|
+
const timeout = options?.timeout ?? this.timeout;
|
|
1335
|
+
const method = options?.method ?? "POST";
|
|
1336
|
+
const controller = new AbortController();
|
|
1337
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1338
|
+
const prepared = encodeRequestBody(body);
|
|
1339
|
+
const encoded = prepared instanceof Promise ? await prepared : prepared;
|
|
1340
|
+
const headers = {
|
|
1341
|
+
"Content-Type": "application/json",
|
|
1342
|
+
Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
|
|
1343
|
+
};
|
|
1344
|
+
if (encoded.contentEncoding) {
|
|
1345
|
+
headers["Content-Encoding"] = encoded.contentEncoding;
|
|
1346
|
+
}
|
|
1230
1347
|
try {
|
|
1231
1348
|
const response = await fetch(url, {
|
|
1232
1349
|
method,
|
|
1233
|
-
headers
|
|
1234
|
-
|
|
1235
|
-
Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
|
|
1236
|
-
},
|
|
1237
|
-
body,
|
|
1350
|
+
headers,
|
|
1351
|
+
body: encoded.body,
|
|
1238
1352
|
signal: controller.signal
|
|
1239
1353
|
});
|
|
1240
1354
|
if (!response.ok) {
|
|
@@ -1683,9 +1797,10 @@ var init_serialize = __esm({
|
|
|
1683
1797
|
"src/serialize.ts"() {
|
|
1684
1798
|
"use strict";
|
|
1685
1799
|
import_superjson = __toESM(require("superjson"), 1);
|
|
1800
|
+
init_payloadBudget();
|
|
1686
1801
|
init_warnOnce();
|
|
1687
|
-
MAX_SERIALIZED_BYTES =
|
|
1688
|
-
MAX_FRAMEWORK_SERIALIZED_BYTES =
|
|
1802
|
+
MAX_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
|
|
1803
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
|
|
1689
1804
|
MAX_SAFE_DEPTH = 6;
|
|
1690
1805
|
}
|
|
1691
1806
|
});
|