chore: initialize from frontend template 4dc033c

This commit is contained in:
DongHyeonka
2026-08-13 18:23:26 +09:00
commit 40107eec84
897 changed files with 234824 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
/**
* §6.4–§6.5. The only boot-time JSON reader.
*
* `response.json()` and unbounded `response.text()` are prohibited: a hostile or
* misconfigured origin must not be able to amplify boot memory, and an HTML
* error page must not reach `JSON.parse` as if it were configuration.
*/
export type BootJsonOperation = "RUNTIME_CONFIG" | "RELEASE_MANIFEST";
export interface BootJsonPolicy {
readonly operation: BootJsonOperation;
readonly maximumBytes: number;
readonly totalDeadlineMs: 5_000;
}
export const BOOT_JSON_POLICIES = Object.freeze({
RUNTIME_CONFIG: Object.freeze({
operation: "RUNTIME_CONFIG" as const,
maximumBytes: 65_536,
totalDeadlineMs: 5_000 as const,
}),
RELEASE_MANIFEST: Object.freeze({
operation: "RELEASE_MANIFEST" as const,
maximumBytes: 1_048_576,
totalDeadlineMs: 5_000 as const,
}),
} satisfies Readonly<Record<BootJsonOperation, BootJsonPolicy>>);
export type BootLoadFailure =
| "FETCH_FAILED"
| "TIMEOUT"
| "HTTP_STATUS_INVALID"
| "CONTENT_TYPE_INVALID"
| "BODY_TOO_LARGE"
| "UTF8_INVALID"
| "JSON_INVALID"
| "SHAPE_INVALID"
| "SECRET_NAME_REJECTED"
| "SCHEMA_INVALID"
| "BUILD_MISMATCH"
| "RELEASE_MISMATCH"
| "ASSET_MISMATCH"
| "CONTRACT_SET_MISMATCH";
export type BootJsonOutcome =
| Readonly<{ ok: true; value: Readonly<Record<string, unknown>> }>
| Readonly<{ ok: false; failure: BootLoadFailure }>;
export type ReadBoundedBootJsonOptions = Readonly<{
fetcher?: typeof fetch;
signal?: AbortSignal;
}>;
function isJsonMediaType(headerValue: string | null): boolean {
if (!headerValue) return false;
const essence = headerValue.split(";", 1)[0]?.trim().toLowerCase() ?? "";
return essence === "application/json" || essence.endsWith("+json");
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
export async function readBoundedBootJson(
url: string,
policy: BootJsonPolicy,
options: ReadBoundedBootJsonOptions = {},
): Promise<BootJsonOutcome> {
if (options.signal?.aborted) return fail("FETCH_FAILED");
const fetcher = options.fetcher ?? fetch;
const controller = new AbortController();
let timedOut = false;
const timer = setTimeout(() => {
timedOut = true;
controller.abort();
}, policy.totalDeadlineMs);
const forwardAbort = () => controller.abort();
options.signal?.addEventListener("abort", forwardAbort, { once: true });
try {
let response: Response;
try {
response = await fetcher(url, {
method: "GET",
cache: "no-store",
credentials: "same-origin",
redirect: "error",
referrerPolicy: "no-referrer",
headers: { Accept: "application/json" },
signal: controller.signal,
});
} catch {
return fail(timedOut ? "TIMEOUT" : "FETCH_FAILED");
}
if (response.status !== 200 || response.redirected) {
await discard(response);
return fail("HTTP_STATUS_INVALID");
}
if (!isJsonMediaType(response.headers.get("content-type"))) {
await discard(response);
return fail("CONTENT_TYPE_INVALID");
}
const declared = Number(response.headers.get("content-length"));
if (Number.isFinite(declared) && declared > policy.maximumBytes) {
await discard(response);
return fail("BODY_TOO_LARGE");
}
const bytes = await readBoundedBytes(response, policy.maximumBytes, controller);
if (bytes === "TOO_LARGE") return fail("BODY_TOO_LARGE");
if (bytes === "STREAM_FAILED") return fail(timedOut ? "TIMEOUT" : "FETCH_FAILED");
let text: string;
try {
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
} catch {
return fail("UTF8_INVALID");
}
let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch {
return fail("JSON_INVALID");
}
if (!isRecord(parsed)) return fail("SHAPE_INVALID");
return Object.freeze({ ok: true as const, value: parsed });
} finally {
clearTimeout(timer);
options.signal?.removeEventListener("abort", forwardAbort);
}
}
async function readBoundedBytes(
response: Response,
maximumBytes: number,
controller: AbortController,
): Promise<Uint8Array | "TOO_LARGE" | "STREAM_FAILED"> {
const body = response.body;
if (!body) {
// A body-less 200 cannot satisfy any boot document.
return new Uint8Array(0);
}
const reader = body.getReader();
const chunks: Uint8Array[] = [];
let total = 0;
try {
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (!value) continue;
total += value.byteLength;
if (total > maximumBytes) {
await reader.cancel().catch(() => {});
controller.abort();
return "TOO_LARGE";
}
chunks.push(value);
}
} catch {
await reader.cancel().catch(() => {});
return "STREAM_FAILED";
}
const output = new Uint8Array(total);
let offset = 0;
for (const chunk of chunks) {
output.set(chunk, offset);
offset += chunk.byteLength;
}
return output;
}
async function discard(response: Response): Promise<void> {
try {
await response.body?.cancel();
} catch {
// Cancelling an already-settled body is not a boot failure.
}
}
function fail(failure: BootLoadFailure): BootJsonOutcome {
return Object.freeze({ ok: false as const, failure });
}