@logbrew/sdk 0.1.36 → 0.1.38
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/core.cjs +44 -213
- package/issue-breadcrumbs.cjs +143 -0
- package/issue-diagnostics.cjs +94 -582
- package/issue-evidence.cjs +256 -0
- package/issue-exception.cjs +263 -0
- package/issue-stack-frames.cjs +220 -0
- package/issue-stack-text.cjs +101 -0
- package/issue-stack.cjs +188 -225
- package/issue-text.cjs +72 -0
- package/log-context.cjs +87 -67
- package/package.json +11 -1
- package/sdk-error.cjs +36 -0
- package/support-ticket.cjs +232 -122
- package/telemetry-context.cjs +343 -251
- package/trace-context.cjs +376 -179
- package/transport-error.cjs +23 -0
- package/validation.cjs +279 -0
- package/winston.cjs +6 -7
package/trace-context.cjs
CHANGED
|
@@ -1,217 +1,414 @@
|
|
|
1
|
+
const { SdkError } = require("./sdk-error.cjs");
|
|
2
|
+
const {
|
|
3
|
+
ZERO_SPAN_ID,
|
|
4
|
+
ZERO_TRACE_ID,
|
|
5
|
+
isRecord,
|
|
6
|
+
requireSpanId,
|
|
7
|
+
requireTraceFlags,
|
|
8
|
+
requireTraceId
|
|
9
|
+
} = require("./validation.cjs");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @import { BaggageEntry, TraceparentContext, TracestateEntry } from "./index.cjs"
|
|
13
|
+
* @typedef {{ traceparent: string, tracestate?: string, baggage?: string }} TraceContextHeaders
|
|
14
|
+
* @typedef {{ readonly key: string, readonly value: string, readonly properties?: readonly string[] }} ReadonlyBaggageEntry
|
|
15
|
+
*/
|
|
16
|
+
|
|
1
17
|
const MAX_TRACESTATE_ENTRIES = 32;
|
|
2
18
|
const MAX_TRACESTATE_LENGTH = 512;
|
|
19
|
+
const MAX_TRACESTATE_VALUE_LENGTH = 256;
|
|
3
20
|
const MAX_BAGGAGE_ENTRIES = 64;
|
|
4
21
|
const MAX_BAGGAGE_LENGTH = 8192;
|
|
22
|
+
const DEFAULT_TRACE_FLAGS = "01";
|
|
23
|
+
const HEX_RADIX = 16;
|
|
24
|
+
const SAMPLED_FLAG_DIVISOR = 2;
|
|
25
|
+
const SAMPLED_FLAG_REMAINDER = 1;
|
|
26
|
+
const KEY_START = 0;
|
|
27
|
+
const SEPARATOR_WIDTH = 1;
|
|
28
|
+
const NO_PROPERTIES = 0;
|
|
29
|
+
const TRACEPARENT_PATTERN =
|
|
30
|
+
/^(?<version>[0-9a-fA-F]{2})-(?<traceId>[0-9a-fA-F]{32})-(?<parentSpanId>[0-9a-fA-F]{16})-(?<traceFlags>[0-9a-fA-F]{2})$/u;
|
|
5
31
|
const TRACESTATE_SIMPLE_KEY_PATTERN = /^[a-z0-9][a-z0-9_\-*/]{0,255}$/u;
|
|
6
32
|
const TRACESTATE_TENANT_KEY_PATTERN = /^[a-z0-9][a-z0-9_\-*/]{0,240}@[a-z][a-z0-9_\-*/]{0,13}$/u;
|
|
7
|
-
const TRACESTATE_VALUE_PATTERN = /^[
|
|
33
|
+
const TRACESTATE_VALUE_PATTERN = /^[ -+--<>-~]*[!-+--<>-~]$/u;
|
|
8
34
|
const BAGGAGE_KEY_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/u;
|
|
9
35
|
const BAGGAGE_PROPERTY_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+(?:=[!#$%&'*+\-.^_`|~0-9A-Za-z]*)?$/u;
|
|
10
36
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.map((entry) => entry.trim())
|
|
19
|
-
.filter((entry) => entry !== "")
|
|
20
|
-
.map((entry) => {
|
|
21
|
-
const separator = entry.indexOf("=");
|
|
22
|
-
if (separator <= 0) {
|
|
23
|
-
throw new SdkError("validation_error", "tracestate entries must use key=value");
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
key: entry.slice(0, separator).trim(),
|
|
27
|
-
value: entry.slice(separator + 1).trim()
|
|
28
|
-
};
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function createTracestate(entries) {
|
|
34
|
-
return serializeTracestate(normalizeTracestateEntries(entries));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function parseBaggage(baggage) {
|
|
38
|
-
if (typeof baggage !== "string" || baggage.trim() === "") {
|
|
39
|
-
throw new SdkError("validation_error", "baggage must be non-empty");
|
|
40
|
-
}
|
|
41
|
-
return normalizeBaggageEntries(
|
|
42
|
-
baggage.split(",")
|
|
43
|
-
.map((entry) => entry.trim())
|
|
44
|
-
.filter((entry) => entry !== "")
|
|
45
|
-
.map((entry) => {
|
|
46
|
-
const [pair, ...rawProperties] = entry.split(";");
|
|
47
|
-
const separator = pair.indexOf("=");
|
|
48
|
-
if (separator <= 0) {
|
|
49
|
-
throw new SdkError("validation_error", "baggage entries must use key=value");
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
key: pair.slice(0, separator).trim(),
|
|
53
|
-
value: decodeBaggageValue(pair.slice(separator + 1).trim()),
|
|
54
|
-
properties: rawProperties.map((property) => property.trim()).filter((property) => property !== "")
|
|
55
|
-
};
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function createBaggage(entries) {
|
|
61
|
-
return serializeBaggage(normalizeBaggageEntries(entries));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function createTraceContextHeaders(input) {
|
|
65
|
-
if (!input || Array.isArray(input) || typeof input !== "object") {
|
|
66
|
-
throw new SdkError("validation_error", "trace context input must be an object");
|
|
67
|
-
}
|
|
68
|
-
const headers = { traceparent: createTraceparent(input) };
|
|
69
|
-
if (input.tracestate !== undefined) {
|
|
70
|
-
const tracestate = typeof input.tracestate === "string"
|
|
71
|
-
? createTracestate(parseTracestate(input.tracestate))
|
|
72
|
-
: createTracestate(input.tracestate);
|
|
73
|
-
if (tracestate !== "") {
|
|
74
|
-
headers.tracestate = tracestate;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (input.baggage !== undefined) {
|
|
78
|
-
const baggage = typeof input.baggage === "string"
|
|
79
|
-
? createBaggage(parseBaggage(input.baggage))
|
|
80
|
-
: createBaggage(input.baggage);
|
|
81
|
-
if (baggage !== "") {
|
|
82
|
-
headers.baggage = baggage;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return headers;
|
|
37
|
+
/**
|
|
38
|
+
* @param {unknown} traceparent
|
|
39
|
+
* @returns {Readonly<Record<string, string>>}
|
|
40
|
+
*/
|
|
41
|
+
function traceparentParts(traceparent) {
|
|
42
|
+
if (typeof traceparent !== "string" || traceparent.trim() === "") {
|
|
43
|
+
throw new SdkError("validation_error", "traceparent must be non-empty");
|
|
86
44
|
}
|
|
45
|
+
const parts = TRACEPARENT_PATTERN.exec(traceparent.trim())?.groups;
|
|
46
|
+
if (parts === undefined) {
|
|
47
|
+
throw new SdkError("validation_error", "traceparent must use W3C version-traceId-parentSpanId-traceFlags format");
|
|
48
|
+
}
|
|
49
|
+
return parts;
|
|
50
|
+
}
|
|
87
51
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!entry || Array.isArray(entry) || typeof entry !== "object") {
|
|
98
|
-
throw new SdkError("validation_error", "tracestate entry must be an object");
|
|
99
|
-
}
|
|
100
|
-
const key = validateTracestateKey(entry.key);
|
|
101
|
-
if (seenKeys.has(key)) {
|
|
102
|
-
throw new SdkError("validation_error", "tracestate keys must be unique");
|
|
103
|
-
}
|
|
104
|
-
seenKeys.add(key);
|
|
105
|
-
return {
|
|
106
|
-
key,
|
|
107
|
-
value: validateTracestateValue(entry.value)
|
|
108
|
-
};
|
|
109
|
-
});
|
|
110
|
-
const serialized = serializeTracestate(normalized);
|
|
111
|
-
if (serialized.length > MAX_TRACESTATE_LENGTH) {
|
|
112
|
-
throw new SdkError("validation_error", `tracestate must be at most ${MAX_TRACESTATE_LENGTH} characters`);
|
|
113
|
-
}
|
|
114
|
-
return normalized;
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} version
|
|
54
|
+
* @param {string} traceId
|
|
55
|
+
* @param {string} parentSpanId
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
function rejectInvalidTraceparent(version, traceId, parentSpanId) {
|
|
59
|
+
if (version === "ff") {
|
|
60
|
+
throw new SdkError("validation_error", "traceparent version ff is not allowed");
|
|
115
61
|
}
|
|
62
|
+
if (traceId === ZERO_TRACE_ID) {
|
|
63
|
+
throw new SdkError("validation_error", "traceparent traceId must not be all zeros");
|
|
64
|
+
}
|
|
65
|
+
if (parentSpanId === ZERO_SPAN_ID) {
|
|
66
|
+
throw new SdkError("validation_error", "traceparent parentSpanId must not be all zeros");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
116
69
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
70
|
+
/**
|
|
71
|
+
* @param {unknown} traceparent
|
|
72
|
+
* @returns {TraceparentContext}
|
|
73
|
+
*/
|
|
74
|
+
function parseTraceparent(traceparent) {
|
|
75
|
+
const parts = traceparentParts(traceparent);
|
|
76
|
+
const version = String(parts["version"]).toLowerCase();
|
|
77
|
+
const traceId = String(parts["traceId"]).toLowerCase();
|
|
78
|
+
const parentSpanId = String(parts["parentSpanId"]).toLowerCase();
|
|
79
|
+
const traceFlags = String(parts["traceFlags"]).toLowerCase();
|
|
80
|
+
rejectInvalidTraceparent(version, traceId, parentSpanId);
|
|
81
|
+
const sampled = Number.parseInt(traceFlags, HEX_RADIX) % SAMPLED_FLAG_DIVISOR === SAMPLED_FLAG_REMAINDER;
|
|
82
|
+
return {
|
|
83
|
+
version,
|
|
84
|
+
|
|
85
|
+
traceId,
|
|
86
|
+
|
|
87
|
+
parentSpanId,
|
|
88
|
+
traceFlags,
|
|
89
|
+
|
|
90
|
+
sampled
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {Readonly<{ traceId?: unknown, spanId?: unknown, traceFlags?: unknown }>} input
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
function createTraceparent({ traceId, spanId, traceFlags = DEFAULT_TRACE_FLAGS }) {
|
|
99
|
+
const validTraceId = requireTraceId(traceId);
|
|
100
|
+
const validSpanId = requireSpanId("spanId", spanId);
|
|
101
|
+
const validTraceFlags = requireTraceFlags(traceFlags);
|
|
102
|
+
return `00-${validTraceId.toLowerCase()}-${validSpanId.toLowerCase()}-${validTraceFlags.toLowerCase()}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {Readonly<{ traceId?: unknown, spanId?: unknown, traceFlags?: unknown }>} input
|
|
107
|
+
* @returns {{ traceparent: string }}
|
|
108
|
+
*/
|
|
109
|
+
function createTraceparentHeaders(input) {
|
|
110
|
+
return { traceparent: createTraceparent(input) };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {readonly Readonly<TracestateEntry>[]} entries
|
|
115
|
+
* @returns {string}
|
|
116
|
+
*/
|
|
117
|
+
function serializeTracestate(entries) {
|
|
118
|
+
return entries.map((entry) => `${entry.key}=${entry.value}`).join(",");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {readonly ReadonlyBaggageEntry[]} entries
|
|
123
|
+
* @returns {string}
|
|
124
|
+
*/
|
|
125
|
+
function serializeBaggage(entries) {
|
|
126
|
+
return entries
|
|
127
|
+
.map((entry) => {
|
|
128
|
+
const properties = entry.properties === undefined ? "" : `;${entry.properties.join(";")}`;
|
|
129
|
+
return `${entry.key}=${encodeURIComponent(entry.value)}${properties}`;
|
|
130
|
+
})
|
|
131
|
+
.join(",");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {unknown} key
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
138
|
+
function validateTracestateKey(key) {
|
|
139
|
+
if (typeof key !== "string" || key.trim() === "") {
|
|
140
|
+
throw new SdkError("validation_error", "tracestate key must be non-empty");
|
|
139
141
|
}
|
|
142
|
+
const normalized = key.trim();
|
|
143
|
+
if (!TRACESTATE_SIMPLE_KEY_PATTERN.test(normalized) && !TRACESTATE_TENANT_KEY_PATTERN.test(normalized)) {
|
|
144
|
+
throw new SdkError("validation_error", "tracestate key must be lowercase W3C tracestate key");
|
|
145
|
+
}
|
|
146
|
+
return normalized;
|
|
147
|
+
}
|
|
140
148
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
/**
|
|
150
|
+
* @param {unknown} value
|
|
151
|
+
* @returns {string}
|
|
152
|
+
*/
|
|
153
|
+
function validateTracestateValue(value) {
|
|
154
|
+
if (
|
|
155
|
+
typeof value !== "string"
|
|
156
|
+
|| value === ""
|
|
157
|
+
|| value.length > MAX_TRACESTATE_VALUE_LENGTH
|
|
158
|
+
|| !TRACESTATE_VALUE_PATTERN.test(value)
|
|
159
|
+
) {
|
|
160
|
+
throw new SdkError("validation_error", "tracestate value must be printable ASCII without comma or equals");
|
|
150
161
|
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
151
164
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
/**
|
|
166
|
+
* @param {unknown} key
|
|
167
|
+
* @returns {string}
|
|
168
|
+
*/
|
|
169
|
+
function validateBaggageKey(key) {
|
|
170
|
+
if (typeof key !== "string" || !BAGGAGE_KEY_PATTERN.test(key)) {
|
|
171
|
+
throw new SdkError("validation_error", "baggage key must use RFC header-name characters");
|
|
157
172
|
}
|
|
173
|
+
return key;
|
|
174
|
+
}
|
|
158
175
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
176
|
+
/**
|
|
177
|
+
* @param {unknown} value
|
|
178
|
+
* @returns {string}
|
|
179
|
+
*/
|
|
180
|
+
function validateBaggageValue(value) {
|
|
181
|
+
if (typeof value !== "string") {
|
|
182
|
+
throw new SdkError("validation_error", "baggage value must be a string");
|
|
164
183
|
}
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
165
186
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
187
|
+
/**
|
|
188
|
+
* @param {unknown} properties
|
|
189
|
+
* @returns {string[] | undefined}
|
|
190
|
+
*/
|
|
191
|
+
function normalizeBaggageProperties(properties) {
|
|
192
|
+
if (properties === undefined) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
if (!Array.isArray(properties)) {
|
|
196
|
+
throw new SdkError("validation_error", "baggage properties must be an array");
|
|
197
|
+
}
|
|
198
|
+
/** @type {readonly unknown[]} */
|
|
199
|
+
const candidates = properties;
|
|
200
|
+
const safeProperties = candidates.map((property) => {
|
|
201
|
+
if (typeof property !== "string" || !BAGGAGE_PROPERTY_PATTERN.test(property)) {
|
|
202
|
+
throw new SdkError("validation_error", "baggage property must use RFC header-name characters");
|
|
169
203
|
}
|
|
170
|
-
return
|
|
204
|
+
return property;
|
|
205
|
+
});
|
|
206
|
+
return safeProperties.length > NO_PROPERTIES ? safeProperties : undefined;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @param {string} value
|
|
211
|
+
* @returns {string}
|
|
212
|
+
*/
|
|
213
|
+
function decodeBaggageValue(value) {
|
|
214
|
+
try {
|
|
215
|
+
return decodeURIComponent(value);
|
|
216
|
+
} catch {
|
|
217
|
+
throw new SdkError("validation_error", "baggage value must use valid percent encoding");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @param {unknown} entries
|
|
223
|
+
* @param {string} label
|
|
224
|
+
* @param {number} maxEntries
|
|
225
|
+
* @returns {readonly unknown[]}
|
|
226
|
+
*/
|
|
227
|
+
function requireEntryList(entries, label, maxEntries) {
|
|
228
|
+
if (!Array.isArray(entries)) {
|
|
229
|
+
throw new SdkError("validation_error", `${label} entries must be an array`);
|
|
230
|
+
}
|
|
231
|
+
if (entries.length > maxEntries) {
|
|
232
|
+
throw new SdkError("validation_error", `${label} must contain at most ${maxEntries} entries`);
|
|
171
233
|
}
|
|
234
|
+
return entries;
|
|
235
|
+
}
|
|
172
236
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
237
|
+
/**
|
|
238
|
+
* @param {unknown} entries
|
|
239
|
+
* @returns {TracestateEntry[]}
|
|
240
|
+
*/
|
|
241
|
+
function normalizeTracestateEntries(entries) {
|
|
242
|
+
const list = requireEntryList(entries, "tracestate", MAX_TRACESTATE_ENTRIES);
|
|
243
|
+
const seenKeys = new Set();
|
|
244
|
+
const normalized = list.map((entry) => {
|
|
245
|
+
if (!isRecord(entry)) {
|
|
246
|
+
throw new SdkError("validation_error", "tracestate entry must be an object");
|
|
176
247
|
}
|
|
177
|
-
|
|
178
|
-
|
|
248
|
+
const key = validateTracestateKey(entry["key"]);
|
|
249
|
+
if (seenKeys.has(key)) {
|
|
250
|
+
throw new SdkError("validation_error", "tracestate keys must be unique");
|
|
179
251
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
return safeProperties.length > 0 ? { properties: safeProperties } : {};
|
|
252
|
+
seenKeys.add(key);
|
|
253
|
+
return { key, value: validateTracestateValue(entry["value"]) };
|
|
254
|
+
});
|
|
255
|
+
if (serializeTracestate(normalized).length > MAX_TRACESTATE_LENGTH) {
|
|
256
|
+
throw new SdkError("validation_error", `tracestate must be at most ${MAX_TRACESTATE_LENGTH} characters`);
|
|
187
257
|
}
|
|
258
|
+
return normalized;
|
|
259
|
+
}
|
|
188
260
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
261
|
+
/**
|
|
262
|
+
* @param {unknown} entry
|
|
263
|
+
* @returns {BaggageEntry}
|
|
264
|
+
*/
|
|
265
|
+
function normalizeBaggageEntry(entry) {
|
|
266
|
+
if (!isRecord(entry)) {
|
|
267
|
+
throw new SdkError("validation_error", "baggage entry must be an object");
|
|
195
268
|
}
|
|
269
|
+
const key = validateBaggageKey(entry["key"]);
|
|
270
|
+
const value = validateBaggageValue(entry["value"]);
|
|
271
|
+
const properties = normalizeBaggageProperties(entry["properties"]);
|
|
272
|
+
return { key, value, ...(properties === undefined ? {} : { properties }) };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @param {unknown} entries
|
|
277
|
+
* @returns {BaggageEntry[]}
|
|
278
|
+
*/
|
|
279
|
+
function normalizeBaggageEntries(entries) {
|
|
280
|
+
const list = requireEntryList(entries, "baggage", MAX_BAGGAGE_ENTRIES);
|
|
281
|
+
const normalized = list.map((entry) => normalizeBaggageEntry(entry));
|
|
282
|
+
if (serializeBaggage(normalized).length > MAX_BAGGAGE_LENGTH) {
|
|
283
|
+
throw new SdkError("validation_error", `baggage must be at most ${MAX_BAGGAGE_LENGTH} characters`);
|
|
284
|
+
}
|
|
285
|
+
return normalized;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @param {string} header
|
|
290
|
+
* @returns {string[]}
|
|
291
|
+
*/
|
|
292
|
+
function headerEntries(header) {
|
|
293
|
+
return header
|
|
294
|
+
.split(",")
|
|
295
|
+
.map((entry) => entry.trim())
|
|
296
|
+
.filter((entry) => entry !== "");
|
|
297
|
+
}
|
|
196
298
|
|
|
299
|
+
/**
|
|
300
|
+
* @param {unknown} tracestate
|
|
301
|
+
* @returns {TracestateEntry[]}
|
|
302
|
+
*/
|
|
303
|
+
function parseTracestate(tracestate) {
|
|
304
|
+
if (typeof tracestate !== "string" || tracestate.trim() === "") {
|
|
305
|
+
throw new SdkError("validation_error", "tracestate must be non-empty");
|
|
306
|
+
}
|
|
307
|
+
return normalizeTracestateEntries(
|
|
308
|
+
headerEntries(tracestate).map((entry) => {
|
|
309
|
+
const separator = entry.indexOf("=");
|
|
310
|
+
if (separator <= KEY_START) {
|
|
311
|
+
throw new SdkError("validation_error", "tracestate entries must use key=value");
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
key: entry.slice(KEY_START, separator).trim(),
|
|
315
|
+
value: entry.slice(separator + SEPARATOR_WIDTH).trim()
|
|
316
|
+
};
|
|
317
|
+
})
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @param {string} entry
|
|
323
|
+
* @returns {ReadonlyBaggageEntry}
|
|
324
|
+
*/
|
|
325
|
+
function parseBaggageEntry(entry) {
|
|
326
|
+
const [pair = "", ...rawProperties] = entry.split(";");
|
|
327
|
+
const separator = pair.indexOf("=");
|
|
328
|
+
if (separator <= KEY_START) {
|
|
329
|
+
throw new SdkError("validation_error", "baggage entries must use key=value");
|
|
330
|
+
}
|
|
331
|
+
const key = pair.slice(KEY_START, separator).trim();
|
|
332
|
+
const value = decodeBaggageValue(pair.slice(separator + SEPARATOR_WIDTH).trim());
|
|
333
|
+
const properties = rawProperties.map((property) => property.trim()).filter((property) => property !== "");
|
|
197
334
|
return {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
parseTracestate
|
|
335
|
+
key,
|
|
336
|
+
value,
|
|
337
|
+
|
|
338
|
+
properties
|
|
203
339
|
};
|
|
204
340
|
}
|
|
205
341
|
|
|
206
|
-
|
|
207
|
-
|
|
342
|
+
/**
|
|
343
|
+
* @param {unknown} baggage
|
|
344
|
+
* @returns {BaggageEntry[]}
|
|
345
|
+
*/
|
|
346
|
+
function parseBaggage(baggage) {
|
|
347
|
+
if (typeof baggage !== "string" || baggage.trim() === "") {
|
|
348
|
+
throw new SdkError("validation_error", "baggage must be non-empty");
|
|
349
|
+
}
|
|
350
|
+
return normalizeBaggageEntries(headerEntries(baggage).map((entry) => parseBaggageEntry(entry)));
|
|
208
351
|
}
|
|
209
352
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
353
|
+
/**
|
|
354
|
+
* @param {unknown} entries
|
|
355
|
+
* @returns {string}
|
|
356
|
+
*/
|
|
357
|
+
function createTracestate(entries) {
|
|
358
|
+
return serializeTracestate(normalizeTracestateEntries(entries));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {unknown} entries
|
|
363
|
+
* @returns {string}
|
|
364
|
+
*/
|
|
365
|
+
function createBaggage(entries) {
|
|
366
|
+
return serializeBaggage(normalizeBaggageEntries(entries));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* @param {unknown} tracestate
|
|
371
|
+
* @returns {{ tracestate?: string }}
|
|
372
|
+
*/
|
|
373
|
+
function tracestateHeader(tracestate) {
|
|
374
|
+
if (tracestate === undefined) {
|
|
375
|
+
return {};
|
|
376
|
+
}
|
|
377
|
+
const value = typeof tracestate === "string" ? createTracestate(parseTracestate(tracestate)) : createTracestate(tracestate);
|
|
378
|
+
return value === "" ? {} : { tracestate: value };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @param {unknown} baggage
|
|
383
|
+
* @returns {{ baggage?: string }}
|
|
384
|
+
*/
|
|
385
|
+
function baggageHeader(baggage) {
|
|
386
|
+
if (baggage === undefined) {
|
|
387
|
+
return {};
|
|
388
|
+
}
|
|
389
|
+
const value = typeof baggage === "string" ? createBaggage(parseBaggage(baggage)) : createBaggage(baggage);
|
|
390
|
+
return value === "" ? {} : { baggage: value };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @param {unknown} input
|
|
395
|
+
* @returns {TraceContextHeaders}
|
|
396
|
+
*/
|
|
397
|
+
function createTraceContextHeaders(input) {
|
|
398
|
+
if (!isRecord(input)) {
|
|
399
|
+
throw new SdkError("validation_error", "trace context input must be an object");
|
|
400
|
+
}
|
|
401
|
+
const traceparent = createTraceparent(input);
|
|
402
|
+
return { traceparent, ...tracestateHeader(input["tracestate"]), ...baggageHeader(input["baggage"]) };
|
|
215
403
|
}
|
|
216
404
|
|
|
217
|
-
module.exports = {
|
|
405
|
+
module.exports = {
|
|
406
|
+
createBaggage,
|
|
407
|
+
createTraceContextHeaders,
|
|
408
|
+
createTraceparent,
|
|
409
|
+
createTraceparentHeaders,
|
|
410
|
+
createTracestate,
|
|
411
|
+
parseBaggage,
|
|
412
|
+
parseTraceparent,
|
|
413
|
+
parseTracestate
|
|
414
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class TransportError extends Error {
|
|
2
|
+
/**
|
|
3
|
+
* @param {string} code
|
|
4
|
+
* @param {string} message
|
|
5
|
+
* @param {boolean} [retryable]
|
|
6
|
+
*/
|
|
7
|
+
constructor(code, message, retryable = false) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "TransportError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.retryable = retryable;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} message
|
|
16
|
+
* @returns {TransportError}
|
|
17
|
+
*/
|
|
18
|
+
static network(message) {
|
|
19
|
+
return new TransportError("network_failure", message, true);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { TransportError };
|