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