Files
msd-core/src/cjs-command-router-adapter.cts
Joe 3cf112b3de refactor(#596): drop dead projection metadata in state-command-router (#633)
cjsFallbackHandler was a no-op pass-through returning only its last
argument, and state-command-router was its sole consumer — every handler
wrapped a thunk in a 5-arg projection tuple whose first four args
(registryCommand, registryArgs, legacyArgs, rawFormatter) were
constructed and immediately discarded (rawFormatter null at 100% of
sites). The SDK-dispatch path the metadata was nominally for is a retired
shim.

- Collapse all 20 state handlers to plain () => state.cmd*(...) thunks,
  matching the post-#298 inline convention used by the verify/roadmap/
  phases routers (complete-phase already used this form).
- Remove the local fallback() helper and Handler type from
  state-command-router.cts.
- Drop cjsFallbackHandler from cjs-command-router-adapter.cts exports —
  no remaining consumers.

Behavior-preserving: each thunk body is unchanged. No test referenced
cjsFallbackHandler. Adapter test (8/8), state-routing tests (36/36), and
the full unit suite (3435 pass / 0 fail) stay green.

Source-only diff; gsd-core/bin/lib/*.cjs are built-at-publish per ADR-457.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-06-02 23:32:10 -04:00

142 lines
3.8 KiB
TypeScript

/**
* CJS Command Router Adapter Module
*
* Compatibility routing for gsd-tools.cjs command families. Uses generated
* command metadata for availability and small family-local argument shapers for
* CJS handler calls.
*
* ADR-457 build-at-publish: the hand-written bin/lib/cjs-command-router-adapter.cjs
* collapsed to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
* from the prior hand-written .cjs; only types are added.
*/
// eslint-disable-next-line @typescript-eslint/no-require-imports
import commandRoutingHub = require('./command-routing-hub.cjs');
const { createHub, ERROR_KINDS } = commandRoutingHub;
// ─── Types ────────────────────────────────────────────────────────────────────
type Handler = () => unknown;
interface RouteCjsCommandFamilyOptions {
args: string[];
subcommands: string[];
handlers: Record<string, Handler>;
defaultSubcommand?: string;
unsupported?: Record<string, string>;
unknownMessage: (subcommand: string, available: string[]) => string;
error: (message: string) => void;
cwd?: string;
raw?: boolean;
}
interface RouteHubCommandFamilyOptions {
family: string;
args: string[];
subcommands: string[];
handlers: Record<string, Handler>;
defaultSubcommand?: string;
unsupported?: Record<string, string>;
unknownMessage: (subcommand: string, available: string[]) => string;
error: (message: string) => void;
cwd?: string;
raw?: boolean;
}
// ─── Implementation ───────────────────────────────────────────────────────────
function routeCjsCommandFamily({
args,
subcommands,
handlers,
defaultSubcommand,
unsupported = {},
unknownMessage,
error,
cwd,
raw,
}: RouteCjsCommandFamilyOptions): void {
routeHubCommandFamily({
family: '__legacy_cjs_family__',
args,
subcommands,
handlers,
defaultSubcommand,
unsupported,
unknownMessage,
error,
cwd,
raw,
});
}
/**
* Hub-backed family router adapter.
*
* Deepens the command-topology seam by routing family handlers through
* CommandRoutingHub's typed Result contract instead of ad-hoc per-router
* lookup + error handling branches.
*/
function routeHubCommandFamily({
family,
args,
subcommands,
handlers,
defaultSubcommand,
unsupported = {},
unknownMessage,
error,
cwd,
raw,
}: RouteHubCommandFamilyOptions): void {
const subcommand = args[1] || defaultSubcommand;
if (subcommand && unsupported[subcommand]) {
error(unsupported[subcommand]);
return;
}
const available = subcommands.filter((s) => !unsupported[s]);
const registryHandlers = Object.fromEntries(
Object.entries(handlers).map(([name, handler]) => [
name,
(): { ok: true; data: unknown } => {
const result = handler();
if (result && typeof result === 'object' && Object.prototype.hasOwnProperty.call(result, 'ok')) {
return result as { ok: true; data: unknown };
}
return { ok: true as const, data: null };
},
]),
);
const hub = createHub({
cjsRegistry: { [family]: registryHandlers },
manifest: { [family]: available },
});
const result = hub.dispatch({
family,
subcommand,
args: args.slice(2),
cwd,
raw,
});
if (result.ok) return;
if (result.kind === ERROR_KINDS.UnknownCommand) {
error(unknownMessage(subcommand ?? '', available));
return;
}
if (result.kind === ERROR_KINDS.InvalidArgs || result.kind === ERROR_KINDS.HandlerRefusal) {
error((result as { reason: string }).reason);
return;
}
error((result as { message: string }).message);
}
export = {
routeCjsCommandFamily,
routeHubCommandFamily,
};