@rsc-kit/core 0.20.7 → 0.20.10

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.
Files changed (55) hide show
  1. package/dist/cache.d.ts +11 -0
  2. package/dist/cache.js +13 -0
  3. package/dist/cache.js.map +1 -1
  4. package/dist/clientEntries.js +4 -1
  5. package/dist/clientEntries.js.map +1 -1
  6. package/dist/earlyHints.d.ts +14 -2
  7. package/dist/earlyHints.js +15 -8
  8. package/dist/earlyHints.js.map +1 -1
  9. package/dist/files.d.ts +38 -0
  10. package/dist/files.js +109 -0
  11. package/dist/files.js.map +1 -1
  12. package/dist/host.d.ts +11 -2
  13. package/dist/host.js +38 -8
  14. package/dist/host.js.map +1 -1
  15. package/dist/js/ActivityRouter.d.ts +11 -0
  16. package/dist/js/ActivityRouter.js +19 -4
  17. package/dist/js/ActivityRouter.js.map +1 -1
  18. package/dist/js/Form.js +24 -2
  19. package/dist/js/Form.js.map +1 -1
  20. package/dist/js/PathnameProvider.d.ts +15 -2
  21. package/dist/js/PathnameProvider.js +17 -2
  22. package/dist/js/PathnameProvider.js.map +1 -1
  23. package/dist/js/SegmentBoundary.js +19 -3
  24. package/dist/js/SegmentBoundary.js.map +1 -1
  25. package/dist/js/activityMarkers.d.ts +7 -0
  26. package/dist/js/activityMarkers.js +18 -0
  27. package/dist/js/activityMarkers.js.map +1 -0
  28. package/dist/js/createViteRscApp.js +122 -76
  29. package/dist/js/createViteRscApp.js.map +1 -1
  30. package/dist/js/earlyClicks.d.ts +3 -1
  31. package/dist/js/earlyClicks.js +28 -5
  32. package/dist/js/earlyClicks.js.map +1 -1
  33. package/dist/js/errors.d.ts +2 -0
  34. package/dist/js/errors.js +11 -0
  35. package/dist/js/errors.js.map +1 -1
  36. package/dist/js/imagePreload.d.ts +37 -0
  37. package/dist/js/imagePreload.js +116 -0
  38. package/dist/js/imagePreload.js.map +1 -0
  39. package/dist/js/navigate.d.ts +33 -0
  40. package/dist/js/navigate.js +200 -12
  41. package/dist/js/navigate.js.map +1 -1
  42. package/dist/js/segmentStore.d.ts +58 -0
  43. package/dist/js/segmentStore.js +134 -4
  44. package/dist/js/segmentStore.js.map +1 -1
  45. package/dist/js/staleAssets.d.ts +10 -0
  46. package/dist/js/staleAssets.js +23 -2
  47. package/dist/js/staleAssets.js.map +1 -1
  48. package/dist/js/viewportPrefetch.js +27 -2
  49. package/dist/js/viewportPrefetch.js.map +1 -1
  50. package/dist/shellHead.d.ts +22 -0
  51. package/dist/shellHead.js +43 -0
  52. package/dist/shellHead.js.map +1 -0
  53. package/dist/vite.js +237 -55
  54. package/dist/vite.js.map +1 -1
  55. package/package.json +1 -1
package/dist/host.js CHANGED
@@ -28,6 +28,7 @@ import { compressed } from "./compress.js";
28
28
  import { withCache } from "./cache.js";
29
29
  import { takeAfterWork, withRequest, withResponseDraft } from "./request.js";
30
30
  import { criticalAssetsOf, linkHeader, mergeAssets } from "./earlyHints.js";
31
+ import { withHead } from "./shellHead.js";
31
32
  /**
32
33
  * @internal For a host adapter that embeds the engine. An app imports this
33
34
  * from `@rsc-kit/core/redirect` - the one the guides teach, and the one an
@@ -328,10 +329,23 @@ export function createRscHandler(options) {
328
329
  return options.props ? await options.props(match, request) : match.params;
329
330
  }
330
331
  /** The page a server action was invoked from, so it can re-render regions of it. */
331
- function pageContext(match, props) {
332
+ /** Whether a document at this url is served from the route's pattern shell: no page or shell of its own, and one for the pattern. */
333
+ async function servedFromPatternShell(url, match) {
334
+ const source = options.prerendered;
335
+ if (!source)
336
+ return false;
337
+ const key = pathKey(url.pathname);
338
+ if ((await source(`${key}.html`)) !== null)
339
+ return false;
340
+ if ((await source(`${key}.ppr.html`)) !== null)
341
+ return false;
342
+ return (await source(`${patternKey(match.route)}.ppr.html`)) !== null;
343
+ }
344
+ function pageContext(match, props, url) {
332
345
  return {
333
346
  component: match.route.component,
334
347
  props,
348
+ url,
335
349
  layouts: match.route.layouts.map((component) => ({
336
350
  component,
337
351
  props: {},
@@ -360,7 +374,6 @@ export function createRscHandler(options) {
360
374
  /** What a stored document's head names, read once per file, for the Link header. */
361
375
  const hinted = new WeakMap();
362
376
  const hintsByKey = new Map();
363
- const EMPTY_ASSETS = { styles: [], modules: [], fonts: [] };
364
377
  /**
365
378
  * The page a payload url belongs to, if this is one.
366
379
  *
@@ -468,7 +481,7 @@ export function createRscHandler(options) {
468
481
  // document, stored ones included - a stored document's own head
469
482
  // names its fonts; a rendered one's is not known until too late.
470
483
  if (response.status === 200 && (response.headers.get("Content-Type") ?? "").startsWith("text/html")) {
471
- const link = linkHeader(mergeAssets(hinted.get(response) ?? EMPTY_ASSETS, engine.criticalAssets?.() ?? null));
484
+ const link = linkHeader(mergeAssets(hinted.get(response) ?? null, engine.criticalAssets?.() ?? null));
472
485
  if (link)
473
486
  response.headers.set("Link", link);
474
487
  }
@@ -742,6 +755,13 @@ export function createRscHandler(options) {
742
755
  });
743
756
  }
744
757
  const from = sharedDepth(request.headers.get(HEADER.segments), chain);
758
+ // A boot - X-RSC and no segments - of a document served from a pattern
759
+ // shell: the shell was rendered for no url, and the client hydrates
760
+ // this payload against it, so the hooks are told nothing here either
761
+ // and read the browser's url once hydration is done. A navigation to
762
+ // the same url renders for it, as it is client-rendered, not hydrated.
763
+ const boot = request.headers.get(HEADER.segments) === null;
764
+ const pathname = boot && (await servedFromPatternShell(url, match)) ? null : url.pathname;
745
765
  // Proposed by the host, decided by the engine: an interceptor can force a
746
766
  // wider render than the client asked for, so what goes back is the depth
747
767
  // that came out, never the one that went in.
@@ -749,7 +769,7 @@ export function createRscHandler(options) {
749
769
  let stream;
750
770
  let segmentDepth;
751
771
  try {
752
- ({ stream, segmentDepth } = await engine.handleRscStream(match.route.component, props, layouts, match.route.loadings, match.route.slots, {}, from, url.pathname));
772
+ ({ stream, segmentDepth } = await engine.handleRscStream(match.route.component, props, layouts, match.route.loadings, match.route.slots, {}, from, url.pathname, pathname));
753
773
  }
754
774
  catch (error) {
755
775
  // Same reasoning as the document path: what the render recorded is
@@ -940,7 +960,7 @@ export function createRscHandler(options) {
940
960
  const state = shellKey === null ? null : await read(`${shellKey}.postponed.json`);
941
961
  if (state === null)
942
962
  return new Response("Nothing to resume", { status: 404 });
943
- const { htmlStream } = await engine.handleRscResume(route.route.component, route.params, route.route.layouts.map((component) => ({ component, props: {} })), route.route.loadings, route.route.slots, {}, JSON.parse(state), undefined, shellKey === key ? pathname : "");
963
+ const { htmlStream } = await engine.handleRscResume(route.route.component, route.params, route.route.layouts.map((component) => ({ component, props: {} })), route.route.loadings, route.route.slots, {}, JSON.parse(state), undefined, shellKey === key ? pathname : "", pathname);
944
964
  // Carries the build version so a caller holding a cached shell can tell
945
965
  // that it no longer belongs to this origin. A shell from an older build
946
966
  // does not make the resume fail — it replays against slots that have moved
@@ -1024,13 +1044,23 @@ export function createRscHandler(options) {
1024
1044
  // A shell found under the route's pattern was frozen for no particular
1025
1045
  // url, so it was rendered with no page key. Handing one over now would
1026
1046
  // key the tree differently from the one being resumed.
1027
- shellKey === key ? url.pathname : "");
1047
+ shellKey === key ? url.pathname : "",
1048
+ // But the url itself, for the hooks: the holes are rendered for it.
1049
+ url.pathname);
1050
+ // A shell stored for the pattern was built without a url, so its title
1051
+ // is the layouts' - the page's generateMetadata reads the params and
1052
+ // was left out. This request has the params.
1053
+ const served = shellKey === key || !engine.resolveMetadata
1054
+ ? shell
1055
+ : withHead(shell, await engine
1056
+ .resolveMetadata(route.route.component, route.params, route.route.layouts.map((component) => ({ component, props: {} })))
1057
+ .catch(() => null));
1028
1058
  // The shell first, then whatever the resume writes. React's own script
1029
1059
  // travels with the resumed segments and moves them into place, so this is
1030
1060
  // a plain concatenation and the holes land without hydration.
1031
1061
  const body = new ReadableStream({
1032
1062
  async start(controller) {
1033
- controller.enqueue(new TextEncoder().encode(shell));
1063
+ controller.enqueue(new TextEncoder().encode(served));
1034
1064
  const reader = htmlStream.getReader();
1035
1065
  try {
1036
1066
  while (true) {
@@ -1392,7 +1422,7 @@ export function createRscHandler(options) {
1392
1422
  const from = refererPath(request.headers.get(HEADER.referer), url.origin, siteHosts);
1393
1423
  const match = from ? matchRoute(routes, from) : null;
1394
1424
  const page = match
1395
- ? pageContext(match, await propsFor(match, request))
1425
+ ? pageContext(match, await propsFor(match, request), from ?? undefined)
1396
1426
  : undefined;
1397
1427
  // Scoped to this action: revalidate() called anywhere inside it, at any
1398
1428
  // depth, marks here and nowhere else — two requests can be in flight and