diff --git a/packages/core/src/frontend/ensure-buffer-dependency.test.ts b/packages/core/src/frontend/ensure-buffer-dependency.test.ts index 431d50b0..0fa8ac39 100644 --- a/packages/core/src/frontend/ensure-buffer-dependency.test.ts +++ b/packages/core/src/frontend/ensure-buffer-dependency.test.ts @@ -52,6 +52,36 @@ describe("ensureBufferDependency", () => { expect(result?.added).toBe(false); }); + // #98: presence is not enough — an out-of-range version must be corrected. + it("should_update_buffer_when_the_declared_version_is_out_of_range", async () => { + const cwd = await scaffold({ name: "app", dependencies: { buffer: "^1.0.0" } }); + + const result = await ensureBufferDependency(cwd, "./frontend/src/contracts"); + + expect(result?.added).toBe(true); + const pkg = JSON.parse(await readFile(path.join(cwd, "frontend", "package.json"), "utf8")); + expect(pkg.dependencies.buffer).toBe("^6.0.3"); + }); + + it("should_update_an_out_of_range_buffer_in_devDependencies_in_place", async () => { + const cwd = await scaffold({ name: "app", devDependencies: { buffer: "5.0.0" } }); + + const result = await ensureBufferDependency(cwd, "./frontend/src/contracts"); + + expect(result?.added).toBe(true); + const pkg = JSON.parse(await readFile(path.join(cwd, "frontend", "package.json"), "utf8")); + expect(pkg.devDependencies.buffer).toBe("^6.0.3"); + expect(pkg.dependencies?.buffer).toBeUndefined(); + }); + + it("should_be_a_noop_when_a_higher_in_range_version_is_declared", async () => { + const cwd = await scaffold({ name: "app", dependencies: { buffer: "^6.1.0" } }); + + const result = await ensureBufferDependency(cwd, "./frontend/src/contracts"); + + expect(result?.added).toBe(false); + }); + it("should_return_undefined_when_no_package_json_is_found", async () => { tmpDir = await mkdtemp(path.join(os.tmpdir(), "caatinga-buffer-dep-none-")); await mkdir(path.join(tmpDir, "frontend", "src", "contracts"), { recursive: true }); diff --git a/packages/core/src/frontend/ensure-buffer-dependency.ts b/packages/core/src/frontend/ensure-buffer-dependency.ts index e8ef2a82..781b6db9 100644 --- a/packages/core/src/frontend/ensure-buffer-dependency.ts +++ b/packages/core/src/frontend/ensure-buffer-dependency.ts @@ -1,5 +1,6 @@ import { readFile, writeFile } from "node:fs/promises"; import path from "node:path"; +import semver from "semver"; // Backs the Buffer polyfill that every generated binding imports. Pinned to the // same major the templates ship so behaviour matches across init and adoption. @@ -75,8 +76,25 @@ export async function ensureBufferDependency( return undefined; } - if (pkg.dependencies?.buffer ?? pkg.devDependencies?.buffer) { - return { packageJsonPath, added: false }; + const existing = pkg.dependencies?.buffer ?? pkg.devDependencies?.buffer; + if (existing) { + // #98: presence alone isn't enough — the templates pin `buffer` to a major + // (^6). If the declared range already resolves within the supported range, + // leave it; otherwise pin it, so a stale `buffer: "^1.0.0"` gets corrected + // instead of silently kept. + const minVersion = semver.minVersion(existing); + if (minVersion && semver.satisfies(minVersion, BUFFER_DEPENDENCY_RANGE)) { + return { packageJsonPath, added: false }; + } + + // Out of range (or unparseable) — update it in the section it already lives. + if (pkg.dependencies?.buffer) { + pkg.dependencies.buffer = BUFFER_DEPENDENCY_RANGE; + } else { + (pkg.devDependencies as Record).buffer = BUFFER_DEPENDENCY_RANGE; + } + await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8"); + return { packageJsonPath, added: true }; } pkg.dependencies = { ...(pkg.dependencies ?? {}), buffer: BUFFER_DEPENDENCY_RANGE };