diff --git a/packages/engine/src/utils/renderProvenance.test.ts b/packages/engine/src/utils/renderProvenance.test.ts index bc96888d1c..933389e816 100644 --- a/packages/engine/src/utils/renderProvenance.test.ts +++ b/packages/engine/src/utils/renderProvenance.test.ts @@ -315,6 +315,17 @@ describe("resolveOwnVersionFrom", () => { expect(resolveOwnVersionFrom(join(root, "node_modules/hyperframes/dist"))).toBe("0.8.71"); }); + it("reads the installed engine when an app bundles it into its own dist", () => { + write(".", { name: "some-server-app", version: "0.0.0" }); + write("node_modules/@hyperframes/engine", { + name: "@hyperframes/engine", + version: "0.8.212", + exports: { "./package.json": "./package.json" }, + }); + mkdirSync(join(root, "dist"), { recursive: true }); + expect(resolveOwnVersionFrom(join(root, "dist"))).toBe("0.8.212"); + }); + it("returns the sentinel, not a foreign version, when our root declares none", () => { write(".", { name: "some-consumer-app", version: "9.9.9" }); write("node_modules/hyperframes", { name: "hyperframes" }); diff --git a/packages/engine/src/utils/renderProvenance.ts b/packages/engine/src/utils/renderProvenance.ts index 9f1cc74522..2f652ae2ab 100644 --- a/packages/engine/src/utils/renderProvenance.ts +++ b/packages/engine/src/utils/renderProvenance.ts @@ -1,4 +1,5 @@ import { readFileSync } from "node:fs"; +import { createRequire } from "node:module"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { readTagCI } from "./ffprobe.js"; @@ -96,10 +97,29 @@ export function resolveOwnVersionFrom(startDir: string): string { dir = parent; } + // An app that bundles the engine into its own dist has no package of ours above the bundle; the + // @hyperframes/engine installed beside it is the one it was built from. + const installed = installedEngineVersion(startDir); + if (installed) return installed; + reportUnresolved(`no package.json matching ${OWN_PACKAGE_NAME} within ${MAX_WALK_DEPTH} levels`); return UNRESOLVED_VERSION; } +function installedEngineVersion(fromDir: string): string | undefined { + try { + const path = createRequire(join(fromDir, "index.js")).resolve( + "@hyperframes/engine/package.json", + ); + const pkg = JSON.parse(readFileSync(path, "utf8")) as { name?: unknown; version?: unknown }; + if (pkg.name === "@hyperframes/engine" && typeof pkg.version === "string" && pkg.version) + return pkg.version; + } catch { + // Not installed beside us: the caller reports it. + } + return undefined; +} + /** * Make the failure audible. The point of this change is that a resolution * failure stops being indistinguishable from data - a silent fallback is what