Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .changeset/cache-consolidate-override.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
"@opennextjs/core": major
"@opennextjs/cloudflare": minor
---

Route all caching through the `cache` override

The incremental cache and the tag cache no longer run inside the server function. They run in the
cache handler function, which the server, the middleware and the composable cache reach through the
`cache` override. Tag revalidation - `hasBeenRevalidated`, `writeTags` and CDN invalidation - moves
with them, so `get`, `set` and `revalidateTags` now handle tags transparently.

`incrementalCache` and `tagCache` are removed from `default.override` and from the middleware
override. Configurations that are not created by `defineCloudflareConfig` should move them to the
top level `cacheHandler` option and set `default.override.cache`:

```diff
default: {
override: {
- incrementalCache: "s3",
- tagCache: "dynamodb",
+ cache: "local",
},
},
+ cacheHandler: {
+ incrementalCache: "s3",
+ tagCache: "dynamodb",
+ },
```

`defineCloudflareConfig` is unchanged: it now wires the cache to the `OpenNextCache` entrypoint on
its own.
11 changes: 9 additions & 2 deletions examples/app-pages-router/open-next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import type { OpenNextConfig, OverrideOptions } from "@opennextjs/core/types/ope
const devOverride = {
wrapper: "express-dev",
converter: "node",
incrementalCache: "fs-dev",
cache: "local",
queue: "direct",
tagCache: "fs-dev-nextMode",
} satisfies OverrideOptions;

export default {
Expand All @@ -26,6 +25,14 @@ export default {
},
loader: "fs-dev",
},
cacheHandler: {
override: {
wrapper: "dummy",
converter: "dummy",
},
incrementalCache: "fs-dev",
tagCache: "fs-dev-nextMode",
},
// You can override the build command here so that you don't have to rebuild next every time you make a change
// buildCommand: "echo 'No build command'",
} satisfies OpenNextConfig;
12 changes: 10 additions & 2 deletions examples/app-router/open-next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ export default {
override: {
wrapper: "express-dev",
converter: "node",
incrementalCache: "fs-dev",
cache: "local",
queue: "direct",
tagCache: "fs-dev-nextMode",
},
},

Expand All @@ -23,6 +22,15 @@ export default {
loader: "fs-dev",
},

cacheHandler: {
override: {
wrapper: "dummy",
converter: "dummy",
},
incrementalCache: "fs-dev",
tagCache: "fs-dev-nextMode",
},

// You can override the build command here so that you don't have to rebuild next every time you make a change
//buildCommand: "echo 'No build command'",
} satisfies OpenNextConfig;
12 changes: 10 additions & 2 deletions examples/experimental/open-next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ export default {
override: {
wrapper: "express-dev",
converter: "node",
incrementalCache: "fs-dev",
queue: "direct",
tagCache: "fs-dev-nextMode",
cache: "local",
},
},

Expand All @@ -19,6 +18,15 @@ export default {
loader: "fs-dev",
},

cacheHandler: {
override: {
wrapper: "dummy",
converter: "dummy",
},
incrementalCache: "fs-dev",
tagCache: "fs-dev-nextMode",
},

// You can override the build command here so that you don't have to rebuild next every time you make a change
//buildCommand: "echo 'No build command'",
} satisfies OpenNextConfig;
12 changes: 10 additions & 2 deletions examples/pages-router/open-next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ export default {
override: {
wrapper: "express-dev",
converter: "node",
incrementalCache: "fs-dev",
queue: "direct",
tagCache: "dummy",
cache: "local",
},
},

Expand All @@ -17,6 +16,15 @@ export default {
loader: "fs-dev",
},

cacheHandler: {
override: {
wrapper: "dummy",
converter: "dummy",
},
incrementalCache: "fs-dev",
tagCache: "dummy",
},

// You can override the build command here so that you don't have to rebuild next every time you make a change
//buildCommand: "echo 'No build command'",
};
13 changes: 9 additions & 4 deletions packages/cloudflare/src/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
} from "@opennextjs/core/types/overrides.js";

import assetResolver from "./overrides/asset-resolver/index.js";
import serviceCache from "./overrides/cache/service-cache.js";

export type Override<T extends BaseOverride> = "dummy" | T | LazyLoadedOverride<T>;

Expand Down Expand Up @@ -65,13 +66,18 @@ export function defineCloudflareConfig(config: CloudflareOverrides = {}): OpenNe
wrapper: "cloudflare-node",
converter: "edge",
proxyExternalRequest: "fetch",
incrementalCache: resolveIncrementalCache(incrementalCache),
tagCache: resolveTagCache(tagCache),
cache: () => serviceCache,
queue: resolveQueue(queue),
cdnInvalidation: resolveCdnInvalidation(cachePurge),
},
routePreloadingBehavior,
},
// The cache runs in the same worker, behind the `OpenNextCache` named entrypoint.
cacheHandler: {
incrementalCache: resolveIncrementalCache(incrementalCache),
tagCache: resolveTagCache(tagCache),
cdnInvalidation: resolveCdnInvalidation(cachePurge),
},
// node:crypto is used to compute cache keys
edgeExternals: ["node:crypto"],
cloudflare: {
Expand All @@ -83,8 +89,7 @@ export function defineCloudflareConfig(config: CloudflareOverrides = {}): OpenNe
wrapper: "cloudflare-edge",
converter: "edge",
proxyExternalRequest: "fetch",
incrementalCache: resolveIncrementalCache(incrementalCache),
tagCache: resolveTagCache(tagCache),
cache: () => serviceCache,
queue: resolveQueue(queue),
},
assetResolver: () => assetResolver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ describe("serviceCache", () => {
});

describe("get", () => {
it("requests the key and the cache type", async () => {
await serviceCache.get("key/with/slashes", "fetch");
it("requests the key, the cache type and the additional tags", async () => {
await serviceCache.get("key/with/slashes", "fetch", ["tag1", "tag2"]);

const { url, method } = lastRequest();
expect(method).toBe("GET");
expect(url.pathname).toBe(`/cache/${encodeURIComponent("key/with/slashes")}`);
expect(url.searchParams.get("type")).toBe("fetch");
expect(url.searchParams.get("tags")).toBe("tag1,tag2");
});

it("omits the tags when there is none", async () => {
await serviceCache.get("key", "cache", []);

expect(lastRequest().url.searchParams.has("tags")).toBe(false);
});

it("returns null on a cache miss", async () => {
Expand Down
10 changes: 7 additions & 3 deletions packages/cloudflare/src/api/overrides/cache/service-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,16 @@ function getCacheService(): Service {
return service;
}

function getCacheUrl(key: string, cacheType?: CacheEntryType) {
function getCacheUrl(key: string, cacheType?: CacheEntryType, additionalTags?: string[]) {
const url = new URL(`/cache/${encodeURIComponent(key)}`, CACHE_ORIGIN);

if (cacheType) {
url.searchParams.set("type", cacheType);
}
if (additionalTags && additionalTags.length > 0) {
url.searchParams.set("tags", additionalTags.join(","));
}

return url.href;
}

Expand All @@ -53,8 +57,8 @@ function getCacheUrl(key: string, cacheType?: CacheEntryType) {
const serviceCache = {
name: NAME,

get: async (key, cacheType) => {
const response = await getCacheService().fetch(getCacheUrl(key, cacheType));
get: async (key, cacheType, additionalTags) => {
const response = await getCacheService().fetch(getCacheUrl(key, cacheType, additionalTags));

const body = await response.text();
const headers: Record<string, string> = {};
Expand Down
5 changes: 4 additions & 1 deletion packages/cloudflare/src/api/overrides/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export function computeCacheKey(key: string, options: KeyOptions) {

export function isPurgeCacheEnabled(): boolean {
// The `?` is required at `openNextConfig?` or the Open Next build fails because of a type error
const cdnInvalidation = globalThis.openNextConfig?.default?.override?.cdnInvalidation;
// The cache handler function only has `cacheHandler` populated, the other functions only have `default`.
const cdnInvalidation =
globalThis.openNextConfig?.cacheHandler?.cdnInvalidation ??
globalThis.openNextConfig?.default?.override?.cdnInvalidation;

return cdnInvalidation !== undefined && cdnInvalidation !== "dummy";
}
Expand Down
23 changes: 13 additions & 10 deletions packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
dftUseEdgeConverter: config.default?.override?.converter === "edge",
dftUseFetchProxy: config.default?.override?.proxyExternalRequest === "fetch",
dftMaybeUseCache:
config.default?.override?.incrementalCache === "dummy" ||
typeof config.default?.override?.incrementalCache === "function",
dftMaybeUseTagCache:
config.default?.override?.tagCache === "dummy" ||
typeof config.default?.override?.incrementalCache === "function",
dftUseCacheClient: typeof config.default?.override?.cache === "function",
chMaybeUseIncrementalCache:
config.cacheHandler?.incrementalCache === "dummy" ||
typeof config.cacheHandler?.incrementalCache === "function",
chMaybeUseTagCache:
config.cacheHandler?.tagCache === "dummy" || typeof config.cacheHandler?.tagCache === "function",
dftMaybeUseQueue:
config.default?.override?.queue === "dummy" ||
config.default?.override?.queue === "direct" ||
Expand All @@ -32,6 +32,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
mwUseCloudflareWrapper: mwConfig?.override?.wrapper === "cloudflare-edge",
mwUseEdgeConverter: mwConfig?.override?.converter === "edge",
mwUseFetchProxy: mwConfig?.override?.proxyExternalRequest === "fetch",
mwUseCacheClient: typeof mwConfig?.override?.cache === "function",
hasCryptoExternal: config.edgeExternals?.includes("node:crypto"),
};

Expand All @@ -48,20 +49,22 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
wrapper: "cloudflare-node",
converter: "edge",
proxyExternalRequest: "fetch",
incrementalCache: "dummy" | function,
tagCache: "dummy" | function,
cache: function,
queue: "dummy" | "direct" | function,
},
},
cacheHandler: {
incrementalCache: "dummy" | function,
tagCache: "dummy" | function,
},
edgeExternals: ["node:crypto"],
middleware: {
external: true,
override: {
wrapper: "cloudflare-edge",
converter: "edge",
proxyExternalRequest: "fetch",
incrementalCache: "dummy" | function,
tagCache: "dummy" | function,
cache: function,
queue: "dummy" | "direct" | function,
},
},
Expand Down
12 changes: 4 additions & 8 deletions packages/cloudflare/src/cli/commands/populate-cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,8 @@ describe("populateCache", () => {
outputDir: "/test/output",
} as BuildOptions,
{
default: {
override: {
incrementalCache: "cf-r2-incremental-cache",
},
cacheHandler: {
incrementalCache: "cf-r2-incremental-cache",
},
} as any, // oxlint-disable-line @typescript-eslint/no-explicit-any
{
Expand Down Expand Up @@ -149,10 +147,8 @@ describe("populateCache", () => {
outputDir: "/test/output",
} as BuildOptions,
{
default: {
override: {
incrementalCache: "cf-r2-incremental-cache",
},
cacheHandler: {
incrementalCache: "cf-r2-incremental-cache",
},
} as any, // oxlint-disable-line @typescript-eslint/no-explicit-any
{
Expand Down
2 changes: 1 addition & 1 deletion packages/cloudflare/src/cli/commands/populate-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function populateCache(
populateCacheOptions: PopulateCacheOptions,
envVars: WorkerEnvVar
) {
const { incrementalCache, tagCache } = config.default.override ?? {};
const { incrementalCache, tagCache } = config.cacheHandler ?? {};

if (!fs.existsSync(buildOpts.outputDir)) {
logger.error("Unable to populate cache: Open Next build not found");
Expand Down
Loading
Loading