@rebask/core 0.1.5 → 0.2.1

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 (86) hide show
  1. package/dist/entry.rsc.d.ts +1 -0
  2. package/dist/entry.rsc.d.ts.map +1 -1
  3. package/dist/entry.rsc.js +1 -0
  4. package/dist/handlers/markdown.d.ts +16 -0
  5. package/dist/handlers/markdown.d.ts.map +1 -0
  6. package/dist/handlers/markdown.js +161 -0
  7. package/dist/handlers/rsc/cache-lookup.d.ts.map +1 -1
  8. package/dist/handlers/rsc/cache-lookup.js +10 -4
  9. package/dist/handlers/rsc/index.js +1 -1
  10. package/dist/handlers/rsc/render-to-response.js +6 -2
  11. package/dist/handlers/rsc/segment-shell.d.ts +1 -1
  12. package/dist/handlers/rsc/segment-shell.d.ts.map +1 -1
  13. package/dist/handlers/rsc/segment-shell.js +5 -9
  14. package/dist/handlers/rsc/state.d.ts +1 -0
  15. package/dist/handlers/rsc/state.d.ts.map +1 -1
  16. package/dist/handlers/rsc/state.js +4 -0
  17. package/dist/lib/app-manifest.d.ts +12 -4
  18. package/dist/lib/app-manifest.d.ts.map +1 -1
  19. package/dist/lib/app-manifest.js +5 -0
  20. package/dist/lib/cache-key-tags.d.ts +2 -0
  21. package/dist/lib/cache-key-tags.d.ts.map +1 -1
  22. package/dist/lib/cache-key-tags.js +2 -0
  23. package/dist/lib/cache-storage.d.ts +7 -2
  24. package/dist/lib/cache-storage.d.ts.map +1 -1
  25. package/dist/lib/cache-storage.js +1 -0
  26. package/dist/lib/config.d.ts +2 -0
  27. package/dist/lib/config.d.ts.map +1 -1
  28. package/dist/lib/config.js +8 -3
  29. package/dist/lib/dev-page-exports.d.ts +5 -0
  30. package/dist/lib/dev-page-exports.d.ts.map +1 -0
  31. package/dist/lib/dev-page-exports.js +14 -0
  32. package/dist/lib/lru-cache.d.ts.map +1 -1
  33. package/dist/lib/lru-cache.js +2 -0
  34. package/dist/lib/markdown.d.ts +5 -0
  35. package/dist/lib/markdown.d.ts.map +1 -0
  36. package/dist/lib/markdown.js +53 -0
  37. package/dist/lib/page-exports.d.ts +9 -0
  38. package/dist/lib/page-exports.d.ts.map +1 -0
  39. package/dist/lib/page-exports.js +47 -0
  40. package/dist/lib/ppr/cache-status.d.ts +3 -0
  41. package/dist/lib/ppr/cache-status.d.ts.map +1 -0
  42. package/dist/lib/ppr/cache-status.js +5 -0
  43. package/dist/lib/ppr/headers.d.ts +1 -1
  44. package/dist/lib/ppr/headers.d.ts.map +1 -1
  45. package/dist/lib/ppr/headers.js +1 -1
  46. package/dist/lib/prerender-params.d.ts +1 -1
  47. package/dist/lib/prerender-params.d.ts.map +1 -1
  48. package/dist/lib/prerender-params.js +17 -2
  49. package/dist/lib/project-files.d.ts +2 -1
  50. package/dist/lib/project-files.d.ts.map +1 -1
  51. package/dist/lib/project-files.js +16 -6
  52. package/dist/lib/route-builder.d.ts +4 -3
  53. package/dist/lib/route-builder.d.ts.map +1 -1
  54. package/dist/lib/route-builder.js +61 -13
  55. package/dist/lib/route-matcher.d.ts +4 -0
  56. package/dist/lib/route-matcher.d.ts.map +1 -1
  57. package/dist/lib/router-cache.d.ts +4 -0
  58. package/dist/lib/router-cache.d.ts.map +1 -1
  59. package/dist/lib/rsc-request.d.ts.map +1 -1
  60. package/dist/lib/rsc-request.js +3 -3
  61. package/dist/lib/server-router/get-router.d.ts.map +1 -1
  62. package/dist/lib/server-router/get-router.js +71 -16
  63. package/dist/lib/server-router/types.d.ts +4 -1
  64. package/dist/lib/server-router/types.d.ts.map +1 -1
  65. package/dist/lib/write-behind-cache.d.ts.map +1 -1
  66. package/dist/lib/write-behind-cache.js +5 -3
  67. package/dist/plugins/devtools.d.ts.map +1 -1
  68. package/dist/plugins/devtools.js +2 -2
  69. package/dist/plugins/ppr.d.ts +5 -0
  70. package/dist/plugins/ppr.d.ts.map +1 -1
  71. package/dist/plugins/prerender.d.ts.map +1 -1
  72. package/dist/plugins/prerender.js +114 -70
  73. package/dist/plugins/router/wint-router.d.ts.map +1 -1
  74. package/dist/plugins/router/wint-router.js +4 -2
  75. package/dist/router.d.ts +1 -1
  76. package/dist/router.d.ts.map +1 -1
  77. package/dist/router.js +16 -6
  78. package/dist/server-router.d.ts +1 -1
  79. package/dist/server-router.d.ts.map +1 -1
  80. package/dist/vite.d.ts +20 -1
  81. package/dist/vite.d.ts.map +1 -1
  82. package/dist/vite.js +17 -4
  83. package/package.json +1 -5
  84. package/dist/components/prefetch-links.d.ts +0 -5
  85. package/dist/components/prefetch-links.d.ts.map +0 -1
  86. package/dist/components/prefetch-links.js +0 -174
@@ -5,12 +5,14 @@ import { proxyRouter } from "virtual:rebask/proxy-router";
5
5
  import { createMatcher } from "virtual:rebask/server-router";
6
6
  import { applyCookies, getSetCookieValues } from "../../cookies.js";
7
7
  import { handleImageRequest } from "../../handlers/image.js";
8
+ import { handleMarkdownRoute, markdownNotFound } from "../../handlers/markdown.js";
8
9
  import { handleRouteHandler } from "../../handlers/route-handler.js";
9
10
  import { buildDefaultNotFoundRoute } from "../../handlers/rsc/errors.js";
10
11
  import { handlePageRoute } from "../../handlers/rsc/index.js";
11
12
  import { handleLayoutRouteRequest } from "../../handlers/segment.js";
12
13
  import { getDevServer } from "../dev-server.js";
13
14
  import { reinjectFlightHeaders, sanitizeForProxy } from "../flight-headers.js";
15
+ import { getMarkdownPathname, prefersMarkdown } from "../markdown.js";
14
16
  import { buildProxy } from "../proxy/build-proxy.js";
15
17
  import { proxyExternal } from "../proxy/proxy-external.js";
16
18
  import { REWRITE_HEADER, rewriteHeaders } from "../proxy/rewrite.js";
@@ -20,35 +22,64 @@ import { RouteType } from "./types.js";
20
22
  function buildEntries(pageRoutes, routeHandlerRoutes, layoutRoutes) {
21
23
  const appEntries = [];
22
24
  const layoutEntries = [];
25
+ const markdownEntries = [];
26
+ const hasMarkdownRoutes = pageRoutes.some((route) => route.markdownFilePath);
23
27
  // Image optimization route
24
28
  if (imageConfig !== null && loadImageHandler !== undefined) {
25
- appEntries.push({
29
+ const imageEntry = {
26
30
  type: RouteType.IMAGE,
27
31
  methods: ["GET"],
28
32
  pattern: imageConfig.path,
29
33
  handler: handleImageRequest,
30
- });
34
+ };
35
+ appEntries.push(imageEntry);
36
+ if (hasMarkdownRoutes)
37
+ markdownEntries.push(imageEntry);
31
38
  }
32
39
  // Page routes — registered for GET and POST:
33
40
  // - GET: page loads, RSC requests, segment requests
34
41
  // - POST: server functions, form submissions, PPR resume
35
42
  for (const route of pageRoutes) {
36
- appEntries.push({
37
- type: RouteType.APP,
38
- methods: ["GET", "POST"],
39
- route,
40
- handler: (req, params) => handlePageRoute(req, route, params),
41
- });
43
+ if (route.hasPage !== false) {
44
+ appEntries.push({
45
+ type: RouteType.APP,
46
+ methods: ["GET", "POST"],
47
+ route,
48
+ handler: (req, params) => handlePageRoute(req, route, params),
49
+ });
50
+ }
51
+ if (!hasMarkdownRoutes)
52
+ continue;
53
+ if (route.markdownFilePath) {
54
+ markdownEntries.push({
55
+ type: RouteType.APP,
56
+ methods: ["GET", "HEAD"],
57
+ route,
58
+ handler: (req, params) => handleMarkdownRoute(req, route, params),
59
+ });
60
+ }
61
+ else if (route.hasPage !== false) {
62
+ // A specific page without Markdown still wins negotiation over a Markdown catch-all.
63
+ markdownEntries.push({
64
+ type: RouteType.APP,
65
+ methods: ["GET"],
66
+ route,
67
+ handler: (req, params) => handlePageRoute(req, route, params),
68
+ });
69
+ }
42
70
  }
43
71
  // Route handlers — registered for all methods so they can return 405 when the path exists but a
44
72
  // specific method is not implemented.
45
73
  for (const route of routeHandlerRoutes) {
46
- appEntries.push({
74
+ const entry = {
47
75
  type: RouteType.APP,
48
76
  methods: [...HTTP_METHODS],
49
77
  route,
50
78
  handler: (req, params) => handleRouteHandler(req, route, params),
51
- });
79
+ };
80
+ appEntries.push(entry);
81
+ if (hasMarkdownRoutes)
82
+ markdownEntries.push(entry);
52
83
  }
53
84
  // Layout routes — registered under /_rebask/1/{path}
54
85
  for (const route of layoutRoutes) {
@@ -64,14 +95,34 @@ function buildEntries(pageRoutes, routeHandlerRoutes, layoutRoutes) {
64
95
  handler: (req, params) => handleLayoutRouteRequest(req, route, params),
65
96
  });
66
97
  }
67
- return { appEntries, layoutEntries };
98
+ return { appEntries, layoutEntries, markdownEntries };
68
99
  }
69
100
  function createRouter(pageRoutes, routeHandlerRoutes, layoutRoutes, resolvedProxyRouter = proxyRouter) {
70
- const { appEntries, layoutEntries } = buildEntries(pageRoutes, routeHandlerRoutes, layoutRoutes);
101
+ const { appEntries, layoutEntries, markdownEntries } = buildEntries(pageRoutes, routeHandlerRoutes, layoutRoutes);
71
102
  const appMatcher = createMatcher(appEntries);
103
+ const markdownMatcher = markdownEntries.length > 0 ? createMatcher(markdownEntries) : null;
72
104
  const layoutMatcher = layoutEntries.length > 0 ? createMatcher(layoutEntries) : null;
73
105
  const app = async (request) => {
74
- const isRscRequest = request.headers.get("accept")?.startsWith("text/x-component");
106
+ const method = request.method;
107
+ const accept = request.headers.get("accept");
108
+ const isRscRequest = accept?.startsWith("text/x-component");
109
+ const isMarkdownMethod = method === "GET" || method === "HEAD";
110
+ if (!isRscRequest && isMarkdownMethod && markdownMatcher) {
111
+ const url = new URL(request.url);
112
+ const markdownPathname = __REBASK_MARKDOWN_URLS ? getMarkdownPathname(url.pathname) : null;
113
+ if (markdownPathname !== null) {
114
+ url.pathname = markdownPathname;
115
+ const markdownHandler = markdownMatcher.find(method, url.href);
116
+ if (markdownHandler?.route?.type === "page" && markdownHandler.route.markdownFilePath) {
117
+ return (await markdownHandler(request));
118
+ }
119
+ return markdownNotFound(request);
120
+ }
121
+ if (prefersMarkdown(accept)) {
122
+ const handler = markdownMatcher.find(method, request.url);
123
+ return handler ? handler(request) : markdownNotFound(request);
124
+ }
125
+ }
75
126
  const layoutHandler = isRscRequest
76
127
  ? layoutMatcher?.find(request.method, request.url)
77
128
  : undefined;
@@ -161,11 +212,15 @@ export async function getRouter() {
161
212
  if (router)
162
213
  return router;
163
214
  // Development: build routes from module manifest
164
- const { layoutModules, pageModules, proxyModules, routeHandlerModules } = await loadAppManifest();
215
+ const { layoutModules, pageModules, proxyModules, routeHandlerModules, markdownHandlerModules, markdownSourceModules, } = await loadAppManifest();
165
216
  const layouts = Object.keys(layoutModules);
166
217
  const pages = Object.keys(pageModules);
167
- assertNoPageRouteHandlerConflicts(pages, Object.keys(routeHandlerModules));
168
- const routes = buildRoutes(layouts, pages);
218
+ const markdownFiles = [
219
+ ...Object.keys(markdownSourceModules),
220
+ ...Object.keys(markdownHandlerModules),
221
+ ];
222
+ assertNoPageRouteHandlerConflicts(pages, Object.keys(routeHandlerModules), markdownFiles);
223
+ const routes = buildRoutes(layouts, pages, markdownFiles);
169
224
  const routeHandlerRoutes = buildRouteHandlerRoutes(Object.keys(routeHandlerModules));
170
225
  const layoutRoutes = buildLayoutRoutes(layouts);
171
226
  const proxyModule = await Object.values(proxyModules)[0]?.();
@@ -29,9 +29,12 @@ type ImageEntry = {
29
29
  };
30
30
  export type RouteEntry = PageRouteEntry | RouteHandlerEntry | LayoutRSCEntry | ProxyEntry | ImageEntry;
31
31
  type FindHandler<E extends RouteEntry> = ProxyEntry extends E ? (request: Request) => ReturnType<ProxyHandler> : (request: Request) => Promise<Response>;
32
+ export type MatchedHandler<E extends RouteEntry = RouteEntry> = FindHandler<E> & {
33
+ route?: Route | RouteHandlerRoute | LayoutRoute;
34
+ };
32
35
  /** A compiled route matcher that finds the best matching route for a given method + URL. */
33
36
  export interface RouteMatcher<E extends RouteEntry = RouteEntry> {
34
- find(method: string, url: string): FindHandler<E> | null;
37
+ find(method: string, url: string): MatchedHandler<E> | null;
35
38
  }
36
39
  /**
37
40
  * A router plugin provides an implementation of this function via the
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/server-router/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EACX,UAAU,EACV,WAAW,EACX,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,MAAM,qBAAqB,CAAA;AAE5B,oBAAY,SAAS;IACpB,GAAG,IAAA;IACH,KAAK,IAAA;IACL,KAAK,IAAA;CACL;AAED,KAAK,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE3E,KAAK,QAAQ,CAAC,CAAC,IAAI;IAClB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,IAAI,EAAE,SAAS,CAAC,GAAG,CAAA;IACnB,KAAK,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,KAAK,cAAc,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;AACrC,KAAK,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAAA;AACpD,KAAK,cAAc,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAA;AAE3C,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC,KAAK,CAAA;IACrB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,YAAY,CAAA;CACrB,CAAA;AAED,KAAK,UAAU,GAAG;IACjB,IAAI,EAAE,SAAS,CAAC,KAAK,CAAA;IACrB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,UAAU,GACnB,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,UAAU,GACV,UAAU,CAAA;AAEb,KAAK,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI,UAAU,SAAS,CAAC,GAC1D,CAAC,OAAO,EAAE,OAAO,KAAK,UAAU,CAAC,YAAY,CAAC,GAC9C,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE1C,4FAA4F;AAC5F,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC9D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;CACxD;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,CAAA;AAErF,MAAM,WAAW,YAAY;IAC5B,sFAAsF;IACtF,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC/C,uEAAuE;IACvE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5C,qFAAqF;IACrF,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;CACrD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/server-router/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EACX,UAAU,EACV,WAAW,EACX,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,MAAM,qBAAqB,CAAA;AAE5B,oBAAY,SAAS;IACpB,GAAG,IAAA;IACH,KAAK,IAAA;IACL,KAAK,IAAA;CACL;AAED,KAAK,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE3E,KAAK,QAAQ,CAAC,CAAC,IAAI;IAClB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,IAAI,EAAE,SAAS,CAAC,GAAG,CAAA;IACnB,KAAK,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,KAAK,cAAc,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;AACrC,KAAK,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAAA;AACpD,KAAK,cAAc,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAA;AAE3C,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC,KAAK,CAAA;IACrB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,YAAY,CAAA;CACrB,CAAA;AAED,KAAK,UAAU,GAAG;IACjB,IAAI,EAAE,SAAS,CAAC,KAAK,CAAA;IACrB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,UAAU,GACnB,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,UAAU,GACV,UAAU,CAAA;AAEb,KAAK,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI,UAAU,SAAS,CAAC,GAC1D,CAAC,OAAO,EAAE,OAAO,KAAK,UAAU,CAAC,YAAY,CAAC,GAC9C,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE1C,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAChF,KAAK,CAAC,EAAE,KAAK,GAAG,iBAAiB,GAAG,WAAW,CAAA;CAC/C,CAAA;AAED,4FAA4F;AAC5F,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC9D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;CAC3D;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,CAAA;AAErF,MAAM,WAAW,YAAY;IAC5B,sFAAsF;IACtF,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC/C,uEAAuE;IACvE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5C,qFAAqF;IACrF,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;CACrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"write-behind-cache.d.ts","sourceRoot":"","sources":["../../src/lib/write-behind-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAIN,KAAK,YAAY,EACjB,MAAM,oBAAoB,CAAA;AAE3B,UAAU,kBAAkB;IAC3B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB;AAID;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,kBAAuB,GAC9B,YAAY,CAuHd"}
1
+ {"version":3,"file":"write-behind-cache.d.ts","sourceRoot":"","sources":["../../src/lib/write-behind-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAIN,KAAK,YAAY,EACjB,MAAM,oBAAoB,CAAA;AAE3B,UAAU,kBAAkB;IAC3B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB;AAID;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,kBAAuB,GAC9B,YAAY,CAyHd"}
@@ -62,9 +62,11 @@ export function withWriteBehind(storage, options = {}) {
62
62
  return storage.set(key, entry, setOptions);
63
63
  const bytes = entry.type === CacheEntryType.HTML
64
64
  ? entry.html.length + (entry.postponed?.length ?? 0)
65
- : entry.type === CacheEntryType.DURABLE_CACHE
66
- ? entry.data.length
67
- : entry.rsc.length;
65
+ : entry.type === CacheEntryType.MARKDOWN
66
+ ? entry.markdown.length
67
+ : entry.type === CacheEntryType.DURABLE_CACHE
68
+ ? entry.data.length
69
+ : entry.rsc.length;
68
70
  const queued = pending.get(key);
69
71
  if (queued) {
70
72
  pendingBytes += bytes - queued.bytes;
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../../src/plugins/devtools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAmD,MAAM,MAAM,CAAA;AACnF,OAAO,KAAK,EACX,qBAAqB,EAIrB,MAAM,gBAAgB,CAAA;AAQvB,wBAAgB,cAAc,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,GAAG,MAAM,CAwG3F"}
1
+ {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../../src/plugins/devtools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAmD,MAAM,MAAM,CAAA;AACnF,OAAO,KAAK,EACX,qBAAqB,EAIrB,MAAM,gBAAgB,CAAA;AAQvB,wBAAgB,cAAc,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,GAAG,MAAM,CA0G3F"}
@@ -61,11 +61,11 @@ export function devtoolsPlugin(cacheFunctions) {
61
61
  await devtoolsModule.setupRebaskDevtools(devtools, {
62
62
  root: projectRoot,
63
63
  async getRoutes() {
64
- const { pages, layouts, routeHandlers } = await scanAppDirectory(join(projectRoot, "app"));
64
+ const { pages, layouts, routeHandlers, markdownFiles } = await scanAppDirectory(join(projectRoot, "app"));
65
65
  const proxyPath = await findProjectFile(projectRoot, "proxy");
66
66
  const proxyModule = proxyPath && runner && (await runner.import(proxyPath));
67
67
  return {
68
- pages: buildRoutes(layouts, pages).filter((route) => route.type === "page"),
68
+ pages: buildRoutes(layouts, pages, markdownFiles).filter((route) => route.type === "page"),
69
69
  routeHandlers: buildRouteHandlerRoutes(routeHandlers),
70
70
  proxyPatterns: proxyModule ? Object.keys(proxyModule.default ?? proxyModule) : [],
71
71
  };
@@ -2,6 +2,11 @@ import type { Plugin } from "vite";
2
2
  /** The `name` of the Vite plugin. */
3
3
  export declare const REBASK_PPR_PLUGIN_NAME = "rebask:ppr";
4
4
  export interface PprConfig {
5
+ /**
6
+ * Whether page responses include `x-rebask-cache`. Platforms that own page cache set this to
7
+ * `false`.
8
+ */
9
+ emitCacheStatus?: boolean;
5
10
  /**
6
11
  * Rebask only enters the resume path when this header matches `"1"`. Adapters must validate
7
12
  * their own transport credential and set this header before the Rebask handler executes.
@@ -1 +1 @@
1
- {"version":3,"file":"ppr.d.ts","sourceRoot":"","sources":["../../src/plugins/ppr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,qCAAqC;AACrC,eAAO,MAAM,sBAAsB,eAAe,CAAA;AAElD,MAAM,WAAW,SAAS;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAA;IAC5B,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,kBAAkB;IAClC,4EAA4E;IAC5E,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAA;CAC7C;AAKD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CA8BlC"}
1
+ {"version":3,"file":"ppr.d.ts","sourceRoot":"","sources":["../../src/plugins/ppr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,qCAAqC;AACrC,eAAO,MAAM,sBAAsB,eAAe,CAAA;AAElD,MAAM,WAAW,SAAS;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAA;IAC5B,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,kBAAkB;IAClC,4EAA4E;IAC5E,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAA;CAC7C;AAKD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CA8BlC"}
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/plugins/prerender.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAOlC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AASlE,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAOjD,KAAK,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAA;AAEhE,KAAK,sBAAsB,GAAG;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAA;IAC/D,2DAA2D;IAC3D,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;CACb,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;IAC9C,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IAClG;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC,CAAA;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAChD,CAAA;AA2CD,UAAU,sBAAsB;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,eAAe,CAC9B,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,sBAA2B,GAClC,MAAM,CA6VR"}
1
+ {"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../../src/plugins/prerender.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAQlC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AASlE,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAOjD,KAAK,YAAY,GAAG,WAAW,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAA;AAEhE,KAAK,sBAAsB,GAAG;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAA;IAC/D,2DAA2D;IAC3D,EAAE,CAAC,EAAE,MAAM,EAAE,CAAA;CACb,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;IAC9C,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IAClG;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC,CAAA;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAChD,CAAA;AA2CD,UAAU,sBAAsB;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,eAAe,CAC9B,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,sBAA2B,GAClC,MAAM,CA2ZR"}
@@ -63,7 +63,10 @@ export function prerenderPlugin(api, options = {}) {
63
63
  const prerenderOptions = { isBuildPrerender: true };
64
64
  await withFetchCache(() => Promise.all(pageRoutes.map(async (route) => {
65
65
  const isDynamic = route.segments.some(isDynamicSegment);
66
- const paramsResult = isDynamic ? await entryModule.getPrerenderParams(route) : null;
66
+ // Static page + Markdown routes load params only to catch conflicting prerenderParams.
67
+ const paramsResult = isDynamic || (route.hasPage !== false && route.markdownFilePath)
68
+ ? await entryModule.getPrerenderParams(route)
69
+ : null;
67
70
  const resolvedParams = paramsResult && paramsResult.params.length > 0 ? paramsResult.params : null;
68
71
  const allowExtraParams = isDynamic ? (paramsResult?.allowExtraParams ?? true) : true;
69
72
  const { filePath, shellParams } = route;
@@ -96,90 +99,131 @@ export function prerenderPlugin(api, options = {}) {
96
99
  if (allowExtraParams !== false) {
97
100
  requests.push({ pathname: getRoutePathname(route), params: {} });
98
101
  }
99
- const requestResults = await Promise.all(requests.map(async ({ pathname, params }) => {
100
- let prerenderMeta;
101
- const isSharedShell = isDynamic && Object.keys(params).length === 0;
102
- const request = new Request(`http://localhost${pathname}`, {
103
- headers: { accept: "text/html" },
104
- });
105
- const response = await entryModule.handlePageRoute(request, route, params, {
106
- ...prerenderOptions,
107
- // The build URL is only a static prefix. Force unresolved params and skip any
108
- // HTML cached at that URL.
109
- ...(isSharedShell && { resolvedParams: new Set() }),
110
- onBuildPrerenderMeta(meta) {
111
- prerenderMeta = meta;
112
- },
113
- });
114
- if (!prerenderMeta) {
115
- throw new Error(`Missing build prerender metadata for "${route.filePath}" at "${pathname}"`);
116
- }
117
- return { pathname, isSharedShell, response, prerenderMeta };
118
- }));
119
- for (const result of requestResults) {
120
- const { pathname, isSharedShell, response, prerenderMeta } = result;
121
- const { hasShell, cacheLife, postponedState, hasPrerenderRequestIO } = prerenderMeta;
122
- if (hasPrerenderRequestIO && !isSharedShell) {
123
- const ioPaths = (prerenderRequestIOPaths[filePath] ??= []);
124
- ioPaths.push(pathname);
125
- }
126
- if (cacheLife) {
127
- // Shared shell pathname is only a static prefix (not a real URL).
128
- if (!isSharedShell) {
129
- cacheControlByPath[pathname] = mergeCacheControl(cacheControlByPath[pathname], cacheLife);
102
+ if (route.hasPage !== false) {
103
+ const requestResults = await Promise.all(requests.map(async ({ pathname, params }) => {
104
+ let prerenderMeta;
105
+ const isSharedShell = isDynamic && Object.keys(params).length === 0;
106
+ const request = new Request(`http://localhost${pathname}`, {
107
+ headers: { accept: "text/html" },
108
+ });
109
+ const response = await entryModule.handlePageRoute(request, route, params, {
110
+ ...prerenderOptions,
111
+ // The build URL is only a static prefix. Force unresolved params and skip any
112
+ // HTML cached at that URL.
113
+ ...(isSharedShell && { resolvedParams: new Set() }),
114
+ onBuildPrerenderMeta(meta) {
115
+ prerenderMeta = meta;
116
+ },
117
+ });
118
+ if (!prerenderMeta) {
119
+ throw new Error(`Missing build prerender metadata for "${route.filePath}" at "${pathname}"`);
130
120
  }
131
- cacheControlByFilePath[filePath] = mergeCacheControl(cacheControlByFilePath[filePath], cacheLife);
132
- }
133
- if (!hasShell) {
134
- if (isSharedShell) {
135
- noShellRoutes.add(filePath);
121
+ return { pathname, params, isSharedShell, response, prerenderMeta };
122
+ }));
123
+ for (const result of requestResults) {
124
+ const { pathname, params, isSharedShell, response, prerenderMeta } = result;
125
+ const { hasShell, cacheLife, postponedState, hasPrerenderRequestIO } = prerenderMeta;
126
+ if (hasPrerenderRequestIO && !isSharedShell) {
127
+ const ioPaths = (prerenderRequestIOPaths[filePath] ??= []);
128
+ ioPaths.push(pathname);
136
129
  }
137
- if (allowExtraParams !== false) {
138
- const warning = noPrerenderWarning(pathname);
139
- if (failOnDynamic)
140
- throw new Error(warning);
141
- else
142
- console.warn(warning);
130
+ if (cacheLife) {
131
+ // Shared shell pathname is only a static prefix (not a real URL).
132
+ if (!isSharedShell) {
133
+ cacheControlByPath[pathname] = mergeCacheControl(cacheControlByPath[pathname], cacheLife);
134
+ }
135
+ cacheControlByFilePath[filePath] = mergeCacheControl(cacheControlByFilePath[filePath], cacheLife);
143
136
  }
144
- }
145
- if (hasShell && postponedState) {
146
- const html = await response.text();
147
- if (isSharedShell) {
137
+ if (!hasShell) {
138
+ if (isSharedShell) {
139
+ noShellRoutes.add(filePath);
140
+ }
141
+ if (allowExtraParams !== false) {
142
+ const warning = noPrerenderWarning(pathname);
143
+ if (failOnDynamic)
144
+ throw new Error(warning);
145
+ else
146
+ console.warn(warning);
147
+ }
148
+ }
149
+ if (hasShell && postponedState) {
150
+ const html = await response.text();
151
+ if (isSharedShell) {
152
+ await api.notifyShellPrerender({
153
+ kind: "shared",
154
+ filePath,
155
+ postponedState,
156
+ html,
157
+ notFound: prerenderMeta.notFound,
158
+ });
159
+ }
160
+ else {
161
+ await api.notifyShellPrerender({
162
+ kind: "static",
163
+ filePath,
164
+ pathname,
165
+ postponedState,
166
+ html,
167
+ notFound: prerenderMeta.notFound,
168
+ });
169
+ }
170
+ }
171
+ else if (hasShell &&
172
+ cacheLife?.revalidate !== 0 &&
173
+ (!isDynamic || allowExtraParams === false)) {
174
+ const html = await response.text();
175
+ // Capture the RSC payload so adapters can ship it as a STATIC entry.
176
+ // Otherwise every cold RSC request uses the empty pass-through PPR
177
+ // entry and chains through the function.
178
+ const rscPayload = !hasPrerenderRequestIO && !prerenderMeta.notFound
179
+ ? await entryModule
180
+ .handlePageRoute(new Request(`http://localhost${pathname}`, {
181
+ headers: { accept: "text/x-component" },
182
+ }), route, params, prerenderOptions)
183
+ .then((rscResponse) => rscResponse.text())
184
+ : undefined;
148
185
  await api.notifyShellPrerender({
149
- kind: "shared",
186
+ kind: "static-html",
150
187
  filePath,
151
- postponedState,
188
+ pathname,
152
189
  html,
190
+ ...(rscPayload !== undefined && { rscPayload }),
153
191
  notFound: prerenderMeta.notFound,
154
192
  });
155
193
  }
156
194
  else {
195
+ // HTML is already cached inside handlePageRoute; drop the unused body.
196
+ response.body?.cancel().catch(() => { });
197
+ }
198
+ console.log(`[rebask] ${hasShell ? "✓" : "✗"} ${pathname}`);
199
+ }
200
+ }
201
+ if (route.markdownFilePath) {
202
+ for (const { pathname, params } of requests) {
203
+ // A dynamic route's empty-params request is only a shared shell, not a real URL.
204
+ if (isDynamic && Object.keys(params).length === 0)
205
+ continue;
206
+ const request = new Request(`http://localhost${pathname}`, {
207
+ headers: { accept: "text/markdown" },
208
+ });
209
+ let markdownMeta;
210
+ const response = await entryModule.handleMarkdownRoute(request, route, params, {
211
+ isBuildPrerender: true,
212
+ onBuildPrerenderMeta(meta) {
213
+ markdownMeta = meta;
214
+ },
215
+ });
216
+ const markdown = await response.text();
217
+ if (markdownMeta?.static) {
157
218
  await api.notifyShellPrerender({
158
- kind: "static",
159
- filePath,
219
+ kind: "markdown",
160
220
  pathname,
161
- postponedState,
162
- html,
163
- notFound: prerenderMeta.notFound,
221
+ markdown,
222
+ cacheLife: markdownMeta.cacheLife,
164
223
  });
224
+ console.log(`[rebask] ✓ ${pathname}.md`);
165
225
  }
166
226
  }
167
- else if (hasShell &&
168
- cacheLife?.revalidate !== 0 &&
169
- (!isDynamic || allowExtraParams === false)) {
170
- await api.notifyShellPrerender({
171
- kind: "static-html",
172
- filePath,
173
- pathname,
174
- html: await response.text(),
175
- notFound: prerenderMeta.notFound,
176
- });
177
- }
178
- else {
179
- // HTML is already cached inside handlePageRoute; drop the unused body.
180
- response.body?.cancel().catch(() => { });
181
- }
182
- console.log(`[rebask] ${hasShell ? "✓" : "✗"} ${pathname}`);
183
227
  }
184
228
  // Prerender intermediate shells from layout `prerenderParams` (closest/richest first).
185
229
  if (shellParams && allowExtraParams !== false) {
@@ -1 +1 @@
1
- {"version":3,"file":"wint-router.d.ts","sourceRoot":"","sources":["../../../src/plugins/router/wint-router.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAsEhG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,YAAY,CAwDjE"}
1
+ {"version":3,"file":"wint-router.d.ts","sourceRoot":"","sources":["../../../src/plugins/router/wint-router.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,MAAM,kCAAkC,CAAA;AAsEzC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,YAAY,CA4DjE"}
@@ -1,5 +1,5 @@
1
1
  import Wint from "wint-js/turbo";
2
- import { RouteType } from "../../lib/server-router/types.js";
2
+ import { RouteType, } from "../../lib/server-router/types.js";
3
3
  /**
4
4
  * Convert a route's segments to Wint-compatible pattern
5
5
  * Examples:
@@ -110,7 +110,9 @@ export function createMatcher(entries) {
110
110
  }
111
111
  return (req) => entry.handler(req, params);
112
112
  }
113
- return (req) => entry.handler(req, extractParams(entry.route.segments, ctx.params ?? {}));
113
+ const handler = (req) => entry.handler(req, extractParams(entry.route.segments, ctx.params ?? {}));
114
+ handler.route = entry.route;
115
+ return handler;
114
116
  },
115
117
  };
116
118
  }
package/dist/router.d.ts CHANGED
@@ -115,7 +115,7 @@ declare class Router {
115
115
  * Refetch any stale (or missing) segments for a URL whose route tree is
116
116
  * already known, without ever fetching the route tree itself.
117
117
  *
118
- * Designed for hover / focus signals like `<Link>` and `<PrefetchLinks>`. If the route tree
118
+ * Designed for hover / focus signals like `<Link>`. If the route tree
119
119
  * hasn't been cached yet this is a no-op, and it's safe to call repeatedly because in-flight
120
120
  * fetches dedupe and fully fresh trees do zero work.
121
121
  */
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.tsx"],"names":[],"mappings":"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"}
package/dist/router.js CHANGED
@@ -320,7 +320,9 @@ class Router {
320
320
  const search = segmentLocation.search ?? "";
321
321
  const fetchKey = `${segmentPath}?${search}`;
322
322
  const { shellable, shellKey } = this.segmentShell(segmentLocation);
323
- const cachedShell = import.meta.env.PROD && shellable && !skipShell ? this.cache.getShell(shellKey) : undefined;
323
+ const cachedShell = import.meta.env.PROD && shellable && !skipShell
324
+ ? (this.cache.getShell(segmentPath) ?? this.cache.getShell(shellKey))
325
+ : undefined;
324
326
  const reusePolicy = this.cache.getSegmentReusePolicy(segmentLocation, cachedShell);
325
327
  const { completeShell, inBackground } = reusePolicy;
326
328
  let shell = reusePolicy.showShell ? cachedShell : undefined;
@@ -475,8 +477,11 @@ class Router {
475
477
  // constantly, so no stable shell survives to match. Serve the full segment instead.
476
478
  if (import.meta.env.DEV)
477
479
  return null;
478
- const cached = this.cache.getShell(key);
479
- if (cached && !cached.stale)
480
+ const usable = (shell) => shell && !shell.stale && (!shell.staticOnly || shell.path === segmentLocation.path);
481
+ const concrete = this.cache.getShell(segmentLocation.path);
482
+ const shared = this.cache.getShell(key);
483
+ const cached = usable(concrete) ? concrete : usable(shared) ? shared : undefined;
484
+ if (cached)
480
485
  return cached;
481
486
  let inFlight = inflightShellCache.get(key);
482
487
  if (!inFlight || !inFlight.isCurrent()) {
@@ -499,6 +504,10 @@ class Router {
499
504
  inflightShellCache.delete(key);
500
505
  if (!shell)
501
506
  return null;
507
+ // Another path answered the shared key with a static page. It cannot serve this one.
508
+ if (shell.staticOnly && shell.path !== segmentLocation.path) {
509
+ return this.ensureShell(segmentLocation, key, expectedRoutePrediction);
510
+ }
502
511
  const payload = await shell.payload;
503
512
  if (!inFlight.isCurrent())
504
513
  return null;
@@ -511,7 +520,7 @@ class Router {
511
520
  this.cache.markSegmentStale(segmentLocation);
512
521
  throw new RoutePredictionMismatch();
513
522
  }
514
- this.cache.cacheShell(key, shell);
523
+ this.cache.cacheShell(shell.staticOnly ? segmentLocation.path : key, shell);
515
524
  return shell;
516
525
  }
517
526
  async fetchSegmentShell(segmentLocation) {
@@ -536,7 +545,8 @@ class Router {
536
545
  }));
537
546
  const resolvedPayload = await payload;
538
547
  const segmentHints = id === null ? await extractSegmentHints(resolvedPayload.seedData.content) : undefined;
539
- return { bytes, id, payload, cachedAt, ...segmentHints };
548
+ const staticOnly = "routePrediction" in resolvedPayload && resolvedPayload.routePrediction?.[3] === true;
549
+ return { bytes, path: segmentLocation.path, staticOnly, id, payload, cachedAt, ...segmentHints };
540
550
  }
541
551
  /**
542
552
  * Prefetch all missing segments for a route tree and cache them.
@@ -720,7 +730,7 @@ class Router {
720
730
  * Refetch any stale (or missing) segments for a URL whose route tree is
721
731
  * already known, without ever fetching the route tree itself.
722
732
  *
723
- * Designed for hover / focus signals like `<Link>` and `<PrefetchLinks>`. If the route tree
733
+ * Designed for hover / focus signals like `<Link>`. If the route tree
724
734
  * hasn't been cached yet this is a no-op, and it's safe to call repeatedly because in-flight
725
735
  * fetches dedupe and fully fresh trees do zero work.
726
736
  */
@@ -1,5 +1,5 @@
1
1
  export { RouteType } from "./lib/server-router/types.js";
2
- export type { CreateMatcherFn, RouteEntry, RouteMatcher } from "./lib/server-router/types.js";
2
+ export type { CreateMatcherFn, MatchedHandler, RouteEntry, RouteMatcher, } from "./lib/server-router/types.js";
3
3
  export type { LayoutRoute, NotFoundRoute, Route, RouteSegment, RouteHandlerRoute, } from "./lib/route-matcher.js";
4
4
  export { createRouterPlugin, rebaskRouter, type RouterPluginOptions, } from "./plugins/router/router-plugin.js";
5
5
  //# sourceMappingURL=server-router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-router.d.ts","sourceRoot":"","sources":["../src/server-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AACxD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC7F,YAAY,EACX,WAAW,EACX,aAAa,EACb,KAAK,EACL,YAAY,EACZ,iBAAiB,GACjB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACN,kBAAkB,EAClB,YAAY,EACZ,KAAK,mBAAmB,GACxB,MAAM,mCAAmC,CAAA"}
1
+ {"version":3,"file":"server-router.d.ts","sourceRoot":"","sources":["../src/server-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AACxD,YAAY,EACX,eAAe,EACf,cAAc,EACd,UAAU,EACV,YAAY,GACZ,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACX,WAAW,EACX,aAAa,EACb,KAAK,EACL,YAAY,EACZ,iBAAiB,GACjB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACN,kBAAkB,EAClB,YAAY,EACZ,KAAK,mBAAmB,GACxB,MAAM,mCAAmC,CAAA"}