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