From cb290ab6fcb337ee5f08e63fbfebc0bb82936ea7 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Thu, 26 Feb 2026 19:43:22 +0400 Subject: [PATCH 1/4] feat(typeorm): add project root detection via tsconfig.json In deployed environments, process.cwd() may not be the project root (e.g., when the start script does `cd .amplify-hosting/compute/default/ && node app.js`). This adds findProjectRoot() which walks up from cwd looking for tsconfig.json to find the real project root. Co-Authored-By: Claude Opus 4.6 --- .../packages/sqlcommenter-typeorm/src/path.ts | 34 +++++++++++++++++++ .../sqlcommenter-typeorm/test/path.spec.ts | 25 ++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts create mode 100644 nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts new file mode 100644 index 00000000..385594e9 --- /dev/null +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts @@ -0,0 +1,34 @@ +import { existsSync } from "node:fs"; +import { dirname, join } from "node:path"; + +let cachedProjectRoot: string | undefined; + +/** + * Finds the project root by walking up from `process.cwd()` looking for `tsconfig.json`. + * + * In deployed environments, `process.cwd()` may not be the project root + * (e.g., `cd .amplify-hosting/compute/default/ && node app.js`). + * Walking up to find `tsconfig.json` — which is never copied to deployment directories — + * gives us the real project root. + * + * The result is cached since the project root doesn't change during a process's lifetime. + */ +export function findProjectRoot(): string { + if (cachedProjectRoot !== undefined) { + return cachedProjectRoot; + } + let projectRoot = process.cwd(); + for (let d = projectRoot; d !== dirname(d); d = dirname(d)) { + if (existsSync(join(d, "tsconfig.json"))) { + projectRoot = d; + break; + } + } + cachedProjectRoot = projectRoot; + return projectRoot; +} + +/** @internal Exposed for testing only */ +export function _resetProjectRootCache() { + cachedProjectRoot = undefined; +} diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts new file mode 100644 index 00000000..c1fde537 --- /dev/null +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts @@ -0,0 +1,25 @@ +import { test } from "node:test"; +import assert from "node:assert"; +import { existsSync } from "node:fs"; +import { join } from "node:path"; +import { findProjectRoot, _resetProjectRootCache } from "../src/path.js"; + +test("findProjectRoot", async (t) => { + t.afterEach(() => { + _resetProjectRootCache(); + }); + + await t.test("returns a directory containing tsconfig.json", () => { + const root = findProjectRoot(); + assert.ok( + existsSync(join(root, "tsconfig.json")), + `Expected ${root} to contain tsconfig.json`, + ); + }); + + await t.test("caches the result across calls", () => { + const first = findProjectRoot(); + const second = findProjectRoot(); + assert.strictEqual(first, second); + }); +}); From 8cfa280964f9f3266f97adb9dd97ef484c14f321 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Thu, 26 Feb 2026 19:44:55 +0400 Subject: [PATCH 2/4] feat(typeorm): normalize source-map-resolved file paths When compiled JS is relocated (e.g., postbuild copies dist/ to a deployment directory), source map relative paths resolve to wrong absolute paths. This extracts the src/-relative portion from the stack trace path and reconstructs it using the real project root found via tsconfig.json. Co-Authored-By: Claude Opus 4.6 --- .../sqlcommenter-typeorm/src/index.ts | 3 +- .../packages/sqlcommenter-typeorm/src/path.ts | 29 ++++++++++++ .../sqlcommenter-typeorm/test/path.spec.ts | 47 ++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts index 0824b9e8..4ed1186c 100644 --- a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts @@ -1,6 +1,7 @@ import { alreadyHasTrailingComment, serializeTags, type Tag } from "./sqlcommenter.js"; import { als } from "./als.js"; import { pushW3CTraceContext } from "./tracing.js"; +import { resolveFilePath } from "./path.js"; const LIBRARY_NAME = "sqlcommenter-typeorm"; @@ -47,7 +48,7 @@ export function traceCaller(): string | undefined { } const match = methodCaller.match(filepathRegex); if (match) { - return match[1]; + return resolveFilePath(match[1]); } } diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts index 385594e9..2a2c2460 100644 --- a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts @@ -28,6 +28,35 @@ export function findProjectRoot(): string { return projectRoot; } +/** + * Resolves a file path from a stack trace to a correct absolute path. + * + * When compiled JS is relocated (e.g., postbuild copies `dist/` to a deployment directory), + * source-map-resolved paths become incorrect because the relative `sources` entries in + * `.map` files resolve against the new location instead of the original project. + * + * This extracts the `src/`-relative portion and reconstructs the path using the real + * project root. + * + * @param raw - A stack trace entry like "/wrong/path/src/routes/admin.ts:12:15" + * @returns The resolved path like "/project/root/src/routes/admin.ts:12:15" + */ +export function resolveFilePath(raw: string): string { + // Split off :line:column suffix + const match = raw.match(/^(.*?):(\d+:\d+)$/); + if (!match) { + return raw; + } + const [, filePath, lineCol] = match; + const srcIdx = filePath.indexOf("src/"); + if (srcIdx < 0) { + return raw; + } + const projectRoot = findProjectRoot(); + const relativePath = filePath.substring(srcIdx); + return `${projectRoot}/${relativePath}:${lineCol}`; +} + /** @internal Exposed for testing only */ export function _resetProjectRootCache() { cachedProjectRoot = undefined; diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts index c1fde537..2318584a 100644 --- a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts @@ -2,7 +2,11 @@ import { test } from "node:test"; import assert from "node:assert"; import { existsSync } from "node:fs"; import { join } from "node:path"; -import { findProjectRoot, _resetProjectRootCache } from "../src/path.js"; +import { + findProjectRoot, + resolveFilePath, + _resetProjectRootCache, +} from "../src/path.js"; test("findProjectRoot", async (t) => { t.afterEach(() => { @@ -23,3 +27,44 @@ test("findProjectRoot", async (t) => { assert.strictEqual(first, second); }); }); + +test("resolveFilePath", async (t) => { + t.afterEach(() => { + _resetProjectRootCache(); + }); + + await t.test("resolves path with src/ to project root", () => { + const projectRoot = findProjectRoot(); + const result = resolveFilePath( + "/wrong/deploy/dir/src/routes/admin.ts:12:15", + ); + assert.strictEqual(result, `${projectRoot}/src/routes/admin.ts:12:15`); + }); + + await t.test("leaves path without src/ unchanged", () => { + const result = resolveFilePath("/some/other/path/routes/admin.ts:5:10"); + assert.strictEqual(result, "/some/other/path/routes/admin.ts:5:10"); + }); + + await t.test("preserves line:column suffix", () => { + const projectRoot = findProjectRoot(); + const result = resolveFilePath("/bad/path/src/index.ts:99:3"); + assert.strictEqual(result, `${projectRoot}/src/index.ts:99:3`); + }); + + await t.test("uses first src/ occurrence", () => { + const projectRoot = findProjectRoot(); + const result = resolveFilePath( + "/deploy/src/nested/src/routes/admin.ts:1:1", + ); + assert.strictEqual( + result, + `${projectRoot}/src/nested/src/routes/admin.ts:1:1`, + ); + }); + + await t.test("returns raw string if no line:column suffix", () => { + const result = resolveFilePath("/some/path/src/file.ts"); + assert.strictEqual(result, "/some/path/src/file.ts"); + }); +}); From e1eb3f5b98fe7eb2ee0bcfe76d9ef8d4d0f898b9 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Thu, 26 Feb 2026 19:46:11 +0400 Subject: [PATCH 3/4] feat(typeorm): add WSL path support for file tag Inside WSL, absolute paths like /home/user/project/... can't be resolved from Windows-side tooling (dashboards, VS Code terminal). This detects the WSL_DISTRO_NAME env var and prefixes resolved paths with //wsl.localhost/ to make them accessible from Windows. Co-Authored-By: Claude Opus 4.6 --- .../packages/sqlcommenter-typeorm/src/path.ts | 19 ++++++- .../sqlcommenter-typeorm/test/path.spec.ts | 51 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts index 2a2c2460..811866bd 100644 --- a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts @@ -54,7 +54,24 @@ export function resolveFilePath(raw: string): string { } const projectRoot = findProjectRoot(); const relativePath = filePath.substring(srcIdx); - return `${projectRoot}/${relativePath}:${lineCol}`; + const resolved = `${projectRoot}/${relativePath}`; + return `${applyWslPrefix(resolved)}:${lineCol}`; +} + +/** + * Prefixes an absolute path with the WSL network path when running inside WSL. + * + * Inside WSL, absolute paths like `/home/user/project/...` can't be resolved + * from Windows-side tooling (e.g., clickable links in dashboards or VS Code). + * The `WSL_DISTRO_NAME` env var is always set inside WSL, and the path format + * `//wsl.localhost//...` makes paths accessible from Windows. + */ +export function applyWslPrefix(filePath: string): string { + const distro = process.env.WSL_DISTRO_NAME; + if (distro) { + return `//wsl.localhost/${distro}${filePath}`; + } + return filePath; } /** @internal Exposed for testing only */ diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts index 2318584a..a379bb32 100644 --- a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts @@ -5,6 +5,7 @@ import { join } from "node:path"; import { findProjectRoot, resolveFilePath, + applyWslPrefix, _resetProjectRootCache, } from "../src/path.js"; @@ -68,3 +69,53 @@ test("resolveFilePath", async (t) => { assert.strictEqual(result, "/some/path/src/file.ts"); }); }); + +test("applyWslPrefix", async (t) => { + const originalWslDistro = process.env.WSL_DISTRO_NAME; + + t.afterEach(() => { + if (originalWslDistro === undefined) { + delete process.env.WSL_DISTRO_NAME; + } else { + process.env.WSL_DISTRO_NAME = originalWslDistro; + } + }); + + await t.test("prefixes path when WSL_DISTRO_NAME is set", () => { + process.env.WSL_DISTRO_NAME = "Ubuntu"; + const result = applyWslPrefix("/home/user/project/src/index.ts"); + assert.strictEqual( + result, + "//wsl.localhost/Ubuntu/home/user/project/src/index.ts", + ); + }); + + await t.test("returns path unchanged when WSL_DISTRO_NAME is not set", () => { + delete process.env.WSL_DISTRO_NAME; + const result = applyWslPrefix("/home/user/project/src/index.ts"); + assert.strictEqual(result, "/home/user/project/src/index.ts"); + }); +}); + +test("resolveFilePath with WSL", async (t) => { + const originalWslDistro = process.env.WSL_DISTRO_NAME; + + t.afterEach(() => { + _resetProjectRootCache(); + if (originalWslDistro === undefined) { + delete process.env.WSL_DISTRO_NAME; + } else { + process.env.WSL_DISTRO_NAME = originalWslDistro; + } + }); + + await t.test("applies WSL prefix to resolved src/ paths", () => { + process.env.WSL_DISTRO_NAME = "Ubuntu"; + const projectRoot = findProjectRoot(); + const result = resolveFilePath("/wrong/path/src/routes/admin.ts:12:15"); + assert.strictEqual( + result, + `//wsl.localhost/Ubuntu${projectRoot}/src/routes/admin.ts:12:15`, + ); + }); +}); From e9255233c6f2aeed480554f54cd942028e457fbe Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Thu, 26 Feb 2026 19:51:51 +0400 Subject: [PATCH 4/4] chore(typeorm): bump versions to 0.1.0 Co-Authored-By: Claude Opus 4.6 --- .../packages/sqlcommenter-typeorm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json index 54da4b76..6663a142 100644 --- a/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json +++ b/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/package.json @@ -1,6 +1,6 @@ { "name": "@query-doctor/sqlcommenter-typeorm", - "version": "0.0.2", + "version": "0.1.0", "description": "SQLCommenter patch for TypeORM", "main": "dist/cjs/index.js", "type": "module",