@rsc-kit/core 0.23.0 → 0.24.0

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/host.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /** Whether a page or guard of this app answered the request with notFound(). */
2
+ export declare function pageSaidNotFound(request: Request): boolean;
1
3
  export { revalidate } from "./revalidate.js";
2
4
  import { type CriticalAssets } from "./earlyHints.js";
3
5
  /**
package/dist/host.js CHANGED
@@ -13,7 +13,7 @@
13
13
  //
14
14
  // const rsc = createRscHandler({ engine, manifest, assets })
15
15
  // Bun.serve({ fetch: (req) => rsc(req).then((r) => r ?? new Response('', { status: 404 })) })
16
- import { allowFor, matchApiRoute, matchIntercept, matchRoute, retentionKey, sharedDepth, } from "./routing.js";
16
+ import { allowFor, matchApiRoute, matchIntercept, layoutChain, matchRoute, retentionKey, sharedDepth, } from "./routing.js";
17
17
  import { PARAM_PLACEHOLDER, pathKey, patternKey } from "./prerender.js";
18
18
  import { apiKey } from "./apiPrerender.js";
19
19
  import { hostPath, hostSegment, routableHost } from "./hostRouting.js";
@@ -28,6 +28,24 @@ import { withRevalidation } from "./revalidate.js";
28
28
  * internal endpoints answer a plain 404.
29
29
  */
30
30
  const RENDER_NOT_FOUND = Symbol("render-not-found");
31
+ /**
32
+ * Requests a matched page or its guard answered with notFound().
33
+ *
34
+ * The handler answers both those and urls with no route at all with null,
35
+ * and the server in front used to tell them apart by nothing: with a
36
+ * backend configured, a page that called notFound() was forwarded to the
37
+ * backend as though it were not this app's, and its not-found.tsx never
38
+ * showed. Asked with pageSaidNotFound().
39
+ */
40
+ const notFoundRequests = new WeakSet();
41
+ function saidNotFound(request) {
42
+ notFoundRequests.add(request);
43
+ return null;
44
+ }
45
+ /** Whether a page or guard of this app answered the request with notFound(). */
46
+ export function pageSaidNotFound(request) {
47
+ return notFoundRequests.has(request);
48
+ }
31
49
  export { revalidate } from "./revalidate.js";
32
50
  import { currentNotFound, withRedirect } from "./redirect.js";
33
51
  import { compressed } from "./compress.js";
@@ -770,7 +788,7 @@ export function createRscHandler(options) {
770
788
  // Null, as a render that calls notFound() answers: the caller in front
771
789
  // serves not-found.tsx with a 404, the same for a guard as for a page.
772
790
  if (refusal === RENDER_NOT_FOUND)
773
- return null;
791
+ return saidNotFound(request);
774
792
  if (refusal)
775
793
  return refusal;
776
794
  const frozen = await servePrerendered(request, url, match, options.prerendered);
@@ -788,7 +806,7 @@ export function createRscHandler(options) {
788
806
  component,
789
807
  props: {},
790
808
  }));
791
- const chain = match.route.layouts;
809
+ const chain = layoutChain(match.route.layouts, match.params);
792
810
  // A payload request says so with a header on the page's own url, so one
793
811
  // route serves both the document and the navigation that follows it.
794
812
  if (request.headers.get(HEADER.rsc) === null) {
@@ -829,7 +847,7 @@ export function createRscHandler(options) {
829
847
  // route are indistinguishable to whoever is asking — which is the
830
848
  // point of a 404.
831
849
  if (currentNotFound())
832
- return null;
850
+ return saidNotFound(request);
833
851
  // A guard refusing is not a failed render. Without this a visitor
834
852
  // who may not see the page gets a 500, which reads as the
835
853
  // application being broken rather than them being turned away —
@@ -848,7 +866,7 @@ export function createRscHandler(options) {
848
866
  // — the digest carries it to the boundary instead, and the status
849
867
  // stays 200 because the status line has gone.
850
868
  if (currentNotFound())
851
- return null;
869
+ return saidNotFound(request);
852
870
  return new Response(appendLateRedirect(htmlStream, taken), {
853
871
  headers: withVersion({
854
872
  "Content-Type": HTML_TYPE,
@@ -893,7 +911,7 @@ export function createRscHandler(options) {
893
911
  // Same answer the document path gives, so a client navigating to a
894
912
  // url and a browser loading it fresh agree about whether it exists.
895
913
  if (currentNotFound())
896
- return null;
914
+ return saidNotFound(request);
897
915
  // A payload request is guarded exactly as the document is. Narrowing
898
916
  // a request must never narrow what is checked.
899
917
  const status = refusalStatus(error);
@@ -1278,7 +1296,9 @@ export function createRscHandler(options) {
1278
1296
  // boundary it does not have.
1279
1297
  if (meta === null)
1280
1298
  return null;
1281
- const chain = (JSON.parse(meta).layouts ?? []);
1299
+ // By identity, as the client holds it: the stored file names the layouts,
1300
+ // and the url says which values they were rendered for.
1301
+ const chain = layoutChain((JSON.parse(meta).layouts ?? []), route?.params ?? {});
1282
1302
  const shared = sharedDepth(request.headers.get(HEADER.segments), chain);
1283
1303
  // The variant for exactly this depth, or the whole document. Anything else
1284
1304
  // would be a payload for a boundary the client is not holding.
@@ -1428,6 +1448,8 @@ export function createRscHandler(options) {
1428
1448
  const component = under ? under.route.component : intercept.component;
1429
1449
  const props = under ? await propsFor(under, request) : intercept.params;
1430
1450
  const chain = under ? under.route.layouts : [];
1451
+ // Compared and sent by identity; see layoutChain.
1452
+ const held = under ? layoutChain(under.route.layouts, under.params) : [];
1431
1453
  const slots = under ? under.route.slots : {};
1432
1454
  const loadings = under ? under.route.loadings : [];
1433
1455
  // The interceptor alone, when there is a page to open it over and this
@@ -1463,12 +1485,12 @@ export function createRscHandler(options) {
1463
1485
  }),
1464
1486
  });
1465
1487
  }
1466
- const { stream, segmentDepth } = await engine.handleRscStream(component, props, chain.map((layout) => ({ component: layout, props: {} })), loadings, slots, {}, sharedDepth(request.headers.get(HEADER.segments), chain), retentionKey(url.pathname, slot));
1488
+ const { stream, segmentDepth } = await engine.handleRscStream(component, props, chain.map((layout) => ({ component: layout, props: {} })), loadings, slots, {}, sharedDepth(request.headers.get(HEADER.segments), held), retentionKey(url.pathname, slot));
1467
1489
  return new Response(stream, {
1468
1490
  headers: withVersion({
1469
1491
  "Content-Type": FLIGHT_TYPE,
1470
1492
  [HEADER.segmentDepth]: String(segmentDepth),
1471
- [HEADER.layouts]: chain.join(","),
1493
+ [HEADER.layouts]: held.join(","),
1472
1494
  Vary: VARY_ON_RSC,
1473
1495
  "Cache-Control": PER_CLIENT,
1474
1496
  }),