Skip to content

Commit f2c8fbd

Browse files
committed
break up utils.misc to fix circular imports
1 parent e616ee3 commit f2c8fbd

File tree

7 files changed

+242
-235
lines changed

7 files changed

+242
-235
lines changed

packages/utils/src/browser.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { isString } from './is';
2+
3+
/**
4+
* Given a child DOM element, returns a query-selector statement describing that
5+
* and its ancestors
6+
* e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
7+
* @returns generated DOM path
8+
*/
9+
export function htmlTreeAsString(elem: unknown): string {
10+
type SimpleNode = {
11+
parentNode: SimpleNode;
12+
} | null;
13+
14+
// try/catch both:
15+
// - accessing event.target (see getsentry/raven-js#838, #768)
16+
// - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
17+
// - can throw an exception in some circumstances.
18+
try {
19+
let currentElem = elem as SimpleNode;
20+
const MAX_TRAVERSE_HEIGHT = 5;
21+
const MAX_OUTPUT_LEN = 80;
22+
const out = [];
23+
let height = 0;
24+
let len = 0;
25+
const separator = ' > ';
26+
const sepLength = separator.length;
27+
let nextStr;
28+
29+
// eslint-disable-next-line no-plusplus
30+
while (currentElem && height++ < MAX_TRAVERSE_HEIGHT) {
31+
nextStr = _htmlElementAsString(currentElem);
32+
// bail out if
33+
// - nextStr is the 'html' element
34+
// - the length of the string that would be created exceeds MAX_OUTPUT_LEN
35+
// (ignore this limit if we are on the first iteration)
36+
if (nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)) {
37+
break;
38+
}
39+
40+
out.push(nextStr);
41+
42+
len += nextStr.length;
43+
currentElem = currentElem.parentNode;
44+
}
45+
46+
return out.reverse().join(separator);
47+
} catch (_oO) {
48+
return '<unknown>';
49+
}
50+
}
51+
52+
/**
53+
* Returns a simple, query-selector representation of a DOM element
54+
* e.g. [HTMLElement] => input#foo.btn[name=baz]
55+
* @returns generated DOM path
56+
*/
57+
function _htmlElementAsString(el: unknown): string {
58+
const elem = el as {
59+
tagName?: string;
60+
id?: string;
61+
className?: string;
62+
getAttribute(key: string): string;
63+
};
64+
65+
const out = [];
66+
let className;
67+
let classes;
68+
let key;
69+
let attr;
70+
let i;
71+
72+
if (!elem || !elem.tagName) {
73+
return '';
74+
}
75+
76+
out.push(elem.tagName.toLowerCase());
77+
if (elem.id) {
78+
out.push(`#${elem.id}`);
79+
}
80+
81+
// eslint-disable-next-line prefer-const
82+
className = elem.className;
83+
if (className && isString(className)) {
84+
classes = className.split(/\s+/);
85+
for (i = 0; i < classes.length; i++) {
86+
out.push(`.${classes[i]}`);
87+
}
88+
}
89+
const allowedAttrs = ['type', 'name', 'title', 'alt'];
90+
for (i = 0; i < allowedAttrs.length; i++) {
91+
key = allowedAttrs[i];
92+
attr = elem.getAttribute(key);
93+
if (attr) {
94+
out.push(`[${key}="${attr}"]`);
95+
}
96+
}
97+
return out.join('');
98+
}

packages/utils/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
export * from './async';
2+
export * from './browser';
3+
export * from './dsn';
24
export * from './error';
35
export * from './is';
46
export * from './logger';
57
export * from './memo';
68
export * from './misc';
9+
export * from './node';
710
export * from './object';
811
export * from './path';
912
export * from './promisebuffer';
13+
export * from './stacktrace';
1014
export * from './string';
1115
export * from './supports';
1216
export * from './syncpromise';
1317
export * from './instrument';
14-
export * from './dsn';

packages/utils/src/instrument.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { WrappedFunction } from '@sentry/types';
44

55
import { isInstanceOf, isString } from './is';
66
import { logger } from './logger';
7-
import { getFunctionName, getGlobalObject } from './misc';
7+
import { getGlobalObject } from './misc';
88
import { fill } from './object';
9+
import { getFunctionName } from './stacktrace';
910
import { supportsHistory, supportsNativeFetch } from './supports';
1011

1112
const global = getGlobalObject<Window>();

packages/utils/src/misc.ts

Lines changed: 1 addition & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { Event, Integration, Options, StackFrame, WrappedFunction } from '@sentry/types';
33

4-
import { isString } from './is';
5-
import { normalize } from './object';
4+
import { dynamicRequire, isNodeEnv } from './node';
65
import { snipLine } from './string';
76

87
/** Internal */
@@ -22,26 +21,6 @@ interface SentryGlobal {
2221
};
2322
}
2423

25-
/**
26-
* Requires a module which is protected against bundler minification.
27-
*
28-
* @param request The module path to resolve
29-
*/
30-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
31-
export function dynamicRequire(mod: any, request: string): any {
32-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
33-
return mod.require(request);
34-
}
35-
36-
/**
37-
* Checks whether we're in the Node.js or Browser environment
38-
*
39-
* @returns Answer to given question
40-
*/
41-
export function isNodeEnv(): boolean {
42-
return Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
43-
}
44-
4524
const fallbackGlobalObject = {};
4625

4726
/**
@@ -253,103 +232,6 @@ export function getLocationHref(): string {
253232
}
254233
}
255234

256-
/**
257-
* Given a child DOM element, returns a query-selector statement describing that
258-
* and its ancestors
259-
* e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
260-
* @returns generated DOM path
261-
*/
262-
export function htmlTreeAsString(elem: unknown): string {
263-
type SimpleNode = {
264-
parentNode: SimpleNode;
265-
} | null;
266-
267-
// try/catch both:
268-
// - accessing event.target (see getsentry/raven-js#838, #768)
269-
// - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
270-
// - can throw an exception in some circumstances.
271-
try {
272-
let currentElem = elem as SimpleNode;
273-
const MAX_TRAVERSE_HEIGHT = 5;
274-
const MAX_OUTPUT_LEN = 80;
275-
const out = [];
276-
let height = 0;
277-
let len = 0;
278-
const separator = ' > ';
279-
const sepLength = separator.length;
280-
let nextStr;
281-
282-
// eslint-disable-next-line no-plusplus
283-
while (currentElem && height++ < MAX_TRAVERSE_HEIGHT) {
284-
nextStr = _htmlElementAsString(currentElem);
285-
// bail out if
286-
// - nextStr is the 'html' element
287-
// - the length of the string that would be created exceeds MAX_OUTPUT_LEN
288-
// (ignore this limit if we are on the first iteration)
289-
if (nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)) {
290-
break;
291-
}
292-
293-
out.push(nextStr);
294-
295-
len += nextStr.length;
296-
currentElem = currentElem.parentNode;
297-
}
298-
299-
return out.reverse().join(separator);
300-
} catch (_oO) {
301-
return '<unknown>';
302-
}
303-
}
304-
305-
/**
306-
* Returns a simple, query-selector representation of a DOM element
307-
* e.g. [HTMLElement] => input#foo.btn[name=baz]
308-
* @returns generated DOM path
309-
*/
310-
function _htmlElementAsString(el: unknown): string {
311-
const elem = el as {
312-
tagName?: string;
313-
id?: string;
314-
className?: string;
315-
getAttribute(key: string): string;
316-
};
317-
318-
const out = [];
319-
let className;
320-
let classes;
321-
let key;
322-
let attr;
323-
let i;
324-
325-
if (!elem || !elem.tagName) {
326-
return '';
327-
}
328-
329-
out.push(elem.tagName.toLowerCase());
330-
if (elem.id) {
331-
out.push(`#${elem.id}`);
332-
}
333-
334-
// eslint-disable-next-line prefer-const
335-
className = elem.className;
336-
if (className && isString(className)) {
337-
classes = className.split(/\s+/);
338-
for (i = 0; i < classes.length; i++) {
339-
out.push(`.${classes[i]}`);
340-
}
341-
}
342-
const allowedAttrs = ['type', 'name', 'title', 'alt'];
343-
for (i = 0; i < allowedAttrs.length; i++) {
344-
key = allowedAttrs[i];
345-
attr = elem.getAttribute(key);
346-
if (attr) {
347-
out.push(`[${key}="${attr}"]`);
348-
}
349-
}
350-
return out.join('');
351-
}
352-
353235
const INITIAL_TIME = Date.now();
354236
let prevNow = 0;
355237

@@ -471,24 +353,6 @@ export function parseRetryAfterHeader(now: number, header?: string | number | nu
471353
return defaultRetryAfter;
472354
}
473355

474-
const defaultFunctionName = '<anonymous>';
475-
476-
/**
477-
* Safely extract function name from itself
478-
*/
479-
export function getFunctionName(fn: unknown): string {
480-
try {
481-
if (!fn || typeof fn !== 'function') {
482-
return defaultFunctionName;
483-
}
484-
return fn.name || defaultFunctionName;
485-
} catch (e) {
486-
// Just accessing custom props in some Selenium environments
487-
// can cause a "Permission denied" exception (see raven-js#495).
488-
return defaultFunctionName;
489-
}
490-
}
491-
492356
/**
493357
* This function adds context (pre/post/line) lines to the provided frame
494358
*
@@ -520,98 +384,3 @@ export function addContextToFrame(lines: string[], frame: StackFrame, linesOfCon
520384
export function hasTracingEnabled(options: Options): boolean {
521385
return !!options.tracesSampleRate || !!options.tracesSampler;
522386
}
523-
524-
/** Default request keys that'll be used to extract data from the request */
525-
const DEFAULT_REQUEST_KEYS = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
526-
527-
/**
528-
* Normalizes data from the request object, accounting for framework differences.
529-
*
530-
* @param req The request object from which to extract data
531-
* @param keys An optional array of keys to include in the normalized data. Defaults to DEFAULT_REQUEST_KEYS if not
532-
* provided.
533-
* @returns An object containing normalized request data
534-
*/
535-
export function extractNodeRequestData(
536-
req: { [key: string]: any },
537-
keys: string[] = DEFAULT_REQUEST_KEYS,
538-
): { [key: string]: string } {
539-
// make sure we can safely use dynamicRequire below
540-
if (!isNodeEnv()) {
541-
throw new Error("Can't get node request data outside of a node environment");
542-
}
543-
544-
const requestData: { [key: string]: any } = {};
545-
546-
// headers:
547-
// node, express: req.headers
548-
// koa: req.header
549-
const headers = (req.headers || req.header || {}) as {
550-
host?: string;
551-
cookie?: string;
552-
};
553-
// method:
554-
// node, express, koa: req.method
555-
const method = req.method;
556-
// host:
557-
// express: req.hostname in > 4 and req.host in < 4
558-
// koa: req.host
559-
// node: req.headers.host
560-
const host = req.hostname || req.host || headers.host || '<no host>';
561-
// protocol:
562-
// node: <n/a>
563-
// express, koa: req.protocol
564-
const protocol =
565-
req.protocol === 'https' || req.secure || ((req.socket || {}) as { encrypted?: boolean }).encrypted
566-
? 'https'
567-
: 'http';
568-
// url (including path and query string):
569-
// node, express: req.originalUrl
570-
// koa: req.url
571-
const originalUrl = (req.originalUrl || req.url) as string;
572-
// absolute url
573-
const absoluteUrl = `${protocol}://${host}${originalUrl}`;
574-
575-
keys.forEach(key => {
576-
switch (key) {
577-
case 'headers':
578-
requestData.headers = headers;
579-
break;
580-
case 'method':
581-
requestData.method = method;
582-
break;
583-
case 'url':
584-
requestData.url = absoluteUrl;
585-
break;
586-
case 'cookies':
587-
// cookies:
588-
// node, express, koa: req.headers.cookie
589-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
590-
requestData.cookies = dynamicRequire(module, 'cookie').parse(headers.cookie || '');
591-
break;
592-
case 'query_string':
593-
// query string:
594-
// node: req.url (raw)
595-
// express, koa: req.query
596-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
597-
requestData.query_string = dynamicRequire(module, 'url').parse(originalUrl || '', false).query;
598-
break;
599-
case 'data':
600-
if (method === 'GET' || method === 'HEAD') {
601-
break;
602-
}
603-
// body data:
604-
// node, express, koa: req.body
605-
if (req.body !== undefined) {
606-
requestData.data = isString(req.body) ? req.body : JSON.stringify(normalize(req.body));
607-
}
608-
break;
609-
default:
610-
if ({}.hasOwnProperty.call(req, key)) {
611-
requestData[key] = (req as { [key: string]: any })[key];
612-
}
613-
}
614-
});
615-
616-
return requestData;
617-
}

0 commit comments

Comments
 (0)