Severity: Medium
Two independent piles of duplication that bloat the api.
Pattern 1: per-domain HTTP fetch helper
Six modules repeat the exact same fetch + AbortSignal.timeout + if !res.ok throw pattern with subtly different error message formats:
sisyphus-api/src/runner/aevatar-client.ts
sisyphus-api/src/workflows/mainnet-client.ts
sisyphus-api/src/workflows/ornn-client.ts
sisyphus-api/src/workflows/reference-resolver.ts
sisyphus-api/src/ingest/chrono-graph-client.ts
sisyphus-api/src/papers/storage-client.ts
Pattern repeated ~12 times:
const resp = await fetch(url, { signal: AbortSignal.timeout(N) });
if (!resp.ok) {
const body = await resp.text();
throw new Error(`...HTTP ${resp.status} — ${body}`);
}
const result = await resp.json() as ResultType;
Pattern 2: paginated-list + partial-update CRUD
Three controllers reimplement the same two helpers ~3 times each:
sisyphus-api/src/workflows/controllers/WorkflowController.ts:96-117 (list), :158-167 (partial update).
sisyphus-api/src/workflows/controllers/ConnectorController.ts:101-122 (list), ConnectorController.ts (partial update).
sisyphus-api/src/schemas/controllers/SchemaController.ts:107-128 (list), :178-186 (partial update).
Remediation
Add one sisyphus-api/src/shared/http.ts:
export interface HttpJsonOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: Record<string, string>;
body?: unknown;
timeoutMs?: number; // default 15_000
authorization?: string;
}
export async function httpJson<T>(url: string, opts: HttpJsonOptions = {}): Promise<T> {
const { method = 'GET', headers = {}, body, timeoutMs = 15_000, authorization } = opts;
const finalHeaders = { 'Content-Type': 'application/json', ...headers };
if (authorization) finalHeaders.Authorization = authorization;
const resp = await fetch(url, {
method,
headers: finalHeaders,
body: body == null ? undefined : JSON.stringify(body),
signal: AbortSignal.timeout(timeoutMs),
});
if (!resp.ok) {
const text = await resp.text().catch(() => '');
throw new UpstreamError(resp.status, text, url); // see #23 — opaque code, full text logged
}
return resp.json() as Promise<T>;
}
Add sisyphus-api/src/shared/crud.ts:
export async function paginatedList<T>(
col: Collection<T>,
filter: Filter<T>,
page: number, pageSize: number,
sort?: Record<string, 1 | -1>,
): Promise<{ items: T[]; total: number; page: number; pageSize: number }> { ... }
export function pickDefined<T>(body: Partial<T>, keys: (keyof T)[]): Partial<T> { ... }
Halves these files; centralizes timeouts, error mapping (per #23), pagination clamps (per #34).
Severity: Medium
Two independent piles of duplication that bloat the api.
Pattern 1: per-domain HTTP fetch helper
Six modules repeat the exact same
fetch + AbortSignal.timeout + if !res.ok throwpattern with subtly different error message formats:sisyphus-api/src/runner/aevatar-client.tssisyphus-api/src/workflows/mainnet-client.tssisyphus-api/src/workflows/ornn-client.tssisyphus-api/src/workflows/reference-resolver.tssisyphus-api/src/ingest/chrono-graph-client.tssisyphus-api/src/papers/storage-client.tsPattern repeated ~12 times:
Pattern 2: paginated-list + partial-update CRUD
Three controllers reimplement the same two helpers ~3 times each:
sisyphus-api/src/workflows/controllers/WorkflowController.ts:96-117(list),:158-167(partial update).sisyphus-api/src/workflows/controllers/ConnectorController.ts:101-122(list),ConnectorController.ts(partial update).sisyphus-api/src/schemas/controllers/SchemaController.ts:107-128(list),:178-186(partial update).Remediation
Add one
sisyphus-api/src/shared/http.ts:Add
sisyphus-api/src/shared/crud.ts:Halves these files; centralizes timeouts, error mapping (per #23), pagination clamps (per #34).