refactor: centralize typed GSDToolsError construction
This commit is contained in:
16
sdk/src/gsd-tools-error.test.ts
Normal file
16
sdk/src/gsd-tools-error.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { GSDToolsError } from './gsd-tools-error.js';
|
||||
|
||||
describe('GSDToolsError constructors', () => {
|
||||
it('builds timeout-classified errors', () => {
|
||||
const err = GSDToolsError.timeout('timeout', 'state', ['load'], '', 1000);
|
||||
expect(err.classification).toEqual({ kind: 'timeout', timeoutMs: 1000 });
|
||||
expect(err.exitCode).toBeNull();
|
||||
});
|
||||
|
||||
it('builds failure-classified errors', () => {
|
||||
const err = GSDToolsError.failure('boom', 'state', ['load'], 1);
|
||||
expect(err.classification).toEqual({ kind: 'failure' });
|
||||
expect(err.exitCode).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -25,5 +25,41 @@ export class GSDToolsError extends Error {
|
||||
this.classification = options?.classification;
|
||||
}
|
||||
|
||||
static timeout(
|
||||
message: string,
|
||||
command: string,
|
||||
args: string[],
|
||||
stderr = '',
|
||||
timeoutMs?: number,
|
||||
options?: { cause?: unknown; exitCode?: number | null },
|
||||
): GSDToolsError {
|
||||
return new GSDToolsError(
|
||||
message,
|
||||
command,
|
||||
args,
|
||||
options?.exitCode ?? null,
|
||||
stderr,
|
||||
{ cause: options?.cause, classification: timeoutClassification(timeoutMs) },
|
||||
);
|
||||
}
|
||||
|
||||
static failure(
|
||||
message: string,
|
||||
command: string,
|
||||
args: string[],
|
||||
exitCode: number | null,
|
||||
stderr = '',
|
||||
options?: { cause?: unknown },
|
||||
): GSDToolsError {
|
||||
return new GSDToolsError(
|
||||
message,
|
||||
command,
|
||||
args,
|
||||
exitCode,
|
||||
stderr,
|
||||
{ cause: options?.cause, classification: failureClassification() },
|
||||
);
|
||||
}
|
||||
|
||||
public readonly classification?: GSDToolsErrorClassification;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { QuerySubprocessAdapter } from './query-subprocess-adapter.js';
|
||||
import { QueryNativeDirectAdapter } from './query-native-direct-adapter.js';
|
||||
import { QueryNativeHotpathAdapter } from './query-native-hotpath-adapter.js';
|
||||
import { formatQueryRawOutput } from './query-raw-output-projection.js';
|
||||
import { failureClassification, GSDToolsError, timeoutClassification } from './gsd-tools-error.js';
|
||||
import { GSDToolsError } from './gsd-tools-error.js';
|
||||
|
||||
export interface GSDToolsRuntime {
|
||||
registry: ReturnType<typeof createRegistry>;
|
||||
@@ -34,16 +34,16 @@ export function createGSDToolsRuntime(opts: {
|
||||
timeoutMs: opts.timeoutMs,
|
||||
workstream: opts.workstream,
|
||||
createToolsError: (message, command, args, exitCode, stderr, classification) =>
|
||||
new GSDToolsError(message, command, args, exitCode, stderr, {
|
||||
classification: classification ?? failureClassification(),
|
||||
}),
|
||||
classification?.kind === 'timeout'
|
||||
? GSDToolsError.timeout(message, command, args, stderr, classification.timeoutMs, { exitCode })
|
||||
: GSDToolsError.failure(message, command, args, exitCode, stderr),
|
||||
});
|
||||
|
||||
const nativeDirectAdapter = new QueryNativeDirectAdapter({
|
||||
timeoutMs: opts.timeoutMs,
|
||||
dispatch: (registryCommand, registryArgs) => registry.dispatch(registryCommand, registryArgs, opts.projectDir),
|
||||
createTimeoutError: (message, command, args) =>
|
||||
new GSDToolsError(message, command, args, null, '', { classification: timeoutClassification(opts.timeoutMs) }),
|
||||
GSDToolsError.timeout(message, command, args, '', opts.timeoutMs),
|
||||
});
|
||||
|
||||
const transport = new GSDTransport(registry, {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GSDError, exitCodeFor } from './errors.js';
|
||||
import { failureClassification, GSDToolsError, timeoutClassification } from './gsd-tools-error.js';
|
||||
import { GSDToolsError } from './gsd-tools-error.js';
|
||||
import { errorMessage, toFailureSignal } from './query-failure-classification.js';
|
||||
|
||||
/**
|
||||
@@ -7,7 +7,7 @@ import { errorMessage, toFailureSignal } from './query-failure-classification.js
|
||||
*/
|
||||
export function toGSDToolsError(command: string, args: string[], err: unknown): GSDToolsError {
|
||||
if (err instanceof GSDError) {
|
||||
return new GSDToolsError(
|
||||
return GSDToolsError.failure(
|
||||
err.message,
|
||||
command,
|
||||
args,
|
||||
@@ -19,18 +19,9 @@ export function toGSDToolsError(command: string, args: string[], err: unknown):
|
||||
|
||||
const msg = errorMessage(err);
|
||||
const signal = toFailureSignal(err);
|
||||
const classification = signal.kind === 'timeout'
|
||||
? timeoutClassification(signal.timeoutMs)
|
||||
: failureClassification();
|
||||
if (signal.kind === 'timeout') {
|
||||
return GSDToolsError.timeout(msg, command, args, '', signal.timeoutMs, err instanceof Error ? { cause: err } : undefined);
|
||||
}
|
||||
|
||||
return new GSDToolsError(
|
||||
msg,
|
||||
command,
|
||||
args,
|
||||
1,
|
||||
'',
|
||||
err instanceof Error
|
||||
? { cause: err, classification }
|
||||
: { classification },
|
||||
);
|
||||
return GSDToolsError.failure(msg, command, args, 1, '', err instanceof Error ? { cause: err } : undefined);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user