@rsc-kit/core 0.30.29 → 0.30.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/earlyHints.js +5 -1
- package/dist/earlyHints.js.map +1 -1
- package/dist/edgeShells.d.ts +49 -7
- package/dist/edgeShells.js +117 -8
- package/dist/edgeShells.js.map +1 -1
- package/dist/headers.d.ts +18 -12
- package/dist/headers.js +33 -12
- package/dist/headers.js.map +1 -1
- package/dist/host.js +83 -38
- package/dist/host.js.map +1 -1
- package/dist/js/createViteRscApp.js +12 -3
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/pagePayload.d.ts +1 -1
- package/dist/js/pagePayload.js +5 -2
- package/dist/js/pagePayload.js.map +1 -1
- package/dist/manifest.d.ts +5 -0
- package/dist/manifest.js.map +1 -1
- package/dist/vite.d.ts +44 -10
- package/dist/vite.js +133 -33
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/dist/host.js
CHANGED
|
@@ -81,7 +81,7 @@ export { redirect } from "./redirect.js";
|
|
|
81
81
|
// public surface so a host imports from one place.
|
|
82
82
|
/** @internal For a host adapter. An app never matches its own routes. */
|
|
83
83
|
export { matchIntercept, matchRoute, sharedDepth } from "./routing.js";
|
|
84
|
-
import { FLIGHT_TYPE, HEADER, HTML_TYPE, PER_CLIENT, PER_REQUEST, REVALIDATE,
|
|
84
|
+
import { FLIGHT_TYPE, HEADER, HTML_TYPE, PER_CLIENT, PER_REQUEST, REVALIDATE, EDGE, VARY_ON_RSC, } from "./headers.js";
|
|
85
85
|
import { ServerAuthenticationError, ServerAuthorizationError, } from "./js/errors.js";
|
|
86
86
|
import { assertSigningSecret, serveChanges } from "./changed.js";
|
|
87
87
|
/**
|
|
@@ -433,8 +433,8 @@ export function createRscHandler(options) {
|
|
|
433
433
|
// own. Resolved on the first request: the engine reads it from a build
|
|
434
434
|
// product it only has at runtime.
|
|
435
435
|
let version = engine.buildId ? undefined : options.version;
|
|
436
|
-
// The
|
|
437
|
-
let
|
|
436
|
+
// The edge contract's marker, as the build left it beside the pages - see edgeMarker.
|
|
437
|
+
let edgeMarkerRead = null;
|
|
438
438
|
const maxActionBody = options.maxActionBody ?? DEFAULT_MAX_ACTION_BODY;
|
|
439
439
|
const compress = options.compress ?? true;
|
|
440
440
|
// Annotated rather than inferred: the narrowing below is lost inside the
|
|
@@ -519,6 +519,9 @@ export function createRscHandler(options) {
|
|
|
519
519
|
const siteHosts = manifest.build?.hosts ?? [];
|
|
520
520
|
/** Whether responses say what built them. How they were served is always said. */
|
|
521
521
|
const identify = Boolean(manifest.build?.identify);
|
|
522
|
+
// rscKit({ headers }): every response, and every page an edge serves.
|
|
523
|
+
const pageHeaders = manifest.build?.headers ?? {};
|
|
524
|
+
const hasPageHeaders = Object.keys(pageHeaders).length > 0;
|
|
522
525
|
/** How a response was answered, for X-RSC-Kit: a file the build wrote, or a shell of one. */
|
|
523
526
|
const servedFrom = new WeakMap();
|
|
524
527
|
/** What a stored document's head names, read once per file, for the Link header. */
|
|
@@ -637,6 +640,12 @@ export function createRscHandler(options) {
|
|
|
637
640
|
if (!response)
|
|
638
641
|
return null;
|
|
639
642
|
const collected = taken();
|
|
643
|
+
// Under whatever the answer already has, and before the middleware's,
|
|
644
|
+
// so a page or a middleware that asked for its own value keeps it.
|
|
645
|
+
for (const [name, value] of Object.entries(pageHeaders)) {
|
|
646
|
+
if (!response.headers.has(name))
|
|
647
|
+
response.headers.set(name, value);
|
|
648
|
+
}
|
|
640
649
|
collected.forEach((value, name) => {
|
|
641
650
|
// Set-Cookie is applied below, once per cookie: iterating a Headers
|
|
642
651
|
// gives it joined in some runtimes and per-cookie in others, and a
|
|
@@ -688,7 +697,7 @@ export function createRscHandler(options) {
|
|
|
688
697
|
// plain bytes and compresses the page once, so gzip here would be
|
|
689
698
|
// garbage in the middle of a document. The edge drops
|
|
690
699
|
// Accept-Encoding, but this does not rely on it.
|
|
691
|
-
if (compress && !new URL(request.url).pathname.startsWith(
|
|
700
|
+
if (compress && !new URL(request.url).pathname.startsWith(EDGE.prefix)) {
|
|
692
701
|
const answer = await compressed(request, response, servedFrom.get(response) === "stored" ? storedKey(request, response) : undefined);
|
|
693
702
|
const from = servedFrom.get(response);
|
|
694
703
|
if (answer !== response && from)
|
|
@@ -807,10 +816,10 @@ export function createRscHandler(options) {
|
|
|
807
816
|
if (url.pathname === HEADER.changesPath) {
|
|
808
817
|
return await serveChanges(request);
|
|
809
818
|
}
|
|
810
|
-
// The
|
|
811
|
-
// and is a 404 to anyone without the
|
|
812
|
-
if (url.pathname.startsWith(
|
|
813
|
-
return await
|
|
819
|
+
// The edge contract, for a platform's edge. The whole prefix is reserved,
|
|
820
|
+
// and is a 404 to anyone without the token.
|
|
821
|
+
if (url.pathname.startsWith(EDGE.prefix)) {
|
|
822
|
+
return await serveEdgeContract(request, url);
|
|
814
823
|
}
|
|
815
824
|
// A form submitted before the page had a runtime: the browser posts it
|
|
816
825
|
// to the page's own url, as React wrote it, with the action's id among
|
|
@@ -1180,7 +1189,7 @@ export function createRscHandler(options) {
|
|
|
1180
1189
|
return answer;
|
|
1181
1190
|
}
|
|
1182
1191
|
/**
|
|
1183
|
-
* A page's shell, by the rule
|
|
1192
|
+
* A page's shell, by the rule the page endpoint applies: a PPR page, on a
|
|
1184
1193
|
* route with no guard, whose shell the build wrote for this url or for its
|
|
1185
1194
|
* pattern. Anything else is a 404 - the same pages edgeShells leaves out of
|
|
1186
1195
|
* the marker, so the list and the origin cannot disagree.
|
|
@@ -1257,26 +1266,26 @@ export function createRscHandler(options) {
|
|
|
1257
1266
|
"Content-Type": HTML_TYPE,
|
|
1258
1267
|
// Rendered for whoever asked. Never cacheable.
|
|
1259
1268
|
"Cache-Control": PER_CLIENT,
|
|
1260
|
-
...(
|
|
1269
|
+
...(edgeMarkerRead ? { [EDGE.build]: edgeMarkerRead.build } : {}),
|
|
1261
1270
|
}),
|
|
1262
1271
|
});
|
|
1263
1272
|
});
|
|
1264
1273
|
}
|
|
1265
1274
|
/**
|
|
1266
|
-
* The
|
|
1267
|
-
* for a caller holding the
|
|
1268
|
-
* everything under /
|
|
1269
|
-
* app is safe wherever it runs, and only the platform can ask.
|
|
1275
|
+
* The edge contract's two endpoints, for a platform's edge. They exist only
|
|
1276
|
+
* for a caller holding the EDGE_TOKEN the platform set: unset, missing or
|
|
1277
|
+
* wrong, everything under /_edge/ is a 404, as if there were nothing here -
|
|
1278
|
+
* so an app is safe wherever it runs, and only the platform can ask.
|
|
1270
1279
|
*
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1280
|
+
* A page the marker lists as static goes whole, `complete`; a shell goes
|
|
1281
|
+
* with an empty `state`, since the resume reads its own from the build and
|
|
1282
|
+
* ignores the body. Both answers name the build in Edge-Build.
|
|
1274
1283
|
*/
|
|
1275
|
-
async function
|
|
1276
|
-
const answer = await
|
|
1284
|
+
async function serveEdgeContract(request, url) {
|
|
1285
|
+
const answer = await edgeContractAnswer(request, url);
|
|
1277
1286
|
// Every answer here depends on the token, a 404 included: stored by a
|
|
1278
1287
|
// cache in between, one would be served to callers with the wrong token
|
|
1279
|
-
// or none - and a stored 404 would hide a real
|
|
1288
|
+
// or none - and a stored 404 would hide a real page from the edge.
|
|
1280
1289
|
try {
|
|
1281
1290
|
answer.headers.set("Cache-Control", "private, no-store");
|
|
1282
1291
|
return answer;
|
|
@@ -1288,33 +1297,48 @@ export function createRscHandler(options) {
|
|
|
1288
1297
|
});
|
|
1289
1298
|
}
|
|
1290
1299
|
}
|
|
1291
|
-
async function
|
|
1300
|
+
async function edgeContractAnswer(request, url) {
|
|
1292
1301
|
const missing = new Response("Not found", { status: 404 });
|
|
1293
1302
|
const read = options.prerendered;
|
|
1294
|
-
if (!read || !holdsSecret(request,
|
|
1303
|
+
if (!read || !holdsSecret(request, EDGE.token, EDGE.tokenEnv))
|
|
1295
1304
|
return missing;
|
|
1296
|
-
const
|
|
1297
|
-
if (!
|
|
1305
|
+
const marker = await edgeMarker(read);
|
|
1306
|
+
if (!marker)
|
|
1298
1307
|
return missing;
|
|
1299
|
-
|
|
1300
|
-
|
|
1308
|
+
const { build } = marker;
|
|
1309
|
+
if (url.pathname === EDGE.pagePath && (request.method === "GET" || request.method === "HEAD")) {
|
|
1310
|
+
const asked = url.searchParams.get("path");
|
|
1311
|
+
const pathname = contractPathname(asked, url);
|
|
1301
1312
|
if (pathname === null)
|
|
1302
1313
|
return missing;
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1314
|
+
// Whole only for a path the marker names, and only from the document
|
|
1315
|
+
// the build stored for it; anything else is a shell or nothing.
|
|
1316
|
+
const complete = marker.static.has(asked);
|
|
1317
|
+
const html = complete ? await pageAt(pathname, read) : await shellAt(pathname, read);
|
|
1318
|
+
if (html instanceof Response)
|
|
1319
|
+
return html;
|
|
1320
|
+
return new Response(request.method === "HEAD"
|
|
1321
|
+
? null
|
|
1322
|
+
: JSON.stringify({
|
|
1323
|
+
v: 2,
|
|
1324
|
+
build,
|
|
1325
|
+
html,
|
|
1326
|
+
state: "",
|
|
1327
|
+
...(complete ? { complete: true } : {}),
|
|
1328
|
+
// What the page itself goes out with, so the edge's copy does too.
|
|
1329
|
+
...(hasPageHeaders ? { headers: pageHeaders } : {}),
|
|
1330
|
+
}), {
|
|
1307
1331
|
headers: {
|
|
1308
1332
|
"Content-Type": "application/json",
|
|
1309
1333
|
// Fixed for the build, so no ETag. And never stored on the way:
|
|
1310
1334
|
// it was answered only for the token, and a cache between the
|
|
1311
1335
|
// edge and here would hand it to callers without one.
|
|
1312
1336
|
"Cache-Control": "private, no-store",
|
|
1313
|
-
[
|
|
1337
|
+
[EDGE.build]: build,
|
|
1314
1338
|
},
|
|
1315
1339
|
});
|
|
1316
1340
|
}
|
|
1317
|
-
if (url.pathname ===
|
|
1341
|
+
if (url.pathname === EDGE.resumePath && request.method === "POST") {
|
|
1318
1342
|
const raw = url.searchParams.get("url");
|
|
1319
1343
|
const at = raw === null ? -1 : raw.indexOf("?");
|
|
1320
1344
|
const pathname = contractPathname(at === -1 ? raw : raw.slice(0, at), url);
|
|
@@ -1325,13 +1349,34 @@ export function createRscHandler(options) {
|
|
|
1325
1349
|
return missing;
|
|
1326
1350
|
}
|
|
1327
1351
|
/**
|
|
1328
|
-
*
|
|
1329
|
-
*
|
|
1330
|
-
*
|
|
1352
|
+
* A static page's whole document, as the build stored it. The marker lists
|
|
1353
|
+
* no guarded page, and this checks again rather than trusting the list:
|
|
1354
|
+
* a document handed to an edge is served to everyone.
|
|
1355
|
+
*/
|
|
1356
|
+
async function pageAt(pathname, read) {
|
|
1357
|
+
const route = matchRoute(routes, pathname);
|
|
1358
|
+
const key = route && !isGuarded(route) ? storedKeyFor(pathname, route) : null;
|
|
1359
|
+
const html = key === null ? null : await read(`${key}.html`);
|
|
1360
|
+
return html ?? new Response("No such page", { status: 404 });
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* The marker, which the build wrote beside the pages as well as into
|
|
1364
|
+
* .output - its build is not the client's id this host sends a browser,
|
|
1365
|
+
* which a server-only change keeps (see edgeBuildId), and its static list
|
|
1366
|
+
* is what may go whole.
|
|
1331
1367
|
*/
|
|
1332
|
-
async function
|
|
1333
|
-
|
|
1334
|
-
|
|
1368
|
+
async function edgeMarker(read) {
|
|
1369
|
+
if (edgeMarkerRead)
|
|
1370
|
+
return edgeMarkerRead;
|
|
1371
|
+
try {
|
|
1372
|
+
const marker = JSON.parse((await read(EDGE.storedMarker)) ?? "null");
|
|
1373
|
+
if (marker?.build)
|
|
1374
|
+
edgeMarkerRead = { build: marker.build, static: new Set(marker.static ?? []) };
|
|
1375
|
+
}
|
|
1376
|
+
catch {
|
|
1377
|
+
// A marker this build wrote and cannot read back: no edge, not a guess.
|
|
1378
|
+
}
|
|
1379
|
+
return edgeMarkerRead;
|
|
1335
1380
|
}
|
|
1336
1381
|
/**
|
|
1337
1382
|
* A contract path - absolute and decoded once - as the pathname this host
|