@stainlessdev/xray-core 0.6.0-dev.c5f614b → 0.7.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 +67 -0
- package/dist/{chunk-ACDQIGDG.js → chunk-3ILKJJIG.js} +60 -9
- package/dist/chunk-3ILKJJIG.js.map +1 -0
- package/dist/index.cjs +27 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.cjs +60 -8
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +2 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +3 -1
- package/dist/internal.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ACDQIGDG.js.map +0 -1
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.
|
|
@@ -109,19 +109,66 @@ function makeCapturedBody(bytes, totalBytes, truncated, mode) {
|
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
// src/uuid/
|
|
113
|
-
var
|
|
114
|
-
var
|
|
112
|
+
// src/uuid/base48.ts
|
|
113
|
+
var base48AlphabetLex = "256789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
|
114
|
+
var base48Zero = base48AlphabetLex.charAt(0);
|
|
115
115
|
var maxChunkBytes = 32;
|
|
116
116
|
var chunkToEncodedLength = /* @__PURE__ */ new Map();
|
|
117
117
|
var encodedLengthToChunk = /* @__PURE__ */ new Map();
|
|
118
118
|
for (let size = 1; size <= maxChunkBytes; size += 1) {
|
|
119
|
-
const encodedLength = Math.ceil(size * 8 / Math.log2(
|
|
119
|
+
const encodedLength = Math.ceil(size * 8 / Math.log2(48));
|
|
120
120
|
chunkToEncodedLength.set(size, encodedLength);
|
|
121
121
|
encodedLengthToChunk.set(encodedLength, size);
|
|
122
122
|
}
|
|
123
123
|
var maxEncodedChunk = chunkToEncodedLength.get(maxChunkBytes);
|
|
124
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) {
|
|
125
172
|
let value = 0n;
|
|
126
173
|
for (const byte of bytes) {
|
|
127
174
|
value = value << 8n | BigInt(byte);
|
|
@@ -136,7 +183,7 @@ function encodeChunk(bytes, size) {
|
|
|
136
183
|
value /= 62n;
|
|
137
184
|
}
|
|
138
185
|
}
|
|
139
|
-
const targetLength =
|
|
186
|
+
const targetLength = chunkToEncodedLength2.get(size);
|
|
140
187
|
if (!targetLength) {
|
|
141
188
|
throw new Error(`base62: unsupported chunk size ${size}`);
|
|
142
189
|
}
|
|
@@ -149,8 +196,8 @@ function encodeBase62Lex(buffer) {
|
|
|
149
196
|
let result = "";
|
|
150
197
|
for (let offset = 0; offset < buffer.length; ) {
|
|
151
198
|
const remaining = buffer.length - offset;
|
|
152
|
-
const size = remaining >=
|
|
153
|
-
result +=
|
|
199
|
+
const size = remaining >= maxChunkBytes2 ? maxChunkBytes2 : remaining;
|
|
200
|
+
result += encodeChunk2(buffer.slice(offset, offset + size), size);
|
|
154
201
|
offset += size;
|
|
155
202
|
}
|
|
156
203
|
return result;
|
|
@@ -182,8 +229,11 @@ function uuidv7() {
|
|
|
182
229
|
function uuidv7base62() {
|
|
183
230
|
return encodeBase62Lex(uuidv7Bytes());
|
|
184
231
|
}
|
|
232
|
+
function uuidv7base48() {
|
|
233
|
+
return encodeBase48Lex(uuidv7Bytes());
|
|
234
|
+
}
|
|
185
235
|
function generateRequestId() {
|
|
186
|
-
return `${requestIdPrefix}${
|
|
236
|
+
return `${requestIdPrefix}${uuidv7base48()}`;
|
|
187
237
|
}
|
|
188
238
|
|
|
189
239
|
// src/state.ts
|
|
@@ -244,10 +294,11 @@ export {
|
|
|
244
294
|
makeCapturedBody,
|
|
245
295
|
uuidv7,
|
|
246
296
|
uuidv7base62,
|
|
297
|
+
uuidv7base48,
|
|
247
298
|
generateRequestId,
|
|
248
299
|
bindContext,
|
|
249
300
|
getContextState,
|
|
250
301
|
bindObject,
|
|
251
302
|
getContextFromObject
|
|
252
303
|
};
|
|
253
|
-
//# sourceMappingURL=chunk-
|
|
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
|
@@ -1070,14 +1070,14 @@ function isNodeRuntime() {
|
|
|
1070
1070
|
return !!maybeProcess?.versions?.node;
|
|
1071
1071
|
}
|
|
1072
1072
|
|
|
1073
|
-
// src/uuid/
|
|
1074
|
-
var
|
|
1075
|
-
var
|
|
1073
|
+
// src/uuid/base48.ts
|
|
1074
|
+
var base48AlphabetLex = "256789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
|
1075
|
+
var base48Zero = base48AlphabetLex.charAt(0);
|
|
1076
1076
|
var maxChunkBytes = 32;
|
|
1077
1077
|
var chunkToEncodedLength = /* @__PURE__ */ new Map();
|
|
1078
1078
|
var encodedLengthToChunk = /* @__PURE__ */ new Map();
|
|
1079
1079
|
for (let size = 1; size <= maxChunkBytes; size += 1) {
|
|
1080
|
-
const encodedLength = Math.ceil(size * 8 / Math.log2(
|
|
1080
|
+
const encodedLength = Math.ceil(size * 8 / Math.log2(48));
|
|
1081
1081
|
chunkToEncodedLength.set(size, encodedLength);
|
|
1082
1082
|
encodedLengthToChunk.set(encodedLength, size);
|
|
1083
1083
|
}
|
|
@@ -1089,21 +1089,21 @@ function encodeChunk(bytes, size) {
|
|
|
1089
1089
|
}
|
|
1090
1090
|
let encoded = "";
|
|
1091
1091
|
if (value === 0n) {
|
|
1092
|
-
encoded =
|
|
1092
|
+
encoded = base48Zero;
|
|
1093
1093
|
} else {
|
|
1094
1094
|
while (value > 0n) {
|
|
1095
|
-
const mod = value %
|
|
1096
|
-
encoded =
|
|
1097
|
-
value /=
|
|
1095
|
+
const mod = value % 48n;
|
|
1096
|
+
encoded = base48AlphabetLex[Number(mod)] + encoded;
|
|
1097
|
+
value /= 48n;
|
|
1098
1098
|
}
|
|
1099
1099
|
}
|
|
1100
1100
|
const targetLength = chunkToEncodedLength.get(size);
|
|
1101
1101
|
if (!targetLength) {
|
|
1102
|
-
throw new Error(`
|
|
1102
|
+
throw new Error(`base48: unsupported chunk size ${size}`);
|
|
1103
1103
|
}
|
|
1104
|
-
return encoded.padStart(targetLength,
|
|
1104
|
+
return encoded.padStart(targetLength, base48Zero);
|
|
1105
1105
|
}
|
|
1106
|
-
function
|
|
1106
|
+
function encodeBase48Lex(buffer) {
|
|
1107
1107
|
if (buffer.length === 0) {
|
|
1108
1108
|
return "";
|
|
1109
1109
|
}
|
|
@@ -1117,6 +1117,19 @@ function encodeBase62Lex(buffer) {
|
|
|
1117
1117
|
return result;
|
|
1118
1118
|
}
|
|
1119
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
|
+
|
|
1120
1133
|
// src/uuid/index.ts
|
|
1121
1134
|
var requestIdPrefix = "req_";
|
|
1122
1135
|
function uuidv7Bytes() {
|
|
@@ -1135,11 +1148,11 @@ function uuidv7Bytes() {
|
|
|
1135
1148
|
bytes[8] = byte8 & 63 | 128;
|
|
1136
1149
|
return bytes;
|
|
1137
1150
|
}
|
|
1138
|
-
function
|
|
1139
|
-
return
|
|
1151
|
+
function uuidv7base48() {
|
|
1152
|
+
return encodeBase48Lex(uuidv7Bytes());
|
|
1140
1153
|
}
|
|
1141
1154
|
function generateRequestId() {
|
|
1142
|
-
return `${requestIdPrefix}${
|
|
1155
|
+
return `${requestIdPrefix}${uuidv7base48()}`;
|
|
1143
1156
|
}
|
|
1144
1157
|
|
|
1145
1158
|
// src/state.ts
|