refactor: deepen query architecture seams with compatibility shims

This commit is contained in:
Tom Boucher
2026-05-04 21:16:09 -04:00
parent c6a35d6398
commit 0500bdf619
18 changed files with 488 additions and 337 deletions

View File

@@ -1,16 +1,14 @@
import type { QueryRegistry } from './registry.js';
import { extractField } from './registry.js';
import { normalizeQueryCommand } from './query-command-resolution-strategy.js';
import { runCjsFallbackDispatch } from './query-fallback-executor.js';
import type { QueryDispatchResult } from './query-dispatch-contract.js';
import type { QueryDispatchError, QueryDispatchResult } from './query-dispatch-contract.js';
import type { QueryResult } from './utils.js';
import type { QueryNativeDispatchAdapter } from './query-native-dispatch-adapter.js';
import type { CommandTopology } from './command-topology.js';
import { mapFallbackDispatchError, mapNativeDispatchError, toDispatchFailure } from './query-dispatch-error-mapper.js';
import { formatSuccess } from './query-dispatch-formatting.js';
import { unknownCommandError, validationError } from './query-error-taxonomy.js';
import { planQueryDispatch } from './query-dispatch-plan.js';
import { validateQueryDispatchInput } from './query-dispatch-input-validation.js';
import { dispatchSuccess } from './query-dispatch-result-builder.js';
import type { CommandTopology, CommandTopologyMatch } from './command-topology.js';
import { unknownCommandError, validationError, fallbackDispatchErrorFromSignal, nativeDispatchErrorFromSignal } from './query-error-taxonomy.js';
import { canUseCjsFallback } from './query-fallback-policy.js';
import { toFailureSignal } from '../query-failure-classification.js';
export interface QueryDispatchDeps {
registry: QueryRegistry;
@@ -25,6 +23,142 @@ export interface QueryDispatchDeps {
topology: CommandTopology;
}
export type DispatchMode = 'native' | 'cjs' | 'error';
export interface DispatchPlan {
mode: DispatchMode;
normalized: { command: string; args: string[]; tokens: string[] };
matched: CommandTopologyMatch | null;
noMatchMessage?: string;
noMatchNormalized?: string;
noMatchAttempted?: string[];
noMatchHints?: string[];
}
export type DispatchSuccessFormat = 'json' | 'text' | undefined;
export interface DispatchInputValidationResult {
queryArgs: string[];
pickField?: string;
error?: QueryDispatchResult;
}
export function dispatchFailure(error: QueryDispatchError, stderr: string[] = []): QueryDispatchResult {
return {
ok: false,
error,
stderr,
exit_code: error.code,
};
}
export function dispatchSuccess(stdout: string, stderr: string[] = []): QueryDispatchResult {
return {
ok: true,
stdout,
stderr,
exit_code: 0,
};
}
export function toDispatchFailure(error: QueryDispatchError, stderr: string[] = []): QueryDispatchResult {
return dispatchFailure(error, stderr);
}
export function mapNativeDispatchError(error: unknown, command: string, args: string[]): QueryDispatchError {
return nativeDispatchErrorFromSignal(toFailureSignal(error), command, args);
}
export function mapFallbackDispatchError(error: unknown, command: string, args: string[]): QueryDispatchError {
return fallbackDispatchErrorFromSignal(toFailureSignal(error), command, args);
}
export function formatPick(data: unknown, pickField?: string): unknown {
if (!pickField) return data;
return extractField(data, pickField);
}
export function formatSuccess(data: unknown, format: DispatchSuccessFormat, pickField?: string): string {
if (format === 'text' && typeof data === 'string') {
return data.endsWith('\n') ? data : `${data}\n`;
}
const output = formatPick(data, pickField);
return `${JSON.stringify(output, null, 2)}\n`;
}
export function validateQueryDispatchInput(queryArgv: string[]): DispatchInputValidationResult {
const queryArgs = [...queryArgv];
const pickIdx = queryArgs.indexOf('--pick');
if (pickIdx !== -1) {
if (pickIdx + 1 >= queryArgs.length) {
return {
queryArgs,
error: dispatchFailure(validationError({
message: 'Error: --pick requires a field name',
details: { field: '--pick', reason: 'missing_value' },
})),
};
}
const pickField = queryArgs[pickIdx + 1];
queryArgs.splice(pickIdx, 2);
if (queryArgs.length === 0 || !queryArgs[0]) {
return {
queryArgs,
error: dispatchFailure(validationError({
message: 'Error: "gsd-sdk query" requires a command',
details: { reason: 'missing_command' },
})),
};
}
return { queryArgs, pickField };
}
if (queryArgs.length === 0 || !queryArgs[0]) {
return {
queryArgs,
error: dispatchFailure(validationError({
message: 'Error: "gsd-sdk query" requires a command',
details: { reason: 'missing_command' },
})),
};
}
return { queryArgs };
}
export function planQueryDispatch(
queryArgv: string[],
topology: CommandTopology,
cjsFallbackEnabled: boolean,
): DispatchPlan {
const queryCommand = queryArgv[0];
if (!queryCommand) {
return { mode: 'error', normalized: { command: '', args: [], tokens: [] }, matched: null };
}
const [normCmd, normArgs] = normalizeQueryCommand(queryCommand, queryArgv.slice(1));
const normalizedTokens = [normCmd, ...normArgs];
const resolved = topology.resolve(queryArgv, !cjsFallbackEnabled);
if (resolved.kind === 'match') {
return { mode: 'native', normalized: { command: normCmd, args: normArgs, tokens: normalizedTokens }, matched: resolved };
}
if (cjsFallbackEnabled) {
return { mode: 'cjs', normalized: { command: normCmd, args: normArgs, tokens: normalizedTokens }, matched: null };
}
return {
mode: 'error',
normalized: { command: normCmd, args: normArgs, tokens: normalizedTokens },
matched: null,
noMatchMessage: resolved.message,
noMatchNormalized: resolved.normalized,
noMatchAttempted: resolved.attempted,
noMatchHints: resolved.hints,
};
}
function fail(error: ReturnType<typeof validationError> | ReturnType<typeof unknownCommandError>, stderr: string[] = []): QueryDispatchResult {
return toDispatchFailure(error, stderr);
}