babc42c2ff
- F1 [HIGH — data corruption]: @fastify/compress was compressing 206/Range attachment responses while Content-Range still described the RAW offsets, so a resuming client (curl -C -, download managers) appended encoded bytes as raw → corrupted file. sendFileResponse now sets the request header `x-no-compression` (the documented @fastify/compress opt-out — its onSend skips when the request carries it; the reviewer's `Content-Encoding: identity` does NOT work because compress explicitly excludes `identity` and overwrites it). This opts the whole download route (both 200 full-file and 206 range) out of on-the-fly compression — correct, since attachment bytes are final and mostly binary. - F2: static responses now emit `Vary: Accept-Encoding` (the preCompressed content-negotiated /assets/* were `immutable` without Vary → shared-cache could serve a brotli variant to an identity/gzip-only client). - F3: vite compression `include` extended to .wasm/.onnx so the VAD binaries (~26MB .wasm, ~2.3MB .onnx under public/vad) are precompressed at build (.br emitted) instead of runtime-brotli'd on every request. (include REPLACES the plugin default, so the default js/css/json/html set is re-listed.) - F4: extracted the cache classification into a pure `resolveStaticAssetHeaders` + static.module.spec.ts (3 tests: /assets/* immutable+Vary, index.html no-store, non-hashed not-immutable). Gate: server tsc 0 (deps present), static.module.spec 3/3, client build emits .wasm.br/.onnx.br, frozen install 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import { resolveStaticAssetHeaders } from './static.module';
|
|
|
|
// Unit tests for the static-asset cache classifier extracted from the
|
|
// @fastify/static setHeaders callback (precedent: sandbox.controller.spec.ts).
|
|
describe('resolveStaticAssetHeaders', () => {
|
|
it('marks a content-hashed /assets/ file immutable and sets Vary', () => {
|
|
const headers = resolveStaticAssetHeaders(
|
|
'/app/apps/client/dist/assets/index-a1b2c3.js',
|
|
);
|
|
expect(headers['cache-control']).toBe(
|
|
'public, max-age=31536000, immutable',
|
|
);
|
|
expect(headers['vary']).toBe('Accept-Encoding');
|
|
});
|
|
|
|
it('makes index.html always revalidate (never immutable)', () => {
|
|
const headers = resolveStaticAssetHeaders(
|
|
'/app/apps/client/dist/index.html',
|
|
);
|
|
expect(headers['cache-control']).toBe(
|
|
'no-cache, no-store, must-revalidate',
|
|
);
|
|
expect(headers['vary']).toBe('Accept-Encoding');
|
|
});
|
|
|
|
it('does NOT mark a non-hashed asset immutable but still sets Vary', () => {
|
|
const headers = resolveStaticAssetHeaders(
|
|
'/app/apps/client/dist/locales/en.json',
|
|
);
|
|
// No immutable cache-control — this path keeps @fastify/static's default
|
|
// etag/last-modified revalidation.
|
|
expect(headers['cache-control']).toBeUndefined();
|
|
expect(headers['vary']).toBe('Accept-Encoding');
|
|
});
|
|
});
|