Skip to content

Commit 8011d47

Browse files
refactor: extract shared types and helpers from seed-cache modules
The Node.js and Workers seed-cache modules duplicated manifest types, revalidateCtx, and cache value construction. Extract to seed-cache-shared.ts to prevent drift between the two strategies. Also fixes stale JSDoc referencing "manifestPromise" (renamed to "loadedPromise").
1 parent 97c7a82 commit 8011d47

3 files changed

Lines changed: 88 additions & 95 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Shared types and helpers for seed-cache modules (Node.js and Workers).
3+
*
4+
* Both `seed-cache.ts` (eager, fs-based) and `seed-cache-workers.ts`
5+
* (lazy, fetch-based) read the same vinext-prerender.json manifest and
6+
* produce identical cache entries. This module holds the shared contract.
7+
*/
8+
9+
import type { CachedAppPageValue } from "../shims/cache.js";
10+
11+
// ─── Manifest types ──────────────────────────────────────────────────────────
12+
13+
export interface PrerenderManifest {
14+
buildId: string;
15+
trailingSlash?: boolean;
16+
routes: PrerenderManifestRoute[];
17+
}
18+
19+
export interface PrerenderManifestRoute {
20+
route: string;
21+
status: string;
22+
revalidate?: number | false;
23+
path?: string;
24+
router?: "app" | "pages";
25+
}
26+
27+
// ─── Cache value construction ────────────────────────────────────────────────
28+
29+
/**
30+
* Build the CacheHandler context object from a revalidate value.
31+
* `revalidate: undefined` (static routes) → empty context → no expiry.
32+
*/
33+
export function revalidateCtx(seconds: number | undefined): Record<string, unknown> {
34+
return seconds !== undefined ? { revalidate: seconds } : {};
35+
}
36+
37+
/** Build an APP_PAGE cache value for an HTML entry. */
38+
export function makeHtmlCacheValue(html: string): CachedAppPageValue {
39+
return {
40+
kind: "APP_PAGE",
41+
html,
42+
rscData: undefined,
43+
headers: undefined,
44+
postponed: undefined,
45+
status: undefined,
46+
};
47+
}
48+
49+
/** Build an APP_PAGE cache value for an RSC entry. */
50+
export function makeRscCacheValue(rscData: ArrayBuffer): CachedAppPageValue {
51+
return {
52+
kind: "APP_PAGE",
53+
html: "",
54+
rscData,
55+
headers: undefined,
56+
postponed: undefined,
57+
status: undefined,
58+
};
59+
}

packages/vinext/src/server/seed-cache-workers.ts

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
*
2525
* Concurrency model:
2626
* - A single Workers isolate can process concurrent requests. The manifest
27-
* load is deduplicated via a cached promise (manifestPromise). Two
27+
* load is deduplicated via a cached promise (loadedPromise). Two
2828
* concurrent calls to loadManifest both await the same promise — no
29-
* double-fetch is possible because the assignment to manifestPromise
29+
* double-fetch is possible because the assignment to loadedPromise
3030
* happens synchronously before the first await.
3131
* - Per-route seeding is deduplicated via seedInFlight. Between the
3232
* getCacheHandler().get() check (async, yields) and the seedInFlight.get()
@@ -35,25 +35,16 @@
3535
* promise and the second joins it.
3636
*/
3737

38-
import { getCacheHandler, type CachedAppPageValue } from "../shims/cache.js";
38+
import { getCacheHandler } from "../shims/cache.js";
3939
import { isrCacheKey, setRevalidateDuration } from "./isr-cache.js";
4040
import { getOutputPath, getRscOutputPath } from "../build/prerender.js";
41-
42-
// ─── Manifest types (mirrors seed-cache.ts) ──────────────────────────────────
43-
44-
interface PrerenderManifest {
45-
buildId: string;
46-
trailingSlash?: boolean;
47-
routes: PrerenderManifestRoute[];
48-
}
49-
50-
interface PrerenderManifestRoute {
51-
route: string;
52-
status: string;
53-
revalidate?: number | false;
54-
path?: string;
55-
router?: "app" | "pages";
56-
}
41+
import {
42+
type PrerenderManifest,
43+
type PrerenderManifestRoute,
44+
revalidateCtx,
45+
makeHtmlCacheValue,
46+
makeRscCacheValue,
47+
} from "./seed-cache-shared.js";
5748

5849
/** Loaded manifest + pre-built route lookup. Returned as a unit so they can't desync. */
5950
interface LoadedManifest {
@@ -157,14 +148,6 @@ async function loadManifest(fetchAsset: AssetFetcher): Promise<LoadedManifest |
157148
return loadedPromise;
158149
}
159150

160-
/**
161-
* Build the CacheHandler context object from a revalidate value.
162-
* `revalidate: undefined` (static routes) → empty context → no expiry.
163-
*/
164-
function revalidateCtx(seconds: number | undefined): Record<string, unknown> {
165-
return seconds !== undefined ? { revalidate: seconds } : {};
166-
}
167-
168151
async function doSeedRoute(
169152
manifest: PrerenderManifest,
170153
pathname: string,
@@ -181,17 +164,8 @@ async function doSeedRoute(
181164
const htmlRes = await fetchAsset(`/__prerender/${htmlRelPath}`);
182165
if (!htmlRes.ok) return;
183166

184-
const html = await htmlRes.text();
185-
const htmlValue: CachedAppPageValue = {
186-
kind: "APP_PAGE",
187-
html,
188-
rscData: undefined,
189-
headers: undefined,
190-
postponed: undefined,
191-
status: undefined,
192-
};
193167
const htmlKey = baseKey + ":html";
194-
await handler.set(htmlKey, htmlValue, ctx);
168+
await handler.set(htmlKey, makeHtmlCacheValue(await htmlRes.text()), ctx);
195169
if (revalidateSeconds !== undefined) {
196170
setRevalidateDuration(htmlKey, revalidateSeconds);
197171
}
@@ -201,17 +175,8 @@ async function doSeedRoute(
201175
const rscRes = await fetchAsset(`/__prerender/${rscRelPath}`);
202176
if (!rscRes.ok) return;
203177

204-
const rscData = await rscRes.arrayBuffer();
205-
const rscValue: CachedAppPageValue = {
206-
kind: "APP_PAGE",
207-
html: "",
208-
rscData,
209-
headers: undefined,
210-
postponed: undefined,
211-
status: undefined,
212-
};
213178
const rscKey = baseKey + ":rsc";
214-
await handler.set(rscKey, rscValue, ctx);
179+
await handler.set(rscKey, makeRscCacheValue(await rscRes.arrayBuffer()), ctx);
215180
if (revalidateSeconds !== undefined) {
216181
setRevalidateDuration(rscKey, revalidateSeconds);
217182
}

packages/vinext/src/server/seed-cache.ts

Lines changed: 17 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,15 @@
3131

3232
import fs from "node:fs";
3333
import path from "node:path";
34-
import { getCacheHandler, type CachedAppPageValue } from "../shims/cache.js";
34+
import { getCacheHandler } from "../shims/cache.js";
3535
import { isrCacheKey, setRevalidateDuration } from "./isr-cache.js";
3636
import { getOutputPath, getRscOutputPath } from "../build/prerender.js";
37-
38-
// ─── Manifest types ───────────────────────────────────────────────────────────
39-
40-
interface PrerenderManifest {
41-
buildId: string;
42-
trailingSlash?: boolean;
43-
routes: PrerenderManifestRoute[];
44-
}
45-
46-
interface PrerenderManifestRoute {
47-
route: string;
48-
status: string;
49-
revalidate?: number | false;
50-
path?: string;
51-
router?: "app" | "pages";
52-
}
37+
import {
38+
type PrerenderManifest,
39+
revalidateCtx,
40+
makeHtmlCacheValue,
41+
makeRscCacheValue,
42+
} from "./seed-cache-shared.js";
5343

5444
// ─── Public API ───────────────────────────────────────────────────────────────
5545

@@ -103,14 +93,6 @@ export async function seedMemoryCacheFromPrerender(serverDir: string): Promise<n
10393

10494
// ─── Internals ────────────────────────────────────────────────────────────────
10595

106-
/**
107-
* Build the CacheHandler context object from a revalidate value.
108-
* `revalidate: undefined` (static routes) → empty context → no expiry.
109-
*/
110-
function revalidateCtx(seconds: number | undefined): Record<string, unknown> {
111-
return seconds !== undefined ? { revalidate: seconds } : {};
112-
}
113-
11496
/**
11597
* Seed the HTML cache entry for a single route.
11698
* Returns true if the file existed and was seeded.
@@ -127,17 +109,12 @@ async function seedHtml(
127109
const fullPath = path.join(prerenderDir, relPath);
128110
if (!fs.existsSync(fullPath)) return false;
129111

130-
const htmlValue: CachedAppPageValue = {
131-
kind: "APP_PAGE",
132-
html: fs.readFileSync(fullPath, "utf-8"),
133-
rscData: undefined,
134-
headers: undefined,
135-
postponed: undefined,
136-
status: undefined,
137-
};
138-
139112
const key = baseKey + ":html";
140-
await handler.set(key, htmlValue, revalidateCtx(revalidateSeconds));
113+
await handler.set(
114+
key,
115+
makeHtmlCacheValue(fs.readFileSync(fullPath, "utf-8")),
116+
revalidateCtx(revalidateSeconds),
117+
);
141118

142119
if (revalidateSeconds !== undefined) {
143120
setRevalidateDuration(key, revalidateSeconds);
@@ -162,20 +139,12 @@ async function seedRsc(
162139
if (!fs.existsSync(fullPath)) return;
163140

164141
const rscBuffer = fs.readFileSync(fullPath);
165-
const rscValue: CachedAppPageValue = {
166-
kind: "APP_PAGE",
167-
html: "",
168-
rscData: rscBuffer.buffer.slice(
169-
rscBuffer.byteOffset,
170-
rscBuffer.byteOffset + rscBuffer.byteLength,
171-
),
172-
headers: undefined,
173-
postponed: undefined,
174-
status: undefined,
175-
};
176-
142+
const rscData = rscBuffer.buffer.slice(
143+
rscBuffer.byteOffset,
144+
rscBuffer.byteOffset + rscBuffer.byteLength,
145+
);
177146
const key = baseKey + ":rsc";
178-
await handler.set(key, rscValue, revalidateCtx(revalidateSeconds));
147+
await handler.set(key, makeRscCacheValue(rscData), revalidateCtx(revalidateSeconds));
179148

180149
if (revalidateSeconds !== undefined) {
181150
setRevalidateDuration(key, revalidateSeconds);

0 commit comments

Comments
 (0)