@stainlessdev/xray-core 0.3.0 → 0.3.1-dev.ae29cd0

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.
@@ -160,6 +160,7 @@ function findNestedTarget(obj) {
160
160
  }
161
161
 
162
162
  export {
163
+ encodeBase64,
163
164
  logWithLevel,
164
165
  sanitizeLogString,
165
166
  sanitizeHeaderValues,
@@ -169,4 +170,4 @@ export {
169
170
  bindObject,
170
171
  getContextFromObject
171
172
  };
172
- //# sourceMappingURL=chunk-SQHI5JZH.js.map
173
+ //# sourceMappingURL=chunk-XI5E6C7G.js.map
package/dist/index.cjs CHANGED
@@ -26,6 +26,28 @@ __export(index_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
+ // src/encoding.ts
30
+ var utf8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8", { fatal: true }) : null;
31
+ var utf8DecoderLenient = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8") : null;
32
+ var maybeBuffer = globalThis.Buffer;
33
+ function encodeBase64(bytes) {
34
+ if (maybeBuffer) {
35
+ return maybeBuffer.from(bytes).toString("base64");
36
+ }
37
+ let binary = "";
38
+ for (let i = 0; i < bytes.length; i += 1) {
39
+ const byte = bytes[i];
40
+ if (byte === void 0) {
41
+ continue;
42
+ }
43
+ binary += String.fromCharCode(byte);
44
+ }
45
+ if (typeof btoa !== "undefined") {
46
+ return btoa(binary);
47
+ }
48
+ return "";
49
+ }
50
+
29
51
  // src/route.ts
30
52
  function normalizeRoutePattern(route) {
31
53
  if (!route) {
@@ -142,8 +164,7 @@ var defaultExporterBase = {
142
164
  endpointUrl: DEFAULT_ENDPOINT_URL,
143
165
  headers: {},
144
166
  timeoutMs: 5e3,
145
- spanProcessor: "batch",
146
- sampler: { type: "ratio", ratio: 1 }
167
+ spanProcessor: "batch"
147
168
  };
148
169
  var XrayConfigError = class extends Error {
149
170
  constructor(code, message) {
@@ -234,24 +255,15 @@ function normalizeRoute(cfg) {
234
255
  }
235
256
  function normalizeExporter(endpointUrl, cfg) {
236
257
  const resolvedEndpoint = normalizeExporterEndpoint(cfg?.endpointUrl ?? endpointUrl);
237
- const enabled = cfg?.enabled ?? true;
258
+ const rawHeaders = cfg?.headers ?? defaultExporterBase.headers ?? {};
259
+ const parsed = applyEndpointAuth(resolvedEndpoint, rawHeaders);
238
260
  const exporter = {
239
261
  ...defaultExporterBase,
240
- ...cfg,
241
- enabled,
242
- endpointUrl: resolvedEndpoint,
243
- headers: cfg?.headers ?? defaultExporterBase.headers,
262
+ endpointUrl: parsed.endpointUrl,
263
+ headers: parsed.headers,
244
264
  timeoutMs: cfg?.timeoutMs ?? defaultExporterBase.timeoutMs,
245
- spanProcessor: cfg?.spanProcessor ?? defaultExporterBase.spanProcessor,
246
- sampler: cfg?.sampler ?? defaultExporterBase.sampler
265
+ spanProcessor: cfg?.spanProcessor ?? defaultExporterBase.spanProcessor
247
266
  };
248
- if (exporter.sampler.type === "ratio") {
249
- const ratio = exporter.sampler.ratio ?? 1;
250
- if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) {
251
- throw new XrayConfigError("INVALID_EXPORTER", "sampler.ratio must be between 0 and 1");
252
- }
253
- exporter.sampler = { type: "ratio", ratio };
254
- }
255
267
  return exporter;
256
268
  }
257
269
  function normalizeExporterEndpoint(endpointUrl) {
@@ -270,6 +282,69 @@ function normalizeStringList(values) {
270
282
  }
271
283
  return values.map((entry) => entry.trim()).filter(Boolean);
272
284
  }
285
+ var textEncoder = typeof TextEncoder !== "undefined" ? new TextEncoder() : null;
286
+ var maybeBuffer2 = globalThis.Buffer;
287
+ function applyEndpointAuth(endpointUrl, headers) {
288
+ const resolvedHeaders = headers ?? {};
289
+ let url;
290
+ try {
291
+ url = new URL(endpointUrl);
292
+ } catch {
293
+ return { endpointUrl, headers: resolvedHeaders };
294
+ }
295
+ const username = decodeUserInfo(url.username);
296
+ const password = decodeUserInfo(url.password);
297
+ if (!username && !password) {
298
+ return { endpointUrl, headers: resolvedHeaders };
299
+ }
300
+ url.username = "";
301
+ url.password = "";
302
+ const sanitizedUrl = url.toString();
303
+ if (hasAuthorizationHeader(resolvedHeaders)) {
304
+ return { endpointUrl: sanitizedUrl, headers: resolvedHeaders };
305
+ }
306
+ const authorization = encodeBasicAuth(username, password);
307
+ if (!authorization) {
308
+ return { endpointUrl: sanitizedUrl, headers: resolvedHeaders };
309
+ }
310
+ return {
311
+ endpointUrl: sanitizedUrl,
312
+ headers: {
313
+ ...resolvedHeaders,
314
+ Authorization: authorization
315
+ }
316
+ };
317
+ }
318
+ function decodeUserInfo(value) {
319
+ if (!value) {
320
+ return value;
321
+ }
322
+ try {
323
+ return decodeURIComponent(value);
324
+ } catch {
325
+ return value;
326
+ }
327
+ }
328
+ function hasAuthorizationHeader(headers) {
329
+ return Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
330
+ }
331
+ function encodeBasicAuth(username, password) {
332
+ const raw = `${username}:${password}`;
333
+ let bytes;
334
+ if (textEncoder) {
335
+ bytes = textEncoder.encode(raw);
336
+ } else if (maybeBuffer2) {
337
+ bytes = maybeBuffer2.from(raw, "utf8");
338
+ }
339
+ if (!bytes) {
340
+ return void 0;
341
+ }
342
+ const encoded = encodeBase64(bytes);
343
+ if (!encoded) {
344
+ return void 0;
345
+ }
346
+ return `Basic ${encoded}`;
347
+ }
273
348
 
274
349
  // src/logger.ts
275
350
  var logLevelOrder = {
@@ -639,11 +714,6 @@ function redactJsonPath(value, segments, replacement) {
639
714
  }
640
715
  }
641
716
 
642
- // src/encoding.ts
643
- var utf8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8", { fatal: true }) : null;
644
- var utf8DecoderLenient = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8") : null;
645
- var maybeBuffer = globalThis.Buffer;
646
-
647
717
  // src/request_log.ts
648
718
  var controlChars = /[\x00-\x1F\x7F]/g;
649
719
  function sanitizeLogString(value) {
@@ -768,9 +838,6 @@ var import_semantic_conventions = require("@opentelemetry/semantic-conventions")
768
838
  var import_otlp_transformer = require("@opentelemetry/otlp-transformer");
769
839
  var defaultAttributeCountLimit = 128;
770
840
  function createTracerProvider(config) {
771
- if (!config.exporter.enabled || !config.exporter.endpointUrl) {
772
- return null;
773
- }
774
841
  if (config.exporter.endpointUrl.startsWith("http://")) {
775
842
  import_api.diag.warn("xray: OTLP endpoint uses plaintext HTTP");
776
843
  }
@@ -784,9 +851,10 @@ function createTracerProvider(config) {
784
851
  const exporter = new FetchSpanExporter({
785
852
  endpointUrl: config.exporter.endpointUrl,
786
853
  headers: config.exporter.headers ?? {},
787
- timeoutMillis: config.exporter.timeoutMs
854
+ timeoutMillis: config.exporter.timeoutMs,
855
+ logger: config.logger,
856
+ logLevel: config.logLevel
788
857
  });
789
- const sampler = createSampler(config);
790
858
  const spanProcessor = createSpanProcessor(config.exporter.spanProcessor, exporter);
791
859
  const dropProcessor = new DropFilterSpanProcessor(spanProcessor);
792
860
  const provider = new import_sdk_trace_base.BasicTracerProvider({
@@ -796,7 +864,7 @@ function createTracerProvider(config) {
796
864
  attributeValueLengthLimit
797
865
  },
798
866
  resource,
799
- sampler,
867
+ sampler: new import_sdk_trace_base.AlwaysOnSampler(),
800
868
  spanLimits: {
801
869
  attributeCountLimit: defaultAttributeCountLimit,
802
870
  attributePerEventCountLimit: defaultAttributeCountLimit,
@@ -851,6 +919,8 @@ var FetchSpanExporter = class {
851
919
  this.headers = { ...options.headers };
852
920
  this.timeoutMillis = options.timeoutMillis;
853
921
  this.isShutdown = false;
922
+ this.logger = options.logger;
923
+ this.logLevel = options.logLevel;
854
924
  const protobufSerializer = import_otlp_transformer.ProtobufTraceSerializer && typeof import_otlp_transformer.ProtobufTraceSerializer.serializeRequest === "function" ? import_otlp_transformer.ProtobufTraceSerializer : null;
855
925
  this.serializer = protobufSerializer ?? import_otlp_transformer.JsonTraceSerializer;
856
926
  this.contentType = protobufSerializer ? "application/x-protobuf" : "application/json";
@@ -862,6 +932,9 @@ var FetchSpanExporter = class {
862
932
  }
863
933
  const payload = this.serializer.serializeRequest(spans);
864
934
  if (!payload) {
935
+ logWithLevel(this.logger, "warn", this.logLevel, "xray: OTLP export failed", {
936
+ error: "OTLP export failed: empty payload"
937
+ });
865
938
  resultCallback({
866
939
  code: import_core.ExportResultCode.FAILED,
867
940
  error: new Error("OTLP export failed: empty payload")
@@ -897,6 +970,9 @@ var FetchSpanExporter = class {
897
970
  if (timeout) {
898
971
  clearTimeout(timeout);
899
972
  }
973
+ logWithLevel(this.logger, "warn", this.logLevel, "xray: OTLP export failed", {
974
+ error: err instanceof Error ? err.message : String(err)
975
+ });
900
976
  import_api.diag.error("OTLP export failed", err);
901
977
  resultCallback({ code: import_core.ExportResultCode.FAILED, error: err });
902
978
  });
@@ -919,19 +995,9 @@ function createSpanProcessor(mode, exporter) {
919
995
  exportTimeoutMillis: 3e4
920
996
  });
921
997
  }
922
- function createSampler(config) {
923
- const sampler = config.exporter.sampler;
924
- if (sampler.type === "always_off") {
925
- return new import_sdk_trace_base.AlwaysOffSampler();
926
- }
927
- if (sampler.type === "ratio") {
928
- return new import_sdk_trace_base.TraceIdRatioBasedSampler(sampler.ratio ?? 1);
929
- }
930
- return new import_sdk_trace_base.AlwaysOnSampler();
931
- }
932
998
  function sdkVersion() {
933
999
  if (true) {
934
- return "0.2.0";
1000
+ return "0.3.1";
935
1001
  }
936
1002
  return "unknown";
937
1003
  }
@@ -971,14 +1037,21 @@ function getContextState(ctx) {
971
1037
  // src/emitter.ts
972
1038
  function createEmitter(config) {
973
1039
  const resolved = normalizeConfig(config);
1040
+ logWithLevel(resolved.logger, "info", resolved.logLevel, "xray: emitter configured", {
1041
+ serviceName: resolved.serviceName,
1042
+ environment: resolved.environment,
1043
+ version: resolved.version,
1044
+ exporterEndpoint: resolved.exporter.endpointUrl,
1045
+ spanProcessor: resolved.exporter.spanProcessor
1046
+ });
974
1047
  const tracerProvider = createTracerProvider(resolved);
975
- const tracer = tracerProvider ? tracerFromProvider(tracerProvider) : null;
1048
+ const tracer = tracerFromProvider(tracerProvider);
976
1049
  return {
977
1050
  config: resolved,
978
1051
  startRequest: (req) => startRequest(resolved, tracer, req),
979
1052
  endRequest: (ctx, res, err) => endRequest(resolved, ctx, res, err),
980
- flush: () => tracerProvider ? tracerProvider.forceFlush() : Promise.resolve(),
981
- shutdown: () => tracerProvider ? tracerProvider.shutdown() : Promise.resolve()
1053
+ flush: () => tracerProvider.forceFlush(),
1054
+ shutdown: () => tracerProvider.shutdown()
982
1055
  };
983
1056
  }
984
1057
  function startRequest(config, tracer, req) {
@@ -989,7 +1062,7 @@ function startRequest(config, tracer, req) {
989
1062
  if (req.route && config.route.normalize) {
990
1063
  req.route = config.route.normalizer ? config.route.normalizer(req.route) : normalizeRoutePattern(req.route);
991
1064
  }
992
- const span = tracer ? spanFromTracer(tracer, spanNameFromRequest(req)) : void 0;
1065
+ const span = spanFromTracer(tracer, spanNameFromRequest(req));
993
1066
  const context = {
994
1067
  requestId,
995
1068
  traceId: span?.spanContext().traceId,