From eea8f4765dc82b872437526593258679120258e9 Mon Sep 17 00:00:00 2001 From: 3m1n3nc3 Date: Wed, 21 Oct 2026 00:28:52 +0100 Subject: [PATCH] fix(queue): --queue rejected every value in 0.19.5 musket reads a trailing `: a, b, c` (or `[a, b]`) in an option's description as its allowed choices. The new `--queue` description ended in one, so `queue:work` refused every queue name, including a plain `--queue=webhook`: error: option '--queue ' argument 'webhook' is invalid. Allowed choices are a name, a comma-separated priority list, ... Reword the description without the `: ` list, and add a test that runs the real signature through musket's parser and pins that no queue:work option declares choices (it fails on the 0.19.5 description). Also quote the `*` in the docs' examples: unquoted, zsh aborts with "no matches found" and bash may glob-expand it. --- CHANGELOG.md | 1 + docs/guide/jobs.md | 2 +- docs/guide/queue.md | 5 +-- packages/queue/README.md | 5 +-- .../queue/src/commands/QueueWorkCommand.ts | 2 +- .../queue/tests/queue-work-command.test.ts | 35 +++++++++++++++++++ 6 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 packages/queue/tests/queue-work-command.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index cbb917be..539c2e6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format follows semantic versioning principles. ### Fixed +- `ark queue:work --queue` rejected every value in 0.19.5 ("argument '…' is invalid. Allowed choices are …"): musket reads a trailing `: a, b, c` in an option's description as its allowed choices, and the new `--queue` description had one. The description no longer does, and a test pins that `queue:work` options declare no choices. - `DotPath` (the key type behind `config()` and `configure()`) is now depth-bounded and stops at built-in object types, so configs holding deep or self-referencing library types (nodemailer transports) no longer fail to typecheck with "circularly references itself". The `@ts-expect-error` workarounds in the H3 template and the notifications config are gone. - `build:templates` builds the templates one at a time; run in parallel, each template's prebuild rebuilt the shared packages concurrently and the builds clobbered each other. - Fixed type errors in `@arkstack/auth` (`AuthenticationException` request typing and two tests) and in `@arkstack/common`'s `renderError`, which used the `view()` global without a type for it. diff --git a/docs/guide/jobs.md b/docs/guide/jobs.md index edbe1865..775a1c00 100644 --- a/docs/guide/jobs.md +++ b/docs/guide/jobs.md @@ -87,7 +87,7 @@ With the default `sync` connection the job runs immediately. Configure a `databa ark queue:work ``` -A bare `ark queue:work` processes only the connection's default queue. If jobs go to other queues (`onQueue(...)` or a `queue` property), name them, e.g. `ark queue:work --queue=emails,default`, or use `ark queue:work --queue=*` to work them all. +A bare `ark queue:work` processes only the connection's default queue. If jobs go to other queues (`onQueue(...)` or a `queue` property), name them, e.g. `ark queue:work --queue=emails,default`, or use `ark queue:work '--queue=*'` to work them all (quote the `*` so the shell doesn't expand it). ## Retries & failure handling diff --git a/docs/guide/queue.md b/docs/guide/queue.md index c15d46e0..a8ea2ac5 100644 --- a/docs/guide/queue.md +++ b/docs/guide/queue.md @@ -91,7 +91,7 @@ A worker pulls jobs from a connection and runs them. Run one from the CLI: ark queue:work # daemon on the default connection's default queue ark queue:work redis --queue=emails ark queue:work --queue=high,default # priority list: high first, then default -ark queue:work --queue=* # every queue on the connection +ark queue:work '--queue=*' # every queue on the connection (quote the *) ark queue:work --once # process a single job and exit ark queue:clear redis --queue=emails ``` @@ -106,7 +106,8 @@ ark queue:clear redis --queue=emails - **`*`** works every queue on the connection. The `database` driver takes the oldest due job across all queues; the `redis` driver discovers queues with `SCAN` and works them round-robin. Combine it with names to prioritize some: - `--queue=webhook,*`. + `--queue=webhook,*`. Quote any `*` in a shell (`'--queue=webhook,*'`), or + zsh/bash may expand it as a glob. Every queue that receives jobs needs a worker that lists it (or `*`), or its jobs wait forever. diff --git a/packages/queue/README.md b/packages/queue/README.md index 31f904a5..78d05e8e 100644 --- a/packages/queue/README.md +++ b/packages/queue/README.md @@ -52,7 +52,7 @@ await Queue.clear('emails') ark queue:work # daemon on the default connection's default queue ark queue:work redis --queue=emails ark queue:work --queue=high,default # priority list: high first, then default -ark queue:work --queue=* # every queue on the connection +ark queue:work '--queue=*' # every queue on the connection (quote the *) ark queue:work --once # process a single job and exit ark queue:clear redis --queue=emails ``` @@ -67,7 +67,8 @@ ark queue:clear redis --queue=emails - **`*`** works every queue on the connection. The `database` driver takes the oldest due job across all queues; the `redis` driver discovers queues with `SCAN` and works them round-robin. Combine it with names to prioritize some: - `--queue=webhook,*`. + `--queue=webhook,*`. Quote any `*` in a shell (`'--queue=webhook,*'`), or + zsh/bash may expand it as a glob. Every queue that receives jobs needs a worker that lists it (or `*`), or its jobs wait forever. diff --git a/packages/queue/src/commands/QueueWorkCommand.ts b/packages/queue/src/commands/QueueWorkCommand.ts index 47d9baa6..d4a6a779 100644 --- a/packages/queue/src/commands/QueueWorkCommand.ts +++ b/packages/queue/src/commands/QueueWorkCommand.ts @@ -12,7 +12,7 @@ const reason = (error: unknown): string => (error instanceof Error ? error.messa export class QueueWorkCommand extends Command { protected signature = `queue:work {connection? : The queue connection to work. Defaults to the configured default.} - {--queue= : The queue(s) to process: a name, a comma-separated priority list, or * for every queue.} + {--queue= : The queue(s) to process. One name, a comma-separated priority list, or * for every queue.} {--sleep=3 : Seconds to sleep when no job is available.} {--max-jobs=0 : Stop after processing this many jobs (0 for unlimited).} {--once : Process a single job and exit.} diff --git a/packages/queue/tests/queue-work-command.test.ts b/packages/queue/tests/queue-work-command.test.ts new file mode 100644 index 00000000..2e1aa16b --- /dev/null +++ b/packages/queue/tests/queue-work-command.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest' + +import { QueueWorkCommand } from '../src/commands/QueueWorkCommand' +import { Signature } from '@h3ravel/musket' + +/** + * musket reads `[a, b]` or a trailing `: a, b` in an option's description as its + * allowed choices and rejects any other value. A description that tripped this + * made `--queue` refuse every queue name, so pin that the worker's options stay + * free-form. + */ +describe('queue:work signature', () => { + const options = () => { + const signature = ( + new QueueWorkCommand(undefined as never, undefined as never) as unknown as { + signature: string + } + ).signature + + return Signature.parseOptions(signature) as Array<{ name: string; flags?: string[]; choices: string[] }> + } + + it('accepts any --queue value (no choices list)', () => { + const queue = options().find((option) => option.flags?.includes('--queue')) + + expect(queue).toBeDefined() + expect(queue?.choices).toEqual([]) + }) + + it('declares no choices on any option', () => { + for (const option of options()) { + expect(option.choices, option.name).toEqual([]) + } + }) +})