@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/issue-stack.cjs
CHANGED
|
@@ -1,276 +1,239 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const { SdkError } = require("./sdk-error.cjs");
|
|
4
|
+
const { isRecord } = require("./validation.cjs");
|
|
5
|
+
const {
|
|
6
|
+
SAFE_DEBUG_ID_PATTERN,
|
|
7
|
+
basename,
|
|
8
|
+
frameCoordinateFromText,
|
|
9
|
+
frameIdentity,
|
|
10
|
+
sanitizeFrameFilename
|
|
11
|
+
} = require("./issue-stack-text.cjs");
|
|
12
|
+
const { validateIssueStackFrames, validateNativeStackFrames } = require("./issue-stack-frames.cjs");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @import { IssueStackFrame } from "./index.cjs"
|
|
16
|
+
* @typedef {{ frames: IssueStackFrame[], truncated: boolean }} StackEvidence
|
|
17
|
+
* @typedef {readonly (readonly [string, unknown])[]} DebugIdEntries
|
|
18
|
+
* @typedef {{ functionName: string | undefined, location: string }} FrameLocation
|
|
19
|
+
* @typedef {{ readonly debugId: string, readonly path: string }} DebugIdCandidate
|
|
20
|
+
*/
|
|
21
|
+
|
|
3
22
|
const MAX_ISSUE_STACK_FRAMES = 32;
|
|
4
23
|
const MAX_ISSUE_STACK_FUNCTION_LENGTH = 256;
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
24
|
+
const MAX_RUNTIME_DEBUG_IDS = 512;
|
|
25
|
+
const MIN_FRAME_LOCATION_PARTS = 3;
|
|
26
|
+
const NO_OFFSET = 0;
|
|
27
|
+
const HERMES_BYTECODE_OFFSET = 1;
|
|
28
|
+
const HERMES_BYTECODE_LINE = 1;
|
|
29
|
+
const UNIQUE_MATCH = 1;
|
|
30
|
+
const TEXT_START = 0;
|
|
31
|
+
const MARKER_WIDTH = 1;
|
|
32
|
+
const LAST_CHARACTER = -1;
|
|
33
|
+
const CHROME_FRAME_PREFIX = "at ";
|
|
34
|
+
const HERMES_BYTECODE_PREFIX = "address at ";
|
|
35
|
+
const LINE_BREAK_PATTERN = /\r?\n/u;
|
|
36
|
+
const LEADING_SLASHES_PATTERN = /^\/+/u;
|
|
11
37
|
const DEBUG_ID_REGISTRY = Symbol.for("logbrew.release-artifact.debug-ids");
|
|
12
38
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
frames.push({ ...parsed, ...(debugId ? { debugId } : {}) });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return { frames, truncated };
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} value
|
|
41
|
+
* @returns {string | undefined}
|
|
42
|
+
*/
|
|
43
|
+
function generatedFrameFunction(value) {
|
|
44
|
+
const functionName = frameIdentity(value, MAX_ISSUE_STACK_FUNCTION_LENGTH, false);
|
|
45
|
+
if (
|
|
46
|
+
functionName === undefined
|
|
47
|
+
|| functionName.includes("@")
|
|
48
|
+
|| functionName.includes("/")
|
|
49
|
+
|| functionName.includes("\\")
|
|
50
|
+
|| functionName.includes("?")
|
|
51
|
+
|| functionName.includes("#")
|
|
52
|
+
) {
|
|
53
|
+
return undefined;
|
|
32
54
|
}
|
|
55
|
+
return functionName;
|
|
56
|
+
}
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} line
|
|
60
|
+
* @returns {FrameLocation}
|
|
61
|
+
*/
|
|
62
|
+
function splitFrameFunction(line) {
|
|
63
|
+
if (line.startsWith(CHROME_FRAME_PREFIX)) {
|
|
64
|
+
const location = line.slice(CHROME_FRAME_PREFIX.length).trim();
|
|
65
|
+
if (!location.endsWith(")") || !location.includes("(")) {
|
|
66
|
+
return { functionName: undefined, location };
|
|
37
67
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
throw new SdkError("validation_error", "issue stack frame must be an object");
|
|
44
|
-
}
|
|
45
|
-
if (typeof frame.filename !== "string") {
|
|
46
|
-
throw new SdkError("validation_error", "issue stack frame filename is invalid");
|
|
47
|
-
}
|
|
48
|
-
const filename = sanitizeFrameFilename(frame.filename);
|
|
49
|
-
if (!filename || filename.length > 2048 || hasControlCharacter(filename)) {
|
|
50
|
-
throw new SdkError("validation_error", "issue stack frame filename is invalid");
|
|
51
|
-
}
|
|
52
|
-
const line = frameCoordinateFromText(frame.line);
|
|
53
|
-
const column = frameCoordinateFromText(frame.column);
|
|
54
|
-
if (line === null || column === null || line > 2147483647 || column > 2147483647) {
|
|
55
|
-
throw new SdkError("validation_error", "issue stack frame coordinates must be positive integers");
|
|
56
|
-
}
|
|
57
|
-
const debugId = frame.debugId === undefined
|
|
58
|
-
? undefined
|
|
59
|
-
: typeof frame.debugId === "string" && SAFE_DEBUG_ID_PATTERN.test(frame.debugId.trim())
|
|
60
|
-
? frame.debugId.trim().toLowerCase()
|
|
61
|
-
: null;
|
|
62
|
-
if (debugId === null) {
|
|
63
|
-
throw new SdkError("validation_error", "issue stack frame debugId is invalid");
|
|
64
|
-
}
|
|
65
|
-
const functionName = optionalFrameIdentity(frame.function, MAX_ISSUE_STACK_FUNCTION_LENGTH);
|
|
66
|
-
if (functionName === null) {
|
|
67
|
-
throw new SdkError("validation_error", "issue stack frame function is invalid");
|
|
68
|
-
}
|
|
69
|
-
const moduleName = optionalFrameIdentity(frame.module, MAX_ISSUE_STACK_MODULE_LENGTH, true);
|
|
70
|
-
if (moduleName === null) {
|
|
71
|
-
throw new SdkError("validation_error", "issue stack frame module is invalid");
|
|
72
|
-
}
|
|
73
|
-
const inApp = frame.inApp === undefined
|
|
74
|
-
? undefined
|
|
75
|
-
: typeof frame.inApp === "boolean"
|
|
76
|
-
? frame.inApp
|
|
77
|
-
: null;
|
|
78
|
-
if (inApp === null) {
|
|
79
|
-
throw new SdkError("validation_error", "issue stack frame inApp is invalid");
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
filename,
|
|
83
|
-
line,
|
|
84
|
-
column,
|
|
85
|
-
...(functionName ? { function: functionName } : {}),
|
|
86
|
-
...(moduleName ? { module: moduleName } : {}),
|
|
87
|
-
...(inApp !== undefined ? { inApp } : {}),
|
|
88
|
-
...(debugId ? { debugId } : {})
|
|
89
|
-
};
|
|
90
|
-
});
|
|
68
|
+
const marker = location.lastIndexOf("(");
|
|
69
|
+
return {
|
|
70
|
+
functionName: generatedFrameFunction(location.slice(TEXT_START, marker)),
|
|
71
|
+
location: location.slice(marker + MARKER_WIDTH, LAST_CHARACTER)
|
|
72
|
+
};
|
|
91
73
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (frames === undefined) {
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
if (!Array.isArray(frames) || frames.length === 0 || frames.length > MAX_ISSUE_STACK_FRAMES) {
|
|
98
|
-
throw new SdkError("validation_error", `issue nativeStackFrames must contain 1-${MAX_ISSUE_STACK_FRAMES} frames`);
|
|
99
|
-
}
|
|
100
|
-
return frames.map((frame) => {
|
|
101
|
-
const keys = frame && !Array.isArray(frame) && typeof frame === "object" ? Object.keys(frame) : [];
|
|
102
|
-
if (keys.length !== 3
|
|
103
|
-
|| !keys.every((key) => ["imageUuid", "architecture", "instructionOffset"].includes(key))
|
|
104
|
-
|| typeof frame.imageUuid !== "string" || !NATIVE_IMAGE_UUID_PATTERN.test(frame.imageUuid)
|
|
105
|
-
|| !NATIVE_ARCHITECTURES.has(frame.architecture)
|
|
106
|
-
|| typeof frame.instructionOffset !== "string" || !NATIVE_OFFSET_PATTERN.test(frame.instructionOffset)) {
|
|
107
|
-
throw new SdkError("validation_error", "issue native stack frame is invalid");
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
imageUuid: frame.imageUuid,
|
|
111
|
-
architecture: frame.architecture,
|
|
112
|
-
instructionOffset: frame.instructionOffset
|
|
113
|
-
};
|
|
114
|
-
});
|
|
74
|
+
if (!line.includes("@")) {
|
|
75
|
+
return { functionName: undefined, location: line };
|
|
115
76
|
}
|
|
116
|
-
|
|
117
|
-
return {
|
|
77
|
+
const marker = line.lastIndexOf("@");
|
|
78
|
+
return {
|
|
79
|
+
functionName: generatedFrameFunction(line.slice(TEXT_START, marker)),
|
|
80
|
+
location: line.slice(marker + MARKER_WIDTH)
|
|
81
|
+
};
|
|
118
82
|
}
|
|
119
83
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
let functionName;
|
|
127
|
-
if (location.startsWith("at ")) {
|
|
128
|
-
location = location.slice(3).trim();
|
|
129
|
-
if (location.endsWith(")") && location.includes("(")) {
|
|
130
|
-
const marker = location.lastIndexOf("(");
|
|
131
|
-
functionName = generatedFrameFunction(location.slice(0, marker));
|
|
132
|
-
location = location.slice(marker + 1, -1);
|
|
133
|
-
}
|
|
134
|
-
} else if (location.includes("@")) {
|
|
135
|
-
const marker = location.lastIndexOf("@");
|
|
136
|
-
functionName = generatedFrameFunction(location.slice(0, marker));
|
|
137
|
-
location = location.slice(marker + 1);
|
|
138
|
-
}
|
|
139
|
-
const hermesBytecode = location.startsWith("address at ");
|
|
140
|
-
location = location.replace(/^address at /u, "");
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} location
|
|
86
|
+
* @param {boolean} hermesBytecode
|
|
87
|
+
* @returns {Omit<IssueStackFrame, "function"> | undefined}
|
|
88
|
+
*/
|
|
89
|
+
function frameCoordinates(location, hermesBytecode) {
|
|
141
90
|
const parts = location.split(":");
|
|
142
|
-
if (parts.length <
|
|
143
|
-
return
|
|
91
|
+
if (parts.length < MIN_FRAME_LOCATION_PARTS) {
|
|
92
|
+
return undefined;
|
|
144
93
|
}
|
|
145
94
|
const rawColumn = parts.pop();
|
|
146
95
|
const lineNumber = frameCoordinateFromText(parts.pop());
|
|
147
|
-
const column = frameCoordinateFromText(
|
|
96
|
+
const column = frameCoordinateFromText(
|
|
97
|
+
rawColumn,
|
|
98
|
+
hermesBytecode && lineNumber === HERMES_BYTECODE_LINE ? HERMES_BYTECODE_OFFSET : NO_OFFSET
|
|
99
|
+
);
|
|
148
100
|
const filename = sanitizeFrameFilename(parts.join(":"));
|
|
149
|
-
if (
|
|
150
|
-
return
|
|
101
|
+
if (filename === "" || lineNumber === undefined || column === undefined) {
|
|
102
|
+
return undefined;
|
|
151
103
|
}
|
|
152
104
|
return {
|
|
153
105
|
filename,
|
|
154
106
|
line: lineNumber,
|
|
155
|
-
|
|
156
|
-
|
|
107
|
+
|
|
108
|
+
column
|
|
157
109
|
};
|
|
158
110
|
}
|
|
159
111
|
|
|
160
|
-
|
|
161
|
-
|
|
112
|
+
/**
|
|
113
|
+
* @param {string} rawLine
|
|
114
|
+
* @returns {IssueStackFrame | undefined}
|
|
115
|
+
*/
|
|
116
|
+
function parseJavaScriptStackFrame(rawLine) {
|
|
117
|
+
const line = rawLine.trim();
|
|
118
|
+
if (line === "") {
|
|
162
119
|
return undefined;
|
|
163
120
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|| (rejectLocationText && (identity.includes("?") || identity.includes("#")))) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
return identity;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function generatedFrameFunction(value) {
|
|
178
|
-
const functionName = optionalFrameIdentity(value, MAX_ISSUE_STACK_FUNCTION_LENGTH);
|
|
179
|
-
if (!functionName
|
|
180
|
-
|| functionName.includes("@")
|
|
181
|
-
|| functionName.includes("/")
|
|
182
|
-
|| functionName.includes("\\")
|
|
183
|
-
|| functionName.includes("?")
|
|
184
|
-
|| functionName.includes("#")) {
|
|
121
|
+
const { functionName, location } = splitFrameFunction(line);
|
|
122
|
+
const hermesBytecode = location.startsWith(HERMES_BYTECODE_PREFIX);
|
|
123
|
+
const coordinates = frameCoordinates(
|
|
124
|
+
hermesBytecode ? location.slice(HERMES_BYTECODE_PREFIX.length) : location,
|
|
125
|
+
hermesBytecode
|
|
126
|
+
);
|
|
127
|
+
if (coordinates === undefined) {
|
|
185
128
|
return undefined;
|
|
186
129
|
}
|
|
187
|
-
return functionName;
|
|
130
|
+
return functionName === undefined ? coordinates : { ...coordinates, function: functionName };
|
|
188
131
|
}
|
|
189
132
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function sanitizeFrameFilename(value) {
|
|
200
|
-
let filename = String(value ?? "").trim();
|
|
201
|
-
if (!filename) {
|
|
202
|
-
return "";
|
|
203
|
-
}
|
|
204
|
-
filename = filename.split("?", 1)[0].split("#", 1)[0];
|
|
205
|
-
if (filename.startsWith("file://")) {
|
|
206
|
-
filename = filename.slice("file://".length);
|
|
207
|
-
}
|
|
208
|
-
if (LOCAL_ABSOLUTE_PATH_PATTERN.test(filename)) {
|
|
209
|
-
return basename(filename);
|
|
210
|
-
}
|
|
211
|
-
return filename;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function debugIdForFrame(filename, debugIdMap, SdkError) {
|
|
215
|
-
if (debugIdMap !== undefined && debugIdMap !== null
|
|
216
|
-
&& (!debugIdMap || Array.isArray(debugIdMap) || typeof debugIdMap !== "object")) {
|
|
217
|
-
throw new SdkError("validation_error", "debugIdMap must be an object");
|
|
218
|
-
}
|
|
219
|
-
return debugIdFromEntries(filename, debugIdMap ? Object.entries(debugIdMap) : [])
|
|
220
|
-
?? debugIdFromEntries(filename, runtimeDebugIdEntries());
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} filename
|
|
135
|
+
* @param {string} candidate
|
|
136
|
+
* @returns {boolean}
|
|
137
|
+
*/
|
|
138
|
+
function matchesFramePath(filename, candidate) {
|
|
139
|
+
return filename === candidate
|
|
140
|
+
|| (candidate.includes("/") && filename.endsWith(`/${candidate.replace(LEADING_SLASHES_PATTERN, "")}`));
|
|
221
141
|
}
|
|
222
142
|
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} filename
|
|
145
|
+
* @param {DebugIdEntries} entries
|
|
146
|
+
* @returns {string | undefined}
|
|
147
|
+
*/
|
|
223
148
|
function debugIdFromEntries(filename, entries) {
|
|
224
149
|
const normalizedFilename = sanitizeFrameFilename(filename);
|
|
225
150
|
const frameBasename = basename(normalizedFilename);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
? normalizedDebugId
|
|
241
|
-
: false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return typeof basenameMatch === "string" ? basenameMatch : null;
|
|
151
|
+
/** @type {readonly DebugIdCandidate[]} */
|
|
152
|
+
const candidates = entries.flatMap(([candidate, debugId]) =>
|
|
153
|
+
typeof debugId === "string" && SAFE_DEBUG_ID_PATTERN.test(debugId.trim())
|
|
154
|
+
? [{ debugId: debugId.trim().toLowerCase(), path: sanitizeFrameFilename(candidate) }]
|
|
155
|
+
: []);
|
|
156
|
+
const exact = candidates.find((candidate) => matchesFramePath(normalizedFilename, candidate.path));
|
|
157
|
+
if (exact !== undefined) {
|
|
158
|
+
return exact.debugId;
|
|
159
|
+
}
|
|
160
|
+
const basenameIds = new Set(
|
|
161
|
+
candidates.filter((candidate) => basename(candidate.path) === frameBasename).map((candidate) => candidate.debugId)
|
|
162
|
+
);
|
|
163
|
+
const [onlyId] = basenameIds;
|
|
164
|
+
return basenameIds.size === UNIQUE_MATCH ? onlyId : undefined;
|
|
245
165
|
}
|
|
246
166
|
|
|
167
|
+
/**
|
|
168
|
+
* @returns {DebugIdEntries}
|
|
169
|
+
*/
|
|
247
170
|
function runtimeDebugIdEntries() {
|
|
248
171
|
try {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (!registry || Array.isArray(registry) || typeof registry !== "object") {
|
|
172
|
+
/** @type {unknown} */
|
|
173
|
+
const registry = Object.getOwnPropertyDescriptor(globalThis, DEBUG_ID_REGISTRY)?.value;
|
|
174
|
+
if (!isRecord(registry)) {
|
|
253
175
|
return [];
|
|
254
176
|
}
|
|
255
|
-
return Object.
|
|
256
|
-
(
|
|
257
|
-
|
|
177
|
+
return Object.getOwnPropertyNames(registry)
|
|
178
|
+
.slice(TEXT_START, MAX_RUNTIME_DEBUG_IDS)
|
|
179
|
+
.flatMap((candidate) => {
|
|
180
|
+
const descriptor = Object.getOwnPropertyDescriptor(registry, candidate);
|
|
181
|
+
/** @type {unknown} */
|
|
182
|
+
const value = descriptor?.value;
|
|
183
|
+
return descriptor !== undefined && "value" in descriptor ? [[candidate, value]] : [];
|
|
184
|
+
});
|
|
258
185
|
} catch {
|
|
259
186
|
return [];
|
|
260
187
|
}
|
|
261
188
|
}
|
|
262
189
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
190
|
+
/**
|
|
191
|
+
* @param {string} filename
|
|
192
|
+
* @param {unknown} debugIdMap
|
|
193
|
+
* @returns {string | undefined}
|
|
194
|
+
*/
|
|
195
|
+
function debugIdForFrame(filename, debugIdMap) {
|
|
196
|
+
if (debugIdMap === undefined || debugIdMap === null) {
|
|
197
|
+
return debugIdFromEntries(filename, runtimeDebugIdEntries());
|
|
198
|
+
}
|
|
199
|
+
if (!isRecord(debugIdMap)) {
|
|
200
|
+
throw new SdkError("validation_error", "debugIdMap must be an object");
|
|
201
|
+
}
|
|
202
|
+
return debugIdFromEntries(filename, Object.entries(debugIdMap))
|
|
203
|
+
?? debugIdFromEntries(filename, runtimeDebugIdEntries());
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {Readonly<IssueStackFrame>} frame
|
|
208
|
+
* @param {unknown} debugIdMap
|
|
209
|
+
* @returns {IssueStackFrame}
|
|
210
|
+
*/
|
|
211
|
+
function withDebugId(frame, debugIdMap) {
|
|
212
|
+
const debugId = debugIdForFrame(frame.filename, debugIdMap);
|
|
213
|
+
return debugId === undefined ? frame : { ...frame, debugId };
|
|
267
214
|
}
|
|
268
215
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
216
|
+
/**
|
|
217
|
+
* @param {unknown} stack
|
|
218
|
+
* @param {unknown} debugIdMap
|
|
219
|
+
* @returns {StackEvidence}
|
|
220
|
+
*/
|
|
221
|
+
function javascriptStackEvidence(stack, debugIdMap) {
|
|
222
|
+
if (typeof stack !== "string" || stack.trim() === "") {
|
|
223
|
+
return { frames: [], truncated: false };
|
|
224
|
+
}
|
|
225
|
+
/** @type {IssueStackFrame[]} */
|
|
226
|
+
const frames = [];
|
|
227
|
+
for (const rawLine of stack.split(LINE_BREAK_PATTERN)) {
|
|
228
|
+
const parsed = parseJavaScriptStackFrame(rawLine);
|
|
229
|
+
if (parsed !== undefined) {
|
|
230
|
+
if (frames.length === MAX_ISSUE_STACK_FRAMES) {
|
|
231
|
+
return { frames, truncated: true };
|
|
232
|
+
}
|
|
233
|
+
frames.push(withDebugId(parsed, debugIdMap));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { frames, truncated: false };
|
|
274
237
|
}
|
|
275
238
|
|
|
276
|
-
module.exports = {
|
|
239
|
+
module.exports = { javascriptStackEvidence, validateIssueStackFrames, validateNativeStackFrames };
|
package/issue-text.cjs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { SdkError } = require("./sdk-error.cjs");
|
|
4
|
+
const { hasUnicodeControlCharacter } = require("./validation.cjs");
|
|
5
|
+
|
|
6
|
+
const MACHINE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/u;
|
|
7
|
+
const LOCATION_TEXT_PATTERN = /[?#]/u;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} label
|
|
11
|
+
* @param {unknown} value
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function requireMachineName(label, value) {
|
|
15
|
+
if (typeof value !== "string" || !MACHINE_NAME_PATTERN.test(value)) {
|
|
16
|
+
throw new SdkError("validation_error", `${label} must match ${MACHINE_NAME_PATTERN}`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} label
|
|
23
|
+
* @param {unknown} value
|
|
24
|
+
* @param {number} maxLength
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
function boundedText(label, value, maxLength) {
|
|
28
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
29
|
+
if (normalized === "") {
|
|
30
|
+
throw new SdkError("validation_error", `${label} must be non-empty`);
|
|
31
|
+
}
|
|
32
|
+
if ([...normalized].length > maxLength || hasUnicodeControlCharacter(normalized)) {
|
|
33
|
+
throw new SdkError("validation_error", `${label} is invalid or exceeds ${maxLength} characters`);
|
|
34
|
+
}
|
|
35
|
+
return normalized;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} label
|
|
40
|
+
* @param {unknown} value
|
|
41
|
+
* @param {number} maxLength
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function boundedIdentity(label, value, maxLength) {
|
|
45
|
+
const normalized = boundedText(label, value, maxLength);
|
|
46
|
+
if (LOCATION_TEXT_PATTERN.test(normalized)) {
|
|
47
|
+
throw new SdkError("validation_error", `${label} is invalid or exceeds ${maxLength} characters`);
|
|
48
|
+
}
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} label
|
|
54
|
+
* @param {unknown} value
|
|
55
|
+
* @param {number} maxLength
|
|
56
|
+
* @returns {string | undefined}
|
|
57
|
+
*/
|
|
58
|
+
function optionalText(label, value, maxLength) {
|
|
59
|
+
return value === undefined ? undefined : boundedText(label, value, maxLength);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} label
|
|
64
|
+
* @param {unknown} value
|
|
65
|
+
* @param {number} maxLength
|
|
66
|
+
* @returns {string | undefined}
|
|
67
|
+
*/
|
|
68
|
+
function optionalIdentity(label, value, maxLength) {
|
|
69
|
+
return value === undefined ? undefined : boundedIdentity(label, value, maxLength);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { boundedIdentity, boundedText, optionalIdentity, optionalText, requireMachineName };
|