@stainlessdev/xray-core 0.6.0 → 0.7.0-dev.588fa57

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 ADDED
@@ -0,0 +1,67 @@
1
+ # @stainlessdev/xray-core
2
+
3
+ Core instrumentation for Stainless X-ray request logging. This package is runtime-agnostic and only provides the emitter, config, and types. Use it directly if you need a custom runtime or a custom OpenTelemetry exporter; otherwise prefer `@stainlessdev/xray-node` or `@stainlessdev/xray-fetch`.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pnpm add @stainlessdev/xray-core @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-proto
9
+ ```
10
+
11
+ ## Basic usage
12
+
13
+ ```ts
14
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
15
+ import { createEmitter } from '@stainlessdev/xray-core';
16
+
17
+ const endpointUrl = 'http://localhost:4318';
18
+ const exporter = new OTLPTraceExporter({
19
+ url: `${endpointUrl}/v1/traces`,
20
+ });
21
+
22
+ const xray = createEmitter(
23
+ {
24
+ serviceName: 'my-service',
25
+ endpointUrl,
26
+ },
27
+ exporter,
28
+ );
29
+
30
+ const ctx = xray.startRequest({
31
+ method: 'GET',
32
+ url: 'https://example.com/hello',
33
+ headers: { 'user-agent': 'curl/8.0' },
34
+ startTimeMs: Date.now(),
35
+ });
36
+
37
+ const log = xray.endRequest(ctx, {
38
+ statusCode: 200,
39
+ headers: { 'request-id': 'req_123' },
40
+ endTimeMs: Date.now(),
41
+ });
42
+
43
+ await xray.flush();
44
+ ```
45
+
46
+ ## Request IDs
47
+
48
+ X-ray always produces a request ID for each request. If you do not provide one, it resolves the ID in this order: explicit `requestId`, configured response header name (`requestId.header`, default: `request-id`), then an auto-generated UUIDv7-based ID. Runtime adapters inject the header automatically when missing; if you use `@stainlessdev/xray-core` directly, you are responsible for writing the response header yourself.
49
+
50
+ ## Configuration (high-level)
51
+
52
+ `XrayConfig` lives in `packages/core/src/config.ts`. Common knobs:
53
+
54
+ - `serviceName` (required) and `endpointUrl` (falls back to `STAINLESS_XRAY_ENDPOINT_URL`).
55
+ - `exporter` overrides for OTLP headers, timeout, and span processor.
56
+ - `capture` and `redaction` toggles for headers/body logging.
57
+ - `requestId.header` for the response header name.
58
+ - `route` normalization options.
59
+
60
+ Notes:
61
+
62
+ - `endpointUrl` is required; `/v1/traces` is appended if missing. If both are set, `endpointUrl` wins over `STAINLESS_XRAY_ENDPOINT_URL`.
63
+ - If `endpointUrl` includes basic auth credentials, they are moved to the `Authorization` header automatically.
64
+
65
+ ## When to use this package
66
+
67
+ Use `@stainlessdev/xray-core` only if you need to integrate with a custom runtime or supply your own `SpanExporter`. For Node and fetch-based runtimes, use `@stainlessdev/xray-node` or `@stainlessdev/xray-fetch` for a ready-to-go emitter and request/response adapters.
@@ -0,0 +1,304 @@
1
+ // src/logger.ts
2
+ var logLevelOrder = {
3
+ debug: 10,
4
+ info: 20,
5
+ warn: 30,
6
+ error: 40
7
+ };
8
+ function logWithLevel(logger, level, threshold, message, fields) {
9
+ if (logLevelOrder[level] < logLevelOrder[threshold]) {
10
+ return;
11
+ }
12
+ const fn = logger[level] ?? logger.warn ?? logger.info ?? logger.debug ?? logger.error ?? console.log;
13
+ try {
14
+ fn.call(logger, message, fields);
15
+ } catch {
16
+ }
17
+ }
18
+
19
+ // src/encoding.ts
20
+ var utf8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8", { fatal: true }) : null;
21
+ var utf8DecoderLenient = typeof TextDecoder !== "undefined" ? new TextDecoder("utf-8") : null;
22
+ var maybeBuffer = globalThis.Buffer;
23
+ function encodeBase64(bytes) {
24
+ if (maybeBuffer) {
25
+ return maybeBuffer.from(bytes).toString("base64");
26
+ }
27
+ let binary = "";
28
+ for (let i = 0; i < bytes.length; i += 1) {
29
+ const byte = bytes[i];
30
+ if (byte === void 0) {
31
+ continue;
32
+ }
33
+ binary += String.fromCharCode(byte);
34
+ }
35
+ if (typeof btoa !== "undefined") {
36
+ return btoa(binary);
37
+ }
38
+ return "";
39
+ }
40
+ function isValidUtf8(bytes) {
41
+ if (!utf8Decoder) {
42
+ return false;
43
+ }
44
+ try {
45
+ utf8Decoder.decode(bytes);
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+ function decodeUtf8(bytes) {
52
+ if (utf8DecoderLenient) {
53
+ return utf8DecoderLenient.decode(bytes);
54
+ }
55
+ if (maybeBuffer) {
56
+ return maybeBuffer.from(bytes).toString("utf8");
57
+ }
58
+ return "";
59
+ }
60
+
61
+ // src/request_log.ts
62
+ var controlChars = /[\x00-\x1F\x7F]/g;
63
+ function sanitizeLogString(value) {
64
+ if (!value) {
65
+ return value;
66
+ }
67
+ return value.replace(controlChars, "");
68
+ }
69
+ function sanitizeHeaderValues(headers) {
70
+ if (!headers) {
71
+ return void 0;
72
+ }
73
+ const sanitized = {};
74
+ for (const [key, value] of Object.entries(headers)) {
75
+ const name = sanitizeLogString(key);
76
+ if (Array.isArray(value)) {
77
+ sanitized[name] = value.map((entry) => sanitizeLogString(entry));
78
+ } else {
79
+ sanitized[name] = sanitizeLogString(value);
80
+ }
81
+ }
82
+ return sanitized;
83
+ }
84
+ function makeCapturedBody(bytes, totalBytes, truncated, mode) {
85
+ if (!bytes) {
86
+ return void 0;
87
+ }
88
+ if (mode === "base64") {
89
+ return {
90
+ bytes: totalBytes,
91
+ encoding: "base64",
92
+ truncated,
93
+ value: encodeBase64(bytes)
94
+ };
95
+ }
96
+ if (isValidUtf8(bytes)) {
97
+ return {
98
+ bytes: totalBytes,
99
+ encoding: "utf8",
100
+ truncated,
101
+ value: decodeUtf8(bytes)
102
+ };
103
+ }
104
+ return {
105
+ bytes: totalBytes,
106
+ encoding: "base64",
107
+ truncated,
108
+ value: encodeBase64(bytes)
109
+ };
110
+ }
111
+
112
+ // src/uuid/base48.ts
113
+ var base48AlphabetLex = "256789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
114
+ var base48Zero = base48AlphabetLex.charAt(0);
115
+ var maxChunkBytes = 32;
116
+ var chunkToEncodedLength = /* @__PURE__ */ new Map();
117
+ var encodedLengthToChunk = /* @__PURE__ */ new Map();
118
+ for (let size = 1; size <= maxChunkBytes; size += 1) {
119
+ const encodedLength = Math.ceil(size * 8 / Math.log2(48));
120
+ chunkToEncodedLength.set(size, encodedLength);
121
+ encodedLengthToChunk.set(encodedLength, size);
122
+ }
123
+ var maxEncodedChunk = chunkToEncodedLength.get(maxChunkBytes);
124
+ function encodeChunk(bytes, size) {
125
+ let value = 0n;
126
+ for (const byte of bytes) {
127
+ value = value << 8n | BigInt(byte);
128
+ }
129
+ let encoded = "";
130
+ if (value === 0n) {
131
+ encoded = base48Zero;
132
+ } else {
133
+ while (value > 0n) {
134
+ const mod = value % 48n;
135
+ encoded = base48AlphabetLex[Number(mod)] + encoded;
136
+ value /= 48n;
137
+ }
138
+ }
139
+ const targetLength = chunkToEncodedLength.get(size);
140
+ if (!targetLength) {
141
+ throw new Error(`base48: unsupported chunk size ${size}`);
142
+ }
143
+ return encoded.padStart(targetLength, base48Zero);
144
+ }
145
+ function encodeBase48Lex(buffer) {
146
+ if (buffer.length === 0) {
147
+ return "";
148
+ }
149
+ let result = "";
150
+ for (let offset = 0; offset < buffer.length; ) {
151
+ const remaining = buffer.length - offset;
152
+ const size = remaining >= maxChunkBytes ? maxChunkBytes : remaining;
153
+ result += encodeChunk(buffer.slice(offset, offset + size), size);
154
+ offset += size;
155
+ }
156
+ return result;
157
+ }
158
+
159
+ // src/uuid/base62.ts
160
+ var base62AlphabetLex = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
161
+ var base62Zero = base62AlphabetLex.charAt(0);
162
+ var maxChunkBytes2 = 32;
163
+ var chunkToEncodedLength2 = /* @__PURE__ */ new Map();
164
+ var encodedLengthToChunk2 = /* @__PURE__ */ new Map();
165
+ for (let size = 1; size <= maxChunkBytes2; size += 1) {
166
+ const encodedLength = Math.ceil(size * 8 / Math.log2(62));
167
+ chunkToEncodedLength2.set(size, encodedLength);
168
+ encodedLengthToChunk2.set(encodedLength, size);
169
+ }
170
+ var maxEncodedChunk2 = chunkToEncodedLength2.get(maxChunkBytes2);
171
+ function encodeChunk2(bytes, size) {
172
+ let value = 0n;
173
+ for (const byte of bytes) {
174
+ value = value << 8n | BigInt(byte);
175
+ }
176
+ let encoded = "";
177
+ if (value === 0n) {
178
+ encoded = base62Zero;
179
+ } else {
180
+ while (value > 0n) {
181
+ const mod = value % 62n;
182
+ encoded = base62AlphabetLex[Number(mod)] + encoded;
183
+ value /= 62n;
184
+ }
185
+ }
186
+ const targetLength = chunkToEncodedLength2.get(size);
187
+ if (!targetLength) {
188
+ throw new Error(`base62: unsupported chunk size ${size}`);
189
+ }
190
+ return encoded.padStart(targetLength, base62Zero);
191
+ }
192
+ function encodeBase62Lex(buffer) {
193
+ if (buffer.length === 0) {
194
+ return "";
195
+ }
196
+ let result = "";
197
+ for (let offset = 0; offset < buffer.length; ) {
198
+ const remaining = buffer.length - offset;
199
+ const size = remaining >= maxChunkBytes2 ? maxChunkBytes2 : remaining;
200
+ result += encodeChunk2(buffer.slice(offset, offset + size), size);
201
+ offset += size;
202
+ }
203
+ return result;
204
+ }
205
+
206
+ // src/uuid/index.ts
207
+ var requestIdPrefix = "req_";
208
+ function uuidv7Bytes() {
209
+ const bytes = new Uint8Array(16);
210
+ crypto.getRandomValues(bytes);
211
+ const timestamp = BigInt(Date.now());
212
+ bytes[0] = Number(timestamp >> 40n & 0xffn);
213
+ bytes[1] = Number(timestamp >> 32n & 0xffn);
214
+ bytes[2] = Number(timestamp >> 24n & 0xffn);
215
+ bytes[3] = Number(timestamp >> 16n & 0xffn);
216
+ bytes[4] = Number(timestamp >> 8n & 0xffn);
217
+ bytes[5] = Number(timestamp & 0xffn);
218
+ const byte6 = bytes[6] ?? 0;
219
+ const byte8 = bytes[8] ?? 0;
220
+ bytes[6] = byte6 & 15 | 112;
221
+ bytes[8] = byte8 & 63 | 128;
222
+ return bytes;
223
+ }
224
+ function uuidv7() {
225
+ const bytes = uuidv7Bytes();
226
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
227
+ return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
228
+ }
229
+ function uuidv7base62() {
230
+ return encodeBase62Lex(uuidv7Bytes());
231
+ }
232
+ function uuidv7base48() {
233
+ return encodeBase48Lex(uuidv7Bytes());
234
+ }
235
+ function generateRequestId() {
236
+ return `${requestIdPrefix}${uuidv7base48()}`;
237
+ }
238
+
239
+ // src/state.ts
240
+ var contextMap = /* @__PURE__ */ new WeakMap();
241
+ var objectMap = /* @__PURE__ */ new WeakMap();
242
+ function bindContext(ctx, state) {
243
+ contextMap.set(ctx, state);
244
+ }
245
+ function getContextState(ctx) {
246
+ return contextMap.get(ctx);
247
+ }
248
+ function bindObject(target, state) {
249
+ objectMap.set(target, state);
250
+ }
251
+ function getContextFromObject(target) {
252
+ const state = getStateFromObject(target);
253
+ return state?.context;
254
+ }
255
+ function getStateFromObject(target) {
256
+ if (!target || typeof target !== "object") {
257
+ return void 0;
258
+ }
259
+ if (objectMap.has(target)) {
260
+ return objectMap.get(target);
261
+ }
262
+ const fallback = findNestedTarget(target);
263
+ if (fallback && objectMap.has(fallback)) {
264
+ return objectMap.get(fallback);
265
+ }
266
+ return void 0;
267
+ }
268
+ function findNestedTarget(obj) {
269
+ if (obj.raw && typeof obj.raw === "object") {
270
+ return obj.raw;
271
+ }
272
+ if (obj.req && typeof obj.req === "object") {
273
+ const req = obj.req;
274
+ if (req.raw && typeof req.raw === "object") {
275
+ return req.raw;
276
+ }
277
+ return req;
278
+ }
279
+ if (obj.request && typeof obj.request === "object") {
280
+ const request = obj.request;
281
+ if (request.raw && typeof request.raw === "object") {
282
+ return request.raw;
283
+ }
284
+ return request;
285
+ }
286
+ return null;
287
+ }
288
+
289
+ export {
290
+ encodeBase64,
291
+ logWithLevel,
292
+ sanitizeLogString,
293
+ sanitizeHeaderValues,
294
+ makeCapturedBody,
295
+ uuidv7,
296
+ uuidv7base62,
297
+ uuidv7base48,
298
+ generateRequestId,
299
+ bindContext,
300
+ getContextState,
301
+ bindObject,
302
+ getContextFromObject
303
+ };
304
+ //# sourceMappingURL=chunk-3ILKJJIG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logger.ts","../src/encoding.ts","../src/request_log.ts","../src/uuid/base48.ts","../src/uuid/base62.ts","../src/uuid/index.ts","../src/state.ts"],"sourcesContent":["import type { Logger, LogLevel } from './types';\n\nconst logLevelOrder: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport function logWithLevel(\n logger: Logger,\n level: LogLevel,\n threshold: LogLevel,\n message: string,\n fields?: Record<string, unknown>,\n): void {\n if (logLevelOrder[level] < logLevelOrder[threshold]) {\n return;\n }\n\n const fn =\n logger[level] ?? logger.warn ?? logger.info ?? logger.debug ?? logger.error ?? console.log;\n\n try {\n fn.call(logger, message, fields);\n } catch {\n // Logging should never disrupt instrumentation.\n }\n}\n","const utf8Decoder =\n typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { fatal: true }) : null;\nconst utf8DecoderLenient = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8') : null;\nconst maybeBuffer = (\n globalThis as typeof globalThis & {\n Buffer?: { from(data: Uint8Array): { toString(encoding?: string): string } };\n }\n).Buffer;\n\nexport function encodeBase64(bytes: Uint8Array): string {\n if (maybeBuffer) {\n return maybeBuffer.from(bytes).toString('base64');\n }\n let binary = '';\n for (let i = 0; i < bytes.length; i += 1) {\n const byte = bytes[i];\n if (byte === undefined) {\n continue;\n }\n binary += String.fromCharCode(byte);\n }\n if (typeof btoa !== 'undefined') {\n return btoa(binary);\n }\n return '';\n}\n\nexport function isValidUtf8(bytes: Uint8Array): boolean {\n if (!utf8Decoder) {\n return false;\n }\n try {\n utf8Decoder.decode(bytes);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function decodeUtf8(bytes: Uint8Array): string {\n if (utf8DecoderLenient) {\n return utf8DecoderLenient.decode(bytes);\n }\n if (maybeBuffer) {\n return maybeBuffer.from(bytes).toString('utf8');\n }\n return '';\n}\n","import type { CapturedBody } from './types';\nimport { decodeUtf8, encodeBase64, isValidUtf8 } from './encoding';\n\n// eslint-disable-next-line no-control-regex\nconst controlChars = /[\\x00-\\x1F\\x7F]/g;\n\nexport function sanitizeLogString(value: string): string {\n if (!value) {\n return value;\n }\n return value.replace(controlChars, '');\n}\n\nexport function sanitizeHeaderValues(\n headers: Record<string, string | string[]> | undefined,\n): Record<string, string | string[]> | undefined {\n if (!headers) {\n return undefined;\n }\n\n const sanitized: Record<string, string | string[]> = {};\n for (const [key, value] of Object.entries(headers)) {\n const name = sanitizeLogString(key);\n if (Array.isArray(value)) {\n sanitized[name] = value.map((entry) => sanitizeLogString(entry));\n } else {\n sanitized[name] = sanitizeLogString(value);\n }\n }\n return sanitized;\n}\n\nexport function makeCapturedBody(\n bytes: Uint8Array | undefined,\n totalBytes: number,\n truncated: boolean,\n mode: 'text' | 'base64',\n): CapturedBody | undefined {\n if (!bytes) {\n return undefined;\n }\n\n if (mode === 'base64') {\n return {\n bytes: totalBytes,\n encoding: 'base64',\n truncated,\n value: encodeBase64(bytes),\n };\n }\n\n if (isValidUtf8(bytes)) {\n return {\n bytes: totalBytes,\n encoding: 'utf8',\n truncated,\n value: decodeUtf8(bytes),\n };\n }\n\n return {\n bytes: totalBytes,\n encoding: 'base64',\n truncated,\n value: encodeBase64(bytes),\n };\n}\n","/**\n * Base48 encoding with a vowel-free alphabet to reduce the risk of generating\n * profanity-like substrings in time-ordered identifiers while staying compact.\n * Digits that are commonly read as vowels (0, 1, 3, 4) are excluded to avoid\n * leetspeak-style false positives. The alphabet is ordered by ASCII to preserve\n * lexicographic ordering of fixed-length encodings.\n */\nconst base48AlphabetLex = '256789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz';\nconst base48Zero = base48AlphabetLex.charAt(0);\nconst base48Regex = /^[256789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz]*$/;\n\nconst maxChunkBytes = 32;\nconst chunkToEncodedLength = new Map<number, number>();\nconst encodedLengthToChunk = new Map<number, number>();\n\nfor (let size = 1; size <= maxChunkBytes; size += 1) {\n const encodedLength = Math.ceil((size * 8) / Math.log2(48));\n chunkToEncodedLength.set(size, encodedLength);\n encodedLengthToChunk.set(encodedLength, size);\n}\n\nconst maxEncodedChunk = chunkToEncodedLength.get(maxChunkBytes)!;\n\nfunction encodeChunk(bytes: Uint8Array, size: number): string {\n let value = 0n;\n for (const byte of bytes) {\n value = (value << 8n) | BigInt(byte);\n }\n\n let encoded = '';\n if (value === 0n) {\n encoded = base48Zero;\n } else {\n while (value > 0n) {\n const mod = value % 48n;\n encoded = base48AlphabetLex[Number(mod)] + encoded;\n value /= 48n;\n }\n }\n\n const targetLength = chunkToEncodedLength.get(size);\n if (!targetLength) {\n throw new Error(`base48: unsupported chunk size ${size}`);\n }\n return encoded.padStart(targetLength, base48Zero);\n}\n\nfunction decodeChunk(value: string, size: number): Uint8Array {\n let n = 0n;\n for (const char of value) {\n const index = base48AlphabetLex.indexOf(char);\n if (index === -1) {\n throw new Error('base48: invalid character');\n }\n n = n * 48n + BigInt(index);\n }\n\n const maxValue = 2n ** BigInt(size * 8) - 1n;\n if (n > maxValue) {\n throw new Error('base48: invalid length');\n }\n\n const buffer = new Uint8Array(size);\n for (let i = size - 1; i >= 0; i -= 1) {\n buffer[i] = Number(n & 0xffn);\n n >>= 8n;\n }\n return buffer;\n}\n\nexport function encodeBase48Lex(buffer: Uint8Array): string {\n if (buffer.length === 0) {\n return '';\n }\n\n let result = '';\n for (let offset = 0; offset < buffer.length; ) {\n const remaining = buffer.length - offset;\n const size = remaining >= maxChunkBytes ? maxChunkBytes : remaining;\n result += encodeChunk(buffer.slice(offset, offset + size), size);\n offset += size;\n }\n return result;\n}\n\nexport function decodeBase48Lex(value: string): Uint8Array {\n if (!value) {\n return new Uint8Array();\n }\n if (!base48Regex.test(value)) {\n throw new Error('base48: invalid string');\n }\n\n let totalBytes = 0;\n for (let offset = 0; offset < value.length; ) {\n const remaining = value.length - offset;\n const chunkLength = remaining >= maxEncodedChunk ? maxEncodedChunk : remaining;\n const size = encodedLengthToChunk.get(chunkLength);\n if (!size) {\n throw new Error('base48: invalid length');\n }\n totalBytes += size;\n offset += chunkLength;\n }\n\n const result = new Uint8Array(totalBytes);\n let cursor = 0;\n for (let offset = 0; offset < value.length; ) {\n const remaining = value.length - offset;\n const chunkLength = remaining >= maxEncodedChunk ? maxEncodedChunk : remaining;\n const size = encodedLengthToChunk.get(chunkLength);\n if (!size) {\n throw new Error('base48: invalid length');\n }\n const chunk = decodeChunk(value.slice(offset, offset + chunkLength), size);\n result.set(chunk, cursor);\n cursor += size;\n offset += chunkLength;\n }\n return result;\n}\n","const base62AlphabetLex = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\nconst base62Zero = base62AlphabetLex.charAt(0);\nconst base62Regex = /^[0-9A-Za-z]*$/;\n\nconst maxChunkBytes = 32;\nconst chunkToEncodedLength = new Map<number, number>();\nconst encodedLengthToChunk = new Map<number, number>();\n\nfor (let size = 1; size <= maxChunkBytes; size += 1) {\n const encodedLength = Math.ceil((size * 8) / Math.log2(62));\n chunkToEncodedLength.set(size, encodedLength);\n encodedLengthToChunk.set(encodedLength, size);\n}\n\nconst maxEncodedChunk = chunkToEncodedLength.get(maxChunkBytes)!;\n\nfunction encodeChunk(bytes: Uint8Array, size: number): string {\n let value = 0n;\n for (const byte of bytes) {\n value = (value << 8n) | BigInt(byte);\n }\n\n let encoded = '';\n if (value === 0n) {\n encoded = base62Zero;\n } else {\n while (value > 0n) {\n const mod = value % 62n;\n encoded = base62AlphabetLex[Number(mod)] + encoded;\n value /= 62n;\n }\n }\n\n const targetLength = chunkToEncodedLength.get(size);\n if (!targetLength) {\n throw new Error(`base62: unsupported chunk size ${size}`);\n }\n return encoded.padStart(targetLength, base62Zero);\n}\n\nfunction decodeChunk(value: string, size: number): Uint8Array {\n let n = 0n;\n for (const char of value) {\n const index = base62AlphabetLex.indexOf(char);\n if (index === -1) {\n throw new Error('base62: invalid character');\n }\n n = n * 62n + BigInt(index);\n }\n\n const maxValue = 2n ** BigInt(size * 8) - 1n;\n if (n > maxValue) {\n throw new Error('base62: invalid length');\n }\n\n const buffer = new Uint8Array(size);\n for (let i = size - 1; i >= 0; i -= 1) {\n buffer[i] = Number(n & 0xffn);\n n >>= 8n;\n }\n return buffer;\n}\n\nexport function encodeBase62Lex(buffer: Uint8Array): string {\n if (buffer.length === 0) {\n return '';\n }\n\n let result = '';\n for (let offset = 0; offset < buffer.length; ) {\n const remaining = buffer.length - offset;\n const size = remaining >= maxChunkBytes ? maxChunkBytes : remaining;\n result += encodeChunk(buffer.slice(offset, offset + size), size);\n offset += size;\n }\n return result;\n}\n\nexport function decodeBase62Lex(value: string): Uint8Array {\n if (!value) {\n return new Uint8Array();\n }\n if (!base62Regex.test(value)) {\n throw new Error('base62: invalid string');\n }\n\n let totalBytes = 0;\n for (let offset = 0; offset < value.length; ) {\n const remaining = value.length - offset;\n const chunkLength = remaining >= maxEncodedChunk ? maxEncodedChunk : remaining;\n const size = encodedLengthToChunk.get(chunkLength);\n if (!size) {\n throw new Error('base62: invalid length');\n }\n totalBytes += size;\n offset += chunkLength;\n }\n\n const result = new Uint8Array(totalBytes);\n let cursor = 0;\n for (let offset = 0; offset < value.length; ) {\n const remaining = value.length - offset;\n const chunkLength = remaining >= maxEncodedChunk ? maxEncodedChunk : remaining;\n const size = encodedLengthToChunk.get(chunkLength);\n if (!size) {\n throw new Error('base62: invalid length');\n }\n const chunk = decodeChunk(value.slice(offset, offset + chunkLength), size);\n result.set(chunk, cursor);\n cursor += size;\n offset += chunkLength;\n }\n return result;\n}\n","import { encodeBase48Lex } from './base48';\nimport { encodeBase62Lex } from './base62';\n\nconst requestIdPrefix = 'req_';\n\nfunction uuidv7Bytes(): Uint8Array {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n\n // Encode timestamp in first 48 bits\n const timestamp = BigInt(Date.now());\n bytes[0] = Number((timestamp >> 40n) & 0xffn);\n bytes[1] = Number((timestamp >> 32n) & 0xffn);\n bytes[2] = Number((timestamp >> 24n) & 0xffn);\n bytes[3] = Number((timestamp >> 16n) & 0xffn);\n bytes[4] = Number((timestamp >> 8n) & 0xffn);\n bytes[5] = Number(timestamp & 0xffn);\n\n // Set version (7) and variant (RFC 4122)\n const byte6 = bytes[6] ?? 0;\n const byte8 = bytes[8] ?? 0;\n bytes[6] = (byte6 & 0x0f) | 0x70;\n bytes[8] = (byte8 & 0x3f) | 0x80;\n\n return bytes;\n}\n\n/**\n * Generates a UUIDv7 string.\n * Uses crypto.getRandomValues which is available in all modern JS runtimes\n * (browsers, Node.js 15+, Deno, Bun, Cloudflare Workers, Vercel Edge, etc.)\n */\nexport function uuidv7(): string {\n const bytes = uuidv7Bytes();\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));\n return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10).join('')}`;\n}\n\nexport function uuidv7base62(): string {\n return encodeBase62Lex(uuidv7Bytes());\n}\n\nexport function uuidv7base48(): string {\n return encodeBase48Lex(uuidv7Bytes());\n}\n\nexport function generateRequestId(): string {\n return `${requestIdPrefix}${uuidv7base48()}`;\n}\n","import type { Span } from '@opentelemetry/api';\nimport type { AttributeValue, NormalizedRequest, XrayContext } from './types';\nimport type { CaptureConfig, RedactionConfig, ResolvedXrayConfig } from './config';\n\nexport type RequestState = {\n request: NormalizedRequest;\n config: ResolvedXrayConfig;\n span?: Span;\n context: XrayContext;\n attributes: Record<string, AttributeValue>;\n events: Array<{ name: string; attributes?: Record<string, AttributeValue> }>;\n userId?: string;\n sessionId?: string;\n error?: unknown;\n captureOverride?: Partial<CaptureConfig>;\n redactionOverride?: Partial<RedactionConfig>;\n};\n\nconst contextMap = new WeakMap<XrayContext, RequestState>();\nconst objectMap = new WeakMap<object, RequestState>();\n\nexport function bindContext(ctx: XrayContext, state: RequestState): void {\n contextMap.set(ctx, state);\n}\n\nexport function getContextState(ctx: XrayContext): RequestState | undefined {\n return contextMap.get(ctx);\n}\n\nexport function bindObject(target: object, state: RequestState): void {\n objectMap.set(target, state);\n}\n\nexport function getContextFromObject(target: unknown): XrayContext | undefined {\n const state = getStateFromObject(target);\n return state?.context;\n}\n\nexport function getStateFromObject(target: unknown): RequestState | undefined {\n if (!target || typeof target !== 'object') {\n return undefined;\n }\n if (objectMap.has(target)) {\n return objectMap.get(target);\n }\n\n const fallback = findNestedTarget(target as Record<string, unknown>);\n if (fallback && objectMap.has(fallback)) {\n return objectMap.get(fallback);\n }\n return undefined;\n}\n\nfunction findNestedTarget(obj: Record<string, unknown>): object | null {\n if (obj.raw && typeof obj.raw === 'object') {\n return obj.raw as object;\n }\n if (obj.req && typeof obj.req === 'object') {\n const req = obj.req as Record<string, unknown>;\n if (req.raw && typeof req.raw === 'object') {\n return req.raw as object;\n }\n return req as object;\n }\n if (obj.request && typeof obj.request === 'object') {\n const request = obj.request as Record<string, unknown>;\n if (request.raw && typeof request.raw === 'object') {\n return request.raw as object;\n }\n return request as object;\n }\n return null;\n}\n"],"mappings":"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","names":["maxChunkBytes","chunkToEncodedLength","encodedLengthToChunk","maxEncodedChunk","encodeChunk","maxChunkBytes","encodeChunk"]}
package/dist/index.cjs CHANGED
@@ -141,8 +141,8 @@ function extractParamName(value) {
141
141
  var defaultCapture = {
142
142
  requestHeaders: true,
143
143
  responseHeaders: true,
144
- requestBody: "none",
145
- responseBody: "none",
144
+ requestBody: "text",
145
+ responseBody: "text",
146
146
  maxBodyBytes: 65536
147
147
  };
148
148
  var defaultRedaction = {
@@ -152,8 +152,7 @@ var defaultRedaction = {
152
152
  replacement: "[REDACTED]"
153
153
  };
154
154
  var defaultRequestId = {
155
- header: "x-request-id",
156
- generate: true
155
+ header: "request-id"
157
156
  };
158
157
  var defaultRoute = {
159
158
  normalize: true,
@@ -1062,7 +1061,7 @@ function createSpanProcessor(mode, exporter) {
1062
1061
  }
1063
1062
  function sdkVersion() {
1064
1063
  if (true) {
1065
- return "0.5.1";
1064
+ return "0.7.0";
1066
1065
  }
1067
1066
  return "unknown";
1068
1067
  }
@@ -1071,8 +1070,69 @@ function isNodeRuntime() {
1071
1070
  return !!maybeProcess?.versions?.node;
1072
1071
  }
1073
1072
 
1074
- // src/uuid.ts
1075
- function uuidv7() {
1073
+ // src/uuid/base48.ts
1074
+ var base48AlphabetLex = "256789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
1075
+ var base48Zero = base48AlphabetLex.charAt(0);
1076
+ var maxChunkBytes = 32;
1077
+ var chunkToEncodedLength = /* @__PURE__ */ new Map();
1078
+ var encodedLengthToChunk = /* @__PURE__ */ new Map();
1079
+ for (let size = 1; size <= maxChunkBytes; size += 1) {
1080
+ const encodedLength = Math.ceil(size * 8 / Math.log2(48));
1081
+ chunkToEncodedLength.set(size, encodedLength);
1082
+ encodedLengthToChunk.set(encodedLength, size);
1083
+ }
1084
+ var maxEncodedChunk = chunkToEncodedLength.get(maxChunkBytes);
1085
+ function encodeChunk(bytes, size) {
1086
+ let value = 0n;
1087
+ for (const byte of bytes) {
1088
+ value = value << 8n | BigInt(byte);
1089
+ }
1090
+ let encoded = "";
1091
+ if (value === 0n) {
1092
+ encoded = base48Zero;
1093
+ } else {
1094
+ while (value > 0n) {
1095
+ const mod = value % 48n;
1096
+ encoded = base48AlphabetLex[Number(mod)] + encoded;
1097
+ value /= 48n;
1098
+ }
1099
+ }
1100
+ const targetLength = chunkToEncodedLength.get(size);
1101
+ if (!targetLength) {
1102
+ throw new Error(`base48: unsupported chunk size ${size}`);
1103
+ }
1104
+ return encoded.padStart(targetLength, base48Zero);
1105
+ }
1106
+ function encodeBase48Lex(buffer) {
1107
+ if (buffer.length === 0) {
1108
+ return "";
1109
+ }
1110
+ let result = "";
1111
+ for (let offset = 0; offset < buffer.length; ) {
1112
+ const remaining = buffer.length - offset;
1113
+ const size = remaining >= maxChunkBytes ? maxChunkBytes : remaining;
1114
+ result += encodeChunk(buffer.slice(offset, offset + size), size);
1115
+ offset += size;
1116
+ }
1117
+ return result;
1118
+ }
1119
+
1120
+ // src/uuid/base62.ts
1121
+ var base62AlphabetLex = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1122
+ var base62Zero = base62AlphabetLex.charAt(0);
1123
+ var maxChunkBytes2 = 32;
1124
+ var chunkToEncodedLength2 = /* @__PURE__ */ new Map();
1125
+ var encodedLengthToChunk2 = /* @__PURE__ */ new Map();
1126
+ for (let size = 1; size <= maxChunkBytes2; size += 1) {
1127
+ const encodedLength = Math.ceil(size * 8 / Math.log2(62));
1128
+ chunkToEncodedLength2.set(size, encodedLength);
1129
+ encodedLengthToChunk2.set(encodedLength, size);
1130
+ }
1131
+ var maxEncodedChunk2 = chunkToEncodedLength2.get(maxChunkBytes2);
1132
+
1133
+ // src/uuid/index.ts
1134
+ var requestIdPrefix = "req_";
1135
+ function uuidv7Bytes() {
1076
1136
  const bytes = new Uint8Array(16);
1077
1137
  crypto.getRandomValues(bytes);
1078
1138
  const timestamp = BigInt(Date.now());
@@ -1086,8 +1146,13 @@ function uuidv7() {
1086
1146
  const byte8 = bytes[8] ?? 0;
1087
1147
  bytes[6] = byte6 & 15 | 112;
1088
1148
  bytes[8] = byte8 & 63 | 128;
1089
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
1090
- return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
1149
+ return bytes;
1150
+ }
1151
+ function uuidv7base48() {
1152
+ return encodeBase48Lex(uuidv7Bytes());
1153
+ }
1154
+ function generateRequestId() {
1155
+ return `${requestIdPrefix}${uuidv7base48()}`;
1091
1156
  }
1092
1157
 
1093
1158
  // src/state.ts
@@ -1128,14 +1193,14 @@ function createEmitter(config, exporter) {
1128
1193
  function startRequest(config, tracer, req) {
1129
1194
  const startTimeMs = Number.isFinite(req.startTimeMs) ? req.startTimeMs : Date.now();
1130
1195
  req.startTimeMs = startTimeMs;
1131
- const requestId = resolveRequestId(config, req.requestId, req.headers);
1132
- req.requestId = requestId;
1196
+ const explicitRequestId = normalizeRequestIdCandidate(req.requestId);
1197
+ req.requestId = explicitRequestId;
1133
1198
  if (req.route && config.route.normalize) {
1134
1199
  req.route = config.route.normalizer ? config.route.normalizer(req.route) : normalizeRoutePattern(req.route);
1135
1200
  }
1136
1201
  const span = spanFromTracer(tracer, spanNameFromRequest(req));
1137
1202
  const context = {
1138
- requestId,
1203
+ requestId: explicitRequestId ?? "",
1139
1204
  traceId: span?.spanContext().traceId,
1140
1205
  spanId: span?.spanContext().spanId,
1141
1206
  setUserId: (id) => {
@@ -1214,8 +1279,10 @@ function endRequest(config, ctx, res, err) {
1214
1279
  const endTimeMs = Number.isFinite(res.endTimeMs) ? res.endTimeMs : Date.now();
1215
1280
  res.endTimeMs = endTimeMs;
1216
1281
  if (!state) {
1282
+ const resolvedRequestId2 = resolveFinalRequestId(config, ctx.requestId, res.headers);
1283
+ ctx.requestId = resolvedRequestId2;
1217
1284
  const fallbackLog = {
1218
- requestId: ctx.requestId,
1285
+ requestId: resolvedRequestId2,
1219
1286
  serviceName: config.serviceName,
1220
1287
  method: res.statusCode ? "UNKNOWN" : "UNKNOWN",
1221
1288
  url: "",
@@ -1226,12 +1293,19 @@ function endRequest(config, ctx, res, err) {
1226
1293
  return fallbackLog;
1227
1294
  }
1228
1295
  const request = state.request;
1296
+ const resolvedRequestId = resolveFinalRequestId(
1297
+ config,
1298
+ request.requestId || ctx.requestId,
1299
+ res.headers
1300
+ );
1301
+ request.requestId = resolvedRequestId;
1302
+ ctx.requestId = resolvedRequestId;
1229
1303
  const capture = resolveCapture(config.capture, state.captureOverride);
1230
1304
  const redaction = resolveRedaction(config.redaction, state.redactionOverride);
1231
1305
  const route = request.route;
1232
1306
  const url = sanitizeLogString(request.url);
1233
1307
  const log = {
1234
- requestId: request.requestId ?? ctx.requestId,
1308
+ requestId: resolvedRequestId,
1235
1309
  traceId: state.span?.spanContext().traceId,
1236
1310
  spanId: state.span?.spanContext().spanId,
1237
1311
  serviceName: config.serviceName,
@@ -1308,22 +1382,40 @@ function endRequest(config, ctx, res, err) {
1308
1382
  }
1309
1383
  return redacted;
1310
1384
  }
1311
- function resolveRequestId(config, requestId, headers) {
1312
- if (requestId) {
1313
- return requestId;
1385
+ function resolveFinalRequestId(config, explicitRequestId, responseHeaders) {
1386
+ const explicit = normalizeRequestIdCandidate(explicitRequestId);
1387
+ if (explicit) {
1388
+ return explicit;
1314
1389
  }
1315
- const headerName = config.requestId.header.toLowerCase();
1316
- const headerValue = headers[headerName];
1390
+ const headerValue = resolveHeaderRequestId(config.requestId.header, responseHeaders);
1317
1391
  if (headerValue) {
1318
- const value = Array.isArray(headerValue) ? headerValue[0] : headerValue;
1319
- if (value && value.trim()) {
1320
- return value.trim();
1392
+ return headerValue;
1393
+ }
1394
+ return generateRequestId();
1395
+ }
1396
+ function resolveHeaderRequestId(headerName, headers) {
1397
+ if (!headers) {
1398
+ return void 0;
1399
+ }
1400
+ const target = headerName.toLowerCase();
1401
+ for (const [name, value] of Object.entries(headers)) {
1402
+ if (name.toLowerCase() !== target) {
1403
+ continue;
1404
+ }
1405
+ const entry = Array.isArray(value) ? value[0] : value;
1406
+ const normalized = normalizeRequestIdCandidate(entry);
1407
+ if (normalized) {
1408
+ return normalized;
1321
1409
  }
1322
1410
  }
1323
- if (!config.requestId.generate) {
1324
- return "";
1411
+ return void 0;
1412
+ }
1413
+ function normalizeRequestIdCandidate(value) {
1414
+ if (!value) {
1415
+ return void 0;
1325
1416
  }
1326
- return uuidv7();
1417
+ const trimmed = value.trim();
1418
+ return trimmed ? trimmed : void 0;
1327
1419
  }
1328
1420
  function resolveCapture(base, override) {
1329
1421
  if (!override) {