aboutsummaryrefslogtreecommitdiff
path: root/examples/hello-next/bun-framework-next/bun-runtime-error.ts
diff options
context:
space:
mode:
Diffstat (limited to 'examples/hello-next/bun-framework-next/bun-runtime-error.ts')
-rw-r--r--examples/hello-next/bun-framework-next/bun-runtime-error.ts163
1 files changed, 163 insertions, 0 deletions
diff --git a/examples/hello-next/bun-framework-next/bun-runtime-error.ts b/examples/hello-next/bun-framework-next/bun-runtime-error.ts
new file mode 100644
index 000000000..331040b36
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/bun-runtime-error.ts
@@ -0,0 +1,163 @@
+// Based on https://github.com/stacktracejs/error-stack-parser/blob/master/error-stack-parser.js
+
+import type {
+ StackFrame as StackFrameType,
+ StackFramePosition,
+ StackFrameScope,
+} from "../../../src/api/schema";
+
+export class StackFrame implements StackFrameType {
+ function_name: string;
+ file: string;
+ position: StackFramePosition;
+ scope: StackFrameScope;
+ lineText: string = "";
+ constructor({
+ functionName: function_name = "",
+ fileName: file = "",
+ lineNumber: line = -1,
+ columnNumber: column = -1,
+ source = "",
+ }) {
+ this.function_name = function_name;
+ this.file = file;
+ if (source) this.lineText = source;
+ this.scope = 3;
+ this.position = {
+ line: line,
+ source_offset: -1,
+ line_start: -1,
+ line_stop: -1,
+ column_start: column,
+ column_stop: -1,
+ expression_start: -1,
+ expression_stop: -1,
+ };
+ }
+}
+
+const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
+const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
+const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
+
+export default class RuntimeError {
+ original: Error;
+ stack: StackFrame[];
+
+ static from(error: Error) {
+ const runtime = new RuntimeError();
+ runtime.original = error;
+ runtime.stack = this.parseStack(error);
+ return RuntimeError;
+ }
+
+ /**
+ * Given an Error object, extract the most information from it.
+ *
+ * @param {Error} error object
+ * @return {Array} of StackFrames
+ */
+ static parseStack(error) {
+ if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) {
+ return this.parseV8OrIE(error);
+ } else if (error.stack) {
+ return this.parseFFOrSafari(error);
+ } else {
+ return [];
+ }
+ }
+
+ // Separate line and column numbers from a string of the form: (URI:Line:Column)
+ static extractLocation(urlLike) {
+ // Fail-fast but return locations like "(native)"
+ if (urlLike.indexOf(":") === -1) {
+ return [urlLike];
+ }
+
+ var regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
+ var parts = regExp.exec(urlLike.replace(/[()]/g, ""));
+ return [parts[1], parts[2] || undefined, parts[3] || undefined];
+ }
+
+ static parseV8OrIE(error) {
+ var filtered = error.stack.split("\n").filter(function (line) {
+ return !!line.match(CHROME_IE_STACK_REGEXP);
+ }, this);
+
+ return filtered.map(function (line) {
+ if (line.indexOf("(eval ") > -1) {
+ // Throw away eval information until we implement stacktrace.js/stackframe#8
+ line = line
+ .replace(/eval code/g, "eval")
+ .replace(/(\(eval at [^()]*)|(\),.*$)/g, "");
+ }
+ var sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(");
+
+ // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
+ // case it has spaces in it, as the string is split on \s+ later on
+ var location = sanitizedLine.match(/ (\((.+):(\d+):(\d+)\)$)/);
+
+ // remove the parenthesized location from the line, if it was matched
+ sanitizedLine = location
+ ? sanitizedLine.replace(location[0], "")
+ : sanitizedLine;
+
+ var tokens = sanitizedLine.split(/\s+/).slice(1);
+ // if a location was matched, pass it to extractLocation() otherwise pop the last token
+ var locationParts = this.extractLocation(
+ location ? location[1] : tokens.pop()
+ );
+ var functionName = tokens.join(" ") || undefined;
+ var fileName =
+ ["eval", "<anonymous>"].indexOf(locationParts[0]) > -1
+ ? undefined
+ : locationParts[0];
+
+ return new StackFrame({
+ functionName: functionName,
+ fileName: fileName,
+ lineNumber: locationParts[1],
+ columnNumber: locationParts[2],
+ source: line,
+ });
+ }, this);
+ }
+
+ static parseFFOrSafari(error) {
+ var filtered = error.stack.split("\n").filter(function (line) {
+ return !line.match(SAFARI_NATIVE_CODE_REGEXP);
+ }, this);
+
+ return filtered.map(function (line) {
+ // Throw away eval information until we implement stacktrace.js/stackframe#8
+ if (line.indexOf(" > eval") > -1) {
+ line = line.replace(
+ / line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
+ ":$1"
+ );
+ }
+
+ if (line.indexOf("@") === -1 && line.indexOf(":") === -1) {
+ // Safari eval frames only have function names and nothing else
+ return new StackFrame({
+ functionName: line,
+ });
+ } else {
+ var functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
+ var matches = line.match(functionNameRegex);
+ var functionName = matches && matches[1] ? matches[1] : undefined;
+ var locationParts = this.extractLocation(
+ line.replace(functionNameRegex, "")
+ );
+
+ return new StackFrame({
+ functionName: functionName,
+ fileName: locationParts[0],
+ lineNumber: locationParts[1],
+ columnNumber: locationParts[2],
+ source: line,
+ });
+ }
+ }, this);
+ }
+}