bippy 0.5.28 → 0.5.29

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.
Files changed (43) hide show
  1. package/README.md +0 -2
  2. package/dist/{core-BDWE7M7e.d.ts → core-Cjoce0EW.d.ts} +2 -2
  3. package/dist/core-DBBh-FTl.js +9 -0
  4. package/dist/{core-CEUgwvkw.d.cts → core-Y1ecSyti.d.cts} +2 -2
  5. package/dist/core-xjGqMMEY.cjs +9 -0
  6. package/dist/core.cjs +1 -1
  7. package/dist/core.d.cts +1 -1
  8. package/dist/core.d.ts +1 -1
  9. package/dist/core.js +1 -1
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.iife.js +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/{install-hook-only-CgvoC7AQ.js → install-hook-only-CRye53Kz.cjs} +1 -1
  16. package/dist/{install-hook-only-DZeocJdO.cjs → install-hook-only-DfpahZZO.js} +1 -1
  17. package/dist/install-hook-only.cjs +1 -1
  18. package/dist/install-hook-only.iife.js +1 -1
  19. package/dist/install-hook-only.js +1 -1
  20. package/dist/rdt-hook-Czn6qzdx.js +9 -0
  21. package/dist/rdt-hook-DnMMBqZs.cjs +9 -0
  22. package/dist/source.cjs +6 -6
  23. package/dist/source.d.cts +1 -1
  24. package/dist/source.d.ts +1 -1
  25. package/dist/source.js +12 -12
  26. package/package.json +3 -1
  27. package/src/core.ts +1325 -0
  28. package/src/index.ts +3 -0
  29. package/src/install-hook-only.ts +3 -0
  30. package/src/rdt-hook.ts +237 -0
  31. package/src/source/constants.ts +26 -0
  32. package/src/source/get-display-name-from-source.ts +103 -0
  33. package/src/source/get-source.ts +218 -0
  34. package/src/source/index.ts +6 -0
  35. package/src/source/owner-stack.ts +598 -0
  36. package/src/source/parse-stack.ts +295 -0
  37. package/src/source/symbolication.ts +410 -0
  38. package/src/source/types.ts +6 -0
  39. package/src/types.ts +244 -0
  40. package/dist/core-Ba_4EQvc.cjs +0 -9
  41. package/dist/core-U1d648PH.js +0 -9
  42. package/dist/rdt-hook-5L_ky0r0.js +0 -9
  43. package/dist/rdt-hook-D2m6uUhj.cjs +0 -9
@@ -0,0 +1,295 @@
1
+ export interface StackFrame {
2
+ args?: unknown[];
3
+ columnNumber?: number;
4
+ lineNumber?: number;
5
+ fileName?: string;
6
+ functionName?: string;
7
+ source?: string;
8
+ isServer?: boolean;
9
+ isSymbolicated?: boolean;
10
+ }
11
+
12
+ export interface ParseOptions {
13
+ slice?: number | [number, number];
14
+ allowEmpty?: boolean;
15
+ includeInElement?: boolean;
16
+ }
17
+
18
+ const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
19
+ const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
20
+ const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code\])?$/;
21
+
22
+ const getNonStandardStacktrace = (error: unknown): string | null => {
23
+ if (error && typeof error === 'object') {
24
+ const stacktrace = (error as Record<string, unknown>)['stacktrace'];
25
+ return typeof stacktrace === 'string' ? stacktrace : null;
26
+ }
27
+ return null;
28
+ };
29
+
30
+ export const parseStack = (
31
+ stackString: string,
32
+ options?: ParseOptions,
33
+ ): StackFrame[] => {
34
+ if (options?.includeInElement !== false) {
35
+ const lines = stackString.split('\n');
36
+ const frames: StackFrame[] = [];
37
+ for (const rawLine of lines) {
38
+ if (/^\s*at\s+/.test(rawLine)) {
39
+ const parsed = parseV8OrIeString(rawLine, undefined)[0];
40
+ if (parsed) frames.push(parsed);
41
+ } else if (/^\s*in\s+/.test(rawLine)) {
42
+ const elementName = rawLine
43
+ .replace(/^\s*in\s+/, '')
44
+ .replace(/\s*\(at .*\)$/, '');
45
+ frames.push({ functionName: elementName, source: rawLine });
46
+ } else if (rawLine.match(FIREFOX_SAFARI_STACK_REGEXP)) {
47
+ const parsed = parseFFOrSafariString(rawLine, undefined)[0];
48
+ if (parsed) frames.push(parsed);
49
+ }
50
+ }
51
+ return applySlice(frames, options);
52
+ }
53
+ if (stackString.match(CHROME_IE_STACK_REGEXP)) {
54
+ return parseV8OrIeString(stackString, options);
55
+ }
56
+ return parseFFOrSafariString(stackString, options);
57
+ };
58
+
59
+ export const extractLocation = (
60
+ urlLike: string,
61
+ ): [string, string | undefined, string | undefined] => {
62
+ if (!urlLike.includes(':')) return [urlLike, undefined, undefined];
63
+
64
+ // HACK: Chrome/V8 stack traces wrap location in parens: "(file.js:10:5)"
65
+ // We need to strip these outer parens but preserve parens in paths (e.g., Next.js route groups like "(docs)")
66
+ // Chrome format always ends with `:col)` where digit comes right before the closing paren
67
+ const isWrappedLocation =
68
+ urlLike.startsWith('(') && /:\d+\)$/.test(urlLike);
69
+ const sanitizedResult = isWrappedLocation ? urlLike.slice(1, -1) : urlLike;
70
+
71
+ const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
72
+ const parts = regExp.exec(sanitizedResult);
73
+ if (!parts) return [sanitizedResult, undefined, undefined];
74
+ return [parts[1], parts[2] || undefined, parts[3] || undefined] as const;
75
+ };
76
+
77
+ const applySlice = <T>(lines: T[], options?: ParseOptions): T[] => {
78
+ if (options && options.slice != null) {
79
+ if (Array.isArray(options.slice))
80
+ return lines.slice(options.slice[0], options.slice[1]);
81
+ return lines.slice(0, options.slice);
82
+ }
83
+ return lines;
84
+ };
85
+
86
+ export const parseV8OrIE = (
87
+ error: Error,
88
+ options?: ParseOptions,
89
+ ): StackFrame[] => {
90
+ return parseV8OrIeString(error.stack!, options);
91
+ };
92
+
93
+ export const parseV8OrIeString = (
94
+ stack: string,
95
+ options?: ParseOptions,
96
+ ): StackFrame[] => {
97
+ const filteredLines = applySlice(
98
+ stack.split('\n').filter((line) => {
99
+ return !!line.match(CHROME_IE_STACK_REGEXP);
100
+ }),
101
+ options,
102
+ );
103
+
104
+ return filteredLines.map((line): StackFrame => {
105
+ let currentLine = line;
106
+ if (currentLine.includes('(eval ')) {
107
+ currentLine = currentLine
108
+ .replace(/eval code/g, 'eval')
109
+ .replace(/(\(eval at [^()]*)|(,.*$)/g, '');
110
+ }
111
+ let sanitizedLine = currentLine
112
+ .replace(/^\s+/, '')
113
+ .replace(/\(eval code/g, '(')
114
+ .replace(/^.*?\s+/, '');
115
+
116
+ const locationMatch = sanitizedLine.match(/ (\(.+\)$)/);
117
+
118
+ sanitizedLine = locationMatch
119
+ ? sanitizedLine.replace(locationMatch[0], '')
120
+ : sanitizedLine;
121
+
122
+ const locationParts = extractLocation(
123
+ locationMatch ? locationMatch[1] : sanitizedLine,
124
+ );
125
+ const functionName = (locationMatch && sanitizedLine) || undefined;
126
+ const fileName = ['eval', '<anonymous>'].includes(locationParts[0])
127
+ ? undefined
128
+ : locationParts[0];
129
+
130
+ return {
131
+ functionName,
132
+ fileName,
133
+ lineNumber: locationParts[1] ? +locationParts[1] : undefined,
134
+ columnNumber: locationParts[2] ? +locationParts[2] : undefined,
135
+ source: currentLine,
136
+ };
137
+ });
138
+ };
139
+
140
+ export const parseFFOrSafari = (
141
+ error: Error,
142
+ options?: ParseOptions,
143
+ ): StackFrame[] => {
144
+ return parseFFOrSafariString(error.stack!, options);
145
+ };
146
+
147
+ export const parseFFOrSafariString = (
148
+ stack: string,
149
+ options?: ParseOptions,
150
+ ): StackFrame[] => {
151
+ const filteredLines = applySlice(
152
+ stack.split('\n').filter((line) => {
153
+ return !line.match(SAFARI_NATIVE_CODE_REGEXP);
154
+ }),
155
+ options,
156
+ );
157
+
158
+ return filteredLines.map((line): StackFrame => {
159
+ let currentLine = line;
160
+ if (currentLine.includes(' > eval'))
161
+ currentLine = currentLine.replace(
162
+ / line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
163
+ ':$1',
164
+ );
165
+
166
+ if (!currentLine.includes('@') && !currentLine.includes(':')) {
167
+ return {
168
+ functionName: currentLine,
169
+ };
170
+ } else {
171
+ const functionNameRegex =
172
+ /(([^\n\r"\u2028\u2029]*".[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*(?:@[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*)*(?:[\n\r\u2028\u2029][^@]*)?)?[^@]*)@/;
173
+ const matches = currentLine.match(functionNameRegex);
174
+ const functionName = matches && matches[1] ? matches[1] : undefined;
175
+ const locationParts = extractLocation(
176
+ currentLine.replace(functionNameRegex, ''),
177
+ );
178
+
179
+ return {
180
+ functionName,
181
+ fileName: locationParts[0],
182
+ lineNumber: locationParts[1] ? +locationParts[1] : undefined,
183
+ columnNumber: locationParts[2] ? +locationParts[2] : undefined,
184
+ source: currentLine,
185
+ };
186
+ }
187
+ });
188
+ };
189
+
190
+ export const parseOpera = (
191
+ error: Error,
192
+ options?: ParseOptions,
193
+ ): StackFrame[] => {
194
+ const nonStandardStacktrace = getNonStandardStacktrace(error);
195
+ if (
196
+ !nonStandardStacktrace ||
197
+ (error.message.includes('\n') &&
198
+ error.message.split('\n').length >
199
+ nonStandardStacktrace.split('\n').length)
200
+ ) {
201
+ return parseOpera9(error, options);
202
+ }
203
+ if (!error.stack) return parseOpera10(error, options);
204
+ return parseOpera11(error, options);
205
+ };
206
+
207
+ export const parseOpera9 = (
208
+ error: Error,
209
+ options?: ParseOptions,
210
+ ): StackFrame[] => {
211
+ const lineRegex = /Line (\d+).*script (?:in )?(\S+)/i;
212
+ const messageLines = error.message.split('\n');
213
+ const parsedFrames: StackFrame[] = [];
214
+
215
+ for (let i = 2, len = messageLines.length; i < len; i += 2) {
216
+ const match = lineRegex.exec(messageLines[i]);
217
+ if (match) {
218
+ parsedFrames.push({
219
+ fileName: match[2],
220
+ lineNumber: +match[1],
221
+ source: messageLines[i],
222
+ });
223
+ }
224
+ }
225
+
226
+ return applySlice(parsedFrames, options);
227
+ };
228
+
229
+ export const parseOpera10 = (
230
+ error: Error,
231
+ options?: ParseOptions,
232
+ ): StackFrame[] => {
233
+ const lineRegex =
234
+ /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
235
+ const nonStandardStacktrace = getNonStandardStacktrace(error);
236
+ const stacktraceLines = (nonStandardStacktrace || '').split('\n');
237
+ const parsedFrames: StackFrame[] = [];
238
+
239
+ for (let i = 0, len = stacktraceLines.length; i < len; i += 2) {
240
+ const match = lineRegex.exec(stacktraceLines[i]);
241
+ if (match) {
242
+ parsedFrames.push({
243
+ functionName: match[3] || undefined,
244
+ fileName: match[2],
245
+ lineNumber: match[1] ? +match[1] : undefined,
246
+ source: stacktraceLines[i],
247
+ });
248
+ }
249
+ }
250
+
251
+ return applySlice(parsedFrames, options);
252
+ };
253
+
254
+ export const parseOpera11 = (
255
+ error: Error,
256
+ options?: ParseOptions,
257
+ ): StackFrame[] => {
258
+ const filteredLines = applySlice(
259
+ // @ts-expect-error missing stack property
260
+ error.stack.split('\n').filter((line) => {
261
+ return (
262
+ !!line.match(FIREFOX_SAFARI_STACK_REGEXP) &&
263
+ !line.match(/^Error created at/)
264
+ );
265
+ }),
266
+ options,
267
+ );
268
+
269
+ return filteredLines.map<StackFrame>((line) => {
270
+ const tokens = line.split('@');
271
+ const locationParts = extractLocation(tokens.pop()!);
272
+ const functionCall = tokens.shift() || '';
273
+ const functionName =
274
+ functionCall
275
+ .replace(/<anonymous function(: (\w+))?>/, '$2')
276
+ .replace(/\([^)]*\)/g, '') || undefined;
277
+ let argsRaw: string | undefined;
278
+ if (functionCall.match(/\(([^)]*)\)/))
279
+ argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, '$1');
280
+
281
+ const args =
282
+ argsRaw === undefined || argsRaw === '[arguments not available]'
283
+ ? undefined
284
+ : argsRaw.split(',');
285
+
286
+ return {
287
+ functionName,
288
+ args,
289
+ fileName: locationParts[0],
290
+ lineNumber: locationParts[1] ? +locationParts[1] : undefined,
291
+ columnNumber: locationParts[2] ? +locationParts[2] : undefined,
292
+ source: line,
293
+ };
294
+ });
295
+ };
@@ -0,0 +1,410 @@
1
+ import {
2
+ decode,
3
+ SourceMapMappings,
4
+ type SourceMapSegment,
5
+ } from '@jridgewell/sourcemap-codec';
6
+
7
+ import { StackFrame } from './parse-stack.js';
8
+
9
+ export interface DecodedSourceMapSection {
10
+ map: {
11
+ file?: string;
12
+ mappings: SourceMapSegment[][];
13
+ names?: string[];
14
+ sourceRoot?: string;
15
+ sources: string[];
16
+ sourcesContent?: string[];
17
+ version: 3;
18
+ };
19
+ offset: {
20
+ column: number;
21
+ line: number;
22
+ };
23
+ }
24
+
25
+ // https://tc39.es/ecma426/#sec-index-source-map
26
+ export interface IndexSourceMap {
27
+ file?: string;
28
+ sections: Array<{
29
+ map: StandardSourceMap;
30
+ offset: {
31
+ column: number;
32
+ line: number;
33
+ };
34
+ }>;
35
+ version: 3;
36
+ }
37
+
38
+ export type RawSourceMap = IndexSourceMap | StandardSourceMap;
39
+
40
+ export interface SourceMap {
41
+ file?: string;
42
+ mappings: SourceMapSegment[][];
43
+ names?: string[];
44
+ sections?: DecodedSourceMapSection[];
45
+ sourceRoot?: string;
46
+ sources: string[];
47
+ sourcesContent?: string[];
48
+ version: 3;
49
+ }
50
+
51
+ // https://developer.chrome.com/blog/sourcemaps#the_anatomy_of_a_source_map
52
+ export interface StandardSourceMap {
53
+ file?: string;
54
+ mappings: string;
55
+ names?: string[];
56
+ sourceRoot?: string;
57
+ sources: string[];
58
+ sourcesContent?: string[];
59
+ version: 3;
60
+ }
61
+
62
+ // has a scheme, e.g. http://, https://, file://, data:, etc.
63
+ // https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
64
+ const SCHEME_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
65
+ // inline sourcemap, e.g. data:application/json;base64,...
66
+ const INLINE_SOURCEMAP_REGEX = /^data:application\/json[^,]+base64,/;
67
+ // sourcemap url, e.g. //@ sourceMappingURL=... or /* @ sourceMappingURL=... */ at the end of the file
68
+ const SOURCEMAP_REGEX =
69
+ /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^*]+?)[ \t]*(?:\*\/)[ \t]*$)/;
70
+
71
+ const supportsWeakRef = typeof WeakRef !== 'undefined';
72
+
73
+ export const sourceMapCache = new Map<
74
+ string,
75
+ null | SourceMap | WeakRef<SourceMap>
76
+ >();
77
+ const _pendingSourceMapRequests = new Map<
78
+ string,
79
+ null | Promise<null | SourceMap>
80
+ >();
81
+
82
+ const isWeakRefSourceMap = (
83
+ cachedValue: SourceMap | WeakRef<SourceMap>,
84
+ ): cachedValue is WeakRef<SourceMap> => {
85
+ return supportsWeakRef && cachedValue instanceof WeakRef;
86
+ };
87
+
88
+ const getSourceFromMappings = (
89
+ mappings: SourceMapMappings,
90
+ sources: string[],
91
+ lineIndexInMappings: number,
92
+ column: number,
93
+ ): StackFrame | null => {
94
+ if (lineIndexInMappings < 0 || lineIndexInMappings >= mappings.length) {
95
+ return null;
96
+ }
97
+
98
+ const lineMapping = mappings[lineIndexInMappings];
99
+ if (!lineMapping || lineMapping.length === 0) {
100
+ return null;
101
+ }
102
+
103
+ let closestLineSegment: null | SourceMapSegment = null;
104
+ for (const lineSegment of lineMapping) {
105
+ if (lineSegment[0] <= column) {
106
+ closestLineSegment = lineSegment;
107
+ } else {
108
+ break;
109
+ }
110
+ }
111
+
112
+ if (!closestLineSegment || closestLineSegment.length < 4) {
113
+ return null;
114
+ }
115
+
116
+ const [, sourceIndex, sourceLine, sourceColumn] = closestLineSegment;
117
+
118
+ if (
119
+ sourceIndex === undefined ||
120
+ sourceLine === undefined ||
121
+ sourceColumn === undefined
122
+ ) {
123
+ return null;
124
+ }
125
+
126
+ const fileName = sources[sourceIndex];
127
+
128
+ if (!fileName) {
129
+ return null;
130
+ }
131
+
132
+ return {
133
+ columnNumber: sourceColumn,
134
+ fileName,
135
+ lineNumber: sourceLine + 1,
136
+ };
137
+ };
138
+
139
+ export const getSourceFromSourceMap = (
140
+ sourceMap: SourceMap,
141
+ line: number,
142
+ column: number,
143
+ ): StackFrame | null => {
144
+ if (sourceMap.sections) {
145
+ let targetSection: DecodedSourceMapSection | null = null;
146
+
147
+ for (const section of sourceMap.sections) {
148
+ if (
149
+ line > section.offset.line ||
150
+ (line === section.offset.line && column >= section.offset.column)
151
+ ) {
152
+ targetSection = section;
153
+ } else {
154
+ break;
155
+ }
156
+ }
157
+
158
+ if (!targetSection) {
159
+ return null;
160
+ }
161
+
162
+ const relativeLine = line - targetSection.offset.line;
163
+ const relativeColumn =
164
+ line === targetSection.offset.line
165
+ ? column - targetSection.offset.column
166
+ : column;
167
+
168
+ return getSourceFromMappings(
169
+ targetSection.map.mappings,
170
+ targetSection.map.sources,
171
+ relativeLine,
172
+ relativeColumn,
173
+ );
174
+ }
175
+
176
+ return getSourceFromMappings(
177
+ sourceMap.mappings,
178
+ sourceMap.sources,
179
+ line - 1,
180
+ column,
181
+ );
182
+ };
183
+
184
+ const getSourceMapUrl = (url: string, content: string): null | string => {
185
+ const lines = content.split('\n');
186
+ let sourceMapUrl: string | undefined;
187
+ for (let i = lines.length - 1; i >= 0 && !sourceMapUrl; i--) {
188
+ const regexMatch = lines[i].match(SOURCEMAP_REGEX);
189
+ if (regexMatch) {
190
+ sourceMapUrl = regexMatch[1] || regexMatch[2];
191
+ }
192
+ }
193
+
194
+ if (!sourceMapUrl) {
195
+ return null;
196
+ }
197
+
198
+ const hasScheme = SCHEME_REGEX.test(sourceMapUrl);
199
+ if (
200
+ !(
201
+ INLINE_SOURCEMAP_REGEX.test(sourceMapUrl) ||
202
+ hasScheme ||
203
+ sourceMapUrl.startsWith('/')
204
+ )
205
+ ) {
206
+ const urlSegments = url.split('/');
207
+ urlSegments[urlSegments.length - 1] = sourceMapUrl;
208
+ sourceMapUrl = urlSegments.join('/');
209
+ }
210
+
211
+ return sourceMapUrl;
212
+ };
213
+
214
+ const decodeStandardSourceMap = (
215
+ rawSourceMap: StandardSourceMap,
216
+ ): SourceMap => ({
217
+ file: rawSourceMap.file,
218
+ mappings: decode(rawSourceMap.mappings),
219
+ names: rawSourceMap.names,
220
+ sourceRoot: rawSourceMap.sourceRoot,
221
+ sources: rawSourceMap.sources,
222
+ sourcesContent: rawSourceMap.sourcesContent,
223
+ version: 3,
224
+ });
225
+
226
+ const decodeIndexSourceMap = (rawSourceMap: IndexSourceMap): SourceMap => {
227
+ const decodedSections: DecodedSourceMapSection[] = rawSourceMap.sections.map(
228
+ ({ map, offset }) => ({
229
+ map: {
230
+ ...map,
231
+ mappings: decode(map.mappings),
232
+ },
233
+ offset,
234
+ }),
235
+ );
236
+
237
+ const allSources = new Set<string>();
238
+ for (const section of decodedSections) {
239
+ for (const source of section.map.sources) {
240
+ allSources.add(source);
241
+ }
242
+ }
243
+
244
+ return {
245
+ file: rawSourceMap.file,
246
+ mappings: [],
247
+ names: [],
248
+ sections: decodedSections,
249
+ sourceRoot: undefined,
250
+ sources: Array.from(allSources),
251
+ sourcesContent: undefined,
252
+ version: 3,
253
+ };
254
+ };
255
+
256
+ const isFetchableUrl = (url: string): boolean => {
257
+ if (!url) {
258
+ return false;
259
+ }
260
+
261
+ const trimmedUrl = url.trim();
262
+
263
+ if (!trimmedUrl) {
264
+ return false;
265
+ }
266
+
267
+ const schemeMatch = trimmedUrl.match(SCHEME_REGEX);
268
+
269
+ if (!schemeMatch) {
270
+ return true;
271
+ }
272
+
273
+ const scheme = schemeMatch[0].toLowerCase();
274
+
275
+ return scheme === 'http:' || scheme === 'https:';
276
+ };
277
+
278
+ export const getSourceMapImpl = async (
279
+ bundleUrl: string,
280
+ fetchFn: (url: string) => Promise<Response> = fetch,
281
+ ): Promise<null | SourceMap> => {
282
+ if (!isFetchableUrl(bundleUrl)) {
283
+ return null;
284
+ }
285
+
286
+ let bundleContent: string | undefined;
287
+ try {
288
+ const bundleResponse = await fetchFn(bundleUrl);
289
+ if (!bundleResponse.ok) {
290
+ return null;
291
+ }
292
+ bundleContent = await bundleResponse.text();
293
+ } catch {
294
+ return null;
295
+ }
296
+
297
+ if (!bundleContent) {
298
+ return null;
299
+ }
300
+
301
+ const sourceMapUrl = getSourceMapUrl(bundleUrl, bundleContent);
302
+
303
+ if (!sourceMapUrl) return null;
304
+ if (!isFetchableUrl(sourceMapUrl)) {
305
+ return null;
306
+ }
307
+
308
+ try {
309
+ const sourceMapResponse = await fetchFn(sourceMapUrl);
310
+ if (!sourceMapResponse.ok) {
311
+ return null;
312
+ }
313
+ const rawSourceMap = (await sourceMapResponse.json()) as RawSourceMap;
314
+
315
+ return 'sections' in rawSourceMap
316
+ ? decodeIndexSourceMap(rawSourceMap)
317
+ : decodeStandardSourceMap(rawSourceMap);
318
+ } catch {
319
+ return null;
320
+ }
321
+ };
322
+
323
+ export const getSourceMap = async (
324
+ file: string,
325
+ useCache = true,
326
+ fetchFn?: (url: string) => Promise<Response>,
327
+ ): Promise<null | SourceMap> => {
328
+ if (useCache && sourceMapCache.has(file)) {
329
+ const cachedValue = sourceMapCache.get(file);
330
+ if (cachedValue === null || cachedValue === undefined) {
331
+ return null;
332
+ }
333
+ if (isWeakRefSourceMap(cachedValue)) {
334
+ const sourceMap = cachedValue.deref();
335
+ if (sourceMap) {
336
+ return sourceMap;
337
+ }
338
+ sourceMapCache.delete(file);
339
+ } else {
340
+ return cachedValue;
341
+ }
342
+ }
343
+
344
+ if (useCache && _pendingSourceMapRequests.has(file)) {
345
+ return _pendingSourceMapRequests.get(file)!;
346
+ }
347
+
348
+ const fetchPromise = getSourceMapImpl(file, fetchFn);
349
+ if (useCache) {
350
+ _pendingSourceMapRequests.set(file, fetchPromise);
351
+ }
352
+
353
+ const sourceMap = await fetchPromise;
354
+ if (useCache) {
355
+ _pendingSourceMapRequests.delete(file);
356
+ }
357
+
358
+ if (useCache) {
359
+ if (sourceMap === null) {
360
+ sourceMapCache.set(file, null);
361
+ } else {
362
+ sourceMapCache.set(
363
+ file,
364
+ supportsWeakRef ? new WeakRef(sourceMap) : sourceMap,
365
+ );
366
+ }
367
+ }
368
+
369
+ return sourceMap;
370
+ };
371
+
372
+ export const symbolicateStack = async (
373
+ stack: StackFrame[],
374
+ cache = true,
375
+ fetchFn?: (url: string) => Promise<Response>,
376
+ ): Promise<StackFrame[]> => {
377
+ return await Promise.all(
378
+ stack.map(async (stackFrame) => {
379
+ if (!stackFrame.fileName) return stackFrame;
380
+ const sourceMap = await getSourceMap(stackFrame.fileName, cache, fetchFn);
381
+ if (
382
+ !sourceMap ||
383
+ typeof stackFrame.lineNumber !== 'number' ||
384
+ typeof stackFrame.columnNumber !== 'number'
385
+ ) {
386
+ return stackFrame;
387
+ }
388
+ const symbolicatedSource = getSourceFromSourceMap(
389
+ sourceMap,
390
+ stackFrame.lineNumber,
391
+ stackFrame.columnNumber,
392
+ );
393
+ if (!symbolicatedSource) return stackFrame;
394
+ return {
395
+ ...stackFrame,
396
+ source:
397
+ symbolicatedSource.fileName && stackFrame.source
398
+ ? stackFrame.source.replace(
399
+ stackFrame.fileName,
400
+ symbolicatedSource.fileName,
401
+ )
402
+ : stackFrame.source,
403
+ fileName: symbolicatedSource.fileName,
404
+ lineNumber: symbolicatedSource.lineNumber,
405
+ columnNumber: symbolicatedSource.columnNumber,
406
+ isSymbolicated: true,
407
+ };
408
+ }),
409
+ );
410
+ };
@@ -0,0 +1,6 @@
1
+ export interface FiberSource {
2
+ columnNumber?: number;
3
+ fileName: string;
4
+ lineNumber?: number;
5
+ functionName?: string;
6
+ }