diff --git a/.changeset/vars-partial-parse.md b/.changeset/vars-partial-parse.md new file mode 100644 index 0000000..1942137 --- /dev/null +++ b/.changeset/vars-partial-parse.md @@ -0,0 +1,5 @@ +--- +'incur': patch +--- + +Fixed eager `varsSchema.parse({})` throwing ZodError for required vars populated by middleware. Vars are now initialized with `varsSchema.partial().parse({})`, preserving schema defaults while allowing middleware-populated required fields. diff --git a/src/Cli.test.ts b/src/Cli.test.ts index 7201b57..e6fe71d 100644 --- a/src/Cli.test.ts +++ b/src/Cli.test.ts @@ -6099,6 +6099,25 @@ describe('globals', () => { expect(JSON.parse(output)).toEqual({ chain: 'mainnet' }) }) + test('required (nonoptional) vars populated by middleware do not throw pre-middleware (#188)', async () => { + const cli = Cli.create('test', { + globals: z.object({ chain: z.string().default('ethereum') }), + vars: z.object({ chain: z.string().nonoptional() }), + }) + .use(async (c, next) => { + c.set('chain', c.globals.chain) + await next() + }) + .command('ping', { + run(c) { + return { chain: c.var.chain } + }, + }) + + const { output } = await serve(cli, ['ping', '--json']) + expect(JSON.parse(output)).toEqual({ chain: 'ethereum' }) + }) + test('globals appear in --help output', async () => { const cli = Cli.create('test', { globals: z.object({ diff --git a/src/internal/command.ts b/src/internal/command.ts index 7b29830..a178693 100644 --- a/src/internal/command.ts +++ b/src/internal/command.ts @@ -52,7 +52,7 @@ export async function execute(command: any, options: execute.Options): Promise = varsSchema ? varsSchema.parse({}) : {} + const varsMap: Record = varsSchema ? varsSchema.partial().parse({}) : {} let result: execute.Result | undefined // For streaming with middleware: runCommand suspends on streamConsumed so middleware "after" // runs after the stream is consumed. The wrapped generator resolves it in its finally block.