A file that declares nothing but types and that nothing in the index depends on — a hand-written ambient `.d.ts` of global shims, vendored typings, module augmentation — cannot answer a flow question: no bodies, no call edges, no behaviour, nothing typed by it. But the identifiers it declares are exactly the generic ones a prose question uses (`Body`, `Message`, `ImageMetadata`, `ReadableStream`), so on term overlap it out-scored the implementation. Measured on the new fixture: rank #1 and 51% of delivered source, with the flow's own entry file pushed out of the response entirely. Measured first, per the issue: the Wrangler `worker-configuration.d.ts` that opened this is already handled by CG-25's banner detection, worth 15-46 points of envelope share across four flow queries. CG-25 credited; only the un-bannered case needed anything. `rankPenalty` now multiplies score and graph mass by 0.5 for such files, taken as the STRONGER of it and the generated penalty rather than multiplied — one property two signals see must not be charged twice. Detection is structural, not by extension, and four conditions deep. Two of them were forced by measurement: requiring every symbol to be type-level takes the corpus flag rate from 1-18% (which swept in Kotlin sealed classes, Rust mod.rs re-exports and django's locale tables) down to 0-4%; requiring that nothing depends on the file separates an ambient shim from a working types module, and without it the rule demoted displacement-ts's pipeline `types.ts` and broke the CG-31 gate. A query that NAMES a declared type is exempt, so a question about a type still reaches its declaration at full weight. Precise tokens only, so "…the file body…" cannot exempt a `Body` interface it never meant to name; this needs its own set because `namedSeedIds` is callable-only and a type never becomes one. Regression evidence in docs/benchmarks/explore-declaration-only-cg28.md: 6-repo envelope sweep byte-identical against a clean baseline build, zero ambient files reach the candidate set on VS Code across five queries, corpus flag rate 0-0.74%, both allocation fixtures PASS, full suite 2,978 green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
45 lines
1.2 KiB
TypeScript
45 lines
1.2 KiB
TypeScript
export interface ParsedUpload {
|
|
ok: true;
|
|
key: string;
|
|
body: ReadableStream<Uint8Array>;
|
|
contentType: string;
|
|
width: number;
|
|
height: number;
|
|
format: string;
|
|
}
|
|
|
|
export interface ParseFailure {
|
|
ok: false;
|
|
error: string;
|
|
}
|
|
|
|
/**
|
|
* Pull the object key, declared dimensions and the raw body stream off an
|
|
* upload request. Never buffers the body — the stream is handed straight to
|
|
* the storage layer.
|
|
*/
|
|
export async function parseUploadRequest(
|
|
request: Request,
|
|
): Promise<ParsedUpload | ParseFailure> {
|
|
const url = new URL(request.url);
|
|
const key = url.searchParams.get('key');
|
|
if (!key) return { ok: false, error: 'missing key' };
|
|
if (!request.body) return { ok: false, error: 'missing body' };
|
|
|
|
return {
|
|
ok: true,
|
|
key,
|
|
body: request.body as ReadableStream<Uint8Array>,
|
|
contentType: request.headers.get('content-type') ?? 'application/octet-stream',
|
|
width: numberParam(url, 'width'),
|
|
height: numberParam(url, 'height'),
|
|
format: url.searchParams.get('format') ?? 'jpeg',
|
|
};
|
|
}
|
|
|
|
function numberParam(url: URL, name: string): number {
|
|
const raw = url.searchParams.get(name);
|
|
const parsed = raw ? Number.parseInt(raw, 10) : 0;
|
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
}
|