1*26ad19fcSEvan Bacon/** 2*26ad19fcSEvan Bacon * Copyright (c) 650 Industries. 3*26ad19fcSEvan Bacon * Copyright (c) Meta Platforms, Inc. and affiliates. 4*26ad19fcSEvan Bacon * 5*26ad19fcSEvan Bacon * This source code is licensed under the MIT license found in the 6*26ad19fcSEvan Bacon * LICENSE file in the root directory of this source tree. 7*26ad19fcSEvan Bacon */ 8*26ad19fcSEvan Bacon 9*26ad19fcSEvan Baconimport { StackFrame as UpstreamStackFrame } from 'stacktrace-parser'; 10*26ad19fcSEvan Bacon 11*26ad19fcSEvan Baconimport symbolicateStackTrace from '../modules/symbolicateStackTrace'; 12*26ad19fcSEvan Bacon 13*26ad19fcSEvan Bacontype SymbolicatedStackTrace = any; 14*26ad19fcSEvan Bacon 15*26ad19fcSEvan Bacontype StackFrame = UpstreamStackFrame & { collapse?: boolean }; 16*26ad19fcSEvan Bacon 17*26ad19fcSEvan Baconexport type Stack = StackFrame[]; 18*26ad19fcSEvan Bacon 19*26ad19fcSEvan Baconconst cache: Map<Stack, Promise<SymbolicatedStackTrace>> = new Map(); 20*26ad19fcSEvan Bacon 21*26ad19fcSEvan Bacon/** 22*26ad19fcSEvan Bacon * Sanitize because sometimes, `symbolicateStackTrace` gives us invalid values. 23*26ad19fcSEvan Bacon */ 24*26ad19fcSEvan Baconconst sanitize = ({ 25*26ad19fcSEvan Bacon stack: maybeStack, 26*26ad19fcSEvan Bacon codeFrame, 27*26ad19fcSEvan Bacon}: SymbolicatedStackTrace): SymbolicatedStackTrace => { 28*26ad19fcSEvan Bacon if (!Array.isArray(maybeStack)) { 29*26ad19fcSEvan Bacon throw new Error('Expected stack to be an array.'); 30*26ad19fcSEvan Bacon } 31*26ad19fcSEvan Bacon const stack: StackFrame[] = []; 32*26ad19fcSEvan Bacon for (const maybeFrame of maybeStack) { 33*26ad19fcSEvan Bacon let collapse = false; 34*26ad19fcSEvan Bacon if ('collapse' in maybeFrame) { 35*26ad19fcSEvan Bacon if (typeof maybeFrame.collapse !== 'boolean') { 36*26ad19fcSEvan Bacon throw new Error('Expected stack frame `collapse` to be a boolean.'); 37*26ad19fcSEvan Bacon } 38*26ad19fcSEvan Bacon collapse = maybeFrame.collapse; 39*26ad19fcSEvan Bacon } 40*26ad19fcSEvan Bacon stack.push({ 41*26ad19fcSEvan Bacon arguments: [], 42*26ad19fcSEvan Bacon column: maybeFrame.column, 43*26ad19fcSEvan Bacon file: maybeFrame.file, 44*26ad19fcSEvan Bacon lineNumber: maybeFrame.lineNumber, 45*26ad19fcSEvan Bacon methodName: maybeFrame.methodName, 46*26ad19fcSEvan Bacon collapse, 47*26ad19fcSEvan Bacon }); 48*26ad19fcSEvan Bacon } 49*26ad19fcSEvan Bacon return { stack, codeFrame }; 50*26ad19fcSEvan Bacon}; 51*26ad19fcSEvan Bacon 52*26ad19fcSEvan Baconexport function deleteStack(stack: Stack): void { 53*26ad19fcSEvan Bacon cache.delete(stack); 54*26ad19fcSEvan Bacon} 55*26ad19fcSEvan Bacon 56*26ad19fcSEvan Baconexport function symbolicate(stack: Stack): Promise<SymbolicatedStackTrace> { 57*26ad19fcSEvan Bacon let promise = cache.get(stack); 58*26ad19fcSEvan Bacon if (promise == null) { 59*26ad19fcSEvan Bacon promise = symbolicateStackTrace(stack).then(sanitize); 60*26ad19fcSEvan Bacon cache.set(stack, promise); 61*26ad19fcSEvan Bacon } 62*26ad19fcSEvan Bacon 63*26ad19fcSEvan Bacon return promise; 64*26ad19fcSEvan Bacon} 65