Skip to content

Commit dc444fc

Browse files
committed
refactor(core)!: route all caching through the cache override
The incremental cache and the tag cache stop running inside the server function. They only live in the cache handler function now, and the server, the middleware and the composable cache reach them through the `cache` override. - Remove `incrementalCache` and `tagCache` from `OverrideOptions`; they are only configured under `cacheHandler`. `resolveIncrementalCache` and `resolveTagCache` follow. The esbuild resolve plugin keeps its own fields, so adapter `defaultOverrides` are unaffected. - Move tag revalidation - `hasBeenRevalidated`, `writeTags` and CDN invalidation - out of `adapters/cache.ts`, `composable-cache.ts` and `cacheInterceptor.ts` and into the cache handler, which now applies them in `get`, `set` and `revalidateTags`. - `Cache.get` takes the additional tags to check, so the caller no longer needs the tag cache to resolve them. - `defineCloudflareConfig` wires `cache` to the `OpenNextCache` entrypoint and moves the incremental cache, the tag cache and the cdn invalidation to `cacheHandler`; `ensureCloudflareConfig`, `populateCache` and `isPurgeCacheEnabled` read the new location. - Examples move to `cache: "local"` with a `cacheHandler` block. BREAKING CHANGE: `default.override.incrementalCache` and `default.override.tagCache` are replaced by the top level `cacheHandler` option and `default.override.cache`.
1 parent 8b63860 commit dc444fc

33 files changed

Lines changed: 1147 additions & 1233 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
"@opennextjs/core": major
3+
"@opennextjs/cloudflare": minor
4+
---
5+
6+
Route all caching through the `cache` override
7+
8+
The incremental cache and the tag cache no longer run inside the server function. They run in the
9+
cache handler function, which the server, the middleware and the composable cache reach through the
10+
`cache` override. Tag revalidation - `hasBeenRevalidated`, `writeTags` and CDN invalidation - moves
11+
with them, so `get`, `set` and `revalidateTags` now handle tags transparently.
12+
13+
`incrementalCache` and `tagCache` are removed from `default.override` and from the middleware
14+
override. Configurations that are not created by `defineCloudflareConfig` should move them to the
15+
top level `cacheHandler` option and set `default.override.cache`:
16+
17+
```diff
18+
default: {
19+
override: {
20+
- incrementalCache: "s3",
21+
- tagCache: "dynamodb",
22+
+ cache: "local",
23+
},
24+
},
25+
+ cacheHandler: {
26+
+ incrementalCache: "s3",
27+
+ tagCache: "dynamodb",
28+
+ },
29+
```
30+
31+
`defineCloudflareConfig` is unchanged: it now wires the cache to the `OpenNextCache` entrypoint on
32+
its own.

examples/app-pages-router/open-next.config.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ import type { OpenNextConfig, OverrideOptions } from "@opennextjs/core/types/ope
33
const devOverride = {
44
wrapper: "express-dev",
55
converter: "node",
6-
incrementalCache: "fs-dev",
6+
cache: "local",
77
queue: "direct",
8-
tagCache: "fs-dev-nextMode",
98
} satisfies OverrideOptions;
109

1110
export default {
@@ -26,6 +25,14 @@ export default {
2625
},
2726
loader: "fs-dev",
2827
},
28+
cacheHandler: {
29+
override: {
30+
wrapper: "dummy",
31+
converter: "dummy",
32+
},
33+
incrementalCache: "fs-dev",
34+
tagCache: "fs-dev-nextMode",
35+
},
2936
// You can override the build command here so that you don't have to rebuild next every time you make a change
3037
// buildCommand: "echo 'No build command'",
3138
} satisfies OpenNextConfig;

examples/app-router/open-next.config.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ export default {
55
override: {
66
wrapper: "express-dev",
77
converter: "node",
8-
incrementalCache: "fs-dev",
8+
cache: "local",
99
queue: "direct",
10-
tagCache: "fs-dev-nextMode",
1110
},
1211
},
1312

@@ -23,6 +22,15 @@ export default {
2322
loader: "fs-dev",
2423
},
2524

25+
cacheHandler: {
26+
override: {
27+
wrapper: "dummy",
28+
converter: "dummy",
29+
},
30+
incrementalCache: "fs-dev",
31+
tagCache: "fs-dev-nextMode",
32+
},
33+
2634
// You can override the build command here so that you don't have to rebuild next every time you make a change
2735
//buildCommand: "echo 'No build command'",
2836
} satisfies OpenNextConfig;

examples/experimental/open-next.config.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ export default {
55
override: {
66
wrapper: "express-dev",
77
converter: "node",
8-
incrementalCache: "fs-dev",
98
queue: "direct",
10-
tagCache: "fs-dev-nextMode",
9+
cache: "local",
1110
},
1211
},
1312

@@ -19,6 +18,15 @@ export default {
1918
loader: "fs-dev",
2019
},
2120

21+
cacheHandler: {
22+
override: {
23+
wrapper: "dummy",
24+
converter: "dummy",
25+
},
26+
incrementalCache: "fs-dev",
27+
tagCache: "fs-dev-nextMode",
28+
},
29+
2230
// You can override the build command here so that you don't have to rebuild next every time you make a change
2331
//buildCommand: "echo 'No build command'",
2432
} satisfies OpenNextConfig;

examples/pages-router/open-next.config.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ export default {
33
override: {
44
wrapper: "express-dev",
55
converter: "node",
6-
incrementalCache: "fs-dev",
76
queue: "direct",
8-
tagCache: "dummy",
7+
cache: "local",
98
},
109
},
1110

@@ -17,6 +16,15 @@ export default {
1716
loader: "fs-dev",
1817
},
1918

19+
cacheHandler: {
20+
override: {
21+
wrapper: "dummy",
22+
converter: "dummy",
23+
},
24+
incrementalCache: "fs-dev",
25+
tagCache: "dummy",
26+
},
27+
2028
// You can override the build command here so that you don't have to rebuild next every time you make a change
2129
//buildCommand: "echo 'No build command'",
2230
};

packages/cloudflare/src/api/config.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
} from "@opennextjs/core/types/overrides.js";
1414

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

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

@@ -65,13 +66,18 @@ export function defineCloudflareConfig(config: CloudflareOverrides = {}): OpenNe
6566
wrapper: "cloudflare-node",
6667
converter: "edge",
6768
proxyExternalRequest: "fetch",
68-
incrementalCache: resolveIncrementalCache(incrementalCache),
69-
tagCache: resolveTagCache(tagCache),
69+
cache: () => serviceCache,
7070
queue: resolveQueue(queue),
7171
cdnInvalidation: resolveCdnInvalidation(cachePurge),
7272
},
7373
routePreloadingBehavior,
7474
},
75+
// The cache runs in the same worker, behind the `OpenNextCache` named entrypoint.
76+
cacheHandler: {
77+
incrementalCache: resolveIncrementalCache(incrementalCache),
78+
tagCache: resolveTagCache(tagCache),
79+
cdnInvalidation: resolveCdnInvalidation(cachePurge),
80+
},
7581
// node:crypto is used to compute cache keys
7682
edgeExternals: ["node:crypto"],
7783
cloudflare: {
@@ -83,8 +89,7 @@ export function defineCloudflareConfig(config: CloudflareOverrides = {}): OpenNe
8389
wrapper: "cloudflare-edge",
8490
converter: "edge",
8591
proxyExternalRequest: "fetch",
86-
incrementalCache: resolveIncrementalCache(incrementalCache),
87-
tagCache: resolveTagCache(tagCache),
92+
cache: () => serviceCache,
8893
queue: resolveQueue(queue),
8994
},
9095
assetResolver: () => assetResolver,

packages/cloudflare/src/api/overrides/cache/service-cache.spec.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,20 @@ describe("serviceCache", () => {
2828
});
2929

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

3434
const { url, method } = lastRequest();
3535
expect(method).toBe("GET");
3636
expect(url.pathname).toBe(`/cache/${encodeURIComponent("key/with/slashes")}`);
3737
expect(url.searchParams.get("type")).toBe("fetch");
38+
expect(url.searchParams.get("tags")).toBe("tag1,tag2");
39+
});
40+
41+
it("omits the tags when there is none", async () => {
42+
await serviceCache.get("key", "cache", []);
43+
44+
expect(lastRequest().url.searchParams.has("tags")).toBe(false);
3845
});
3946

4047
it("returns null on a cache miss", async () => {

packages/cloudflare/src/api/overrides/cache/service-cache.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@ function getCacheService(): Service {
3535
return service;
3636
}
3737

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

4141
if (cacheType) {
4242
url.searchParams.set("type", cacheType);
4343
}
44+
if (additionalTags && additionalTags.length > 0) {
45+
url.searchParams.set("tags", additionalTags.join(","));
46+
}
47+
4448
return url.href;
4549
}
4650

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

56-
get: async (key, cacheType) => {
57-
const response = await getCacheService().fetch(getCacheUrl(key, cacheType));
60+
get: async (key, cacheType, additionalTags) => {
61+
const response = await getCacheService().fetch(getCacheUrl(key, cacheType, additionalTags));
5862

5963
const body = await response.text();
6064
const headers: Record<string, string> = {};

packages/cloudflare/src/api/overrides/internal.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ export function computeCacheKey(key: string, options: KeyOptions) {
3434

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

3942
return cdnInvalidation !== undefined && cdnInvalidation !== "dummy";
4043
}

packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
1717
dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
1818
dftUseEdgeConverter: config.default?.override?.converter === "edge",
1919
dftUseFetchProxy: config.default?.override?.proxyExternalRequest === "fetch",
20-
dftMaybeUseCache:
21-
config.default?.override?.incrementalCache === "dummy" ||
22-
typeof config.default?.override?.incrementalCache === "function",
23-
dftMaybeUseTagCache:
24-
config.default?.override?.tagCache === "dummy" ||
25-
typeof config.default?.override?.incrementalCache === "function",
20+
dftUseCacheClient: typeof config.default?.override?.cache === "function",
21+
chMaybeUseIncrementalCache:
22+
config.cacheHandler?.incrementalCache === "dummy" ||
23+
typeof config.cacheHandler?.incrementalCache === "function",
24+
chMaybeUseTagCache:
25+
config.cacheHandler?.tagCache === "dummy" || typeof config.cacheHandler?.tagCache === "function",
2626
dftMaybeUseQueue:
2727
config.default?.override?.queue === "dummy" ||
2828
config.default?.override?.queue === "direct" ||
@@ -32,6 +32,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
3232
mwUseCloudflareWrapper: mwConfig?.override?.wrapper === "cloudflare-edge",
3333
mwUseEdgeConverter: mwConfig?.override?.converter === "edge",
3434
mwUseFetchProxy: mwConfig?.override?.proxyExternalRequest === "fetch",
35+
mwUseCacheClient: typeof mwConfig?.override?.cache === "function",
3536
hasCryptoExternal: config.edgeExternals?.includes("node:crypto"),
3637
};
3738

@@ -48,20 +49,22 @@ export function ensureCloudflareConfig(config: OpenNextConfig) {
4849
wrapper: "cloudflare-node",
4950
converter: "edge",
5051
proxyExternalRequest: "fetch",
51-
incrementalCache: "dummy" | function,
52-
tagCache: "dummy" | function,
52+
cache: function,
5353
queue: "dummy" | "direct" | function,
5454
},
5555
},
56+
cacheHandler: {
57+
incrementalCache: "dummy" | function,
58+
tagCache: "dummy" | function,
59+
},
5660
edgeExternals: ["node:crypto"],
5761
middleware: {
5862
external: true,
5963
override: {
6064
wrapper: "cloudflare-edge",
6165
converter: "edge",
6266
proxyExternalRequest: "fetch",
63-
incrementalCache: "dummy" | function,
64-
tagCache: "dummy" | function,
67+
cache: function,
6568
queue: "dummy" | "direct" | function,
6669
},
6770
},

0 commit comments

Comments
 (0)