chore: repo-wide bloat sweep, remove dead code and fold duplicate helpers (#2150)
* chore: repo-wide bloat sweep, remove dead code and fold duplicate helpers Remove 33 dead files, ~270 unreferenced exports/types, 13 dead i18n namespaces and 4 unused dependencies; fold byte-identical helper copies into one canonical home each (lib/utils chunk/sleep/utcDateStamp, lib/dates/iso, lib/invariants/uuid, lib/xml/escape, lib/reports/sru/format, lib/pdf/number-text, lib/browser/panel-request, lib/api/v1/body + v1ValidationError rolled out to ~55 v1 routes, booking-template schemas). No behaviour change: v1 bodies and status codes, MCP tool schemas, DB writes and money math are untouched. Naive ore rounding was deliberately not swapped for roundOre; see DECISIONS.md 2026-09-02 for the full list of things left alone on purpose. tsc, lint, 19588 unit tests and check:guards green; antipattern baseline ratcheted (naive-ore-round 622 -> 620, hand-rolled-invariant 115 -> 113). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(transactions): import RawTransaction from @/types after the ingest re-export removal CI's type ratchet (check:types, full tsconfig) caught the one test file that still imported the type through lib/transactions/ingest. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
Jakob Wennberg
parent
18cbc4c30a
commit
f266c386f3
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* The settings panels' server calls (Stripe, Shopify, WooCommerce), each
|
||||
* classified into exactly one outcome. Never throws: every arm resolves to a
|
||||
* member of the union, so a call site cannot have a silent path by forgetting
|
||||
* a `catch`. One toast sentence per click; the classification lives outside
|
||||
* the component because component logic has no tests in this repo.
|
||||
*/
|
||||
|
||||
import { fetchWithTimeout, isTimeoutError } from '@/lib/http/fetch-with-timeout'
|
||||
import { getErrorMessage, type ErrorLocale } from '@/lib/errors/get-error-message'
|
||||
import type { ActionFailure } from '@/lib/browser/action-failure'
|
||||
|
||||
/** Deadline for the quick calls (status, toggle, disconnect). */
|
||||
export const PANEL_ACTION_TIMEOUT_MS = 15_000
|
||||
|
||||
export type PanelRequestResult<T> =
|
||||
/** 2xx. `data` is null when the body was not readable JSON. */
|
||||
| { ok: true; data: T | null }
|
||||
| ActionFailure
|
||||
|
||||
export interface PanelRequestOptions {
|
||||
url: string
|
||||
/** Defaults to POST: most of the panel calls are mutations. */
|
||||
method?: 'GET' | 'POST' | 'DELETE'
|
||||
/** JSON request body. Omitted entirely for the routes that ignore it. */
|
||||
body?: unknown
|
||||
/** UI locale, so a server error is reported in the language the user reads. */
|
||||
locale?: ErrorLocale
|
||||
timeoutMs?: number
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null
|
||||
}
|
||||
|
||||
/**
|
||||
* The one sentence for a non-2xx.
|
||||
*
|
||||
* The extension routes refuse with a hand-written Swedish sentence in
|
||||
* `{ error }`, and that sentence is the most specific thing anyone can say:
|
||||
* "Inget anslutet Stripe-konto." tells the user to reconnect, where the status
|
||||
* map's "Resursen kunde inte hittas." tells them nothing. `getErrorMessage`
|
||||
* keeps such a sentence only when it happens to carry one of its Swedish
|
||||
* trigger words, which this route copy does not, so the string is preferred
|
||||
* explicitly.
|
||||
*
|
||||
* `error_en` is honoured first for an English UI because the shared capability
|
||||
* guard emits both (`capabilityBlockedResponse` in
|
||||
* lib/entitlements/has-capability.ts), and `getErrorMessage` only reads
|
||||
* `message_en` inside a structured envelope, not a top-level `error_en`.
|
||||
*
|
||||
* Everything else is `getErrorMessage`'s: a structured envelope, an HTML 502
|
||||
* from the platform, a body that never parsed.
|
||||
*/
|
||||
export function serverErrorMessage(
|
||||
body: unknown,
|
||||
status: number,
|
||||
locale: ErrorLocale,
|
||||
): string {
|
||||
if (isRecord(body)) {
|
||||
if (locale === 'en' && typeof body.error_en === 'string' && body.error_en.trim()) {
|
||||
return body.error_en.trim()
|
||||
}
|
||||
if (typeof body.error === 'string' && body.error.trim()) {
|
||||
return body.error.trim()
|
||||
}
|
||||
}
|
||||
return getErrorMessage(body, { statusCode: status, locale })
|
||||
}
|
||||
|
||||
/** Call one of a panel's endpoints and report exactly why it failed. */
|
||||
export async function panelRequest<T>({
|
||||
url,
|
||||
method = 'POST',
|
||||
body,
|
||||
locale = 'sv',
|
||||
timeoutMs = PANEL_ACTION_TIMEOUT_MS,
|
||||
}: PanelRequestOptions): Promise<PanelRequestResult<T>> {
|
||||
try {
|
||||
const res = await fetchWithTimeout(
|
||||
url,
|
||||
body === undefined
|
||||
? { method }
|
||||
: {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
{ timeoutMs, description: `${method} ${url}` },
|
||||
)
|
||||
|
||||
// Read the body on both arms: the failure arm needs the route's own
|
||||
// sentence, the success arm needs the sync counts. A body that is not JSON
|
||||
// (an HTML error page, an empty 502, a response truncated mid-stream) leaves
|
||||
// null, and neither arm then claims anything it cannot support.
|
||||
const payload = await res.json().catch(() => null)
|
||||
|
||||
if (!res.ok) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: 'server',
|
||||
status: res.status,
|
||||
message: serverErrorMessage(payload, res.status, locale),
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true, data: payload as T | null }
|
||||
} catch (err) {
|
||||
if (isTimeoutError(err)) return { ok: false, reason: 'timeout' }
|
||||
return { ok: false, reason: 'network', message: getErrorMessage(err, { locale }) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user