Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/fix-invalid-string-length.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@maastrich/hashup": patch
---

Fix `Error: Invalid string length` when hashing very large dependency graphs.

`combineHashes` used `hashes.join("")` to feed the sha256 hasher. With
millions of entries (each a 64-char hash) the joined string exceeds
V8's maximum string length (~512 MB) and the join itself throws. Feed
each hash to the hasher with `update()` instead — sha256 is incremental,
so the output hash is byte-for-byte identical to the old implementation
on inputs that used to succeed.
8 changes: 6 additions & 2 deletions src/lib/combine-hashes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { createHash } from "node:crypto";

export function combineHashes(hashes: string[]): string {
return createHash("sha256").update(hashes.join("")).digest("hex");
export function combineHashes(hashes: readonly string[]): string {
const hasher = createHash("sha256");
for (let i = 0; i < hashes.length; i++) {
hasher.update(hashes[i] as string);
}
return hasher.digest("hex");
}
28 changes: 28 additions & 0 deletions tests/combine-hashes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createHash } from "node:crypto";
import { describe, expect, test } from "vite-plus/test";
import { combineHashes } from "../src/lib/combine-hashes.js";

describe("combineHashes", () => {
test("matches the reference sha256(join)", () => {
const hashes = ["a", "b", "c", "deadbeef"];
const expected = createHash("sha256").update(hashes.join("")).digest("hex");

expect(combineHashes(hashes)).toBe(expected);
});

test("does not throw `Invalid string length` on very large inputs", () => {
// A single 64-char hash × 10M = 640M chars, past V8's max string length.
// Guards against anyone reintroducing `hashes.join('')`.
const oneHash = "a".repeat(64);
const hashes = new Array<string>(10_000_000).fill(oneHash);

expect(() => combineHashes(hashes)).not.toThrow();
});

test("reference `hashes.join('')` would throw on the same input", () => {
const oneHash = "a".repeat(64);
const hashes = new Array<string>(10_000_000).fill(oneHash);

expect(() => hashes.join("")).toThrow(/Invalid string length/);
});
});
Loading