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