From 00d0f2f2b09335e9c6f44754d92d8b497e210993 Mon Sep 17 00:00:00 2001 From: Kevin Green Date: Wed, 22 Apr 2026 13:58:58 -0400 Subject: [PATCH 1/2] Add support for nodejs 24 stacktraces --- js/src/stackutil.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/stackutil.ts b/js/src/stackutil.ts index 71d9ae9ba..712e8dbf0 100644 --- a/js/src/stackutil.ts +++ b/js/src/stackutil.ts @@ -13,16 +13,16 @@ function getStackTrace(): StackTraceEntry[] { } const traceLines = trace.split("\n"); const out: StackTraceEntry[] = []; - const stackFrameRegex = /at(.*)\((.*):(\d+):(\d+)\)/; + const stackFrameRegex = /^\s*at\s+(?:(.+?)\s+\()?(.*):(\d+):(\d+)\)?\s*$/; for (const traceLine of traceLines.slice(1)) { const matches = traceLine.match(stackFrameRegex); if (matches === null || matches.length !== 5) { continue; } const entry: StackTraceEntry = { - functionName: matches[1].trim(), + functionName: matches[1]?.trim() ?? "", fileName: matches[2], - lineNo: parseInt(matches[3]), + lineNo: parseInt(matches[3], 10), }; if (!isNaN(entry.lineNo)) { out.push(entry); From 425d9645213be008e9919770067f4d732b70ee54 Mon Sep 17 00:00:00 2001 From: Kevin Green Date: Wed, 22 Apr 2026 14:45:34 -0400 Subject: [PATCH 2/2] Add tests --- js/src/stackutil.test.ts | 37 ++++++++++++++++++++++++++++ js/src/stackutil.ts | 2 +- js/tests/helpers/stackutil-caller.ts | 14 +++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 js/src/stackutil.test.ts create mode 100644 js/tests/helpers/stackutil-caller.ts diff --git a/js/src/stackutil.test.ts b/js/src/stackutil.test.ts new file mode 100644 index 000000000..d492234b4 --- /dev/null +++ b/js/src/stackutil.test.ts @@ -0,0 +1,37 @@ +import { expect, test } from "vitest"; +import { + callerFromAnonymousFunction, + callerFromModuleInit, + callerFromNamedFunction, +} from "../tests/helpers/stackutil-caller"; + +function normalizeFileName(fileName: string) { + return fileName.replace(/^file:\/\//, ""); +} + +test("getCallerLocation works with a real top-level caller frame", () => { + expect(callerFromModuleInit).toBeDefined(); + expect(callerFromModuleInit!.caller_lineno).toBeGreaterThan(1); + expect(normalizeFileName(callerFromModuleInit!.caller_filename)).toMatch( + /tests[\\/]helpers[\\/]stackutil-caller\.(ts|js)$/, + ); +}); + +test("getCallerLocation works with a real named function caller frame", () => { + const location = callerFromNamedFunction(); + expect(location).toBeDefined(); + expect(location!.caller_lineno).toBeGreaterThan(1); + expect(normalizeFileName(location!.caller_filename)).toMatch( + /tests[\\/]helpers[\\/]stackutil-caller\.(ts|js)$/, + ); + expect(location!.caller_functionname).toContain("callerFromNamedFunction"); +}); + +test("getCallerLocation works with a real anonymous function caller frame", () => { + const location = callerFromAnonymousFunction(); + expect(location).toBeDefined(); + expect(location!.caller_lineno).toBeGreaterThan(1); + expect(normalizeFileName(location!.caller_filename)).toMatch( + /tests[\\/]helpers[\\/]stackutil-caller\.(ts|js)$/, + ); +}); diff --git a/js/src/stackutil.ts b/js/src/stackutil.ts index 712e8dbf0..45cbda0c6 100644 --- a/js/src/stackutil.ts +++ b/js/src/stackutil.ts @@ -22,7 +22,7 @@ function getStackTrace(): StackTraceEntry[] { const entry: StackTraceEntry = { functionName: matches[1]?.trim() ?? "", fileName: matches[2], - lineNo: parseInt(matches[3], 10), + lineNo: parseInt(matches[3]), }; if (!isNaN(entry.lineNo)) { out.push(entry); diff --git a/js/tests/helpers/stackutil-caller.ts b/js/tests/helpers/stackutil-caller.ts new file mode 100644 index 000000000..e0d3a40da --- /dev/null +++ b/js/tests/helpers/stackutil-caller.ts @@ -0,0 +1,14 @@ +import { configureNode } from "../../src/node/config"; +import { getCallerLocation } from "../../src/stackutil"; + +configureNode(); + +export const callerFromModuleInit = getCallerLocation(); + +export function callerFromNamedFunction() { + return getCallerLocation(); +} + +export function callerFromAnonymousFunction() { + return (() => getCallerLocation())(); +}