Skip to content

Commit 6632e3f

Browse files
committed
refactor: remove fallback option from dispatchOrgScopedList
The fallback added complexity for minimal benefit — a shared local handler assigned to each mode key is clearer and just as concise.
1 parent d89ad8a commit 6632e3f

File tree

3 files changed

+14
-22
lines changed

3 files changed

+14
-22
lines changed

AGENTS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,6 @@ Two abstraction levels exist for list commands:
329329
330330
Key rules when writing overrides:
331331
- Each mode handler receives a `HandlerContext<T>` with the narrowed `parsed` plus shared I/O (`stdout`, `cwd`, `flags`). Access parsed fields via `ctx.parsed.org`, `ctx.parsed.projectSlug`, etc. — no manual `Extract<>` casts needed.
332-
- When multiple modes share one handler, use the `fallback` option instead of repeating the handler for each key: `fallback: (ctx) => sharedHandler({ ...ctx, extra })`.
333332
- Commands with extra fields (e.g., `stderr`, `setContext`) spread the context and add them: `(ctx) => handle({ ...ctx, flags, stderr, setContext })`. Override `ctx.flags` with the command-specific flags type when needed.
334333
- `resolveCursor()` must be called **inside** the `org-all` override closure, not before `dispatchOrgScopedList`, so that `--cursor` validation errors fire correctly for non-org-all modes.
335334
- `handleProjectSearch` errors must use `"Project"` as the `ContextError` resource, not `config.entityName`.

src/commands/issue/list.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
import {
4545
dispatchOrgScopedList,
4646
type ListCommandMeta,
47+
type ModeHandler,
4748
} from "../../lib/org-list.js";
4849
import {
4950
type ResolvedTarget,
@@ -700,15 +701,20 @@ export const listCommand = buildCommand({
700701

701702
const parsed = parseOrgProjectArg(target);
702703

704+
// biome-ignore lint/suspicious/noExplicitAny: shared handler accepts any mode variant
705+
const resolveAndHandle: ModeHandler<any> = (ctx) =>
706+
handleResolvedTargets({ ...ctx, flags, stderr, setContext });
707+
703708
await dispatchOrgScopedList({
704709
config: issueListMeta,
705710
stdout,
706711
cwd,
707712
flags,
708713
parsed,
709-
fallback: (ctx) =>
710-
handleResolvedTargets({ ...ctx, flags, stderr, setContext }),
711714
overrides: {
715+
"auto-detect": resolveAndHandle,
716+
explicit: resolveAndHandle,
717+
"project-search": resolveAndHandle,
712718
"org-all": (ctx) =>
713719
handleOrgAllIssues({
714720
stdout: ctx.stdout,

src/lib/org-list.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -719,38 +719,24 @@ export type DispatchOptions<TEntity = unknown, TWithOrg = unknown> = {
719719
/**
720720
* Per-mode handler overrides. Each key matches a `ParsedOrgProject["type"]`.
721721
* Provided handlers replace the corresponding default handler; unspecified
722-
* modes fall back to {@link fallback} (if given) then the defaults from
723-
* {@link buildDefaultHandlers}.
722+
* modes fall back to the defaults from {@link buildDefaultHandlers}.
724723
*/
725724
overrides?: ModeOverrides;
726-
/**
727-
* Fallback handler invoked for any mode not covered by `overrides`.
728-
*
729-
* Useful when most modes share the same handler and only one or two need
730-
* custom logic. Receives the full `HandlerContext<ParsedOrgProject["type"]>`
731-
* so it can be reused across modes without casts.
732-
*
733-
* Resolution order: `overrides[mode]` → `fallback` → default handler.
734-
*/
735-
fallback?: ModeHandler;
736725
};
737726

738727
/**
739728
* Validate the cursor flag and dispatch to the correct mode handler.
740729
*
741730
* Builds a {@link HandlerContext} from the shared fields (stdout, cwd, flags,
742-
* parsed) and passes it to the resolved handler. Resolution order:
743-
*
744-
* 1. `overrides[parsed.type]` — caller-supplied per-mode handler
745-
* 2. `fallback` — catch-all handler for modes not in overrides
746-
* 3. Default handler from {@link buildDefaultHandlers}
731+
* parsed) and passes it to the resolved handler. Merges default handlers
732+
* with caller-provided overrides using `{ ...defaults, ...overrides }`.
747733
*
748734
* This is the single entry point for all org-scoped list commands.
749735
*/
750736
export async function dispatchOrgScopedList<TEntity, TWithOrg>(
751737
options: DispatchOptions<TEntity, TWithOrg>
752738
): Promise<void> {
753-
const { config, stdout, cwd, flags, parsed, overrides, fallback } = options;
739+
const { config, stdout, cwd, flags, parsed, overrides } = options;
754740

755741
// Cursor pagination is only supported in org-all mode
756742
if (flags.cursor && parsed.type !== "org-all") {
@@ -763,7 +749,8 @@ export async function dispatchOrgScopedList<TEntity, TWithOrg>(
763749
}
764750

765751
const defaults = buildDefaultHandlers(config);
766-
const handler = overrides?.[parsed.type] ?? fallback ?? defaults[parsed.type];
752+
const handlers: ModeHandlerMap = { ...defaults, ...overrides };
753+
const handler = handlers[parsed.type];
767754

768755
const ctx: HandlerContext = { parsed, stdout, cwd, flags };
769756

0 commit comments

Comments
 (0)