misc.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const object = require('./object.js');
  3. const string = require('./string.js');
  4. const worldwide = require('./worldwide.js');
  5. /**
  6. * UUID4 generator
  7. *
  8. * @returns string Generated UUID4.
  9. */
  10. function uuid4() {
  11. const gbl = worldwide.GLOBAL_OBJ ;
  12. const crypto = gbl.crypto || gbl.msCrypto;
  13. let getRandomByte = () => Math.random() * 16;
  14. try {
  15. if (crypto && crypto.randomUUID) {
  16. return crypto.randomUUID().replace(/-/g, '');
  17. }
  18. if (crypto && crypto.getRandomValues) {
  19. getRandomByte = () => {
  20. // crypto.getRandomValues might return undefined instead of the typed array
  21. // in old Chromium versions (e.g. 23.0.1235.0 (151422))
  22. // However, `typedArray` is still filled in-place.
  23. // @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#typedarray
  24. const typedArray = new Uint8Array(1);
  25. crypto.getRandomValues(typedArray);
  26. return typedArray[0];
  27. };
  28. }
  29. } catch (_) {
  30. // some runtimes can crash invoking crypto
  31. // https://github.com/getsentry/sentry-javascript/issues/8935
  32. }
  33. // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
  34. // Concatenating the following numbers as strings results in '10000000100040008000100000000000'
  35. return (([1e7] ) + 1e3 + 4e3 + 8e3 + 1e11).replace(/[018]/g, c =>
  36. // eslint-disable-next-line no-bitwise
  37. ((c ) ^ ((getRandomByte() & 15) >> ((c ) / 4))).toString(16),
  38. );
  39. }
  40. function getFirstException(event) {
  41. return event.exception && event.exception.values ? event.exception.values[0] : undefined;
  42. }
  43. /**
  44. * Extracts either message or type+value from an event that can be used for user-facing logs
  45. * @returns event's description
  46. */
  47. function getEventDescription(event) {
  48. const { message, event_id: eventId } = event;
  49. if (message) {
  50. return message;
  51. }
  52. const firstException = getFirstException(event);
  53. if (firstException) {
  54. if (firstException.type && firstException.value) {
  55. return `${firstException.type}: ${firstException.value}`;
  56. }
  57. return firstException.type || firstException.value || eventId || '<unknown>';
  58. }
  59. return eventId || '<unknown>';
  60. }
  61. /**
  62. * Adds exception values, type and value to an synthetic Exception.
  63. * @param event The event to modify.
  64. * @param value Value of the exception.
  65. * @param type Type of the exception.
  66. * @hidden
  67. */
  68. function addExceptionTypeValue(event, value, type) {
  69. const exception = (event.exception = event.exception || {});
  70. const values = (exception.values = exception.values || []);
  71. const firstException = (values[0] = values[0] || {});
  72. if (!firstException.value) {
  73. firstException.value = value || '';
  74. }
  75. if (!firstException.type) {
  76. firstException.type = type || 'Error';
  77. }
  78. }
  79. /**
  80. * Adds exception mechanism data to a given event. Uses defaults if the second parameter is not passed.
  81. *
  82. * @param event The event to modify.
  83. * @param newMechanism Mechanism data to add to the event.
  84. * @hidden
  85. */
  86. function addExceptionMechanism(event, newMechanism) {
  87. const firstException = getFirstException(event);
  88. if (!firstException) {
  89. return;
  90. }
  91. const defaultMechanism = { type: 'generic', handled: true };
  92. const currentMechanism = firstException.mechanism;
  93. firstException.mechanism = { ...defaultMechanism, ...currentMechanism, ...newMechanism };
  94. if (newMechanism && 'data' in newMechanism) {
  95. const mergedData = { ...(currentMechanism && currentMechanism.data), ...newMechanism.data };
  96. firstException.mechanism.data = mergedData;
  97. }
  98. }
  99. // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
  100. const SEMVER_REGEXP =
  101. /^(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-]+)*))?$/;
  102. /**
  103. * Represents Semantic Versioning object
  104. */
  105. /**
  106. * Parses input into a SemVer interface
  107. * @param input string representation of a semver version
  108. */
  109. function parseSemver(input) {
  110. const match = input.match(SEMVER_REGEXP) || [];
  111. const major = parseInt(match[1], 10);
  112. const minor = parseInt(match[2], 10);
  113. const patch = parseInt(match[3], 10);
  114. return {
  115. buildmetadata: match[5],
  116. major: isNaN(major) ? undefined : major,
  117. minor: isNaN(minor) ? undefined : minor,
  118. patch: isNaN(patch) ? undefined : patch,
  119. prerelease: match[4],
  120. };
  121. }
  122. /**
  123. * This function adds context (pre/post/line) lines to the provided frame
  124. *
  125. * @param lines string[] containing all lines
  126. * @param frame StackFrame that will be mutated
  127. * @param linesOfContext number of context lines we want to add pre/post
  128. */
  129. function addContextToFrame(lines, frame, linesOfContext = 5) {
  130. // When there is no line number in the frame, attaching context is nonsensical and will even break grouping
  131. if (frame.lineno === undefined) {
  132. return;
  133. }
  134. const maxLines = lines.length;
  135. const sourceLine = Math.max(Math.min(maxLines - 1, frame.lineno - 1), 0);
  136. frame.pre_context = lines
  137. .slice(Math.max(0, sourceLine - linesOfContext), sourceLine)
  138. .map((line) => string.snipLine(line, 0));
  139. frame.context_line = string.snipLine(lines[Math.min(maxLines - 1, sourceLine)], frame.colno || 0);
  140. frame.post_context = lines
  141. .slice(Math.min(sourceLine + 1, maxLines), sourceLine + 1 + linesOfContext)
  142. .map((line) => string.snipLine(line, 0));
  143. }
  144. /**
  145. * Checks whether or not we've already captured the given exception (note: not an identical exception - the very object
  146. * in question), and marks it captured if not.
  147. *
  148. * This is useful because it's possible for an error to get captured by more than one mechanism. After we intercept and
  149. * record an error, we rethrow it (assuming we've intercepted it before it's reached the top-level global handlers), so
  150. * that we don't interfere with whatever effects the error might have had were the SDK not there. At that point, because
  151. * the error has been rethrown, it's possible for it to bubble up to some other code we've instrumented. If it's not
  152. * caught after that, it will bubble all the way up to the global handlers (which of course we also instrument). This
  153. * function helps us ensure that even if we encounter the same error more than once, we only record it the first time we
  154. * see it.
  155. *
  156. * Note: It will ignore primitives (always return `false` and not mark them as seen), as properties can't be set on
  157. * them. {@link: Object.objectify} can be used on exceptions to convert any that are primitives into their equivalent
  158. * object wrapper forms so that this check will always work. However, because we need to flag the exact object which
  159. * will get rethrown, and because that rethrowing happens outside of the event processing pipeline, the objectification
  160. * must be done before the exception captured.
  161. *
  162. * @param A thrown exception to check or flag as having been seen
  163. * @returns `true` if the exception has already been captured, `false` if not (with the side effect of marking it seen)
  164. */
  165. function checkOrSetAlreadyCaught(exception) {
  166. // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
  167. if (exception && (exception ).__sentry_captured__) {
  168. return true;
  169. }
  170. try {
  171. // set it this way rather than by assignment so that it's not ennumerable and therefore isn't recorded by the
  172. // `ExtraErrorData` integration
  173. object.addNonEnumerableProperty(exception , '__sentry_captured__', true);
  174. } catch (err) {
  175. // `exception` is a primitive, so we can't mark it seen
  176. }
  177. return false;
  178. }
  179. /**
  180. * Checks whether the given input is already an array, and if it isn't, wraps it in one.
  181. *
  182. * @param maybeArray Input to turn into an array, if necessary
  183. * @returns The input, if already an array, or an array with the input as the only element, if not
  184. */
  185. function arrayify(maybeArray) {
  186. return Array.isArray(maybeArray) ? maybeArray : [maybeArray];
  187. }
  188. exports.addContextToFrame = addContextToFrame;
  189. exports.addExceptionMechanism = addExceptionMechanism;
  190. exports.addExceptionTypeValue = addExceptionTypeValue;
  191. exports.arrayify = arrayify;
  192. exports.checkOrSetAlreadyCaught = checkOrSetAlreadyCaught;
  193. exports.getEventDescription = getEventDescription;
  194. exports.parseSemver = parseSemver;
  195. exports.uuid4 = uuid4;
  196. //# sourceMappingURL=misc.js.map