body.mjs
52 lines and 3 functions in DrDustinEdwards/site-runtime, at commit 89e9d58 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| readCappedBytes | 14 to 43 | 11 | 100% | WatchCRAP 11 | No data: mutation testing has not run on this repo yet |
| <anonymous:28> | 28 to 28 | 1 | 100% | OkCRAP 1 | No data: mutation testing has not run on this repo yet |
| readCapped | 51 to 54 | 2 | 100% | OkCRAP 2 | No data: mutation testing has not run on this repo yet |
Imports
It imports no other module of this repo. Imported by csp-report.mjs.
Source
/**
* A request body read to a byte ceiling, counted as the chunks arrive. The declared Content-Length can be absent or
* understated, and `request.text()` holds the whole body in memory before any check can run, so neither is a cap.
*
* Over the ceiling is `null`, never a truncated body, so the caller answers 413 instead of parsing a fragment. A
* stream that fails mid-read THROWS: an empty body is no safe stand-in, because every caller parses what it gets.
*/
/**
* @param {{ body: ReadableStream<Uint8Array> | null }} source a Request, or anything with a body stream
* @param {number} max the ceiling in bytes
* @returns {Promise<Uint8Array | null>} the body (empty when there is none), or null when it is longer than `max`
*/
export async function readCappedBytes(source, max) {
if (!Number.isInteger(max) || max < 0) throw new RangeError(`the ceiling must be a whole number of bytes, got ${max}`);
if (!source.body) return new Uint8Array(0);
const reader = source.body.getReader();
/** @type {Uint8Array[]} */
const chunks = [];
let total = 0;
try {
for (;;) {
const { done, value } = await reader.read();
if (done) break;
if (!value) continue;
total += value.byteLength;
if (total > max) {
await reader.cancel().catch(() => {});
return null;
}
chunks.push(value);
}
} catch (error) {
throw new Error(`the body stream failed after ${total} bytes: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
}
const joined = new Uint8Array(total);
let at = 0;
for (const chunk of chunks) {
joined.set(chunk, at);
at += chunk.byteLength;
}
return joined;
}
/**
* `readCappedBytes`, decoded as UTF-8: "" for no body, null when over the ceiling.
* @param {{ body: ReadableStream<Uint8Array> | null }} source
* @param {number} max
* @returns {Promise<string | null>}
*/
export async function readCapped(source, max) {
const bytes = await readCappedBytes(source, max);
return bytes === null ? null : new TextDecoder().decode(bytes);
}