refactor: split native adapter timeout and failure seams
This commit is contained in:
@@ -44,6 +44,8 @@ export function createGSDToolsRuntime(opts: {
|
||||
dispatch: (registryCommand, registryArgs) => registry.dispatch(registryCommand, registryArgs, opts.projectDir),
|
||||
createTimeoutError: (message, command, args) =>
|
||||
GSDToolsError.timeout(message, command, args, '', opts.timeoutMs),
|
||||
createFailureError: (message, command, args, cause) =>
|
||||
GSDToolsError.failure(message, command, args, 1, '', { cause }),
|
||||
});
|
||||
|
||||
const transport = new GSDTransport(registry, {
|
||||
|
||||
35
sdk/src/query-native-direct-adapter.test.ts
Normal file
35
sdk/src/query-native-direct-adapter.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { GSDToolsError } from './gsd-tools-error.js';
|
||||
import { QueryNativeDirectAdapter } from './query-native-direct-adapter.js';
|
||||
|
||||
describe('QueryNativeDirectAdapter', () => {
|
||||
it('wraps native failures as typed failure errors', async () => {
|
||||
const adapter = new QueryNativeDirectAdapter({
|
||||
timeoutMs: 1000,
|
||||
dispatch: async () => {
|
||||
throw new Error('boom');
|
||||
},
|
||||
createTimeoutError: (message, command, args) => GSDToolsError.timeout(message, command, args),
|
||||
createFailureError: (message, command, args, cause) => GSDToolsError.failure(message, command, args, 1, '', { cause }),
|
||||
});
|
||||
|
||||
await expect(adapter.dispatchJson('state', ['load'], 'state.load', [])).rejects.toMatchObject({
|
||||
classification: { kind: 'failure' },
|
||||
command: 'state',
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves timeout errors', async () => {
|
||||
const timeoutErr = GSDToolsError.timeout('timeout', 'state', ['load']);
|
||||
const adapter = new QueryNativeDirectAdapter({
|
||||
timeoutMs: 1000,
|
||||
dispatch: async () => {
|
||||
throw timeoutErr;
|
||||
},
|
||||
createTimeoutError: (message, command, args) => GSDToolsError.timeout(message, command, args),
|
||||
createFailureError: (message, command, args, cause) => GSDToolsError.failure(message, command, args, 1, '', { cause }),
|
||||
});
|
||||
|
||||
await expect(adapter.dispatchJson('state', ['load'], 'state.load', [])).rejects.toBe(timeoutErr);
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,13 @@
|
||||
import { formatQueryRawOutput } from './query-raw-output-projection.js';
|
||||
import { timeoutMessage } from './query-failure-classification.js';
|
||||
import { GSDToolsError } from './gsd-tools-error.js';
|
||||
import { errorMessage, timeoutMessage } from './query-failure-classification.js';
|
||||
import type { QueryResult } from './query/utils.js';
|
||||
|
||||
export interface QueryNativeDirectAdapterDeps {
|
||||
timeoutMs: number;
|
||||
dispatch: (registryCommand: string, registryArgs: string[]) => Promise<QueryResult>;
|
||||
createTimeoutError: (message: string, command: string, args: string[]) => Error;
|
||||
createFailureError: (message: string, command: string, args: string[], cause: unknown) => Error;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15,7 +17,12 @@ export class QueryNativeDirectAdapter {
|
||||
constructor(private readonly deps: QueryNativeDirectAdapterDeps) {}
|
||||
|
||||
async dispatchResult(legacyCommand: string, legacyArgs: string[], registryCommand: string, registryArgs: string[]): Promise<QueryResult> {
|
||||
return this.withTimeout(legacyCommand, legacyArgs, this.deps.dispatch(registryCommand, registryArgs));
|
||||
try {
|
||||
return await this.withTimeout(legacyCommand, legacyArgs, this.deps.dispatch(registryCommand, registryArgs));
|
||||
} catch (error) {
|
||||
if (error instanceof GSDToolsError) throw error;
|
||||
throw this.deps.createFailureError(errorMessage(error), legacyCommand, legacyArgs, error);
|
||||
}
|
||||
}
|
||||
|
||||
async dispatchJson(legacyCommand: string, legacyArgs: string[], registryCommand: string, registryArgs: string[]): Promise<unknown> {
|
||||
|
||||
Reference in New Issue
Block a user