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/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.
|
|
100
|
+
__version__ = "0.36.0";
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
103
|
|
|
@@ -111,6 +111,87 @@ var init_constants = __esm({
|
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
+
// src/readEnv.ts
|
|
115
|
+
function readEnv(name) {
|
|
116
|
+
if (typeof process !== "undefined" && process.env) {
|
|
117
|
+
return process.env[name];
|
|
118
|
+
}
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
var init_readEnv = __esm({
|
|
122
|
+
"src/readEnv.ts"() {
|
|
123
|
+
"use strict";
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// src/compress.ts
|
|
128
|
+
function toArrayBuffer(view) {
|
|
129
|
+
return view.buffer.slice(
|
|
130
|
+
view.byteOffset,
|
|
131
|
+
view.byteOffset + view.byteLength
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
async function gzipViaStream(bytes) {
|
|
135
|
+
const stream = new Blob([bytes]).stream().pipeThrough(new CompressionStream("gzip"));
|
|
136
|
+
return await new Response(stream).arrayBuffer();
|
|
137
|
+
}
|
|
138
|
+
function encodeRequestBody(body) {
|
|
139
|
+
if (readEnv(DISABLE_COMPRESSION_ENV)) {
|
|
140
|
+
return { body };
|
|
141
|
+
}
|
|
142
|
+
const bytes = new TextEncoder().encode(body);
|
|
143
|
+
if (bytes.byteLength < MIN_COMPRESSED_BYTES) {
|
|
144
|
+
return { body };
|
|
145
|
+
}
|
|
146
|
+
if (gzipNode) {
|
|
147
|
+
return gzipNode(bytes).then(
|
|
148
|
+
(compressed) => ({
|
|
149
|
+
body: toArrayBuffer(compressed),
|
|
150
|
+
contentEncoding: "gzip"
|
|
151
|
+
}),
|
|
152
|
+
() => ({ body })
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (typeof CompressionStream === "undefined") {
|
|
156
|
+
return { body };
|
|
157
|
+
}
|
|
158
|
+
return gzipViaStream(bytes).then(
|
|
159
|
+
(compressed) => ({ body: compressed, contentEncoding: "gzip" }),
|
|
160
|
+
() => ({ body })
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
var DISABLE_COMPRESSION_ENV, MIN_COMPRESSED_BYTES, gzipNode, _nodeGzipReady;
|
|
164
|
+
var init_compress = __esm({
|
|
165
|
+
"src/compress.ts"() {
|
|
166
|
+
"use strict";
|
|
167
|
+
init_readEnv();
|
|
168
|
+
DISABLE_COMPRESSION_ENV = "BITFAB_DISABLE_COMPRESSION";
|
|
169
|
+
MIN_COMPRESSED_BYTES = 8192;
|
|
170
|
+
_nodeGzipReady = (typeof process !== "undefined" && process.versions?.node ? (
|
|
171
|
+
// The join trick hides "node:zlib" from static analysis so bundlers that
|
|
172
|
+
// ban Node.js built-ins don't fail at build time. webpackIgnore tells
|
|
173
|
+
// webpack/turbopack to emit a native import() so Node.js can resolve the
|
|
174
|
+
// module at runtime. Same pattern as `asyncStorage.ts`.
|
|
175
|
+
import(
|
|
176
|
+
/* webpackIgnore: true */
|
|
177
|
+
["node", "zlib"].join(":")
|
|
178
|
+
).then(({ gzip }) => {
|
|
179
|
+
gzipNode = (data) => new Promise((resolve, reject) => {
|
|
180
|
+
gzip(data, (error, result) => {
|
|
181
|
+
if (error) {
|
|
182
|
+
reject(error);
|
|
183
|
+
} else {
|
|
184
|
+
resolve(result);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}).catch(() => {
|
|
189
|
+
})
|
|
190
|
+
) : Promise.resolve()).then(() => {
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
114
195
|
// src/errors.ts
|
|
115
196
|
var BitfabError;
|
|
116
197
|
var init_errors = __esm({
|
|
@@ -160,6 +241,131 @@ var init_replayContext = __esm({
|
|
|
160
241
|
}
|
|
161
242
|
});
|
|
162
243
|
|
|
244
|
+
// src/payloadBudget.ts
|
|
245
|
+
function byteLength(value) {
|
|
246
|
+
return textEncoder ? textEncoder.encode(value).length : value.length;
|
|
247
|
+
}
|
|
248
|
+
function carrierByteLength(body) {
|
|
249
|
+
return carrierBytesOf(textEncoder ? textEncoder.encode(body) : null, body);
|
|
250
|
+
}
|
|
251
|
+
function carrierBytesOf(encoded, body) {
|
|
252
|
+
if (!encoded) {
|
|
253
|
+
return body.length + 2;
|
|
254
|
+
}
|
|
255
|
+
let extra = 2;
|
|
256
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
257
|
+
const byte = encoded[i];
|
|
258
|
+
if (byte === 34 || byte === 92) {
|
|
259
|
+
extra += 1;
|
|
260
|
+
} else if (byte < 32) {
|
|
261
|
+
extra += byte === 8 || byte === 9 || byte === 10 || byte === 12 || byte === 13 ? 1 : 5;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return encoded.length + extra;
|
|
265
|
+
}
|
|
266
|
+
function fitsCarrierBudget(body) {
|
|
267
|
+
const units = body.length;
|
|
268
|
+
if (units * MAX_BYTES_PER_UNIT + 2 <= MAX_SPAN_CARRIER_BYTES) {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
if (units + 2 > MAX_SPAN_CARRIER_BYTES) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
return carrierByteLength(body) <= MAX_SPAN_CARRIER_BYTES;
|
|
275
|
+
}
|
|
276
|
+
function asRecord(value) {
|
|
277
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
278
|
+
}
|
|
279
|
+
function cloneTrimmable(payload) {
|
|
280
|
+
const copy = { ...payload };
|
|
281
|
+
const containers = [];
|
|
282
|
+
const spanData = asRecord(copy.span_data);
|
|
283
|
+
if (spanData) {
|
|
284
|
+
const clone = { ...spanData };
|
|
285
|
+
copy.span_data = clone;
|
|
286
|
+
containers.push(clone);
|
|
287
|
+
}
|
|
288
|
+
const rawSpan = asRecord(copy.rawSpan);
|
|
289
|
+
const rawSpanData = rawSpan && asRecord(rawSpan.span_data);
|
|
290
|
+
if (rawSpan && rawSpanData) {
|
|
291
|
+
const clone = { ...rawSpanData };
|
|
292
|
+
copy.rawSpan = { ...rawSpan, span_data: clone };
|
|
293
|
+
containers.push(clone);
|
|
294
|
+
}
|
|
295
|
+
if (containers.length === 0) {
|
|
296
|
+
containers.push(copy);
|
|
297
|
+
}
|
|
298
|
+
return { copy, containers };
|
|
299
|
+
}
|
|
300
|
+
function collectCandidates(containers) {
|
|
301
|
+
const candidates = [];
|
|
302
|
+
for (const container of containers) {
|
|
303
|
+
for (const [key, value] of Object.entries(container)) {
|
|
304
|
+
if (STRUCTURAL_SPAN_KEYS.has(key) || value == null) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
let size;
|
|
308
|
+
try {
|
|
309
|
+
size = byteLength(JSON.stringify(value) ?? "");
|
|
310
|
+
} catch {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
candidates.push({ container, key, size });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return candidates.sort((a, b) => b.size - a.size);
|
|
317
|
+
}
|
|
318
|
+
function trimPayloadToBudget(payload, encode) {
|
|
319
|
+
const { copy, containers } = cloneTrimmable(payload);
|
|
320
|
+
const candidates = collectCandidates(containers);
|
|
321
|
+
if (candidates.length === 0) {
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
const trimmed = [];
|
|
325
|
+
for (const candidate of candidates) {
|
|
326
|
+
candidate.container[candidate.key] = `<unserializable: too_large_${candidate.size}_bytes>`;
|
|
327
|
+
trimmed.push(candidate.key);
|
|
328
|
+
let body;
|
|
329
|
+
try {
|
|
330
|
+
body = encode(copy);
|
|
331
|
+
} catch {
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
if (fitsCarrierBudget(body)) {
|
|
335
|
+
return { value: copy, trimmed };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
function markPayloadTrimmed(value, trimmed) {
|
|
341
|
+
const existing = Array.isArray(value.errors) ? value.errors : [];
|
|
342
|
+
value.errors = [
|
|
343
|
+
...existing,
|
|
344
|
+
{
|
|
345
|
+
source: "sdk",
|
|
346
|
+
step: "payload_budget",
|
|
347
|
+
error: `trimmed oversized field(s) to fit the ${MAX_SPAN_CARRIER_BYTES}-byte span carrier budget: ${[
|
|
348
|
+
...new Set(trimmed)
|
|
349
|
+
].join(", ")}`
|
|
350
|
+
}
|
|
351
|
+
];
|
|
352
|
+
}
|
|
353
|
+
var MAX_SPAN_CARRIER_BYTES, textEncoder, MAX_BYTES_PER_UNIT, STRUCTURAL_SPAN_KEYS;
|
|
354
|
+
var init_payloadBudget = __esm({
|
|
355
|
+
"src/payloadBudget.ts"() {
|
|
356
|
+
"use strict";
|
|
357
|
+
MAX_SPAN_CARRIER_BYTES = 28e5;
|
|
358
|
+
textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
|
|
359
|
+
MAX_BYTES_PER_UNIT = 3;
|
|
360
|
+
STRUCTURAL_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
361
|
+
"name",
|
|
362
|
+
"type",
|
|
363
|
+
"function_name",
|
|
364
|
+
"error_source"
|
|
365
|
+
]);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
163
369
|
// src/warnOnce.ts
|
|
164
370
|
function warnOnce(key, message) {
|
|
165
371
|
if (warned.has(key)) {
|
|
@@ -181,8 +387,37 @@ var init_warnOnce = __esm({
|
|
|
181
387
|
|
|
182
388
|
// src/serializePayload.ts
|
|
183
389
|
function serializePayloadBody(payload) {
|
|
390
|
+
const encoded = encodePayloadBody(payload);
|
|
391
|
+
if (fitsCarrierBudget(encoded.body)) {
|
|
392
|
+
return { body: encoded.body, dropped: encoded.dropped };
|
|
393
|
+
}
|
|
394
|
+
return applyPayloadBudget(encoded);
|
|
395
|
+
}
|
|
396
|
+
function applyPayloadBudget(encoded) {
|
|
397
|
+
const result = encoded.value ? trimPayloadToBudget(
|
|
398
|
+
encoded.value,
|
|
399
|
+
(value) => encodePayloadBody(value).body
|
|
400
|
+
) : void 0;
|
|
401
|
+
if (!result) {
|
|
402
|
+
return { body: encoded.body, dropped: encoded.dropped };
|
|
403
|
+
}
|
|
404
|
+
warnOnce(
|
|
405
|
+
"payload:over-budget",
|
|
406
|
+
`a span payload exceeded the ${MAX_SPAN_CARRIER_BYTES}-byte carrier budget; its largest field(s) (${[
|
|
407
|
+
...new Set(result.trimmed)
|
|
408
|
+
].join(
|
|
409
|
+
", "
|
|
410
|
+
)}) were replaced with placeholders so the span still ships. The span is incomplete and may not be replayable.`
|
|
411
|
+
);
|
|
412
|
+
markPayloadTrimmed(result.value, result.trimmed);
|
|
413
|
+
return {
|
|
414
|
+
body: encodePayloadBody(result.value).body,
|
|
415
|
+
dropped: encoded.dropped
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function encodePayloadBody(payload) {
|
|
184
419
|
try {
|
|
185
|
-
return { body: JSON.stringify(payload), dropped: [] };
|
|
420
|
+
return { body: JSON.stringify(payload), dropped: [], value: payload };
|
|
186
421
|
} catch {
|
|
187
422
|
const dropped = [];
|
|
188
423
|
const sanitize = (value, seen) => {
|
|
@@ -247,12 +482,11 @@ function serializePayloadBody(payload) {
|
|
|
247
482
|
sanitized = sanitize(payload, /* @__PURE__ */ new WeakSet());
|
|
248
483
|
} catch (error) {
|
|
249
484
|
const message = error instanceof Error ? error.message : String(error);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
dropped
|
|
253
|
-
};
|
|
485
|
+
const marker = { error: `payload_serialize_failed: ${message}` };
|
|
486
|
+
return { body: JSON.stringify(marker), dropped, value: marker };
|
|
254
487
|
}
|
|
255
|
-
|
|
488
|
+
const isRecord = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
|
|
489
|
+
if (dropped.length > 0 && isRecord) {
|
|
256
490
|
const obj = sanitized;
|
|
257
491
|
const existing = Array.isArray(obj.errors) ? obj.errors : [];
|
|
258
492
|
obj.errors = [
|
|
@@ -266,29 +500,21 @@ function serializePayloadBody(payload) {
|
|
|
266
500
|
}
|
|
267
501
|
];
|
|
268
502
|
}
|
|
269
|
-
return {
|
|
503
|
+
return {
|
|
504
|
+
body: JSON.stringify(sanitized),
|
|
505
|
+
dropped,
|
|
506
|
+
value: isRecord ? sanitized : void 0
|
|
507
|
+
};
|
|
270
508
|
}
|
|
271
509
|
}
|
|
272
510
|
var init_serializePayload = __esm({
|
|
273
511
|
"src/serializePayload.ts"() {
|
|
274
512
|
"use strict";
|
|
513
|
+
init_payloadBudget();
|
|
275
514
|
init_warnOnce();
|
|
276
515
|
}
|
|
277
516
|
});
|
|
278
517
|
|
|
279
|
-
// src/readEnv.ts
|
|
280
|
-
function readEnv(name) {
|
|
281
|
-
if (typeof process !== "undefined" && process.env) {
|
|
282
|
-
return process.env[name];
|
|
283
|
-
}
|
|
284
|
-
return void 0;
|
|
285
|
-
}
|
|
286
|
-
var init_readEnv = __esm({
|
|
287
|
-
"src/readEnv.ts"() {
|
|
288
|
-
"use strict";
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
|
|
292
518
|
// src/unrefTimer.ts
|
|
293
519
|
function unrefTimer(timer) {
|
|
294
520
|
const handle = timer;
|
|
@@ -334,7 +560,7 @@ function recordTraceSubmission(operation, payload) {
|
|
|
334
560
|
return;
|
|
335
561
|
}
|
|
336
562
|
if (operation === "external_span") {
|
|
337
|
-
const rawSpan =
|
|
563
|
+
const rawSpan = asRecord2(payload.rawSpan);
|
|
338
564
|
if (typeof rawSpan?.id !== "string") {
|
|
339
565
|
submissionCounter += 1;
|
|
340
566
|
}
|
|
@@ -371,14 +597,14 @@ function takeReplaySpanCounts(traceIds) {
|
|
|
371
597
|
}
|
|
372
598
|
return counts;
|
|
373
599
|
}
|
|
374
|
-
function
|
|
600
|
+
function asRecord2(value) {
|
|
375
601
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
376
602
|
}
|
|
377
603
|
function resolveSourceTraceId(payload) {
|
|
378
604
|
if (typeof payload.sourceTraceId === "string") {
|
|
379
605
|
return payload.sourceTraceId;
|
|
380
606
|
}
|
|
381
|
-
const rawTrace =
|
|
607
|
+
const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
|
|
382
608
|
return typeof rawTrace?.id === "string" ? rawTrace.id : void 0;
|
|
383
609
|
}
|
|
384
610
|
function otlpValue(value) {
|
|
@@ -436,32 +662,27 @@ function spanToOtlp(span) {
|
|
|
436
662
|
}
|
|
437
663
|
return result;
|
|
438
664
|
}
|
|
439
|
-
function
|
|
665
|
+
function encodeSpan(span) {
|
|
666
|
+
const json = JSON.stringify(spanToOtlp(span));
|
|
667
|
+
return { json, size: byteLength(json) };
|
|
668
|
+
}
|
|
669
|
+
function requestEnvelope(first) {
|
|
440
670
|
const scope = first.instrumentationScope;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
]
|
|
455
|
-
}
|
|
456
|
-
]
|
|
457
|
-
};
|
|
671
|
+
const resource = JSON.stringify({
|
|
672
|
+
attributes: otlpAttributes(
|
|
673
|
+
first.resource.attributes
|
|
674
|
+
)
|
|
675
|
+
});
|
|
676
|
+
const scopeJson = JSON.stringify({
|
|
677
|
+
name: scope.name,
|
|
678
|
+
version: scope.version ?? ""
|
|
679
|
+
});
|
|
680
|
+
const head = `{"resourceSpans":[{"resource":${resource},"scopeSpans":[{"scope":${scopeJson},"spans":[`;
|
|
681
|
+
const tail = "]}]}]}";
|
|
682
|
+
return { head, tail, size: byteLength(head) + byteLength(tail) };
|
|
458
683
|
}
|
|
459
|
-
function
|
|
460
|
-
|
|
461
|
-
if (typeof TextEncoder !== "undefined") {
|
|
462
|
-
return new TextEncoder().encode(json).length;
|
|
463
|
-
}
|
|
464
|
-
return json.length;
|
|
684
|
+
function encodeRequest(envelope, spans) {
|
|
685
|
+
return envelope.head + spans.map((span) => span.json).join(",") + envelope.tail;
|
|
465
686
|
}
|
|
466
687
|
function delay(ms) {
|
|
467
688
|
return new Promise((resolve) => {
|
|
@@ -511,16 +732,12 @@ function isRetryable(error) {
|
|
|
511
732
|
}
|
|
512
733
|
return RETRYABLE_STATUSES.has(status) || status >= 500;
|
|
513
734
|
}
|
|
514
|
-
function normalizeCollectorEndpoint(endpoint) {
|
|
515
|
-
const trimmed = endpoint.replace(/\/+$/, "");
|
|
516
|
-
return trimmed.endsWith("/v1/traces") ? trimmed : `${trimmed}/v1/traces`;
|
|
517
|
-
}
|
|
518
735
|
function endSpan(span, endTime) {
|
|
519
736
|
span.end(endTime);
|
|
520
737
|
}
|
|
521
738
|
function spanName(operation, payload) {
|
|
522
739
|
if (operation === "external_span") {
|
|
523
|
-
const spanData =
|
|
740
|
+
const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
|
|
524
741
|
if (typeof spanData?.name === "string") {
|
|
525
742
|
return spanData.name;
|
|
526
743
|
}
|
|
@@ -531,8 +748,8 @@ function spanName(operation, payload) {
|
|
|
531
748
|
return `bitfab.${operation}`;
|
|
532
749
|
}
|
|
533
750
|
function payloadTimestamp(payload, field) {
|
|
534
|
-
const rawSpan =
|
|
535
|
-
const rawTrace =
|
|
751
|
+
const rawSpan = asRecord2(payload.rawSpan);
|
|
752
|
+
const rawTrace = asRecord2(payload.externalTrace) ?? asRecord2(payload.rawTrace);
|
|
536
753
|
const raw = rawSpan?.[field] ?? rawTrace?.[field];
|
|
537
754
|
if (typeof raw !== "string") {
|
|
538
755
|
return void 0;
|
|
@@ -541,7 +758,7 @@ function payloadTimestamp(payload, field) {
|
|
|
541
758
|
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
542
759
|
}
|
|
543
760
|
function hasError(payload) {
|
|
544
|
-
const spanData =
|
|
761
|
+
const spanData = asRecord2(asRecord2(payload.rawSpan)?.span_data);
|
|
545
762
|
if (spanData?.error != null) {
|
|
546
763
|
return true;
|
|
547
764
|
}
|
|
@@ -551,7 +768,6 @@ function hasError(payload) {
|
|
|
551
768
|
function createOtelTransport(options) {
|
|
552
769
|
return new OtelBatchTransport({
|
|
553
770
|
...options,
|
|
554
|
-
collectorEndpoint: readEnv(COLLECTOR_ENDPOINT_ENV) || void 0,
|
|
555
771
|
exportConcurrency: readBoundedIntEnv(
|
|
556
772
|
EXPORT_CONCURRENCY_ENV,
|
|
557
773
|
MAX_EXPORT_CONCURRENCY,
|
|
@@ -586,7 +802,7 @@ function shutdownOtelTransports(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS) {
|
|
|
586
802
|
(transport, remaining) => transport.shutdown(remaining)
|
|
587
803
|
);
|
|
588
804
|
}
|
|
589
|
-
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,
|
|
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;
|
|
590
806
|
var init_otel = __esm({
|
|
591
807
|
"src/otel.ts"() {
|
|
592
808
|
"use strict";
|
|
@@ -596,6 +812,7 @@ var init_otel = __esm({
|
|
|
596
812
|
import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
597
813
|
init_constants();
|
|
598
814
|
init_errors();
|
|
815
|
+
init_payloadBudget();
|
|
599
816
|
init_readEnv();
|
|
600
817
|
init_serializePayload();
|
|
601
818
|
init_unrefTimer();
|
|
@@ -606,10 +823,8 @@ var init_otel = __esm({
|
|
|
606
823
|
MAX_EXPORT_REQUEST_BYTES = 3e6;
|
|
607
824
|
MAX_REQUEST_BYTES_ENV = "BITFAB_OTEL_MAX_REQUEST_BYTES";
|
|
608
825
|
EXPORT_CONCURRENCY_ENV = "BITFAB_OTEL_EXPORT_CONCURRENCY";
|
|
609
|
-
COLLECTOR_ENDPOINT_ENV = "BITFAB_OTEL_EXPORTER_ENDPOINT";
|
|
610
826
|
MAX_QUEUE_SIZE = 8192;
|
|
611
827
|
DIRECT_MAX_EXPORT_BATCH_SIZE = 512;
|
|
612
|
-
COLLECTOR_MAX_EXPORT_BATCH_SIZE = 32;
|
|
613
828
|
DIRECT_MAX_REQUEST_BATCH_SIZE = 8;
|
|
614
829
|
DEFAULT_EXPORT_CONCURRENCY = 32;
|
|
615
830
|
MAX_EXPORT_CONCURRENCY = 64;
|
|
@@ -623,6 +838,7 @@ var init_otel = __esm({
|
|
|
623
838
|
traceSubmissionSpanIds = /* @__PURE__ */ new Map();
|
|
624
839
|
replayTraceSubmissions = /* @__PURE__ */ new Set();
|
|
625
840
|
submissionCounter = 0;
|
|
841
|
+
SPAN_SEPARATOR_BYTES = 1;
|
|
626
842
|
OtlpPayloadTooLargeError = class extends Error {
|
|
627
843
|
};
|
|
628
844
|
OtlpPartialSuccessError = class extends Error {
|
|
@@ -651,57 +867,55 @@ var init_otel = __esm({
|
|
|
651
867
|
return true;
|
|
652
868
|
}
|
|
653
869
|
let encoded;
|
|
870
|
+
let envelope;
|
|
654
871
|
try {
|
|
655
|
-
encoded = spans.map(
|
|
872
|
+
encoded = spans.map(encodeSpan);
|
|
873
|
+
envelope = requestEnvelope(spans[0]);
|
|
656
874
|
} catch (error) {
|
|
657
875
|
logError("failed to encode an OpenTelemetry span batch", error);
|
|
658
876
|
return false;
|
|
659
877
|
}
|
|
660
|
-
const
|
|
661
|
-
const batches = this.buildRequestBatches(first, encoded);
|
|
878
|
+
const batches = this.buildRequestBatches(envelope, encoded);
|
|
662
879
|
const results = await mapWithConcurrency(
|
|
663
880
|
batches,
|
|
664
881
|
this.exportConcurrency,
|
|
665
|
-
(batch) => this.send(
|
|
882
|
+
(batch) => this.send(envelope, batch)
|
|
666
883
|
);
|
|
667
884
|
return results.every(Boolean);
|
|
668
885
|
}
|
|
669
|
-
buildRequestBatches(
|
|
886
|
+
buildRequestBatches(envelope, spans) {
|
|
670
887
|
const batches = [];
|
|
671
888
|
let current = [];
|
|
889
|
+
let size = envelope.size;
|
|
672
890
|
for (const span of spans) {
|
|
673
|
-
|
|
674
|
-
|
|
891
|
+
const addition = span.size + (current.length > 0 ? SPAN_SEPARATOR_BYTES : 0);
|
|
892
|
+
if (current.length > 0 && (current.length >= this.maxRequestBatchSize || size + addition > this.maxRequestBytes)) {
|
|
893
|
+
batches.push({ spans: current, size });
|
|
675
894
|
current = [];
|
|
895
|
+
size = envelope.size;
|
|
676
896
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
batches.push(current);
|
|
680
|
-
current = [span];
|
|
681
|
-
} else {
|
|
682
|
-
current = candidate;
|
|
683
|
-
}
|
|
897
|
+
current.push(span);
|
|
898
|
+
size += span.size + (current.length > 1 ? SPAN_SEPARATOR_BYTES : 0);
|
|
684
899
|
}
|
|
685
900
|
if (current.length > 0) {
|
|
686
|
-
batches.push(current);
|
|
901
|
+
batches.push({ spans: current, size });
|
|
687
902
|
}
|
|
688
903
|
return batches;
|
|
689
904
|
}
|
|
690
|
-
async send(
|
|
691
|
-
|
|
692
|
-
if (encodedSize(payload) > this.maxRequestBytes) {
|
|
905
|
+
async send(envelope, batch) {
|
|
906
|
+
if (batch.size > this.maxRequestBytes) {
|
|
693
907
|
logError(
|
|
694
908
|
"a single OpenTelemetry span exceeded the configured request-size target and could not be exported"
|
|
695
909
|
);
|
|
696
910
|
return false;
|
|
697
911
|
}
|
|
698
912
|
try {
|
|
699
|
-
await this.sendWithRetries(
|
|
913
|
+
await this.sendWithRetries(encodeRequest(envelope, batch.spans));
|
|
700
914
|
return true;
|
|
701
915
|
} catch (error) {
|
|
702
916
|
if (error instanceof OtlpPayloadTooLargeError) {
|
|
703
917
|
logError(
|
|
704
|
-
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"
|
|
918
|
+
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"
|
|
705
919
|
);
|
|
706
920
|
return false;
|
|
707
921
|
}
|
|
@@ -725,15 +939,15 @@ var init_otel = __esm({
|
|
|
725
939
|
* the server does not yet understand. The fix is a client-supplied
|
|
726
940
|
* idempotency key that ingestion dedupes on.
|
|
727
941
|
*/
|
|
728
|
-
async sendWithRetries(
|
|
942
|
+
async sendWithRetries(body) {
|
|
729
943
|
for (let attempt = 0; attempt < MAX_SEND_ATTEMPTS; attempt += 1) {
|
|
730
944
|
try {
|
|
731
945
|
const response = await this.directSender(
|
|
732
946
|
OTLP_TRACES_ENDPOINT,
|
|
733
|
-
|
|
947
|
+
body,
|
|
734
948
|
EXPORT_TIMEOUT_MILLIS
|
|
735
949
|
);
|
|
736
|
-
const partialSuccess =
|
|
950
|
+
const partialSuccess = asRecord2(response?.partialSuccess);
|
|
737
951
|
const rejected = partialSuccess?.rejectedSpans;
|
|
738
952
|
if (rejected !== void 0 && rejected !== "0" && rejected !== 0) {
|
|
739
953
|
logError(
|
|
@@ -761,115 +975,6 @@ var init_otel = __esm({
|
|
|
761
975
|
async forceFlush() {
|
|
762
976
|
}
|
|
763
977
|
};
|
|
764
|
-
CollectorSpanExporter = class {
|
|
765
|
-
constructor(endpoint, apiKey, maxRequestBytes) {
|
|
766
|
-
this.endpoint = endpoint;
|
|
767
|
-
this.apiKey = apiKey;
|
|
768
|
-
this.maxRequestBytes = maxRequestBytes;
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* Loaded through a dynamic import rather than a top-level one so bundlers
|
|
772
|
-
* code-split it: Collector delivery is opt-in, and a consumer who never sets
|
|
773
|
-
* an endpoint should not pay for the exporter in their initial bundle. It is
|
|
774
|
-
* a hard dependency, so this cannot fail for want of the package.
|
|
775
|
-
*/
|
|
776
|
-
loadExporterModule() {
|
|
777
|
-
if (!this.pendingModule) {
|
|
778
|
-
this.pendingModule = import("@opentelemetry/exporter-trace-otlp-proto");
|
|
779
|
-
}
|
|
780
|
-
return this.pendingModule;
|
|
781
|
-
}
|
|
782
|
-
export(spans, resultCallback) {
|
|
783
|
-
void this.exportAsync(spans).then(
|
|
784
|
-
(succeeded) => {
|
|
785
|
-
resultCallback({
|
|
786
|
-
code: succeeded ? import_core.ExportResultCode.SUCCESS : import_core.ExportResultCode.FAILED
|
|
787
|
-
});
|
|
788
|
-
},
|
|
789
|
-
(error) => {
|
|
790
|
-
resultCallback({ code: import_core.ExportResultCode.FAILED, error });
|
|
791
|
-
}
|
|
792
|
-
);
|
|
793
|
-
}
|
|
794
|
-
async exportAsync(spans) {
|
|
795
|
-
if (spans.length === 0) {
|
|
796
|
-
return true;
|
|
797
|
-
}
|
|
798
|
-
let delegate;
|
|
799
|
-
try {
|
|
800
|
-
delegate = await this.resolveDelegate();
|
|
801
|
-
} catch (error) {
|
|
802
|
-
logError("failed to build the OTLP Collector exporter", error);
|
|
803
|
-
return false;
|
|
804
|
-
}
|
|
805
|
-
const results = await Promise.all(
|
|
806
|
-
this.partition(spans).map(
|
|
807
|
-
(batch) => new Promise((resolve) => {
|
|
808
|
-
try {
|
|
809
|
-
delegate.export(batch, (result) => {
|
|
810
|
-
resolve(result.code === import_core.ExportResultCode.SUCCESS);
|
|
811
|
-
});
|
|
812
|
-
} catch (error) {
|
|
813
|
-
logError("Collector export threw", error);
|
|
814
|
-
resolve(false);
|
|
815
|
-
}
|
|
816
|
-
})
|
|
817
|
-
)
|
|
818
|
-
);
|
|
819
|
-
return results.every(Boolean);
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* Partition by the encoded JSON size of each carrier rather than its encoded
|
|
823
|
-
* protobuf size. Protobuf is strictly smaller than the equivalent JSON for
|
|
824
|
-
* these payloads, so the JSON figure is a conservative bound that keeps every
|
|
825
|
-
* request under the target without pulling `@opentelemetry/otlp-transformer`
|
|
826
|
-
* into the dependency set purely to measure bytes.
|
|
827
|
-
*/
|
|
828
|
-
partition(spans) {
|
|
829
|
-
const batches = [];
|
|
830
|
-
let current = [];
|
|
831
|
-
let currentSize = 0;
|
|
832
|
-
for (const span of spans) {
|
|
833
|
-
const size = encodedSize(spanToOtlp(span));
|
|
834
|
-
if (current.length > 0 && currentSize + size > this.maxRequestBytes) {
|
|
835
|
-
batches.push(current);
|
|
836
|
-
current = [];
|
|
837
|
-
currentSize = 0;
|
|
838
|
-
}
|
|
839
|
-
current.push(span);
|
|
840
|
-
currentSize += size;
|
|
841
|
-
}
|
|
842
|
-
if (current.length > 0) {
|
|
843
|
-
batches.push(current);
|
|
844
|
-
}
|
|
845
|
-
return batches;
|
|
846
|
-
}
|
|
847
|
-
async resolveDelegate() {
|
|
848
|
-
const apiKey = this.apiKey() ?? "";
|
|
849
|
-
if (this.delegate && this.delegateApiKey === apiKey) {
|
|
850
|
-
return this.delegate;
|
|
851
|
-
}
|
|
852
|
-
const { OTLPTraceExporter } = await this.loadExporterModule();
|
|
853
|
-
const previous = this.delegate;
|
|
854
|
-
this.delegate = new OTLPTraceExporter({
|
|
855
|
-
url: this.endpoint,
|
|
856
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
857
|
-
timeoutMillis: EXPORT_TIMEOUT_MILLIS
|
|
858
|
-
});
|
|
859
|
-
this.delegateApiKey = apiKey;
|
|
860
|
-
if (previous) {
|
|
861
|
-
void previous.shutdown().catch(() => {
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
return this.delegate;
|
|
865
|
-
}
|
|
866
|
-
async shutdown() {
|
|
867
|
-
await this.delegate?.shutdown();
|
|
868
|
-
}
|
|
869
|
-
async forceFlush() {
|
|
870
|
-
await this.delegate?.forceFlush?.();
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
978
|
DeliveryTrackingExporter = class {
|
|
874
979
|
constructor(exporter) {
|
|
875
980
|
this.exporter = exporter;
|
|
@@ -912,27 +1017,22 @@ var init_otel = __esm({
|
|
|
912
1017
|
OtelBatchTransport = class {
|
|
913
1018
|
constructor(options) {
|
|
914
1019
|
this.closed = false;
|
|
915
|
-
const collectorEndpoint = options.collectorEndpoint;
|
|
916
1020
|
const maxRequestBytes = options.maxRequestBytes ?? MAX_EXPORT_REQUEST_BYTES;
|
|
917
1021
|
const maxRequestBatchSize = options.maxRequestBatchSize ?? DIRECT_MAX_REQUEST_BATCH_SIZE;
|
|
918
1022
|
if (maxRequestBatchSize <= 0) {
|
|
919
1023
|
throw new BitfabError("maxRequestBatchSize must be a positive integer");
|
|
920
1024
|
}
|
|
921
1025
|
this.deliveryTracker = new DeliveryTrackingExporter(
|
|
922
|
-
|
|
1026
|
+
new BitfabSpanExporter(
|
|
923
1027
|
options.directSender,
|
|
924
1028
|
maxRequestBytes,
|
|
925
1029
|
maxRequestBatchSize,
|
|
926
1030
|
options.exportConcurrency ?? DEFAULT_EXPORT_CONCURRENCY
|
|
927
|
-
) : new CollectorSpanExporter(
|
|
928
|
-
normalizeCollectorEndpoint(collectorEndpoint),
|
|
929
|
-
options.apiKey,
|
|
930
|
-
maxRequestBytes
|
|
931
1031
|
)
|
|
932
1032
|
);
|
|
933
1033
|
this.processor = new import_sdk_trace_base.BatchSpanProcessor(this.deliveryTracker, {
|
|
934
1034
|
maxQueueSize: options.maxQueueSize ?? MAX_QUEUE_SIZE,
|
|
935
|
-
maxExportBatchSize: options.maxExportBatchSize ??
|
|
1035
|
+
maxExportBatchSize: options.maxExportBatchSize ?? DIRECT_MAX_EXPORT_BATCH_SIZE,
|
|
936
1036
|
scheduledDelayMillis: SCHEDULE_DELAY_MILLIS,
|
|
937
1037
|
exportTimeoutMillis: options.exportTimeoutMillis ?? EXPORT_TIMEOUT_MILLIS
|
|
938
1038
|
});
|
|
@@ -1085,6 +1185,7 @@ var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCL
|
|
|
1085
1185
|
var init_http = __esm({
|
|
1086
1186
|
"src/http.ts"() {
|
|
1087
1187
|
"use strict";
|
|
1188
|
+
init_compress();
|
|
1088
1189
|
init_constants();
|
|
1089
1190
|
init_errors();
|
|
1090
1191
|
init_replayContext();
|
|
@@ -1149,8 +1250,7 @@ var init_http = __esm({
|
|
|
1149
1250
|
}
|
|
1150
1251
|
if (!this.traceTransport) {
|
|
1151
1252
|
this.traceTransport = createTraceTransport({
|
|
1152
|
-
|
|
1153
|
-
directSender: (endpoint, payload, timeoutMs) => this.request(endpoint, payload, {
|
|
1253
|
+
directSender: (endpoint, body, timeoutMs) => this.sendEncoded(endpoint, body, {
|
|
1154
1254
|
timeout: timeoutMs
|
|
1155
1255
|
})
|
|
1156
1256
|
});
|
|
@@ -1220,11 +1320,6 @@ var init_http = __esm({
|
|
|
1220
1320
|
* @throws {BitfabError} If the request fails
|
|
1221
1321
|
*/
|
|
1222
1322
|
async request(endpoint, payload, options) {
|
|
1223
|
-
const url = `${this.serviceUrl}${endpoint}`;
|
|
1224
|
-
const timeout = options?.timeout ?? this.timeout;
|
|
1225
|
-
const method = options?.method ?? "POST";
|
|
1226
|
-
const controller = new AbortController();
|
|
1227
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1228
1323
|
const { body, dropped } = serializePayloadBody(payload);
|
|
1229
1324
|
if (dropped.length > 0) {
|
|
1230
1325
|
try {
|
|
@@ -1234,14 +1329,33 @@ var init_http = __esm({
|
|
|
1234
1329
|
} catch {
|
|
1235
1330
|
}
|
|
1236
1331
|
}
|
|
1332
|
+
return this.sendEncoded(endpoint, body, options);
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* POST an already-encoded body. The span transport encodes its own batches,
|
|
1336
|
+
* so routing them back through {@link HttpClient.request} would encode the
|
|
1337
|
+
* same data twice.
|
|
1338
|
+
*/
|
|
1339
|
+
async sendEncoded(endpoint, body, options) {
|
|
1340
|
+
const url = `${this.serviceUrl}${endpoint}`;
|
|
1341
|
+
const timeout = options?.timeout ?? this.timeout;
|
|
1342
|
+
const method = options?.method ?? "POST";
|
|
1343
|
+
const controller = new AbortController();
|
|
1344
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1345
|
+
const prepared = encodeRequestBody(body);
|
|
1346
|
+
const encoded = prepared instanceof Promise ? await prepared : prepared;
|
|
1347
|
+
const headers = {
|
|
1348
|
+
"Content-Type": "application/json",
|
|
1349
|
+
Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
|
|
1350
|
+
};
|
|
1351
|
+
if (encoded.contentEncoding) {
|
|
1352
|
+
headers["Content-Encoding"] = encoded.contentEncoding;
|
|
1353
|
+
}
|
|
1237
1354
|
try {
|
|
1238
1355
|
const response = await fetch(url, {
|
|
1239
1356
|
method,
|
|
1240
|
-
headers
|
|
1241
|
-
|
|
1242
|
-
Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
|
|
1243
|
-
},
|
|
1244
|
-
body,
|
|
1357
|
+
headers,
|
|
1358
|
+
body: encoded.body,
|
|
1245
1359
|
signal: controller.signal
|
|
1246
1360
|
});
|
|
1247
1361
|
if (!response.ok) {
|
|
@@ -1690,9 +1804,10 @@ var init_serialize = __esm({
|
|
|
1690
1804
|
"src/serialize.ts"() {
|
|
1691
1805
|
"use strict";
|
|
1692
1806
|
import_superjson = __toESM(require("superjson"), 1);
|
|
1807
|
+
init_payloadBudget();
|
|
1693
1808
|
init_warnOnce();
|
|
1694
|
-
MAX_SERIALIZED_BYTES =
|
|
1695
|
-
MAX_FRAMEWORK_SERIALIZED_BYTES =
|
|
1809
|
+
MAX_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
|
|
1810
|
+
MAX_FRAMEWORK_SERIALIZED_BYTES = MAX_SPAN_CARRIER_BYTES;
|
|
1696
1811
|
MAX_SAFE_DEPTH = 6;
|
|
1697
1812
|
}
|
|
1698
1813
|
});
|