init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

145
shared/logger/node_modules/@sentry/utils/esm/baggage.js generated vendored Normal file
View File

@@ -0,0 +1,145 @@
import { isString } from './is.js';
import { logger } from './logger.js';
const BAGGAGE_HEADER_NAME = 'baggage';
const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-';
const SENTRY_BAGGAGE_KEY_PREFIX_REGEX = /^sentry-/;
/**
* Max length of a serialized baggage string
*
* https://www.w3.org/TR/baggage/#limits
*/
const MAX_BAGGAGE_STRING_LENGTH = 8192;
/**
* Takes a baggage header and turns it into Dynamic Sampling Context, by extracting all the "sentry-" prefixed values
* from it.
*
* @param baggageHeader A very bread definition of a baggage header as it might appear in various frameworks.
* @returns The Dynamic Sampling Context that was found on `baggageHeader`, if there was any, `undefined` otherwise.
*/
function baggageHeaderToDynamicSamplingContext(
// Very liberal definition of what any incoming header might look like
baggageHeader,
) {
if (!isString(baggageHeader) && !Array.isArray(baggageHeader)) {
return undefined;
}
// Intermediary object to store baggage key value pairs of incoming baggage headers on.
// It is later used to read Sentry-DSC-values from.
let baggageObject = {};
if (Array.isArray(baggageHeader)) {
// Combine all baggage headers into one object containing the baggage values so we can later read the Sentry-DSC-values from it
baggageObject = baggageHeader.reduce((acc, curr) => {
const currBaggageObject = baggageHeaderToObject(curr);
return {
...acc,
...currBaggageObject,
};
}, {});
} else {
// Return undefined if baggage header is an empty string (technically an empty baggage header is not spec conform but
// this is how we choose to handle it)
if (!baggageHeader) {
return undefined;
}
baggageObject = baggageHeaderToObject(baggageHeader);
}
// Read all "sentry-" prefixed values out of the baggage object and put it onto a dynamic sampling context object.
const dynamicSamplingContext = Object.entries(baggageObject).reduce((acc, [key, value]) => {
if (key.match(SENTRY_BAGGAGE_KEY_PREFIX_REGEX)) {
const nonPrefixedKey = key.slice(SENTRY_BAGGAGE_KEY_PREFIX.length);
acc[nonPrefixedKey] = value;
}
return acc;
}, {});
// Only return a dynamic sampling context object if there are keys in it.
// A keyless object means there were no sentry values on the header, which means that there is no DSC.
if (Object.keys(dynamicSamplingContext).length > 0) {
return dynamicSamplingContext ;
} else {
return undefined;
}
}
/**
* Turns a Dynamic Sampling Object into a baggage header by prefixing all the keys on the object with "sentry-".
*
* @param dynamicSamplingContext The Dynamic Sampling Context to turn into a header. For convenience and compatibility
* with the `getDynamicSamplingContext` method on the Transaction class ,this argument can also be `undefined`. If it is
* `undefined` the function will return `undefined`.
* @returns a baggage header, created from `dynamicSamplingContext`, or `undefined` either if `dynamicSamplingContext`
* was `undefined`, or if `dynamicSamplingContext` didn't contain any values.
*/
function dynamicSamplingContextToSentryBaggageHeader(
// this also takes undefined for convenience and bundle size in other places
dynamicSamplingContext,
) {
// Prefix all DSC keys with "sentry-" and put them into a new object
const sentryPrefixedDSC = Object.entries(dynamicSamplingContext).reduce(
(acc, [dscKey, dscValue]) => {
if (dscValue) {
acc[`${SENTRY_BAGGAGE_KEY_PREFIX}${dscKey}`] = dscValue;
}
return acc;
},
{},
);
return objectToBaggageHeader(sentryPrefixedDSC);
}
/**
* Will parse a baggage header, which is a simple key-value map, into a flat object.
*
* @param baggageHeader The baggage header to parse.
* @returns a flat object containing all the key-value pairs from `baggageHeader`.
*/
function baggageHeaderToObject(baggageHeader) {
return baggageHeader
.split(',')
.map(baggageEntry => baggageEntry.split('=').map(keyOrValue => decodeURIComponent(keyOrValue.trim())))
.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
}
/**
* Turns a flat object (key-value pairs) into a baggage header, which is also just key-value pairs.
*
* @param object The object to turn into a baggage header.
* @returns a baggage header string, or `undefined` if the object didn't have any values, since an empty baggage header
* is not spec compliant.
*/
function objectToBaggageHeader(object) {
if (Object.keys(object).length === 0) {
// An empty baggage header is not spec compliant: We return undefined.
return undefined;
}
return Object.entries(object).reduce((baggageHeader, [objectKey, objectValue], currentIndex) => {
const baggageEntry = `${encodeURIComponent(objectKey)}=${encodeURIComponent(objectValue)}`;
const newBaggageHeader = currentIndex === 0 ? baggageEntry : `${baggageHeader},${baggageEntry}`;
if (newBaggageHeader.length > MAX_BAGGAGE_STRING_LENGTH) {
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
logger.warn(
`Not adding key: ${objectKey} with val: ${objectValue} to baggage header due to exceeding baggage size limits.`,
);
return baggageHeader;
} else {
return newBaggageHeader;
}
}, '');
}
export { BAGGAGE_HEADER_NAME, MAX_BAGGAGE_STRING_LENGTH, SENTRY_BAGGAGE_KEY_PREFIX, SENTRY_BAGGAGE_KEY_PREFIX_REGEX, baggageHeaderToDynamicSamplingContext, dynamicSamplingContextToSentryBaggageHeader };
//# sourceMappingURL=baggage.js.map

152
shared/logger/node_modules/@sentry/utils/esm/browser.js generated vendored Normal file
View File

@@ -0,0 +1,152 @@
import { isString } from './is.js';
import { getGlobalObject } from './worldwide.js';
// eslint-disable-next-line deprecation/deprecation
const WINDOW = getGlobalObject();
const DEFAULT_MAX_STRING_LENGTH = 80;
/**
* Given a child DOM element, returns a query-selector statement describing that
* and its ancestors
* e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
* @returns generated DOM path
*/
function htmlTreeAsString(
elem,
options = {},
) {
// try/catch both:
// - accessing event.target (see getsentry/raven-js#838, #768)
// - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
// - can throw an exception in some circumstances.
try {
let currentElem = elem ;
const MAX_TRAVERSE_HEIGHT = 5;
const out = [];
let height = 0;
let len = 0;
const separator = ' > ';
const sepLength = separator.length;
let nextStr;
const keyAttrs = Array.isArray(options) ? options : options.keyAttrs;
const maxStringLength = (!Array.isArray(options) && options.maxStringLength) || DEFAULT_MAX_STRING_LENGTH;
while (currentElem && height++ < MAX_TRAVERSE_HEIGHT) {
nextStr = _htmlElementAsString(currentElem, keyAttrs);
// bail out if
// - nextStr is the 'html' element
// - the length of the string that would be created exceeds maxStringLength
// (ignore this limit if we are on the first iteration)
if (nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= maxStringLength)) {
break;
}
out.push(nextStr);
len += nextStr.length;
currentElem = currentElem.parentNode;
}
return out.reverse().join(separator);
} catch (_oO) {
return '<unknown>';
}
}
/**
* Returns a simple, query-selector representation of a DOM element
* e.g. [HTMLElement] => input#foo.btn[name=baz]
* @returns generated DOM path
*/
function _htmlElementAsString(el, keyAttrs) {
const elem = el
;
const out = [];
let className;
let classes;
let key;
let attr;
let i;
if (!elem || !elem.tagName) {
return '';
}
out.push(elem.tagName.toLowerCase());
// Pairs of attribute keys defined in `serializeAttribute` and their values on element.
const keyAttrPairs =
keyAttrs && keyAttrs.length
? keyAttrs.filter(keyAttr => elem.getAttribute(keyAttr)).map(keyAttr => [keyAttr, elem.getAttribute(keyAttr)])
: null;
if (keyAttrPairs && keyAttrPairs.length) {
keyAttrPairs.forEach(keyAttrPair => {
out.push(`[${keyAttrPair[0]}="${keyAttrPair[1]}"]`);
});
} else {
if (elem.id) {
out.push(`#${elem.id}`);
}
// eslint-disable-next-line prefer-const
className = elem.className;
if (className && isString(className)) {
classes = className.split(/\s+/);
for (i = 0; i < classes.length; i++) {
out.push(`.${classes[i]}`);
}
}
}
const allowedAttrs = ['aria-label', 'type', 'name', 'title', 'alt'];
for (i = 0; i < allowedAttrs.length; i++) {
key = allowedAttrs[i];
attr = elem.getAttribute(key);
if (attr) {
out.push(`[${key}="${attr}"]`);
}
}
return out.join('');
}
/**
* A safe form of location.href
*/
function getLocationHref() {
try {
return WINDOW.document.location.href;
} catch (oO) {
return '';
}
}
/**
* Gets a DOM element by using document.querySelector.
*
* This wrapper will first check for the existance of the function before
* actually calling it so that we don't have to take care of this check,
* every time we want to access the DOM.
*
* Reason: DOM/querySelector is not available in all environments.
*
* We have to cast to any because utils can be consumed by a variety of environments,
* and we don't want to break TS users. If you know what element will be selected by
* `document.querySelector`, specify it as part of the generic call. For example,
* `const element = getDomElement<Element>('selector');`
*
* @param selector the selector string passed on to document.querySelector
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getDomElement(selector) {
if (WINDOW.document && WINDOW.document.querySelector) {
return WINDOW.document.querySelector(selector) ;
}
return null;
}
export { getDomElement, getLocationHref, htmlTreeAsString };
//# sourceMappingURL=browser.js.map

View File

@@ -0,0 +1,59 @@
/**
* Polyfill for the optional chain operator, `?.`, given previous conversion of the expression into an array of values,
* descriptors, and functions.
*
* Adapted from Sucrase (https://github.com/alangpierce/sucrase)
* See https://github.com/alangpierce/sucrase/blob/265887868966917f3b924ce38dfad01fbab1329f/src/transformers/OptionalChainingNullishTransformer.ts#L15
*
* @param ops Array result of expression conversion
* @returns The value of the expression
*/
function _optionalChain(ops) {
let lastAccessLHS = undefined;
let value = ops[0];
let i = 1;
while (i < ops.length) {
const op = ops[i] ;
const fn = ops[i + 1] ;
i += 2;
// by checking for loose equality to `null`, we catch both `null` and `undefined`
if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) {
// really we're meaning to return `undefined` as an actual value here, but it saves bytes not to write it
return;
}
if (op === 'access' || op === 'optionalAccess') {
lastAccessLHS = value;
value = fn(value);
} else if (op === 'call' || op === 'optionalCall') {
value = fn((...args) => (value ).call(lastAccessLHS, ...args));
lastAccessLHS = undefined;
}
}
return value;
}
// Sucrase version
// function _optionalChain(ops) {
// let lastAccessLHS = undefined;
// let value = ops[0];
// let i = 1;
// while (i < ops.length) {
// const op = ops[i];
// const fn = ops[i + 1];
// i += 2;
// if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) {
// return undefined;
// }
// if (op === 'access' || op === 'optionalAccess') {
// lastAccessLHS = value;
// value = fn(value);
// } else if (op === 'call' || op === 'optionalCall') {
// value = fn((...args) => value.call(lastAccessLHS, ...args));
// lastAccessLHS = undefined;
// }
// }
// return value;
// }
export { _optionalChain };
//# sourceMappingURL=_optionalChain.js.map

View File

@@ -0,0 +1,25 @@
import { createEnvelope } from './envelope.js';
import { dateTimestampInSeconds } from './time.js';
/**
* Creates client report envelope
* @param discarded_events An array of discard events
* @param dsn A DSN that can be set on the header. Optional.
*/
function createClientReportEnvelope(
discarded_events,
dsn,
timestamp,
) {
const clientReportItem = [
{ type: 'client_report' },
{
timestamp: timestamp || dateTimestampInSeconds(),
discarded_events,
},
];
return createEnvelope(dsn ? { dsn } : {}, [clientReportItem]);
}
export { createClientReportEnvelope };
//# sourceMappingURL=clientreport.js.map

126
shared/logger/node_modules/@sentry/utils/esm/dsn.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
import { logger } from './logger.js';
/** Regular expression used to parse a Dsn. */
const DSN_REGEX = /^(?:(\w+):)\/\/(?:(\w+)(?::(\w+)?)?@)([\w.-]+)(?::(\d+))?\/(.+)/;
function isValidProtocol(protocol) {
return protocol === 'http' || protocol === 'https';
}
/**
* Renders the string representation of this Dsn.
*
* By default, this will render the public representation without the password
* component. To get the deprecated private representation, set `withPassword`
* to true.
*
* @param withPassword When set to true, the password will be included.
*/
function dsnToString(dsn, withPassword = false) {
const { host, path, pass, port, projectId, protocol, publicKey } = dsn;
return (
`${protocol}://${publicKey}${withPassword && pass ? `:${pass}` : ''}` +
`@${host}${port ? `:${port}` : ''}/${path ? `${path}/` : path}${projectId}`
);
}
/**
* Parses a Dsn from a given string.
*
* @param str A Dsn as string
* @returns Dsn as DsnComponents or undefined if @param str is not a valid DSN string
*/
function dsnFromString(str) {
const match = DSN_REGEX.exec(str);
if (!match) {
// This should be logged to the console
// eslint-disable-next-line no-console
console.error(`Invalid Sentry Dsn: ${str}`);
return undefined;
}
const [protocol, publicKey, pass = '', host, port = '', lastPath] = match.slice(1);
let path = '';
let projectId = lastPath;
const split = projectId.split('/');
if (split.length > 1) {
path = split.slice(0, -1).join('/');
projectId = split.pop() ;
}
if (projectId) {
const projectMatch = projectId.match(/^\d+/);
if (projectMatch) {
projectId = projectMatch[0];
}
}
return dsnFromComponents({ host, pass, path, projectId, port, protocol: protocol , publicKey });
}
function dsnFromComponents(components) {
return {
protocol: components.protocol,
publicKey: components.publicKey || '',
pass: components.pass || '',
host: components.host,
port: components.port || '',
path: components.path || '',
projectId: components.projectId,
};
}
function validateDsn(dsn) {
if (!(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
return true;
}
const { port, projectId, protocol } = dsn;
const requiredComponents = ['protocol', 'publicKey', 'host', 'projectId'];
const hasMissingRequiredComponent = requiredComponents.find(component => {
if (!dsn[component]) {
logger.error(`Invalid Sentry Dsn: ${component} missing`);
return true;
}
return false;
});
if (hasMissingRequiredComponent) {
return false;
}
if (!projectId.match(/^\d+$/)) {
logger.error(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
return false;
}
if (!isValidProtocol(protocol)) {
logger.error(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
return false;
}
if (port && isNaN(parseInt(port, 10))) {
logger.error(`Invalid Sentry Dsn: Invalid port ${port}`);
return false;
}
return true;
}
/**
* Creates a valid Sentry Dsn object, identifying a Sentry instance and project.
* @returns a valid DsnComponents object or `undefined` if @param from is an invalid DSN source
*/
function makeDsn(from) {
const components = typeof from === 'string' ? dsnFromString(from) : dsnFromComponents(from);
if (!components || !validateDsn(components)) {
return undefined;
}
return components;
}
export { dsnFromString, dsnToString, makeDsn };
//# sourceMappingURL=dsn.js.map

34
shared/logger/node_modules/@sentry/utils/esm/env.js generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/*
* This module exists for optimizations in the build process through rollup and terser. We define some global
* constants, which can be overridden during build. By guarding certain pieces of code with functions that return these
* constants, we can control whether or not they appear in the final bundle. (Any code guarded by a false condition will
* never run, and will hence be dropped during treeshaking.) The two primary uses for this are stripping out calls to
* `logger` and preventing node-related code from appearing in browser bundles.
*
* Attention:
* This file should not be used to define constants/flags that are intended to be used for tree-shaking conducted by
* users. These flags should live in their respective packages, as we identified user tooling (specifically webpack)
* having issues tree-shaking these constants across package boundaries.
* An example for this is the __SENTRY_DEBUG__ constant. It is declared in each package individually because we want
* users to be able to shake away expressions that it guards.
*/
/**
* Figures out if we're building a browser bundle.
*
* @returns true if this is a browser bundle build.
*/
function isBrowserBundle() {
return typeof __SENTRY_BROWSER_BUNDLE__ !== 'undefined' && !!__SENTRY_BROWSER_BUNDLE__;
}
/**
* Get source of SDK.
*/
function getSDKSource() {
// @ts-ignore "npm" is injected by rollup during build process
return "npm";
}
export { getSDKSource, isBrowserBundle };
//# sourceMappingURL=env.js.map

View File

@@ -0,0 +1,232 @@
import { dsnToString } from './dsn.js';
import { normalize } from './normalize.js';
import { dropUndefinedKeys } from './object.js';
/**
* Creates an envelope.
* Make sure to always explicitly provide the generic to this function
* so that the envelope types resolve correctly.
*/
function createEnvelope(headers, items = []) {
return [headers, items] ;
}
/**
* Add an item to an envelope.
* Make sure to always explicitly provide the generic to this function
* so that the envelope types resolve correctly.
*/
function addItemToEnvelope(envelope, newItem) {
const [headers, items] = envelope;
return [headers, [...items, newItem]] ;
}
/**
* Convenience function to loop through the items and item types of an envelope.
* (This function was mostly created because working with envelope types is painful at the moment)
*
* If the callback returns true, the rest of the items will be skipped.
*/
function forEachEnvelopeItem(
envelope,
callback,
) {
const envelopeItems = envelope[1];
for (const envelopeItem of envelopeItems) {
const envelopeItemType = envelopeItem[0].type;
const result = callback(envelopeItem, envelopeItemType);
if (result) {
return true;
}
}
return false;
}
/**
* Returns true if the envelope contains any of the given envelope item types
*/
function envelopeContainsItemType(envelope, types) {
return forEachEnvelopeItem(envelope, (_, type) => types.includes(type));
}
/**
* Encode a string to UTF8.
*/
function encodeUTF8(input, textEncoder) {
const utf8 = textEncoder || new TextEncoder();
return utf8.encode(input);
}
/**
* Serializes an envelope.
*/
function serializeEnvelope(envelope, textEncoder) {
const [envHeaders, items] = envelope;
// Initially we construct our envelope as a string and only convert to binary chunks if we encounter binary data
let parts = JSON.stringify(envHeaders);
function append(next) {
if (typeof parts === 'string') {
parts = typeof next === 'string' ? parts + next : [encodeUTF8(parts, textEncoder), next];
} else {
parts.push(typeof next === 'string' ? encodeUTF8(next, textEncoder) : next);
}
}
for (const item of items) {
const [itemHeaders, payload] = item;
append(`\n${JSON.stringify(itemHeaders)}\n`);
if (typeof payload === 'string' || payload instanceof Uint8Array) {
append(payload);
} else {
let stringifiedPayload;
try {
stringifiedPayload = JSON.stringify(payload);
} catch (e) {
// In case, despite all our efforts to keep `payload` circular-dependency-free, `JSON.strinify()` still
// fails, we try again after normalizing it again with infinite normalization depth. This of course has a
// performance impact but in this case a performance hit is better than throwing.
stringifiedPayload = JSON.stringify(normalize(payload));
}
append(stringifiedPayload);
}
}
return typeof parts === 'string' ? parts : concatBuffers(parts);
}
function concatBuffers(buffers) {
const totalLength = buffers.reduce((acc, buf) => acc + buf.length, 0);
const merged = new Uint8Array(totalLength);
let offset = 0;
for (const buffer of buffers) {
merged.set(buffer, offset);
offset += buffer.length;
}
return merged;
}
/**
* Parses an envelope
*/
function parseEnvelope(
env,
textEncoder,
textDecoder,
) {
let buffer = typeof env === 'string' ? textEncoder.encode(env) : env;
function readBinary(length) {
const bin = buffer.subarray(0, length);
// Replace the buffer with the remaining data excluding trailing newline
buffer = buffer.subarray(length + 1);
return bin;
}
function readJson() {
let i = buffer.indexOf(0xa);
// If we couldn't find a newline, we must have found the end of the buffer
if (i < 0) {
i = buffer.length;
}
return JSON.parse(textDecoder.decode(readBinary(i))) ;
}
const envelopeHeader = readJson();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const items = [];
while (buffer.length) {
const itemHeader = readJson();
const binaryLength = typeof itemHeader.length === 'number' ? itemHeader.length : undefined;
items.push([itemHeader, binaryLength ? readBinary(binaryLength) : readJson()]);
}
return [envelopeHeader, items];
}
/**
* Creates attachment envelope items
*/
function createAttachmentEnvelopeItem(
attachment,
textEncoder,
) {
const buffer = typeof attachment.data === 'string' ? encodeUTF8(attachment.data, textEncoder) : attachment.data;
return [
dropUndefinedKeys({
type: 'attachment',
length: buffer.length,
filename: attachment.filename,
content_type: attachment.contentType,
attachment_type: attachment.attachmentType,
}),
buffer,
];
}
const ITEM_TYPE_TO_DATA_CATEGORY_MAP = {
session: 'session',
sessions: 'session',
attachment: 'attachment',
transaction: 'transaction',
event: 'error',
client_report: 'internal',
user_report: 'default',
profile: 'profile',
replay_event: 'replay',
replay_recording: 'replay',
check_in: 'monitor',
};
/**
* Maps the type of an envelope item to a data category.
*/
function envelopeItemTypeToDataCategory(type) {
return ITEM_TYPE_TO_DATA_CATEGORY_MAP[type];
}
/** Extracts the minimal SDK info from from the metadata or an events */
function getSdkMetadataForEnvelopeHeader(metadataOrEvent) {
if (!metadataOrEvent || !metadataOrEvent.sdk) {
return;
}
const { name, version } = metadataOrEvent.sdk;
return { name, version };
}
/**
* Creates event envelope headers, based on event, sdk info and tunnel
* Note: This function was extracted from the core package to make it available in Replay
*/
function createEventEnvelopeHeaders(
event,
sdkInfo,
tunnel,
dsn,
) {
const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext;
return {
event_id: event.event_id ,
sent_at: new Date().toISOString(),
...(sdkInfo && { sdk: sdkInfo }),
...(!!tunnel && { dsn: dsnToString(dsn) }),
...(dynamicSamplingContext && {
trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
}),
};
}
export { addItemToEnvelope, createAttachmentEnvelopeItem, createEnvelope, createEventEnvelopeHeaders, envelopeContainsItemType, envelopeItemTypeToDataCategory, forEachEnvelopeItem, getSdkMetadataForEnvelopeHeader, parseEnvelope, serializeEnvelope };
//# sourceMappingURL=envelope.js.map

17
shared/logger/node_modules/@sentry/utils/esm/error.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
/** An error emitted by Sentry SDKs and related utilities. */
class SentryError extends Error {
/** Display name of this error instance. */
constructor( message, logLevel = 'warn') {
super(message);this.message = message;
this.name = new.target.prototype.constructor.name;
// This sets the prototype to be `Error`, not `SentryError`. It's unclear why we do this, but commenting this line
// out causes various (seemingly totally unrelated) playwright tests consistently time out. FYI, this makes
// instances of `SentryError` fail `obj instanceof SentryError` checks.
Object.setPrototypeOf(this, new.target.prototype);
this.logLevel = logLevel;
}
}
export { SentryError };
//# sourceMappingURL=error.js.map

View File

@@ -0,0 +1,631 @@
import { isString } from './is.js';
import { logger, CONSOLE_LEVELS } from './logger.js';
import { fill } from './object.js';
import { getFunctionName } from './stacktrace.js';
import { supportsNativeFetch } from './supports.js';
import { getGlobalObject } from './worldwide.js';
import { supportsHistory } from './vendor/supportsHistory.js';
// eslint-disable-next-line deprecation/deprecation
const WINDOW = getGlobalObject();
const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v2__';
/**
* Instrument native APIs to call handlers that can be used to create breadcrumbs, APM spans etc.
* - Console API
* - Fetch API
* - XHR API
* - History API
* - DOM API (click/typing)
* - Error API
* - UnhandledRejection API
*/
const handlers = {};
const instrumented = {};
/** Instruments given API */
function instrument(type) {
if (instrumented[type]) {
return;
}
instrumented[type] = true;
switch (type) {
case 'console':
instrumentConsole();
break;
case 'dom':
instrumentDOM();
break;
case 'xhr':
instrumentXHR();
break;
case 'fetch':
instrumentFetch();
break;
case 'history':
instrumentHistory();
break;
case 'error':
instrumentError();
break;
case 'unhandledrejection':
instrumentUnhandledRejection();
break;
default:
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('unknown instrumentation type:', type);
return;
}
}
/**
* Add handler that will be called when given type of instrumentation triggers.
* Use at your own risk, this might break without changelog notice, only used internally.
* @hidden
*/
function addInstrumentationHandler(type, callback) {
handlers[type] = handlers[type] || [];
(handlers[type] ).push(callback);
instrument(type);
}
/** JSDoc */
function triggerHandlers(type, data) {
if (!type || !handlers[type]) {
return;
}
for (const handler of handlers[type] || []) {
try {
handler(data);
} catch (e) {
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
logger.error(
`Error while triggering instrumentation handler.\nType: ${type}\nName: ${getFunctionName(handler)}\nError:`,
e,
);
}
}
}
/** JSDoc */
function instrumentConsole() {
if (!('console' in WINDOW)) {
return;
}
CONSOLE_LEVELS.forEach(function (level) {
if (!(level in WINDOW.console)) {
return;
}
fill(WINDOW.console, level, function (originalConsoleMethod) {
return function (...args) {
triggerHandlers('console', { args, level });
// this fails for some browsers. :(
if (originalConsoleMethod) {
originalConsoleMethod.apply(WINDOW.console, args);
}
};
});
});
}
/** JSDoc */
function instrumentFetch() {
if (!supportsNativeFetch()) {
return;
}
fill(WINDOW, 'fetch', function (originalFetch) {
return function (...args) {
const { method, url } = parseFetchArgs(args);
const handlerData = {
args,
fetchData: {
method,
url,
},
startTimestamp: Date.now(),
};
triggerHandlers('fetch', {
...handlerData,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return originalFetch.apply(WINDOW, args).then(
(response) => {
triggerHandlers('fetch', {
...handlerData,
endTimestamp: Date.now(),
response,
});
return response;
},
(error) => {
triggerHandlers('fetch', {
...handlerData,
endTimestamp: Date.now(),
error,
});
// NOTE: If you are a Sentry user, and you are seeing this stack frame,
// it means the sentry.javascript SDK caught an error invoking your application code.
// This is expected behavior and NOT indicative of a bug with sentry.javascript.
throw error;
},
);
};
});
}
function hasProp(obj, prop) {
return !!obj && typeof obj === 'object' && !!(obj )[prop];
}
function getUrlFromResource(resource) {
if (typeof resource === 'string') {
return resource;
}
if (!resource) {
return '';
}
if (hasProp(resource, 'url')) {
return resource.url;
}
if (resource.toString) {
return resource.toString();
}
return '';
}
/**
* Parses the fetch arguments to find the used Http method and the url of the request
*/
function parseFetchArgs(fetchArgs) {
if (fetchArgs.length === 0) {
return { method: 'GET', url: '' };
}
if (fetchArgs.length === 2) {
const [url, options] = fetchArgs ;
return {
url: getUrlFromResource(url),
method: hasProp(options, 'method') ? String(options.method).toUpperCase() : 'GET',
};
}
const arg = fetchArgs[0];
return {
url: getUrlFromResource(arg ),
method: hasProp(arg, 'method') ? String(arg.method).toUpperCase() : 'GET',
};
}
/** JSDoc */
function instrumentXHR() {
if (!('XMLHttpRequest' in WINDOW)) {
return;
}
const xhrproto = XMLHttpRequest.prototype;
fill(xhrproto, 'open', function (originalOpen) {
return function ( ...args) {
const url = args[1];
const xhrInfo = (this[SENTRY_XHR_DATA_KEY] = {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
method: isString(args[0]) ? args[0].toUpperCase() : args[0],
url: args[1],
request_headers: {},
});
// if Sentry key appears in URL, don't capture it as a request
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (isString(url) && xhrInfo.method === 'POST' && url.match(/sentry_key/)) {
this.__sentry_own_request__ = true;
}
const onreadystatechangeHandler = () => {
// For whatever reason, this is not the same instance here as from the outer method
const xhrInfo = this[SENTRY_XHR_DATA_KEY];
if (!xhrInfo) {
return;
}
if (this.readyState === 4) {
try {
// touching statusCode in some platforms throws
// an exception
xhrInfo.status_code = this.status;
} catch (e) {
/* do nothing */
}
triggerHandlers('xhr', {
args: args ,
endTimestamp: Date.now(),
startTimestamp: Date.now(),
xhr: this,
} );
}
};
if ('onreadystatechange' in this && typeof this.onreadystatechange === 'function') {
fill(this, 'onreadystatechange', function (original) {
return function ( ...readyStateArgs) {
onreadystatechangeHandler();
return original.apply(this, readyStateArgs);
};
});
} else {
this.addEventListener('readystatechange', onreadystatechangeHandler);
}
// Intercepting `setRequestHeader` to access the request headers of XHR instance.
// This will only work for user/library defined headers, not for the default/browser-assigned headers.
// Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.
fill(this, 'setRequestHeader', function (original) {
return function ( ...setRequestHeaderArgs) {
const [header, value] = setRequestHeaderArgs ;
const xhrInfo = this[SENTRY_XHR_DATA_KEY];
if (xhrInfo) {
xhrInfo.request_headers[header.toLowerCase()] = value;
}
return original.apply(this, setRequestHeaderArgs);
};
});
return originalOpen.apply(this, args);
};
});
fill(xhrproto, 'send', function (originalSend) {
return function ( ...args) {
const sentryXhrData = this[SENTRY_XHR_DATA_KEY];
if (sentryXhrData && args[0] !== undefined) {
sentryXhrData.body = args[0];
}
triggerHandlers('xhr', {
args,
startTimestamp: Date.now(),
xhr: this,
});
return originalSend.apply(this, args);
};
});
}
let lastHref;
/** JSDoc */
function instrumentHistory() {
if (!supportsHistory()) {
return;
}
const oldOnPopState = WINDOW.onpopstate;
WINDOW.onpopstate = function ( ...args) {
const to = WINDOW.location.href;
// keep track of the current URL state, as we always receive only the updated state
const from = lastHref;
lastHref = to;
triggerHandlers('history', {
from,
to,
});
if (oldOnPopState) {
// Apparently this can throw in Firefox when incorrectly implemented plugin is installed.
// https://github.com/getsentry/sentry-javascript/issues/3344
// https://github.com/bugsnag/bugsnag-js/issues/469
try {
return oldOnPopState.apply(this, args);
} catch (_oO) {
// no-empty
}
}
};
/** @hidden */
function historyReplacementFunction(originalHistoryFunction) {
return function ( ...args) {
const url = args.length > 2 ? args[2] : undefined;
if (url) {
// coerce to string (this is what pushState does)
const from = lastHref;
const to = String(url);
// keep track of the current URL state, as we always receive only the updated state
lastHref = to;
triggerHandlers('history', {
from,
to,
});
}
return originalHistoryFunction.apply(this, args);
};
}
fill(WINDOW.history, 'pushState', historyReplacementFunction);
fill(WINDOW.history, 'replaceState', historyReplacementFunction);
}
const debounceDuration = 1000;
let debounceTimerID;
let lastCapturedEvent;
/**
* Decide whether the current event should finish the debounce of previously captured one.
* @param previous previously captured event
* @param current event to be captured
*/
function shouldShortcircuitPreviousDebounce(previous, current) {
// If there was no previous event, it should always be swapped for the new one.
if (!previous) {
return true;
}
// If both events have different type, then user definitely performed two separate actions. e.g. click + keypress.
if (previous.type !== current.type) {
return true;
}
try {
// If both events have the same type, it's still possible that actions were performed on different targets.
// e.g. 2 clicks on different buttons.
if (previous.target !== current.target) {
return true;
}
} catch (e) {
// just accessing `target` property can throw an exception in some rare circumstances
// see: https://github.com/getsentry/sentry-javascript/issues/838
}
// If both events have the same type _and_ same `target` (an element which triggered an event, _not necessarily_
// to which an event listener was attached), we treat them as the same action, as we want to capture
// only one breadcrumb. e.g. multiple clicks on the same button, or typing inside a user input box.
return false;
}
/**
* Decide whether an event should be captured.
* @param event event to be captured
*/
function shouldSkipDOMEvent(event) {
// We are only interested in filtering `keypress` events for now.
if (event.type !== 'keypress') {
return false;
}
try {
const target = event.target ;
if (!target || !target.tagName) {
return true;
}
// Only consider keypress events on actual input elements. This will disregard keypresses targeting body
// e.g.tabbing through elements, hotkeys, etc.
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
return false;
}
} catch (e) {
// just accessing `target` property can throw an exception in some rare circumstances
// see: https://github.com/getsentry/sentry-javascript/issues/838
}
return true;
}
/**
* Wraps addEventListener to capture UI breadcrumbs
* @param handler function that will be triggered
* @param globalListener indicates whether event was captured by the global event listener
* @returns wrapped breadcrumb events handler
* @hidden
*/
function makeDOMEventHandler(handler, globalListener = false) {
return (event) => {
// It's possible this handler might trigger multiple times for the same
// event (e.g. event propagation through node ancestors).
// Ignore if we've already captured that event.
if (!event || lastCapturedEvent === event) {
return;
}
// We always want to skip _some_ events.
if (shouldSkipDOMEvent(event)) {
return;
}
const name = event.type === 'keypress' ? 'input' : event.type;
// If there is no debounce timer, it means that we can safely capture the new event and store it for future comparisons.
if (debounceTimerID === undefined) {
handler({
event: event,
name,
global: globalListener,
});
lastCapturedEvent = event;
}
// If there is a debounce awaiting, see if the new event is different enough to treat it as a unique one.
// If that's the case, emit the previous event and store locally the newly-captured DOM event.
else if (shouldShortcircuitPreviousDebounce(lastCapturedEvent, event)) {
handler({
event: event,
name,
global: globalListener,
});
lastCapturedEvent = event;
}
// Start a new debounce timer that will prevent us from capturing multiple events that should be grouped together.
clearTimeout(debounceTimerID);
debounceTimerID = WINDOW.setTimeout(() => {
debounceTimerID = undefined;
}, debounceDuration);
};
}
/** JSDoc */
function instrumentDOM() {
if (!('document' in WINDOW)) {
return;
}
// Make it so that any click or keypress that is unhandled / bubbled up all the way to the document triggers our dom
// handlers. (Normally we have only one, which captures a breadcrumb for each click or keypress.) Do this before
// we instrument `addEventListener` so that we don't end up attaching this handler twice.
const triggerDOMHandler = triggerHandlers.bind(null, 'dom');
const globalDOMEventHandler = makeDOMEventHandler(triggerDOMHandler, true);
WINDOW.document.addEventListener('click', globalDOMEventHandler, false);
WINDOW.document.addEventListener('keypress', globalDOMEventHandler, false);
// After hooking into click and keypress events bubbled up to `document`, we also hook into user-handled
// clicks & keypresses, by adding an event listener of our own to any element to which they add a listener. That
// way, whenever one of their handlers is triggered, ours will be, too. (This is needed because their handler
// could potentially prevent the event from bubbling up to our global listeners. This way, our handler are still
// guaranteed to fire at least once.)
['EventTarget', 'Node'].forEach((target) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const proto = (WINDOW )[target] && (WINDOW )[target].prototype;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
if (!proto || !proto.hasOwnProperty || !proto.hasOwnProperty('addEventListener')) {
return;
}
fill(proto, 'addEventListener', function (originalAddEventListener) {
return function (
type,
listener,
options,
) {
if (type === 'click' || type == 'keypress') {
try {
const el = this ;
const handlers = (el.__sentry_instrumentation_handlers__ = el.__sentry_instrumentation_handlers__ || {});
const handlerForType = (handlers[type] = handlers[type] || { refCount: 0 });
if (!handlerForType.handler) {
const handler = makeDOMEventHandler(triggerDOMHandler);
handlerForType.handler = handler;
originalAddEventListener.call(this, type, handler, options);
}
handlerForType.refCount++;
} catch (e) {
// Accessing dom properties is always fragile.
// Also allows us to skip `addEventListenrs` calls with no proper `this` context.
}
}
return originalAddEventListener.call(this, type, listener, options);
};
});
fill(
proto,
'removeEventListener',
function (originalRemoveEventListener) {
return function (
type,
listener,
options,
) {
if (type === 'click' || type == 'keypress') {
try {
const el = this ;
const handlers = el.__sentry_instrumentation_handlers__ || {};
const handlerForType = handlers[type];
if (handlerForType) {
handlerForType.refCount--;
// If there are no longer any custom handlers of the current type on this element, we can remove ours, too.
if (handlerForType.refCount <= 0) {
originalRemoveEventListener.call(this, type, handlerForType.handler, options);
handlerForType.handler = undefined;
delete handlers[type]; // eslint-disable-line @typescript-eslint/no-dynamic-delete
}
// If there are no longer any custom handlers of any type on this element, cleanup everything.
if (Object.keys(handlers).length === 0) {
delete el.__sentry_instrumentation_handlers__;
}
}
} catch (e) {
// Accessing dom properties is always fragile.
// Also allows us to skip `addEventListenrs` calls with no proper `this` context.
}
}
return originalRemoveEventListener.call(this, type, listener, options);
};
},
);
});
}
let _oldOnErrorHandler = null;
/** JSDoc */
function instrumentError() {
_oldOnErrorHandler = WINDOW.onerror;
WINDOW.onerror = function (msg, url, line, column, error) {
triggerHandlers('error', {
column,
error,
line,
msg,
url,
});
if (_oldOnErrorHandler && !_oldOnErrorHandler.__SENTRY_LOADER__) {
// eslint-disable-next-line prefer-rest-params
return _oldOnErrorHandler.apply(this, arguments);
}
return false;
};
WINDOW.onerror.__SENTRY_INSTRUMENTED__ = true;
}
let _oldOnUnhandledRejectionHandler = null;
/** JSDoc */
function instrumentUnhandledRejection() {
_oldOnUnhandledRejectionHandler = WINDOW.onunhandledrejection;
WINDOW.onunhandledrejection = function (e) {
triggerHandlers('unhandledrejection', e);
if (_oldOnUnhandledRejectionHandler && !_oldOnUnhandledRejectionHandler.__SENTRY_LOADER__) {
// eslint-disable-next-line prefer-rest-params
return _oldOnUnhandledRejectionHandler.apply(this, arguments);
}
return true;
};
WINDOW.onunhandledrejection.__SENTRY_INSTRUMENTED__ = true;
}
export { SENTRY_XHR_DATA_KEY, addInstrumentationHandler, parseFetchArgs };
//# sourceMappingURL=instrument.js.map

179
shared/logger/node_modules/@sentry/utils/esm/is.js generated vendored Normal file
View File

@@ -0,0 +1,179 @@
// eslint-disable-next-line @typescript-eslint/unbound-method
const objectToString = Object.prototype.toString;
/**
* Checks whether given value's type is one of a few Error or Error-like
* {@link isError}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isError(wat) {
switch (objectToString.call(wat)) {
case '[object Error]':
case '[object Exception]':
case '[object DOMException]':
return true;
default:
return isInstanceOf(wat, Error);
}
}
/**
* Checks whether given value is an instance of the given built-in class.
*
* @param wat The value to be checked
* @param className
* @returns A boolean representing the result.
*/
function isBuiltin(wat, className) {
return objectToString.call(wat) === `[object ${className}]`;
}
/**
* Checks whether given value's type is ErrorEvent
* {@link isErrorEvent}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isErrorEvent(wat) {
return isBuiltin(wat, 'ErrorEvent');
}
/**
* Checks whether given value's type is DOMError
* {@link isDOMError}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isDOMError(wat) {
return isBuiltin(wat, 'DOMError');
}
/**
* Checks whether given value's type is DOMException
* {@link isDOMException}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isDOMException(wat) {
return isBuiltin(wat, 'DOMException');
}
/**
* Checks whether given value's type is a string
* {@link isString}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isString(wat) {
return isBuiltin(wat, 'String');
}
/**
* Checks whether given value is a primitive (undefined, null, number, boolean, string, bigint, symbol)
* {@link isPrimitive}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isPrimitive(wat) {
return wat === null || (typeof wat !== 'object' && typeof wat !== 'function');
}
/**
* Checks whether given value's type is an object literal
* {@link isPlainObject}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isPlainObject(wat) {
return isBuiltin(wat, 'Object');
}
/**
* Checks whether given value's type is an Event instance
* {@link isEvent}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isEvent(wat) {
return typeof Event !== 'undefined' && isInstanceOf(wat, Event);
}
/**
* Checks whether given value's type is an Element instance
* {@link isElement}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isElement(wat) {
return typeof Element !== 'undefined' && isInstanceOf(wat, Element);
}
/**
* Checks whether given value's type is an regexp
* {@link isRegExp}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isRegExp(wat) {
return isBuiltin(wat, 'RegExp');
}
/**
* Checks whether given value has a then function.
* @param wat A value to be checked.
*/
function isThenable(wat) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return Boolean(wat && wat.then && typeof wat.then === 'function');
}
/**
* Checks whether given value's type is a SyntheticEvent
* {@link isSyntheticEvent}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isSyntheticEvent(wat) {
return isPlainObject(wat) && 'nativeEvent' in wat && 'preventDefault' in wat && 'stopPropagation' in wat;
}
/**
* Checks whether given value is NaN
* {@link isNaN}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isNaN(wat) {
return typeof wat === 'number' && wat !== wat;
}
/**
* Checks whether given value's type is an instance of provided constructor.
* {@link isInstanceOf}.
*
* @param wat A value to be checked.
* @param base A constructor to be used in a check.
* @returns A boolean representing the result.
*/
function isInstanceOf(wat, base) {
try {
return wat instanceof base;
} catch (_e) {
return false;
}
}
export { isDOMError, isDOMException, isElement, isError, isErrorEvent, isEvent, isInstanceOf, isNaN, isPlainObject, isPrimitive, isRegExp, isString, isSyntheticEvent, isThenable };
//# sourceMappingURL=is.js.map

83
shared/logger/node_modules/@sentry/utils/esm/logger.js generated vendored Normal file
View File

@@ -0,0 +1,83 @@
import { getGlobalSingleton, GLOBAL_OBJ } from './worldwide.js';
/** Prefix for logging strings */
const PREFIX = 'Sentry Logger ';
const CONSOLE_LEVELS = ['debug', 'info', 'warn', 'error', 'log', 'assert', 'trace'] ;
/**
* Temporarily disable sentry console instrumentations.
*
* @param callback The function to run against the original `console` messages
* @returns The results of the callback
*/
function consoleSandbox(callback) {
if (!('console' in GLOBAL_OBJ)) {
return callback();
}
const originalConsole = GLOBAL_OBJ.console ;
const wrappedLevels = {};
// Restore all wrapped console methods
CONSOLE_LEVELS.forEach(level => {
// TODO(v7): Remove this check as it's only needed for Node 6
const originalWrappedFunc =
originalConsole[level] && (originalConsole[level] ).__sentry_original__;
if (level in originalConsole && originalWrappedFunc) {
wrappedLevels[level] = originalConsole[level] ;
originalConsole[level] = originalWrappedFunc ;
}
});
try {
return callback();
} finally {
// Revert restoration to wrapped state
Object.keys(wrappedLevels).forEach(level => {
originalConsole[level] = wrappedLevels[level ];
});
}
}
function makeLogger() {
let enabled = false;
const logger = {
enable: () => {
enabled = true;
},
disable: () => {
enabled = false;
},
};
if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
CONSOLE_LEVELS.forEach(name => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
logger[name] = (...args) => {
if (enabled) {
consoleSandbox(() => {
GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args);
});
}
};
});
} else {
CONSOLE_LEVELS.forEach(name => {
logger[name] = () => undefined;
});
}
return logger ;
}
// Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used
let logger;
if ((typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)) {
logger = getGlobalSingleton('logger', makeLogger);
} else {
logger = makeLogger();
}
export { CONSOLE_LEVELS, consoleSandbox, logger };
//# sourceMappingURL=logger.js.map

45
shared/logger/node_modules/@sentry/utils/esm/memo.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Helper to decycle json objects
*/
function memoBuilder() {
const hasWeakSet = typeof WeakSet === 'function';
const inner = hasWeakSet ? new WeakSet() : [];
function memoize(obj) {
if (hasWeakSet) {
if (inner.has(obj)) {
return true;
}
inner.add(obj);
return false;
}
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < inner.length; i++) {
const value = inner[i];
if (value === obj) {
return true;
}
}
inner.push(obj);
return false;
}
function unmemoize(obj) {
if (hasWeakSet) {
inner.delete(obj);
} else {
for (let i = 0; i < inner.length; i++) {
if (inner[i] === obj) {
inner.splice(i, 1);
break;
}
}
}
}
return [memoize, unmemoize];
}
export { memoBuilder };
//# sourceMappingURL=memo.js.map

197
shared/logger/node_modules/@sentry/utils/esm/misc.js generated vendored Normal file
View File

@@ -0,0 +1,197 @@
import { addNonEnumerableProperty } from './object.js';
import { snipLine } from './string.js';
import { GLOBAL_OBJ } from './worldwide.js';
/**
* UUID4 generator
*
* @returns string Generated UUID4.
*/
function uuid4() {
const gbl = GLOBAL_OBJ ;
const crypto = gbl.crypto || gbl.msCrypto;
if (crypto && crypto.randomUUID) {
return crypto.randomUUID().replace(/-/g, '');
}
const getRandomByte =
crypto && crypto.getRandomValues ? () => crypto.getRandomValues(new Uint8Array(1))[0] : () => Math.random() * 16;
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
// Concatenating the following numbers as strings results in '10000000100040008000100000000000'
return (([1e7] ) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
// eslint-disable-next-line no-bitwise
((c ) ^ ((getRandomByte() & 15) >> ((c ) / 4))).toString(16),
);
}
function getFirstException(event) {
return event.exception && event.exception.values ? event.exception.values[0] : undefined;
}
/**
* Extracts either message or type+value from an event that can be used for user-facing logs
* @returns event's description
*/
function getEventDescription(event) {
const { message, event_id: eventId } = event;
if (message) {
return message;
}
const firstException = getFirstException(event);
if (firstException) {
if (firstException.type && firstException.value) {
return `${firstException.type}: ${firstException.value}`;
}
return firstException.type || firstException.value || eventId || '<unknown>';
}
return eventId || '<unknown>';
}
/**
* Adds exception values, type and value to an synthetic Exception.
* @param event The event to modify.
* @param value Value of the exception.
* @param type Type of the exception.
* @hidden
*/
function addExceptionTypeValue(event, value, type) {
const exception = (event.exception = event.exception || {});
const values = (exception.values = exception.values || []);
const firstException = (values[0] = values[0] || {});
if (!firstException.value) {
firstException.value = value || '';
}
if (!firstException.type) {
firstException.type = type || 'Error';
}
}
/**
* Adds exception mechanism data to a given event. Uses defaults if the second parameter is not passed.
*
* @param event The event to modify.
* @param newMechanism Mechanism data to add to the event.
* @hidden
*/
function addExceptionMechanism(event, newMechanism) {
const firstException = getFirstException(event);
if (!firstException) {
return;
}
const defaultMechanism = { type: 'generic', handled: true };
const currentMechanism = firstException.mechanism;
firstException.mechanism = { ...defaultMechanism, ...currentMechanism, ...newMechanism };
if (newMechanism && 'data' in newMechanism) {
const mergedData = { ...(currentMechanism && currentMechanism.data), ...newMechanism.data };
firstException.mechanism.data = mergedData;
}
}
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
const SEMVER_REGEXP =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
/**
* Represents Semantic Versioning object
*/
/**
* Parses input into a SemVer interface
* @param input string representation of a semver version
*/
function parseSemver(input) {
const match = input.match(SEMVER_REGEXP) || [];
const major = parseInt(match[1], 10);
const minor = parseInt(match[2], 10);
const patch = parseInt(match[3], 10);
return {
buildmetadata: match[5],
major: isNaN(major) ? undefined : major,
minor: isNaN(minor) ? undefined : minor,
patch: isNaN(patch) ? undefined : patch,
prerelease: match[4],
};
}
/**
* This function adds context (pre/post/line) lines to the provided frame
*
* @param lines string[] containing all lines
* @param frame StackFrame that will be mutated
* @param linesOfContext number of context lines we want to add pre/post
*/
function addContextToFrame(lines, frame, linesOfContext = 5) {
// When there is no line number in the frame, attaching context is nonsensical and will even break grouping
if (frame.lineno === undefined) {
return;
}
const maxLines = lines.length;
const sourceLine = Math.max(Math.min(maxLines, frame.lineno - 1), 0);
frame.pre_context = lines
.slice(Math.max(0, sourceLine - linesOfContext), sourceLine)
.map((line) => snipLine(line, 0));
frame.context_line = snipLine(lines[Math.min(maxLines - 1, sourceLine)], frame.colno || 0);
frame.post_context = lines
.slice(Math.min(sourceLine + 1, maxLines), sourceLine + 1 + linesOfContext)
.map((line) => snipLine(line, 0));
}
/**
* Checks whether or not we've already captured the given exception (note: not an identical exception - the very object
* in question), and marks it captured if not.
*
* This is useful because it's possible for an error to get captured by more than one mechanism. After we intercept and
* record an error, we rethrow it (assuming we've intercepted it before it's reached the top-level global handlers), so
* that we don't interfere with whatever effects the error might have had were the SDK not there. At that point, because
* the error has been rethrown, it's possible for it to bubble up to some other code we've instrumented. If it's not
* caught after that, it will bubble all the way up to the global handlers (which of course we also instrument). This
* function helps us ensure that even if we encounter the same error more than once, we only record it the first time we
* see it.
*
* Note: It will ignore primitives (always return `false` and not mark them as seen), as properties can't be set on
* them. {@link: Object.objectify} can be used on exceptions to convert any that are primitives into their equivalent
* object wrapper forms so that this check will always work. However, because we need to flag the exact object which
* will get rethrown, and because that rethrowing happens outside of the event processing pipeline, the objectification
* must be done before the exception captured.
*
* @param A thrown exception to check or flag as having been seen
* @returns `true` if the exception has already been captured, `false` if not (with the side effect of marking it seen)
*/
function checkOrSetAlreadyCaught(exception) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (exception && (exception ).__sentry_captured__) {
return true;
}
try {
// set it this way rather than by assignment so that it's not ennumerable and therefore isn't recorded by the
// `ExtraErrorData` integration
addNonEnumerableProperty(exception , '__sentry_captured__', true);
} catch (err) {
// `exception` is a primitive, so we can't mark it seen
}
return false;
}
/**
* Checks whether the given input is already an array, and if it isn't, wraps it in one.
*
* @param maybeArray Input to turn into an array, if necessary
* @returns The input, if already an array, or an array with the input as the only element, if not
*/
function arrayify(maybeArray) {
return Array.isArray(maybeArray) ? maybeArray : [maybeArray];
}
export { addContextToFrame, addExceptionMechanism, addExceptionTypeValue, arrayify, checkOrSetAlreadyCaught, getEventDescription, parseSemver, uuid4 };
//# sourceMappingURL=misc.js.map

66
shared/logger/node_modules/@sentry/utils/esm/node.js generated vendored Normal file
View File

@@ -0,0 +1,66 @@
import { isBrowserBundle } from './env.js';
/**
* NOTE: In order to avoid circular dependencies, if you add a function to this module and it needs to print something,
* you must either a) use `console.log` rather than the logger, or b) put your function elsewhere.
*/
/**
* Checks whether we're in the Node.js or Browser environment
*
* @returns Answer to given question
*/
function isNodeEnv() {
// explicitly check for browser bundles as those can be optimized statically
// by terser/rollup.
return (
!isBrowserBundle() &&
Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'
);
}
/**
* Requires a module which is protected against bundler minification.
*
* @param request The module path to resolve
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
function dynamicRequire(mod, request) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return mod.require(request);
}
/**
* Helper for dynamically loading module that should work with linked dependencies.
* The problem is that we _should_ be using `require(require.resolve(moduleName, { paths: [cwd()] }))`
* However it's _not possible_ to do that with Webpack, as it has to know all the dependencies during
* build time. `require.resolve` is also not available in any other way, so we cannot create,
* a fake helper like we do with `dynamicRequire`.
*
* We always prefer to use local package, thus the value is not returned early from each `try/catch` block.
* That is to mimic the behavior of `require.resolve` exactly.
*
* @param moduleName module name to require
* @returns possibly required module
*/
function loadModule(moduleName) {
let mod;
try {
mod = dynamicRequire(module, moduleName);
} catch (e) {
// no-empty
}
try {
const { cwd } = dynamicRequire(module, 'process');
mod = dynamicRequire(module, `${cwd()}/node_modules/${moduleName}`) ;
} catch (e) {
// no-empty
}
return mod;
}
export { dynamicRequire, isNodeEnv, loadModule };
//# sourceMappingURL=node.js.map

View File

@@ -0,0 +1,263 @@
import { isNaN, isSyntheticEvent } from './is.js';
import { memoBuilder } from './memo.js';
import { convertToPlainObject } from './object.js';
import { getFunctionName } from './stacktrace.js';
/**
* Recursively normalizes the given object.
*
* - Creates a copy to prevent original input mutation
* - Skips non-enumerable properties
* - When stringifying, calls `toJSON` if implemented
* - Removes circular references
* - Translates non-serializable values (`undefined`/`NaN`/functions) to serializable format
* - Translates known global objects/classes to a string representations
* - Takes care of `Error` object serialization
* - Optionally limits depth of final output
* - Optionally limits number of properties/elements included in any single object/array
*
* @param input The object to be normalized.
* @param depth The max depth to which to normalize the object. (Anything deeper stringified whole.)
* @param maxProperties The max number of elements or properties to be included in any single array or
* object in the normallized output.
* @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function normalize(input, depth = 100, maxProperties = +Infinity) {
try {
// since we're at the outermost level, we don't provide a key
return visit('', input, depth, maxProperties);
} catch (err) {
return { ERROR: `**non-serializable** (${err})` };
}
}
/** JSDoc */
function normalizeToSize(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
object,
// Default Node.js REPL depth
depth = 3,
// 100kB, as 200kB is max payload size, so half sounds reasonable
maxSize = 100 * 1024,
) {
const normalized = normalize(object, depth);
if (jsonSize(normalized) > maxSize) {
return normalizeToSize(object, depth - 1, maxSize);
}
return normalized ;
}
/**
* Visits a node to perform normalization on it
*
* @param key The key corresponding to the given node
* @param value The node to be visited
* @param depth Optional number indicating the maximum recursion depth
* @param maxProperties Optional maximum number of properties/elements included in any single object/array
* @param memo Optional Memo class handling decycling
*/
function visit(
key,
value,
depth = +Infinity,
maxProperties = +Infinity,
memo = memoBuilder(),
) {
const [memoize, unmemoize] = memo;
// Get the simple cases out of the way first
if (
value == null || // this matches null and undefined -> eqeq not eqeqeq
(['number', 'boolean', 'string'].includes(typeof value) && !isNaN(value))
) {
return value ;
}
const stringified = stringifyValue(key, value);
// Anything we could potentially dig into more (objects or arrays) will have come back as `"[object XXXX]"`.
// Everything else will have already been serialized, so if we don't see that pattern, we're done.
if (!stringified.startsWith('[object ')) {
return stringified;
}
// From here on, we can assert that `value` is either an object or an array.
// Do not normalize objects that we know have already been normalized. As a general rule, the
// "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
// have already been normalized.
if ((value )['__sentry_skip_normalization__']) {
return value ;
}
// We can set `__sentry_override_normalization_depth__` on an object to ensure that from there
// We keep a certain amount of depth.
// This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.
const remainingDepth =
typeof (value )['__sentry_override_normalization_depth__'] === 'number'
? ((value )['__sentry_override_normalization_depth__'] )
: depth;
// We're also done if we've reached the max depth
if (remainingDepth === 0) {
// At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
return stringified.replace('object ', '');
}
// If we've already visited this branch, bail out, as it's circular reference. If not, note that we're seeing it now.
if (memoize(value)) {
return '[Circular ~]';
}
// If the value has a `toJSON` method, we call it to extract more information
const valueWithToJSON = value ;
if (valueWithToJSON && typeof valueWithToJSON.toJSON === 'function') {
try {
const jsonValue = valueWithToJSON.toJSON();
// We need to normalize the return value of `.toJSON()` in case it has circular references
return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);
} catch (err) {
// pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
}
}
// At this point we know we either have an object or an array, we haven't seen it before, and we're going to recurse
// because we haven't yet reached the max depth. Create an accumulator to hold the results of visiting each
// property/entry, and keep track of the number of items we add to it.
const normalized = (Array.isArray(value) ? [] : {}) ;
let numAdded = 0;
// Before we begin, convert`Error` and`Event` instances into plain objects, since some of each of their relevant
// properties are non-enumerable and otherwise would get missed.
const visitable = convertToPlainObject(value );
for (const visitKey in visitable) {
// Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.
if (!Object.prototype.hasOwnProperty.call(visitable, visitKey)) {
continue;
}
if (numAdded >= maxProperties) {
normalized[visitKey] = '[MaxProperties ~]';
break;
}
// Recursively visit all the child nodes
const visitValue = visitable[visitKey];
normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);
numAdded++;
}
// Once we've visited all the branches, remove the parent from memo storage
unmemoize(value);
// Return accumulated values
return normalized;
}
/* eslint-disable complexity */
/**
* Stringify the given value. Handles various known special values and types.
*
* Not meant to be used on simple primitives which already have a string representation, as it will, for example, turn
* the number 1231 into "[Object Number]", nor on `null`, as it will throw.
*
* @param value The value to stringify
* @returns A stringified representation of the given value
*/
function stringifyValue(
key,
// this type is a tiny bit of a cheat, since this function does handle NaN (which is technically a number), but for
// our internal use, it'll do
value,
) {
try {
if (key === 'domain' && value && typeof value === 'object' && (value )._events) {
return '[Domain]';
}
if (key === 'domainEmitter') {
return '[DomainEmitter]';
}
// It's safe to use `global`, `window`, and `document` here in this manner, as we are asserting using `typeof` first
// which won't throw if they are not present.
if (typeof global !== 'undefined' && value === global) {
return '[Global]';
}
// eslint-disable-next-line no-restricted-globals
if (typeof window !== 'undefined' && value === window) {
return '[Window]';
}
// eslint-disable-next-line no-restricted-globals
if (typeof document !== 'undefined' && value === document) {
return '[Document]';
}
// React's SyntheticEvent thingy
if (isSyntheticEvent(value)) {
return '[SyntheticEvent]';
}
if (typeof value === 'number' && value !== value) {
return '[NaN]';
}
if (typeof value === 'function') {
return `[Function: ${getFunctionName(value)}]`;
}
if (typeof value === 'symbol') {
return `[${String(value)}]`;
}
// stringified BigInts are indistinguishable from regular numbers, so we need to label them to avoid confusion
if (typeof value === 'bigint') {
return `[BigInt: ${String(value)}]`;
}
// Now that we've knocked out all the special cases and the primitives, all we have left are objects. Simply casting
// them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
// `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
// we can make sure that only plain objects come out that way.
const objName = getConstructorName(value);
// Handle HTML Elements
if (/^HTML(\w*)Element$/.test(objName)) {
return `[HTMLElement: ${objName}]`;
}
return `[object ${objName}]`;
} catch (err) {
return `**non-serializable** (${err})`;
}
}
/* eslint-enable complexity */
function getConstructorName(value) {
const prototype = Object.getPrototypeOf(value);
return prototype ? prototype.constructor.name : 'null prototype';
}
/** Calculates bytes size of input string */
function utf8Length(value) {
// eslint-disable-next-line no-bitwise
return ~-encodeURI(value).split(/%..|./).length;
}
/** Calculates bytes size of input object */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function jsonSize(value) {
return utf8Length(JSON.stringify(value));
}
export { normalize, normalizeToSize, visit as walk };
//# sourceMappingURL=normalize.js.map

279
shared/logger/node_modules/@sentry/utils/esm/object.js generated vendored Normal file
View File

@@ -0,0 +1,279 @@
import { htmlTreeAsString } from './browser.js';
import { isError, isEvent, isInstanceOf, isElement, isPlainObject, isPrimitive } from './is.js';
import { truncate } from './string.js';
/**
* Replace a method in an object with a wrapped version of itself.
*
* @param source An object that contains a method to be wrapped.
* @param name The name of the method to be wrapped.
* @param replacementFactory A higher-order function that takes the original version of the given method and returns a
* wrapped version. Note: The function returned by `replacementFactory` needs to be a non-arrow function, in order to
* preserve the correct value of `this`, and the original method must be called using `origMethod.call(this, <other
* args>)` or `origMethod.apply(this, [<other args>])` (rather than being called directly), again to preserve `this`.
* @returns void
*/
function fill(source, name, replacementFactory) {
if (!(name in source)) {
return;
}
const original = source[name] ;
const wrapped = replacementFactory(original) ;
// Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
if (typeof wrapped === 'function') {
try {
markFunctionWrapped(wrapped, original);
} catch (_Oo) {
// This can throw if multiple fill happens on a global object like XMLHttpRequest
// Fixes https://github.com/getsentry/sentry-javascript/issues/2043
}
}
source[name] = wrapped;
}
/**
* Defines a non-enumerable property on the given object.
*
* @param obj The object on which to set the property
* @param name The name of the property to be set
* @param value The value to which to set the property
*/
function addNonEnumerableProperty(obj, name, value) {
Object.defineProperty(obj, name, {
// enumerable: false, // the default, so we can save on bundle size by not explicitly setting it
value: value,
writable: true,
configurable: true,
});
}
/**
* Remembers the original function on the wrapped function and
* patches up the prototype.
*
* @param wrapped the wrapper function
* @param original the original function that gets wrapped
*/
function markFunctionWrapped(wrapped, original) {
const proto = original.prototype || {};
wrapped.prototype = original.prototype = proto;
addNonEnumerableProperty(wrapped, '__sentry_original__', original);
}
/**
* This extracts the original function if available. See
* `markFunctionWrapped` for more information.
*
* @param func the function to unwrap
* @returns the unwrapped version of the function if available.
*/
function getOriginalFunction(func) {
return func.__sentry_original__;
}
/**
* Encodes given object into url-friendly format
*
* @param object An object that contains serializable values
* @returns string Encoded
*/
function urlEncode(object) {
return Object.keys(object)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`)
.join('&');
}
/**
* Transforms any `Error` or `Event` into a plain object with all of their enumerable properties, and some of their
* non-enumerable properties attached.
*
* @param value Initial source that we have to transform in order for it to be usable by the serializer
* @returns An Event or Error turned into an object - or the value argurment itself, when value is neither an Event nor
* an Error.
*/
function convertToPlainObject(value)
{
if (isError(value)) {
return {
message: value.message,
name: value.name,
stack: value.stack,
...getOwnProperties(value),
};
} else if (isEvent(value)) {
const newObj
= {
type: value.type,
target: serializeEventTarget(value.target),
currentTarget: serializeEventTarget(value.currentTarget),
...getOwnProperties(value),
};
if (typeof CustomEvent !== 'undefined' && isInstanceOf(value, CustomEvent)) {
newObj.detail = value.detail;
}
return newObj;
} else {
return value;
}
}
/** Creates a string representation of the target of an `Event` object */
function serializeEventTarget(target) {
try {
return isElement(target) ? htmlTreeAsString(target) : Object.prototype.toString.call(target);
} catch (_oO) {
return '<unknown>';
}
}
/** Filters out all but an object's own properties */
function getOwnProperties(obj) {
if (typeof obj === 'object' && obj !== null) {
const extractedProps = {};
for (const property in obj) {
if (Object.prototype.hasOwnProperty.call(obj, property)) {
extractedProps[property] = (obj )[property];
}
}
return extractedProps;
} else {
return {};
}
}
/**
* Given any captured exception, extract its keys and create a sorted
* and truncated list that will be used inside the event message.
* eg. `Non-error exception captured with keys: foo, bar, baz`
*/
function extractExceptionKeysForMessage(exception, maxLength = 40) {
const keys = Object.keys(convertToPlainObject(exception));
keys.sort();
if (!keys.length) {
return '[object has no keys]';
}
if (keys[0].length >= maxLength) {
return truncate(keys[0], maxLength);
}
for (let includedKeys = keys.length; includedKeys > 0; includedKeys--) {
const serialized = keys.slice(0, includedKeys).join(', ');
if (serialized.length > maxLength) {
continue;
}
if (includedKeys === keys.length) {
return serialized;
}
return truncate(serialized, maxLength);
}
return '';
}
/**
* Given any object, return a new object having removed all fields whose value was `undefined`.
* Works recursively on objects and arrays.
*
* Attention: This function keeps circular references in the returned object.
*/
function dropUndefinedKeys(inputValue) {
// This map keeps track of what already visited nodes map to.
// Our Set - based memoBuilder doesn't work here because we want to the output object to have the same circular
// references as the input object.
const memoizationMap = new Map();
// This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
return _dropUndefinedKeys(inputValue, memoizationMap);
}
function _dropUndefinedKeys(inputValue, memoizationMap) {
if (isPlainObject(inputValue)) {
// If this node has already been visited due to a circular reference, return the object it was mapped to in the new object
const memoVal = memoizationMap.get(inputValue);
if (memoVal !== undefined) {
return memoVal ;
}
const returnValue = {};
// Store the mapping of this value in case we visit it again, in case of circular data
memoizationMap.set(inputValue, returnValue);
for (const key of Object.keys(inputValue)) {
if (typeof inputValue[key] !== 'undefined') {
returnValue[key] = _dropUndefinedKeys(inputValue[key], memoizationMap);
}
}
return returnValue ;
}
if (Array.isArray(inputValue)) {
// If this node has already been visited due to a circular reference, return the array it was mapped to in the new object
const memoVal = memoizationMap.get(inputValue);
if (memoVal !== undefined) {
return memoVal ;
}
const returnValue = [];
// Store the mapping of this value in case we visit it again, in case of circular data
memoizationMap.set(inputValue, returnValue);
inputValue.forEach((item) => {
returnValue.push(_dropUndefinedKeys(item, memoizationMap));
});
return returnValue ;
}
return inputValue;
}
/**
* Ensure that something is an object.
*
* Turns `undefined` and `null` into `String`s and all other primitives into instances of their respective wrapper
* classes (String, Boolean, Number, etc.). Acts as the identity function on non-primitives.
*
* @param wat The subject of the objectification
* @returns A version of `wat` which can safely be used with `Object` class methods
*/
function objectify(wat) {
let objectified;
switch (true) {
case wat === undefined || wat === null:
objectified = new String(wat);
break;
// Though symbols and bigints do have wrapper classes (`Symbol` and `BigInt`, respectively), for whatever reason
// those classes don't have constructors which can be used with the `new` keyword. We therefore need to cast each as
// an object in order to wrap it.
case typeof wat === 'symbol' || typeof wat === 'bigint':
objectified = Object(wat);
break;
// this will catch the remaining primitives: `String`, `Number`, and `Boolean`
case isPrimitive(wat):
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
objectified = new (wat ).constructor(wat);
break;
// by process of elimination, at this point we know that `wat` must already be an object
default:
objectified = wat;
break;
}
return objectified;
}
export { addNonEnumerableProperty, convertToPlainObject, dropUndefinedKeys, extractExceptionKeysForMessage, fill, getOriginalFunction, markFunctionWrapped, objectify, urlEncode };
//# sourceMappingURL=object.js.map

View File

@@ -0,0 +1,102 @@
import { SentryError } from './error.js';
import { rejectedSyncPromise, SyncPromise, resolvedSyncPromise } from './syncpromise.js';
/**
* Creates an new PromiseBuffer object with the specified limit
* @param limit max number of promises that can be stored in the buffer
*/
function makePromiseBuffer(limit) {
const buffer = [];
function isReady() {
return limit === undefined || buffer.length < limit;
}
/**
* Remove a promise from the queue.
*
* @param task Can be any PromiseLike<T>
* @returns Removed promise.
*/
function remove(task) {
return buffer.splice(buffer.indexOf(task), 1)[0];
}
/**
* Add a promise (representing an in-flight action) to the queue, and set it to remove itself on fulfillment.
*
* @param taskProducer A function producing any PromiseLike<T>; In previous versions this used to be `task:
* PromiseLike<T>`, but under that model, Promises were instantly created on the call-site and their executor
* functions therefore ran immediately. Thus, even if the buffer was full, the action still happened. By
* requiring the promise to be wrapped in a function, we can defer promise creation until after the buffer
* limit check.
* @returns The original promise.
*/
function add(taskProducer) {
if (!isReady()) {
return rejectedSyncPromise(new SentryError('Not adding Promise because buffer limit was reached.'));
}
// start the task and add its promise to the queue
const task = taskProducer();
if (buffer.indexOf(task) === -1) {
buffer.push(task);
}
void task
.then(() => remove(task))
// Use `then(null, rejectionHandler)` rather than `catch(rejectionHandler)` so that we can use `PromiseLike`
// rather than `Promise`. `PromiseLike` doesn't have a `.catch` method, making its polyfill smaller. (ES5 didn't
// have promises, so TS has to polyfill when down-compiling.)
.then(null, () =>
remove(task).then(null, () => {
// We have to add another catch here because `remove()` starts a new promise chain.
}),
);
return task;
}
/**
* Wait for all promises in the queue to resolve or for timeout to expire, whichever comes first.
*
* @param timeout The time, in ms, after which to resolve to `false` if the queue is still non-empty. Passing `0` (or
* not passing anything) will make the promise wait as long as it takes for the queue to drain before resolving to
* `true`.
* @returns A promise which will resolve to `true` if the queue is already empty or drains before the timeout, and
* `false` otherwise
*/
function drain(timeout) {
return new SyncPromise((resolve, reject) => {
let counter = buffer.length;
if (!counter) {
return resolve(true);
}
// wait for `timeout` ms and then resolve to `false` (if not cancelled first)
const capturedSetTimeout = setTimeout(() => {
if (timeout && timeout > 0) {
resolve(false);
}
}, timeout);
// if all promises resolve in time, cancel the timer and resolve to `true`
buffer.forEach(item => {
void resolvedSyncPromise(item).then(() => {
if (!--counter) {
clearTimeout(capturedSetTimeout);
resolve(true);
}
}, reject);
});
});
}
return {
$: buffer,
add,
drain,
};
}
export { makePromiseBuffer };
//# sourceMappingURL=promisebuffer.js.map

View File

@@ -0,0 +1,97 @@
// Intentionally keeping the key broad, as we don't know for sure what rate limit headers get returned from backend
const DEFAULT_RETRY_AFTER = 60 * 1000; // 60 seconds
/**
* Extracts Retry-After value from the request header or returns default value
* @param header string representation of 'Retry-After' header
* @param now current unix timestamp
*
*/
function parseRetryAfterHeader(header, now = Date.now()) {
const headerDelay = parseInt(`${header}`, 10);
if (!isNaN(headerDelay)) {
return headerDelay * 1000;
}
const headerDate = Date.parse(`${header}`);
if (!isNaN(headerDate)) {
return headerDate - now;
}
return DEFAULT_RETRY_AFTER;
}
/**
* Gets the time that the given category is disabled until for rate limiting.
* In case no category-specific limit is set but a general rate limit across all categories is active,
* that time is returned.
*
* @return the time in ms that the category is disabled until or 0 if there's no active rate limit.
*/
function disabledUntil(limits, category) {
return limits[category] || limits.all || 0;
}
/**
* Checks if a category is rate limited
*/
function isRateLimited(limits, category, now = Date.now()) {
return disabledUntil(limits, category) > now;
}
/**
* Update ratelimits from incoming headers.
*
* @return the updated RateLimits object.
*/
function updateRateLimits(
limits,
{ statusCode, headers },
now = Date.now(),
) {
const updatedRateLimits = {
...limits,
};
// "The name is case-insensitive."
// https://developer.mozilla.org/en-US/docs/Web/API/Headers/get
const rateLimitHeader = headers && headers['x-sentry-rate-limits'];
const retryAfterHeader = headers && headers['retry-after'];
if (rateLimitHeader) {
/**
* rate limit headers are of the form
* <header>,<header>,..
* where each <header> is of the form
* <retry_after>: <categories>: <scope>: <reason_code>
* where
* <retry_after> is a delay in seconds
* <categories> is the event type(s) (error, transaction, etc) being rate limited and is of the form
* <category>;<category>;...
* <scope> is what's being limited (org, project, or key) - ignored by SDK
* <reason_code> is an arbitrary string like "org_quota" - ignored by SDK
*/
for (const limit of rateLimitHeader.trim().split(',')) {
const [retryAfter, categories] = limit.split(':', 2);
const headerDelay = parseInt(retryAfter, 10);
const delay = (!isNaN(headerDelay) ? headerDelay : 60) * 1000; // 60sec default
if (!categories) {
updatedRateLimits.all = now + delay;
} else {
for (const category of categories.split(';')) {
updatedRateLimits[category] = now + delay;
}
}
}
} else if (retryAfterHeader) {
updatedRateLimits.all = now + parseRetryAfterHeader(retryAfterHeader, now);
} else if (statusCode === 429) {
updatedRateLimits.all = now + 60 * 1000;
}
return updatedRateLimits;
}
export { DEFAULT_RETRY_AFTER, disabledUntil, isRateLimited, parseRetryAfterHeader, updateRateLimits };
//# sourceMappingURL=ratelimit.js.map

View File

@@ -0,0 +1,36 @@
// Note: Ideally the `SeverityLevel` type would be derived from `validSeverityLevels`, but that would mean either
//
// a) moving `validSeverityLevels` to `@sentry/types`,
// b) moving the`SeverityLevel` type here, or
// c) importing `validSeverityLevels` from here into `@sentry/types`.
//
// Option A would make `@sentry/types` a runtime dependency of `@sentry/utils` (not good), and options B and C would
// create a circular dependency between `@sentry/types` and `@sentry/utils` (also not good). So a TODO accompanying the
// type, reminding anyone who changes it to change this list also, will have to do.
const validSeverityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug'];
/**
* Converts a string-based level into a member of the deprecated {@link Severity} enum.
*
* @deprecated `severityFromString` is deprecated. Please use `severityLevelFromString` instead.
*
* @param level String representation of Severity
* @returns Severity
*/
function severityFromString(level) {
return severityLevelFromString(level) ;
}
/**
* Converts a string-based level into a `SeverityLevel`, normalizing it along the way.
*
* @param level String representation of desired `SeverityLevel`.
* @returns The `SeverityLevel` corresponding to the given string, or 'log' if the string isn't a valid level.
*/
function severityLevelFromString(level) {
return (level === 'warn' ? 'warning' : validSeverityLevels.includes(level) ? level : 'log') ;
}
export { severityFromString, severityLevelFromString, validSeverityLevels };
//# sourceMappingURL=severity.js.map

View File

@@ -0,0 +1,136 @@
import { node } from './node-stack-trace.js';
const STACKTRACE_FRAME_LIMIT = 50;
// Used to sanitize webpack (error: *) wrapped stack errors
const WEBPACK_ERROR_REGEXP = /\(error: (.*)\)/;
/**
* Creates a stack parser with the supplied line parsers
*
* StackFrames are returned in the correct order for Sentry Exception
* frames and with Sentry SDK internal frames removed from the top and bottom
*
*/
function createStackParser(...parsers) {
const sortedParsers = parsers.sort((a, b) => a[0] - b[0]).map(p => p[1]);
return (stack, skipFirst = 0) => {
const frames = [];
const lines = stack.split('\n');
for (let i = skipFirst; i < lines.length; i++) {
const line = lines[i];
// Ignore lines over 1kb as they are unlikely to be stack frames.
// Many of the regular expressions use backtracking which results in run time that increases exponentially with
// input size. Huge strings can result in hangs/Denial of Service:
// https://github.com/getsentry/sentry-javascript/issues/2286
if (line.length > 1024) {
continue;
}
// https://github.com/getsentry/sentry-javascript/issues/5459
// Remove webpack (error: *) wrappers
const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line;
// https://github.com/getsentry/sentry-javascript/issues/7813
// Skip Error: lines
if (cleanedLine.match(/\S*Error: /)) {
continue;
}
for (const parser of sortedParsers) {
const frame = parser(cleanedLine);
if (frame) {
frames.push(frame);
break;
}
}
if (frames.length >= STACKTRACE_FRAME_LIMIT) {
break;
}
}
return stripSentryFramesAndReverse(frames);
};
}
/**
* Gets a stack parser implementation from Options.stackParser
* @see Options
*
* If options contains an array of line parsers, it is converted into a parser
*/
function stackParserFromStackParserOptions(stackParser) {
if (Array.isArray(stackParser)) {
return createStackParser(...stackParser);
}
return stackParser;
}
/**
* Removes Sentry frames from the top and bottom of the stack if present and enforces a limit of max number of frames.
* Assumes stack input is ordered from top to bottom and returns the reverse representation so call site of the
* function that caused the crash is the last frame in the array.
* @hidden
*/
function stripSentryFramesAndReverse(stack) {
if (!stack.length) {
return [];
}
const localStack = stack.slice(0, STACKTRACE_FRAME_LIMIT);
const lastFrameFunction = localStack[localStack.length - 1].function;
// If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call)
if (lastFrameFunction && /sentryWrapped/.test(lastFrameFunction)) {
localStack.pop();
}
// Reversing in the middle of the procedure allows us to just pop the values off the stack
localStack.reverse();
const firstFrameFunction = localStack[localStack.length - 1].function;
// If stack ends with one of our internal API calls, remove it (ends, meaning it's the bottom of the stack - aka top-most call)
if (firstFrameFunction && /captureMessage|captureException/.test(firstFrameFunction)) {
localStack.pop();
}
return localStack.map(frame => ({
...frame,
filename: frame.filename || localStack[localStack.length - 1].filename,
function: frame.function || '?',
}));
}
const defaultFunctionName = '<anonymous>';
/**
* Safely extract function name from itself
*/
function getFunctionName(fn) {
try {
if (!fn || typeof fn !== 'function') {
return defaultFunctionName;
}
return fn.name || defaultFunctionName;
} catch (e) {
// Just accessing custom props in some Selenium environments
// can cause a "Permission denied" exception (see raven-js#495).
return defaultFunctionName;
}
}
/**
* Node.js stack line parser
*
* This is in @sentry/utils so it can be used from the Electron SDK in the browser for when `nodeIntegration == true`.
* This allows it to be used without referencing or importing any node specific code which causes bundlers to complain
*/
function nodeStackLineParser(getModule) {
return [90, node(getModule)];
}
export { createStackParser, getFunctionName, nodeStackLineParser, stackParserFromStackParserOptions, stripSentryFramesAndReverse };
//# sourceMappingURL=stacktrace.js.map

132
shared/logger/node_modules/@sentry/utils/esm/string.js generated vendored Normal file
View File

@@ -0,0 +1,132 @@
import { isString, isRegExp } from './is.js';
/**
* Truncates given string to the maximum characters count
*
* @param str An object that contains serializable values
* @param max Maximum number of characters in truncated string (0 = unlimited)
* @returns string Encoded
*/
function truncate(str, max = 0) {
if (typeof str !== 'string' || max === 0) {
return str;
}
return str.length <= max ? str : `${str.slice(0, max)}...`;
}
/**
* This is basically just `trim_line` from
* https://github.com/getsentry/sentry/blob/master/src/sentry/lang/javascript/processor.py#L67
*
* @param str An object that contains serializable values
* @param max Maximum number of characters in truncated string
* @returns string Encoded
*/
function snipLine(line, colno) {
let newLine = line;
const lineLength = newLine.length;
if (lineLength <= 150) {
return newLine;
}
if (colno > lineLength) {
// eslint-disable-next-line no-param-reassign
colno = lineLength;
}
let start = Math.max(colno - 60, 0);
if (start < 5) {
start = 0;
}
let end = Math.min(start + 140, lineLength);
if (end > lineLength - 5) {
end = lineLength;
}
if (end === lineLength) {
start = Math.max(end - 140, 0);
}
newLine = newLine.slice(start, end);
if (start > 0) {
newLine = `'{snip} ${newLine}`;
}
if (end < lineLength) {
newLine += ' {snip}';
}
return newLine;
}
/**
* Join values in array
* @param input array of values to be joined together
* @param delimiter string to be placed in-between values
* @returns Joined values
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function safeJoin(input, delimiter) {
if (!Array.isArray(input)) {
return '';
}
const output = [];
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < input.length; i++) {
const value = input[i];
try {
output.push(String(value));
} catch (e) {
output.push('[value cannot be serialized]');
}
}
return output.join(delimiter);
}
/**
* Checks if the given value matches a regex or string
*
* @param value The string to test
* @param pattern Either a regex or a string against which `value` will be matched
* @param requireExactStringMatch If true, `value` must match `pattern` exactly. If false, `value` will match
* `pattern` if it contains `pattern`. Only applies to string-type patterns.
*/
function isMatchingPattern(
value,
pattern,
requireExactStringMatch = false,
) {
if (!isString(value)) {
return false;
}
if (isRegExp(pattern)) {
return pattern.test(value);
}
if (isString(pattern)) {
return requireExactStringMatch ? value === pattern : value.includes(pattern);
}
return false;
}
/**
* Test the given string against an array of strings and regexes. By default, string matching is done on a
* substring-inclusion basis rather than a strict equality basis
*
* @param testString The string to test
* @param patterns The patterns against which to test the string
* @param requireExactStringMatch If true, `testString` must match one of the given string patterns exactly in order to
* count. If false, `testString` will match a string pattern if it contains that pattern.
* @returns
*/
function stringMatchesSomePattern(
testString,
patterns = [],
requireExactStringMatch = false,
) {
return patterns.some(pattern => isMatchingPattern(testString, pattern, requireExactStringMatch));
}
export { isMatchingPattern, safeJoin, snipLine, stringMatchesSomePattern, truncate };
//# sourceMappingURL=string.js.map

View File

@@ -0,0 +1,161 @@
import { logger } from './logger.js';
import { getGlobalObject } from './worldwide.js';
// eslint-disable-next-line deprecation/deprecation
const WINDOW = getGlobalObject();
/**
* Tells whether current environment supports ErrorEvent objects
* {@link supportsErrorEvent}.
*
* @returns Answer to the given question.
*/
function supportsErrorEvent() {
try {
new ErrorEvent('');
return true;
} catch (e) {
return false;
}
}
/**
* Tells whether current environment supports DOMError objects
* {@link supportsDOMError}.
*
* @returns Answer to the given question.
*/
function supportsDOMError() {
try {
// Chrome: VM89:1 Uncaught TypeError: Failed to construct 'DOMError':
// 1 argument required, but only 0 present.
// @ts-ignore It really needs 1 argument, not 0.
new DOMError('');
return true;
} catch (e) {
return false;
}
}
/**
* Tells whether current environment supports DOMException objects
* {@link supportsDOMException}.
*
* @returns Answer to the given question.
*/
function supportsDOMException() {
try {
new DOMException('');
return true;
} catch (e) {
return false;
}
}
/**
* Tells whether current environment supports Fetch API
* {@link supportsFetch}.
*
* @returns Answer to the given question.
*/
function supportsFetch() {
if (!('fetch' in WINDOW)) {
return false;
}
try {
new Headers();
new Request('http://www.example.com');
new Response();
return true;
} catch (e) {
return false;
}
}
/**
* isNativeFetch checks if the given function is a native implementation of fetch()
*/
// eslint-disable-next-line @typescript-eslint/ban-types
function isNativeFetch(func) {
return func && /^function fetch\(\)\s+\{\s+\[native code\]\s+\}$/.test(func.toString());
}
/**
* Tells whether current environment supports Fetch API natively
* {@link supportsNativeFetch}.
*
* @returns true if `window.fetch` is natively implemented, false otherwise
*/
function supportsNativeFetch() {
if (!supportsFetch()) {
return false;
}
// Fast path to avoid DOM I/O
// eslint-disable-next-line @typescript-eslint/unbound-method
if (isNativeFetch(WINDOW.fetch)) {
return true;
}
// window.fetch is implemented, but is polyfilled or already wrapped (e.g: by a chrome extension)
// so create a "pure" iframe to see if that has native fetch
let result = false;
const doc = WINDOW.document;
// eslint-disable-next-line deprecation/deprecation
if (doc && typeof (doc.createElement ) === 'function') {
try {
const sandbox = doc.createElement('iframe');
sandbox.hidden = true;
doc.head.appendChild(sandbox);
if (sandbox.contentWindow && sandbox.contentWindow.fetch) {
// eslint-disable-next-line @typescript-eslint/unbound-method
result = isNativeFetch(sandbox.contentWindow.fetch);
}
doc.head.removeChild(sandbox);
} catch (err) {
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', err);
}
}
return result;
}
/**
* Tells whether current environment supports ReportingObserver API
* {@link supportsReportingObserver}.
*
* @returns Answer to the given question.
*/
function supportsReportingObserver() {
return 'ReportingObserver' in WINDOW;
}
/**
* Tells whether current environment supports Referrer Policy API
* {@link supportsReferrerPolicy}.
*
* @returns Answer to the given question.
*/
function supportsReferrerPolicy() {
// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default'
// (see https://caniuse.com/#feat=referrer-policy),
// it doesn't. And it throws an exception instead of ignoring this parameter...
// REF: https://github.com/getsentry/raven-js/issues/1233
if (!supportsFetch()) {
return false;
}
try {
new Request('_', {
referrerPolicy: 'origin' ,
});
return true;
} catch (e) {
return false;
}
}
export { isNativeFetch, supportsDOMError, supportsDOMException, supportsErrorEvent, supportsFetch, supportsNativeFetch, supportsReferrerPolicy, supportsReportingObserver };
//# sourceMappingURL=supports.js.map

View File

@@ -0,0 +1,191 @@
import { isThenable } from './is.js';
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/** SyncPromise internal states */
var States; (function (States) {
/** Pending */
const PENDING = 0; States[States["PENDING"] = PENDING] = "PENDING";
/** Resolved / OK */
const RESOLVED = 1; States[States["RESOLVED"] = RESOLVED] = "RESOLVED";
/** Rejected / Error */
const REJECTED = 2; States[States["REJECTED"] = REJECTED] = "REJECTED";
})(States || (States = {}));
// Overloads so we can call resolvedSyncPromise without arguments and generic argument
/**
* Creates a resolved sync promise.
*
* @param value the value to resolve the promise with
* @returns the resolved sync promise
*/
function resolvedSyncPromise(value) {
return new SyncPromise(resolve => {
resolve(value);
});
}
/**
* Creates a rejected sync promise.
*
* @param value the value to reject the promise with
* @returns the rejected sync promise
*/
function rejectedSyncPromise(reason) {
return new SyncPromise((_, reject) => {
reject(reason);
});
}
/**
* Thenable class that behaves like a Promise and follows it's interface
* but is not async internally
*/
class SyncPromise {
__init() {this._state = States.PENDING;}
__init2() {this._handlers = [];}
constructor(
executor,
) {SyncPromise.prototype.__init.call(this);SyncPromise.prototype.__init2.call(this);SyncPromise.prototype.__init3.call(this);SyncPromise.prototype.__init4.call(this);SyncPromise.prototype.__init5.call(this);SyncPromise.prototype.__init6.call(this);
try {
executor(this._resolve, this._reject);
} catch (e) {
this._reject(e);
}
}
/** JSDoc */
then(
onfulfilled,
onrejected,
) {
return new SyncPromise((resolve, reject) => {
this._handlers.push([
false,
result => {
if (!onfulfilled) {
// TODO: ¯\_(ツ)_/¯
// TODO: FIXME
resolve(result );
} else {
try {
resolve(onfulfilled(result));
} catch (e) {
reject(e);
}
}
},
reason => {
if (!onrejected) {
reject(reason);
} else {
try {
resolve(onrejected(reason));
} catch (e) {
reject(e);
}
}
},
]);
this._executeHandlers();
});
}
/** JSDoc */
catch(
onrejected,
) {
return this.then(val => val, onrejected);
}
/** JSDoc */
finally(onfinally) {
return new SyncPromise((resolve, reject) => {
let val;
let isRejected;
return this.then(
value => {
isRejected = false;
val = value;
if (onfinally) {
onfinally();
}
},
reason => {
isRejected = true;
val = reason;
if (onfinally) {
onfinally();
}
},
).then(() => {
if (isRejected) {
reject(val);
return;
}
resolve(val );
});
});
}
/** JSDoc */
__init3() {this._resolve = (value) => {
this._setResult(States.RESOLVED, value);
};}
/** JSDoc */
__init4() {this._reject = (reason) => {
this._setResult(States.REJECTED, reason);
};}
/** JSDoc */
__init5() {this._setResult = (state, value) => {
if (this._state !== States.PENDING) {
return;
}
if (isThenable(value)) {
void (value ).then(this._resolve, this._reject);
return;
}
this._state = state;
this._value = value;
this._executeHandlers();
};}
/** JSDoc */
__init6() {this._executeHandlers = () => {
if (this._state === States.PENDING) {
return;
}
const cachedHandlers = this._handlers.slice();
this._handlers = [];
cachedHandlers.forEach(handler => {
if (handler[0]) {
return;
}
if (this._state === States.RESOLVED) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handler[1](this._value );
}
if (this._state === States.REJECTED) {
handler[2](this._value);
}
handler[0] = true;
});
};}
}
export { SyncPromise, rejectedSyncPromise, resolvedSyncPromise };
//# sourceMappingURL=syncpromise.js.map

183
shared/logger/node_modules/@sentry/utils/esm/time.js generated vendored Normal file
View File

@@ -0,0 +1,183 @@
import { isNodeEnv, dynamicRequire } from './node.js';
import { getGlobalObject } from './worldwide.js';
// eslint-disable-next-line deprecation/deprecation
const WINDOW = getGlobalObject();
/**
* An object that can return the current timestamp in seconds since the UNIX epoch.
*/
/**
* A TimestampSource implementation for environments that do not support the Performance Web API natively.
*
* Note that this TimestampSource does not use a monotonic clock. A call to `nowSeconds` may return a timestamp earlier
* than a previously returned value. We do not try to emulate a monotonic behavior in order to facilitate debugging. It
* is more obvious to explain "why does my span have negative duration" than "why my spans have zero duration".
*/
const dateTimestampSource = {
nowSeconds: () => Date.now() / 1000,
};
/**
* A partial definition of the [Performance Web API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Performance}
* for accessing a high-resolution monotonic clock.
*/
/**
* Returns a wrapper around the native Performance API browser implementation, or undefined for browsers that do not
* support the API.
*
* Wrapping the native API works around differences in behavior from different browsers.
*/
function getBrowserPerformance() {
const { performance } = WINDOW;
if (!performance || !performance.now) {
return undefined;
}
// Replace performance.timeOrigin with our own timeOrigin based on Date.now().
//
// This is a partial workaround for browsers reporting performance.timeOrigin such that performance.timeOrigin +
// performance.now() gives a date arbitrarily in the past.
//
// Additionally, computing timeOrigin in this way fills the gap for browsers where performance.timeOrigin is
// undefined.
//
// The assumption that performance.timeOrigin + performance.now() ~= Date.now() is flawed, but we depend on it to
// interact with data coming out of performance entries.
//
// Note that despite recommendations against it in the spec, browsers implement the Performance API with a clock that
// might stop when the computer is asleep (and perhaps under other circumstances). Such behavior causes
// performance.timeOrigin + performance.now() to have an arbitrary skew over Date.now(). In laptop computers, we have
// observed skews that can be as long as days, weeks or months.
//
// See https://github.com/getsentry/sentry-javascript/issues/2590.
//
// BUG: despite our best intentions, this workaround has its limitations. It mostly addresses timings of pageload
// transactions, but ignores the skew built up over time that can aversely affect timestamps of navigation
// transactions of long-lived web pages.
const timeOrigin = Date.now() - performance.now();
return {
now: () => performance.now(),
timeOrigin,
};
}
/**
* Returns the native Performance API implementation from Node.js. Returns undefined in old Node.js versions that don't
* implement the API.
*/
function getNodePerformance() {
try {
const perfHooks = dynamicRequire(module, 'perf_hooks') ;
return perfHooks.performance;
} catch (_) {
return undefined;
}
}
/**
* The Performance API implementation for the current platform, if available.
*/
const platformPerformance = isNodeEnv() ? getNodePerformance() : getBrowserPerformance();
const timestampSource =
platformPerformance === undefined
? dateTimestampSource
: {
nowSeconds: () => (platformPerformance.timeOrigin + platformPerformance.now()) / 1000,
};
/**
* Returns a timestamp in seconds since the UNIX epoch using the Date API.
*/
const dateTimestampInSeconds = dateTimestampSource.nowSeconds.bind(dateTimestampSource);
/**
* Returns a timestamp in seconds since the UNIX epoch using either the Performance or Date APIs, depending on the
* availability of the Performance API.
*
* See `usingPerformanceAPI` to test whether the Performance API is used.
*
* BUG: Note that because of how browsers implement the Performance API, the clock might stop when the computer is
* asleep. This creates a skew between `dateTimestampInSeconds` and `timestampInSeconds`. The
* skew can grow to arbitrary amounts like days, weeks or months.
* See https://github.com/getsentry/sentry-javascript/issues/2590.
*/
const timestampInSeconds = timestampSource.nowSeconds.bind(timestampSource);
/**
* Re-exported with an old name for backwards-compatibility.
* TODO (v8): Remove this
*
* @deprecated Use `timestampInSeconds` instead.
*/
const timestampWithMs = timestampInSeconds;
/**
* A boolean that is true when timestampInSeconds uses the Performance API to produce monotonic timestamps.
*/
const usingPerformanceAPI = platformPerformance !== undefined;
/**
* Internal helper to store what is the source of browserPerformanceTimeOrigin below. For debugging only.
*/
let _browserPerformanceTimeOriginMode;
/**
* The number of milliseconds since the UNIX epoch. This value is only usable in a browser, and only when the
* performance API is available.
*/
const browserPerformanceTimeOrigin = (() => {
// Unfortunately browsers may report an inaccurate time origin data, through either performance.timeOrigin or
// performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin
// data as reliable if they are within a reasonable threshold of the current time.
const { performance } = WINDOW;
if (!performance || !performance.now) {
_browserPerformanceTimeOriginMode = 'none';
return undefined;
}
const threshold = 3600 * 1000;
const performanceNow = performance.now();
const dateNow = Date.now();
// if timeOrigin isn't available set delta to threshold so it isn't used
const timeOriginDelta = performance.timeOrigin
? Math.abs(performance.timeOrigin + performanceNow - dateNow)
: threshold;
const timeOriginIsReliable = timeOriginDelta < threshold;
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
// Also as of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always
// a valid fallback. In the absence of an initial time provided by the browser, fallback to the current time from the
// Date API.
// eslint-disable-next-line deprecation/deprecation
const navigationStart = performance.timing && performance.timing.navigationStart;
const hasNavigationStart = typeof navigationStart === 'number';
// if navigationStart isn't available set delta to threshold so it isn't used
const navigationStartDelta = hasNavigationStart ? Math.abs(navigationStart + performanceNow - dateNow) : threshold;
const navigationStartIsReliable = navigationStartDelta < threshold;
if (timeOriginIsReliable || navigationStartIsReliable) {
// Use the more reliable time origin
if (timeOriginDelta <= navigationStartDelta) {
_browserPerformanceTimeOriginMode = 'timeOrigin';
return performance.timeOrigin;
} else {
_browserPerformanceTimeOriginMode = 'navigationStart';
return navigationStart;
}
}
// Either both timeOrigin and navigationStart are skewed or neither is available, fallback to Date.
_browserPerformanceTimeOriginMode = 'dateNow';
return dateNow;
})();
export { _browserPerformanceTimeOriginMode, browserPerformanceTimeOrigin, dateTimestampInSeconds, timestampInSeconds, timestampWithMs, usingPerformanceAPI };
//# sourceMappingURL=time.js.map

View File

@@ -0,0 +1,39 @@
const TRACEPARENT_REGEXP = new RegExp(
'^[ \\t]*' + // whitespace
'([0-9a-f]{32})?' + // trace_id
'-?([0-9a-f]{16})?' + // span_id
'-?([01])?' + // sampled
'[ \\t]*$', // whitespace
);
/**
* Extract transaction context data from a `sentry-trace` header.
*
* @param traceparent Traceparent string
*
* @returns Object containing data from the header, or undefined if traceparent string is malformed
*/
function extractTraceparentData(traceparent) {
const matches = traceparent.match(TRACEPARENT_REGEXP);
if (!traceparent || !matches) {
// empty string or no matches is invalid traceparent data
return undefined;
}
let parentSampled;
if (matches[3] === '1') {
parentSampled = true;
} else if (matches[3] === '0') {
parentSampled = false;
}
return {
traceId: matches[1],
parentSampled,
parentSpanId: matches[2],
};
}
export { TRACEPARENT_REGEXP, extractTraceparentData };
//# sourceMappingURL=tracing.js.map

72
shared/logger/node_modules/@sentry/utils/esm/url.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
/**
* Parses string form of URL into an object
* // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
* // intentionally using regex and not <a/> href parsing trick because React Native and other
* // environments where DOM might not be available
* @returns parsed URL object
*/
function parseUrl(url) {
if (!url) {
return {};
}
const match = url.match(/^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
if (!match) {
return {};
}
// coerce to undefined values to empty string so we don't get 'undefined'
const query = match[6] || '';
const fragment = match[8] || '';
return {
host: match[4],
path: match[5],
protocol: match[2],
search: query,
hash: fragment,
relative: match[5] + query + fragment, // everything minus origin
};
}
/**
* Strip the query string and fragment off of a given URL or path (if present)
*
* @param urlPath Full URL or path, including possible query string and/or fragment
* @returns URL or path without query string or fragment
*/
function stripUrlQueryAndFragment(urlPath) {
// eslint-disable-next-line no-useless-escape
return urlPath.split(/[\?#]/, 1)[0];
}
/**
* Returns number of URL segments of a passed string URL.
*/
function getNumberOfUrlSegments(url) {
// split at '/' or at '\/' to split regex urls correctly
return url.split(/\\?\//).filter(s => s.length > 0 && s !== ',').length;
}
/**
* Takes a URL object and returns a sanitized string which is safe to use as span description
* see: https://develop.sentry.dev/sdk/data-handling/#structuring-data
*/
function getSanitizedUrlString(url) {
const { protocol, host, path } = url;
const filteredHost =
(host &&
host
// Always filter out authority
.replace(/^.*@/, '[filtered]:[filtered]@')
// Don't show standard :80 (http) and :443 (https) ports to reduce the noise
.replace(':80', '')
.replace(':443', '')) ||
'';
return `${protocol ? `${protocol}://` : ''}${filteredHost}${path}`;
}
export { getNumberOfUrlSegments, getSanitizedUrlString, parseUrl, stripUrlQueryAndFragment };
//# sourceMappingURL=url.js.map

View File

@@ -0,0 +1,29 @@
import { getGlobalObject } from '../worldwide.js';
// Based on https://github.com/angular/angular.js/pull/13945/files
// eslint-disable-next-line deprecation/deprecation
const WINDOW = getGlobalObject();
/**
* Tells whether current environment supports History API
* {@link supportsHistory}.
*
* @returns Answer to the given question.
*/
function supportsHistory() {
// NOTE: in Chrome App environment, touching history.pushState, *even inside
// a try/catch block*, will cause Chrome to output an error to console.error
// borrowed from: https://github.com/angular/angular.js/pull/13945/files
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const chrome = (WINDOW ).chrome;
const isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
const hasHistoryApi = 'history' in WINDOW && !!WINDOW.history.pushState && !!WINDOW.history.replaceState;
return !isChromePackagedApp && hasHistoryApi;
}
export { supportsHistory };
//# sourceMappingURL=supportsHistory.js.map

View File

@@ -0,0 +1,70 @@
/** Internal global with common properties and Sentry extensions */
// The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification
// https://github.com/zloirock/core-js/blob/1b944df55282cdc99c90db5f49eb0b6eda2cc0a3/packages/core-js/internals/global.js
// core-js has the following licence:
//
// Copyright (c) 2014-2022 Denis Pushkarev
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/** Returns 'obj' if it's the global object, otherwise returns undefined */
function isGlobalObj(obj) {
return obj && obj.Math == Math ? obj : undefined;
}
/** Get's the global object for the current JavaScript runtime */
const GLOBAL_OBJ =
(typeof globalThis == 'object' && isGlobalObj(globalThis)) ||
// eslint-disable-next-line no-restricted-globals
(typeof window == 'object' && isGlobalObj(window)) ||
(typeof self == 'object' && isGlobalObj(self)) ||
(typeof global == 'object' && isGlobalObj(global)) ||
(function () {
return this;
})() ||
{};
/**
* @deprecated Use GLOBAL_OBJ instead or WINDOW from @sentry/browser. This will be removed in v8
*/
function getGlobalObject() {
return GLOBAL_OBJ ;
}
/**
* Returns a global singleton contained in the global `__SENTRY__` object.
*
* If the singleton doesn't already exist in `__SENTRY__`, it will be created using the given factory
* function and added to the `__SENTRY__` object.
*
* @param name name of the global singleton on __SENTRY__
* @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__`
* @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value
* @returns the singleton
*/
function getGlobalSingleton(name, creator, obj) {
const gbl = (obj || GLOBAL_OBJ) ;
const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {});
const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator());
return singleton;
}
export { GLOBAL_OBJ, getGlobalObject, getGlobalSingleton };
//# sourceMappingURL=worldwide.js.map