diff --git a/.changeset/fix-worker-count-env-validation.md b/.changeset/fix-worker-count-env-validation.md new file mode 100644 index 0000000..cea33fb --- /dev/null +++ b/.changeset/fix-worker-count-env-validation.md @@ -0,0 +1,5 @@ +--- +"codemap": patch +--- + +Reject malformed `CODEMAP_PARSE_WORKERS` values (e.g. `2abc`, `1.5`) instead of silently truncating with `parseInt`. diff --git a/src/worker-pool.test.ts b/src/worker-pool.test.ts new file mode 100644 index 0000000..88b8a2f --- /dev/null +++ b/src/worker-pool.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, test } from "bun:test"; + +import { parseParseWorkerCountOverride } from "./worker-pool"; + +describe("parseParseWorkerCountOverride", () => { + test("accepts valid decimal integers", () => { + expect(parseParseWorkerCountOverride("2")).toBe(2); + expect(parseParseWorkerCountOverride("32")).toBe(32); + expect(parseParseWorkerCountOverride("999")).toBe(32); + }); + + test("rejects malformed or non-positive values", () => { + expect(parseParseWorkerCountOverride("2abc")).toBeNull(); + expect(parseParseWorkerCountOverride("1.5")).toBeNull(); + expect(parseParseWorkerCountOverride("0")).toBeNull(); + expect(parseParseWorkerCountOverride("-1")).toBeNull(); + expect(parseParseWorkerCountOverride(" 2")).toBeNull(); + }); + + test("treats unset or empty as no override", () => { + expect(parseParseWorkerCountOverride(undefined)).toBeNull(); + expect(parseParseWorkerCountOverride("")).toBeNull(); + }); +}); diff --git a/src/worker-pool.ts b/src/worker-pool.ts index 140b9e3..3f3e289 100644 --- a/src/worker-pool.ts +++ b/src/worker-pool.ts @@ -20,14 +20,25 @@ const WORKER_URL_NODE = new URL( import.meta.url, ); +const PARSE_WORKER_COUNT_RE = /^\d+$/; + +/** Returns clamped override [1, 32], or `null` when unset/empty/invalid. */ +export function parseParseWorkerCountOverride( + env: string | undefined, +): number | null { + if (env === undefined || env === "") return null; + if (!PARSE_WORKER_COUNT_RE.test(env)) return null; + const parsed = Number(env); + if (!Number.isSafeInteger(parsed) || parsed < 1) return null; + return Math.min(parsed, 32); +} + // Override via `CODEMAP_PARSE_WORKERS` (clamped [1, 32]); default formula unchanged when unset. function resolveWorkerCount(): number { const env = process.env.CODEMAP_PARSE_WORKERS; + const override = parseParseWorkerCountOverride(env); + if (override !== null) return override; if (env !== undefined && env !== "") { - const parsed = Number.parseInt(env, 10); - if (Number.isFinite(parsed) && parsed >= 1) { - return Math.min(parsed, 32); - } console.error( `[worker-pool] ignoring invalid CODEMAP_PARSE_WORKERS=${JSON.stringify(env)} (expected positive integer ≤32)`, );