upload current sources
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Public error phrases — the ONLY text derived from a caught error that may
|
||||
* reach a wire response (`HealthStatus.poller.lastError`, `SyncRun.error`,
|
||||
* `SyncObjectProgress.error`; governed contract docs/api/worker.openapi.json).
|
||||
*
|
||||
* Allowlist keyed on error class / upstream HTTP status, mirroring the 502
|
||||
* mapping in server.ts: a raw `err.message` is never copied out, because
|
||||
* Arena error bodies can echo request data (and, for the future S/4HANA leg,
|
||||
* OData error payloads can echo business data). Anything not on the list gets
|
||||
* the unclassified phrase; the full error stays in the worker log, which is
|
||||
* where diagnosis belongs (index.ts logs `{ name, status, message }`).
|
||||
*
|
||||
* `TimeoutError` is what Node's fetch rejects with when the request's
|
||||
* `AbortSignal.timeout(...)` fires (measured on this box, Node 24.15.0,
|
||||
* 2026-09-14: `name=TimeoutError`, a DOMException).
|
||||
*/
|
||||
|
||||
import { ArenaApiError } from '../arena/session.js';
|
||||
import { IngestBufferFullError } from '../poller/arenaDeltaPoll.js';
|
||||
|
||||
export const UNCLASSIFIED_ERROR_PHRASE = 'Unclassified worker error; see worker logs.';
|
||||
export const INGEST_BUFFER_FULL_PHRASE = 'Ingest buffer full at poll time.';
|
||||
export const ARENA_TIMEOUT_PHRASE = 'Upstream Arena PLM request timed out.';
|
||||
export const ARENA_MALFORMED_PHRASE = 'Upstream Arena PLM response was malformed.';
|
||||
|
||||
export function arenaStatusPhrase(status: number): string {
|
||||
// A 2xx ArenaApiError is a response we could not interpret (e.g. login
|
||||
// answered 200 without an arenaSessionId), not an upstream failure code.
|
||||
if (status >= 200 && status < 300) return ARENA_MALFORMED_PHRASE;
|
||||
return `Upstream Arena PLM request failed (HTTP ${status}).`;
|
||||
}
|
||||
|
||||
export function publicErrorPhrase(err: unknown): string {
|
||||
if (err instanceof ArenaApiError) return arenaStatusPhrase(err.status);
|
||||
if (err instanceof IngestBufferFullError) return INGEST_BUFFER_FULL_PHRASE;
|
||||
if (hasName(err, 'TimeoutError')) return ARENA_TIMEOUT_PHRASE;
|
||||
return UNCLASSIFIED_ERROR_PHRASE;
|
||||
}
|
||||
|
||||
/** Log-safe summary of an error: class name, message, and upstream status if any. */
|
||||
export function describeErrorForLog(err: unknown): { name: string; message: string; status?: number } {
|
||||
if (err instanceof ArenaApiError) return { name: err.name, message: err.message, status: err.status };
|
||||
if (err instanceof Error) return { name: err.name, message: err.message };
|
||||
return { name: typeof err, message: String(err) };
|
||||
}
|
||||
|
||||
function hasName(err: unknown, name: string): boolean {
|
||||
return typeof err === 'object' && err !== null && 'name' in err && (err as { name: unknown }).name === name;
|
||||
}
|
||||
Reference in New Issue
Block a user