From d6f50c048ce2cef3a5b2ada7deb8f087f556eb15 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Sat, 26 Sep 2026 16:27:46 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20restore=20the=20default=20for=20?= =?UTF-8?q?--retries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `retries` was declared with a schema that rejects `undefined`, which makes the field required: every invocation that left the flag off failed with `retries: Invalid input: expected number, received undefined`, including the one this project's deploy workflow has always used. 0.2.6 accepted it, so 0.3.0 could not be adopted without editing every caller. The default cannot be a `field.default`, because it depends on `--strict`, which a field cannot see. `main.ts` already computes it once both are parsed; the schema just has to admit the absent value, so it is now `z.number().optional()` and the help text names both defaults. Closes #19 --- config.ts | 6 +++-- test/config.test.ts | 57 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 test/config.test.ts diff --git a/config.ts b/config.ts index 7e357b9..ef5e2cc 100644 --- a/config.ts +++ b/config.ts @@ -36,8 +36,10 @@ export const config = program({ }, retries: { description: - "Number of times to retry a failed download before giving up. Defaults to 0 in strict mode.", - ...field(z.number()), + "Number of times to retry a failed download before giving up. Defaults to 3, or 0 in strict mode.", + // optional rather than defaulted: the default depends on --strict, which + // a field cannot see, so `main.ts` supplies it once both are parsed + ...field(z.number().optional()), }, }), }); diff --git a/test/config.test.ts b/test/config.test.ts new file mode 100644 index 0000000..fa4f90c --- /dev/null +++ b/test/config.test.ts @@ -0,0 +1,57 @@ +import { expect } from "@std/expect"; +import { describe, it } from "@std/testing/bdd"; + +import { config } from "../config.ts"; + +// the three flags the deploy workflow has always passed, and nothing else +let deploy = [ + "--site=http://127.0.0.1:8000", + "--output=www/built", + "--base=https://frontside.com/effection", +]; + +function parse(args: string[]) { + // an empty env, so a STATICALIZE_* variable on the machine cannot sway this + let parser = config.createParser({ + envs: [{ name: "ENV", value: {} }], + args, + }); + if (parser.type !== "main") { + throw new Error(`expected a main parser, got ${parser.type}`); + } + return parser.parse(); +} + +function value(args: string[]) { + let result = parse(args); + if (!result.ok) { + throw result.error; + } + return result.value; +} + +describe("config", () => { + it("accepts an invocation that passes no --retries", () => { + expect(parse(deploy).ok).toEqual(true); + }); + + it("leaves retries unset, so its default can depend on --strict", () => { + expect(value(deploy).retries).toEqual(undefined); + }); + + it("takes --retries when it is given", () => { + expect(value([...deploy, "--retries=1"]).retries).toEqual(1); + }); + + it("defaults concurrency", () => { + expect(value(deploy).concurrency).toEqual(75); + }); + + it("defaults strict to off", () => { + expect(value(deploy).strict).toEqual(false); + }); + + it("still refuses an invocation with no --base", () => { + expect(parse(["--site=http://127.0.0.1:8000"]).ok).toEqual(false); + }); +});