Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ffee0e6
fix(cloudflare): scale cacheability probing
james-elicx Aug 27, 2026
06489ce
fix(cloudflare): preserve cache policy probe identities
james-elicx Aug 27, 2026
c0e329f
test(cache): cover conditional response vetoes
james-elicx Aug 27, 2026
2889006
fix(cache): match header policy sources exactly
james-elicx Aug 27, 2026
8bb244b
perf(cache): compute probe safety per pattern
james-elicx Aug 27, 2026
957e20b
fix(build): omit empty route ownership metadata
james-elicx Aug 27, 2026
83394fd
fix(cloudflare): classify cacheability per concrete path
james-elicx Aug 27, 2026
2da8056
fix(cloudflare): retain zero-path static fallbacks
james-elicx Aug 27, 2026
034a97d
test(cloudflare): cover on-demand static fallbacks
james-elicx Aug 27, 2026
4de0594
fix(cloudflare): admit custom vary cache variants
james-elicx Aug 27, 2026
0e03037
fix(cloudflare): keep wildcard vary private
james-elicx Aug 27, 2026
15c1835
fix(cloudflare): reject wildcard vary during probing
james-elicx Aug 27, 2026
9c660ab
fix(cloudflare): reject empty Cache Components params
james-elicx Aug 27, 2026
cc784c9
fix(cloudflare): scope empty params validation to pages
james-elicx Aug 27, 2026
99498f5
fix(cloudflare): remove cacheability response size limits
james-elicx Aug 27, 2026
5b34270
fix(cloudflare): bound completed cache admission
james-elicx Aug 27, 2026
dc769c4
fix(cloudflare): release failed admission captures
james-elicx Aug 27, 2026
b9b1de5
fix(cloudflare): honor staged readiness retries
james-elicx Aug 27, 2026
e261507
fix(cloudflare): retain probed manifest in dist
james-elicx Aug 27, 2026
b2d5c92
fix(cloudflare): probe staged readiness without rendering
james-elicx Aug 27, 2026
5036225
fix(cloudflare): verify staged readiness response
james-elicx Aug 27, 2026
0bc7f63
fix(cloudflare): reserve staged readiness endpoint
james-elicx Aug 27, 2026
ffa711f
fix(cloudflare): relax cacheability manifest bounds
james-elicx Sep 1, 2026
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cloudflare/src/cache/cdn-adapter.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ function formatCacheTag(tags: readonly string[]): string | null {

export class CloudflareCdnCacheAdapter implements CdnCacheAdapter {
readonly requiresCompletedResponseAdmission = true;
readonly responseVary = "verbatim" as const;

constructor(
private readonly versionMetadata?: WorkerVersionMetadata,
Expand Down
40 changes: 11 additions & 29 deletions packages/cloudflare/src/cacheability-artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
} from "vinext/internal/server/cacheability-manifest";
import {
cacheabilityManifestByteLimitError,
cacheabilityManifestRouteLimitError,
MAX_CACHEABILITY_MANIFEST_BYTES,
MAX_CACHEABILITY_MANIFEST_ROUTES,
} from "./cacheability-manifest-limits.js";

type JavaScriptToken = {
Expand Down Expand Up @@ -194,16 +192,15 @@ function assertManifestModuleReachable(configPath: string): void {
}

/**
* Upload a version-specific manifest from an isolated copy of the built Worker.
* The application build already imports this stable module asset, so the final
* upload only replaces its contents and never rewrites generated JavaScript.
* Write the version-specific manifest into the built Worker artifact.
* The application build already imports this stable module asset, so the
* completed dist directory remains the exact input to the final upload.
*/
export function withCacheabilityManifestArtifact<T>(
export function writeCacheabilityManifestArtifact(
root: string,
configuredPath: string | undefined,
manifest: CacheabilityManifest,
upload: (configPath: string) => T,
): T {
): string {
const configPath = resolveGeneratedServerConfig(root, configuredPath);
assertManifestModuleReachable(configPath);
const serverDirectory = path.dirname(configPath);
Expand All @@ -213,34 +210,19 @@ export function withCacheabilityManifestArtifact<T>(
`Two-stage CDN warming requires ${CACHEABILITY_MANIFEST_MODULE} in the generated Worker artifact. Rebuild the app before deploying.`,
);
}
const routeCount = Object.keys(manifest.routes).length;
if (routeCount > MAX_CACHEABILITY_MANIFEST_ROUTES) {
throw cacheabilityManifestRouteLimitError(routeCount);
}
const serializedManifest = JSON.stringify(manifest);
const manifestBytes = Buffer.byteLength(serializedManifest);
if (manifestBytes > MAX_CACHEABILITY_MANIFEST_BYTES) {
throw cacheabilityManifestByteLimitError(manifestBytes);
}

const isolatedDirectory = fs.mkdtempSync(
path.join(path.dirname(serverDirectory), ".vinext-cache-"),
);
const manifestSource = `export default ${JSON.stringify(serializedManifest)};\n`;
const pendingManifestPath = `${manifestPath}.${process.pid}.tmp`;
try {
fs.cpSync(serverDirectory, isolatedDirectory, { recursive: true });
const isolatedManifestPath = path.join(isolatedDirectory, CACHEABILITY_MANIFEST_MODULE);
if (!fs.lstatSync(isolatedManifestPath).isFile()) {
throw new Error(
`Two-stage CDN warming requires ${CACHEABILITY_MANIFEST_MODULE} to be a regular Worker module.`,
);
}
fs.writeFileSync(
isolatedManifestPath,
`export default ${JSON.stringify(serializedManifest)};\n`,
"utf8",
);
return upload(path.relative(root, path.join(isolatedDirectory, path.basename(configPath))));
fs.writeFileSync(pendingManifestPath, manifestSource, "utf8");
fs.renameSync(pendingManifestPath, manifestPath);
} finally {
fs.rmSync(isolatedDirectory, { recursive: true, force: true });
if (fs.existsSync(pendingManifestPath)) fs.unlinkSync(pendingManifestPath);
}
return path.relative(root, configPath);
}
14 changes: 2 additions & 12 deletions packages/cloudflare/src/cacheability-manifest-limits.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
export const MAX_CACHEABILITY_MANIFEST_BYTES = 1024 * 1024;
export const MAX_CACHEABILITY_MANIFEST_ROUTES = 10_000;

export function cacheabilityManifestRouteLimitError(
routeCount: number,
limit = MAX_CACHEABILITY_MANIFEST_ROUTES,
): Error {
return new Error(
`Two-stage CDN warming produced ${routeCount} cacheable identities; the limit is ${limit}. Narrow prerender discovery or split the deployment before retrying.`,
);
}
export const MAX_CACHEABILITY_MANIFEST_BYTES = 2 * 1024 * 1024;

export function cacheabilityManifestByteLimitError(
manifestBytes: number,
limit = MAX_CACHEABILITY_MANIFEST_BYTES,
): Error {
return new Error(
`Two-stage CDN warming produced a ${manifestBytes}-byte cacheability manifest; the limit is ${limit} bytes. Narrow prerender discovery or split the deployment before retrying.`,
`Two-stage CDN warming produced a ${manifestBytes}-byte route-pattern cacheability manifest; the limit is ${limit} bytes. Reduce the number or length of route patterns, or split the deployment before retrying.`,
);
}
Loading
Loading