mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-18 21:02:50 +03:00
Merged after a maintainer rework that kept every one of @HouMinXi's commits intact. **What the rework added on top of the contribution:** the new DB health-check behaviour is gated behind a default-off feature flag (`src/shared/constants/featureFlagDefinitions.ts`, `defaultValue: "false"`), documented in `docs/reference/FEATURE_FLAGS.md` with the description key carried into all 66 locales, so the release default is unchanged and the new bounds only apply when an operator opts in. The optional-FTS5 migration set was reconciled by hand with the "180" entry that landed meanwhile (`src/lib/db/migrationRunner/constants.ts`). **Carried from your rebased head:** the `/api/db/health` local-only classification in `src/server/authz/routeGuard.ts` plus its `routeGuard` assertion — `runManagedDbHealthCheck()` forks native diagnostics into a child process, so Hard Rules #15/#17 apply. Re-verified here: 37 pass / 0 fail. Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thank you for the depth of this one — the resource-bounds suite and the sql.js startup/backup coverage are the kind of tests that keep a database layer honest.
78 lines
2.5 KiB
TypeScript
78 lines
2.5 KiB
TypeScript
type DirectFetchOptions = RequestInit & { dispatcher?: unknown };
|
|
type DirectFetch = (
|
|
input: RequestInfo | URL,
|
|
options: DirectFetchOptions
|
|
) => Promise<Response>;
|
|
|
|
const DEFAULT_DIRECT_HEADERS_TIMEOUT_MS = 30_000;
|
|
const DIRECT_RESPONSE_START_TIMEOUT_CODE = "DIRECT_RESPONSE_START_TIMEOUT";
|
|
|
|
export function resolveDirectHeadersTimeoutMs(
|
|
env: Record<string, string | undefined> = process.env
|
|
): number {
|
|
const raw = env.OMNIROUTE_DIRECT_HEADERS_TIMEOUT_MS;
|
|
if (raw == null || raw.trim() === "") return DEFAULT_DIRECT_HEADERS_TIMEOUT_MS;
|
|
const parsed = Number(raw);
|
|
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
}
|
|
|
|
function createDirectResponseStartTimeout(timeoutMs: number): Error & { code: string } {
|
|
const err = new Error(
|
|
`Direct response did not start within ${timeoutMs}ms — retrying on a fresh socket`
|
|
) as Error & { code: string };
|
|
err.name = "TimeoutError";
|
|
err.code = DIRECT_RESPONSE_START_TIMEOUT_CODE;
|
|
return err;
|
|
}
|
|
|
|
export function isDirectResponseStartTimeout(err: unknown): boolean {
|
|
return (
|
|
!!err &&
|
|
typeof err === "object" &&
|
|
"code" in err &&
|
|
err.code === DIRECT_RESPONSE_START_TIMEOUT_CODE
|
|
);
|
|
}
|
|
|
|
function mergeAbortSignals(
|
|
primary: AbortSignal | null | undefined,
|
|
secondary: AbortSignal
|
|
): AbortSignal {
|
|
if (!primary) return secondary;
|
|
if (primary.aborted) return primary;
|
|
const controller = new AbortController();
|
|
const onPrimaryAbort = () => controller.abort(primary.reason);
|
|
const onSecondaryAbort = () => controller.abort(secondary.reason);
|
|
const cleanup = () => {
|
|
primary.removeEventListener("abort", onPrimaryAbort);
|
|
secondary.removeEventListener("abort", onSecondaryAbort);
|
|
};
|
|
primary.addEventListener("abort", onPrimaryAbort, { once: true });
|
|
secondary.addEventListener("abort", onSecondaryAbort, { once: true });
|
|
controller.signal.addEventListener("abort", cleanup, { once: true });
|
|
return controller.signal;
|
|
}
|
|
|
|
export async function directFetchWithBoundedResponseStart(
|
|
input: RequestInfo | URL,
|
|
options: DirectFetchOptions,
|
|
fetchImpl: DirectFetch,
|
|
timeoutMs: number
|
|
): Promise<Response> {
|
|
if (!timeoutMs || timeoutMs <= 0) return fetchImpl(input, options);
|
|
const attemptController = new AbortController();
|
|
const timer = setTimeout(
|
|
() => attemptController.abort(createDirectResponseStartTimeout(timeoutMs)),
|
|
timeoutMs
|
|
);
|
|
timer.unref?.();
|
|
try {
|
|
return await fetchImpl(input, {
|
|
...options,
|
|
signal: mergeAbortSignals(options.signal, attemptController.signal),
|
|
});
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
}
|