vinext 0.0.44 → 0.0.46

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 (323) hide show
  1. package/dist/build/google-fonts/build-url.d.ts +10 -0
  2. package/dist/build/google-fonts/build-url.js +30 -0
  3. package/dist/build/google-fonts/build-url.js.map +1 -0
  4. package/dist/build/google-fonts/font-data.js +24985 -0
  5. package/dist/build/google-fonts/font-data.js.map +1 -0
  6. package/dist/build/google-fonts/font-metadata.d.ts +17 -0
  7. package/dist/build/google-fonts/font-metadata.js +7 -0
  8. package/dist/build/google-fonts/font-metadata.js.map +1 -0
  9. package/dist/build/google-fonts/get-axes.d.ts +7 -0
  10. package/dist/build/google-fonts/get-axes.js +39 -0
  11. package/dist/build/google-fonts/get-axes.js.map +1 -0
  12. package/dist/build/google-fonts/sort-variants.d.ts +5 -0
  13. package/dist/build/google-fonts/sort-variants.js +14 -0
  14. package/dist/build/google-fonts/sort-variants.js.map +1 -0
  15. package/dist/build/google-fonts/validate.d.ts +28 -0
  16. package/dist/build/google-fonts/validate.js +56 -0
  17. package/dist/build/google-fonts/validate.js.map +1 -0
  18. package/dist/build/layout-classification.d.ts +1 -1
  19. package/dist/build/layout-classification.js.map +1 -1
  20. package/dist/build/nitro-route-rules.d.ts +1 -1
  21. package/dist/build/nitro-route-rules.js.map +1 -1
  22. package/dist/build/precompress.d.ts +1 -1
  23. package/dist/build/precompress.js.map +1 -1
  24. package/dist/build/prerender.d.ts +1 -7
  25. package/dist/build/prerender.js +17 -6
  26. package/dist/build/prerender.js.map +1 -1
  27. package/dist/build/run-prerender.d.ts +1 -13
  28. package/dist/build/run-prerender.js +5 -1
  29. package/dist/build/run-prerender.js.map +1 -1
  30. package/dist/build/standalone.d.ts +1 -1
  31. package/dist/build/standalone.js +4 -3
  32. package/dist/build/standalone.js.map +1 -1
  33. package/dist/check.js +30 -18
  34. package/dist/check.js.map +1 -1
  35. package/dist/cli.js +4 -0
  36. package/dist/cli.js.map +1 -1
  37. package/dist/cloudflare/kv-cache-handler.d.ts +5 -0
  38. package/dist/cloudflare/kv-cache-handler.js +56 -35
  39. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  40. package/dist/cloudflare/tpr.d.ts +1 -16
  41. package/dist/cloudflare/tpr.js +1 -1
  42. package/dist/cloudflare/tpr.js.map +1 -1
  43. package/dist/config/config-matchers.js +1 -0
  44. package/dist/config/config-matchers.js.map +1 -1
  45. package/dist/config/dotenv.d.ts +1 -1
  46. package/dist/config/dotenv.js.map +1 -1
  47. package/dist/config/next-config.d.ts +38 -2
  48. package/dist/config/next-config.js +24 -0
  49. package/dist/config/next-config.js.map +1 -1
  50. package/dist/deploy.d.ts +1 -1
  51. package/dist/deploy.js +18 -23
  52. package/dist/deploy.js.map +1 -1
  53. package/dist/entries/app-rsc-entry.js +387 -1718
  54. package/dist/entries/app-rsc-entry.js.map +1 -1
  55. package/dist/entries/app-rsc-manifest.d.ts +24 -0
  56. package/dist/entries/app-rsc-manifest.js +153 -0
  57. package/dist/entries/app-rsc-manifest.js.map +1 -0
  58. package/dist/entries/pages-server-entry.js +13 -103
  59. package/dist/entries/pages-server-entry.js.map +1 -1
  60. package/dist/index.js +59 -34
  61. package/dist/index.js.map +1 -1
  62. package/dist/init.d.ts +1 -1
  63. package/dist/init.js.map +1 -1
  64. package/dist/plugins/async-hooks-stub.d.ts +1 -2
  65. package/dist/plugins/async-hooks-stub.js +2 -2
  66. package/dist/plugins/async-hooks-stub.js.map +1 -1
  67. package/dist/plugins/fonts.d.ts +1 -20
  68. package/dist/plugins/fonts.js +42 -21
  69. package/dist/plugins/fonts.js.map +1 -1
  70. package/dist/plugins/rsc-client-shim-excludes.d.ts +6 -0
  71. package/dist/plugins/rsc-client-shim-excludes.js +27 -0
  72. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -0
  73. package/dist/plugins/server-externals-manifest.d.ts +1 -11
  74. package/dist/plugins/server-externals-manifest.js +1 -1
  75. package/dist/plugins/server-externals-manifest.js.map +1 -1
  76. package/dist/routing/app-router.d.ts +14 -5
  77. package/dist/routing/app-router.js +82 -5
  78. package/dist/routing/app-router.js.map +1 -1
  79. package/dist/routing/file-matcher.d.ts +1 -3
  80. package/dist/routing/file-matcher.js +1 -1
  81. package/dist/routing/file-matcher.js.map +1 -1
  82. package/dist/routing/route-pattern.d.ts +9 -0
  83. package/dist/routing/route-pattern.js +90 -0
  84. package/dist/routing/route-pattern.js.map +1 -0
  85. package/dist/routing/route-trie.js +10 -11
  86. package/dist/routing/route-trie.js.map +1 -1
  87. package/dist/routing/utils.d.ts +1 -29
  88. package/dist/routing/utils.js +1 -1
  89. package/dist/routing/utils.js.map +1 -1
  90. package/dist/server/app-browser-entry.js +63 -5
  91. package/dist/server/app-browser-entry.js.map +1 -1
  92. package/dist/server/app-browser-state.d.ts +1 -1
  93. package/dist/server/app-browser-state.js.map +1 -1
  94. package/dist/server/app-browser-stream.d.ts +1 -1
  95. package/dist/server/app-browser-stream.js.map +1 -1
  96. package/dist/server/app-elements.d.ts +1 -2
  97. package/dist/server/app-elements.js +1 -1
  98. package/dist/server/app-elements.js.map +1 -1
  99. package/dist/server/app-middleware.d.ts +32 -0
  100. package/dist/server/app-middleware.js +147 -0
  101. package/dist/server/app-middleware.js.map +1 -0
  102. package/dist/server/app-page-boundary-render.d.ts +5 -1
  103. package/dist/server/app-page-boundary-render.js +52 -30
  104. package/dist/server/app-page-boundary-render.js.map +1 -1
  105. package/dist/server/app-page-boundary.d.ts +13 -1
  106. package/dist/server/app-page-boundary.js +37 -17
  107. package/dist/server/app-page-boundary.js.map +1 -1
  108. package/dist/server/app-page-cache.d.ts +4 -1
  109. package/dist/server/app-page-cache.js +38 -2
  110. package/dist/server/app-page-cache.js.map +1 -1
  111. package/dist/server/app-page-dispatch.d.ts +120 -0
  112. package/dist/server/app-page-dispatch.js +332 -0
  113. package/dist/server/app-page-dispatch.js.map +1 -0
  114. package/dist/server/app-page-execution.d.ts +6 -3
  115. package/dist/server/app-page-execution.js +22 -10
  116. package/dist/server/app-page-execution.js.map +1 -1
  117. package/dist/server/app-page-head.d.ts +55 -0
  118. package/dist/server/app-page-head.js +196 -0
  119. package/dist/server/app-page-head.js.map +1 -0
  120. package/dist/server/app-page-method.d.ts +16 -0
  121. package/dist/server/app-page-method.js +30 -0
  122. package/dist/server/app-page-method.js.map +1 -0
  123. package/dist/server/app-page-params.d.ts +7 -0
  124. package/dist/server/app-page-params.js +28 -0
  125. package/dist/server/app-page-params.js.map +1 -0
  126. package/dist/server/app-page-probe.d.ts +1 -1
  127. package/dist/server/app-page-probe.js.map +1 -1
  128. package/dist/server/app-page-render.d.ts +4 -3
  129. package/dist/server/app-page-render.js +54 -8
  130. package/dist/server/app-page-render.js.map +1 -1
  131. package/dist/server/app-page-request.d.ts +5 -5
  132. package/dist/server/app-page-request.js.map +1 -1
  133. package/dist/server/app-page-response.d.ts +1 -1
  134. package/dist/server/app-page-response.js.map +1 -1
  135. package/dist/server/app-page-route-wiring.d.ts +15 -11
  136. package/dist/server/app-page-route-wiring.js +31 -9
  137. package/dist/server/app-page-route-wiring.js.map +1 -1
  138. package/dist/server/app-page-stream.d.ts +12 -1
  139. package/dist/server/app-page-stream.js +10 -4
  140. package/dist/server/app-page-stream.js.map +1 -1
  141. package/dist/server/app-prerender-endpoints.d.ts +19 -0
  142. package/dist/server/app-prerender-endpoints.js +96 -0
  143. package/dist/server/app-prerender-endpoints.js.map +1 -0
  144. package/dist/server/app-prerender-static-params.d.ts +16 -0
  145. package/dist/server/app-prerender-static-params.js +14 -0
  146. package/dist/server/app-prerender-static-params.js.map +1 -0
  147. package/dist/server/app-route-handler-cache.d.ts +4 -1
  148. package/dist/server/app-route-handler-cache.js +6 -2
  149. package/dist/server/app-route-handler-cache.js.map +1 -1
  150. package/dist/server/app-route-handler-dispatch.d.ts +42 -0
  151. package/dist/server/app-route-handler-dispatch.js +147 -0
  152. package/dist/server/app-route-handler-dispatch.js.map +1 -0
  153. package/dist/server/app-route-handler-execution.d.ts +7 -3
  154. package/dist/server/app-route-handler-execution.js +32 -6
  155. package/dist/server/app-route-handler-execution.js.map +1 -1
  156. package/dist/server/app-route-handler-policy.d.ts +6 -2
  157. package/dist/server/app-route-handler-policy.js +8 -3
  158. package/dist/server/app-route-handler-policy.js.map +1 -1
  159. package/dist/server/app-route-handler-response.d.ts +2 -1
  160. package/dist/server/app-route-handler-response.js +44 -4
  161. package/dist/server/app-route-handler-response.js.map +1 -1
  162. package/dist/server/app-route-handler-runtime.d.ts +5 -2
  163. package/dist/server/app-route-handler-runtime.js +108 -2
  164. package/dist/server/app-route-handler-runtime.js.map +1 -1
  165. package/dist/server/app-router-entry.js.map +1 -1
  166. package/dist/server/app-rsc-errors.d.ts +27 -0
  167. package/dist/server/app-rsc-errors.js +42 -0
  168. package/dist/server/app-rsc-errors.js.map +1 -0
  169. package/dist/server/app-rsc-route-matching.d.ts +40 -0
  170. package/dist/server/app-rsc-route-matching.js +66 -0
  171. package/dist/server/app-rsc-route-matching.js.map +1 -0
  172. package/dist/server/app-server-action-execution.d.ts +120 -0
  173. package/dist/server/app-server-action-execution.js +355 -0
  174. package/dist/server/app-server-action-execution.js.map +1 -0
  175. package/dist/server/app-ssr-entry.d.ts +7 -0
  176. package/dist/server/app-ssr-entry.js +30 -9
  177. package/dist/server/app-ssr-entry.js.map +1 -1
  178. package/dist/server/app-ssr-stream.d.ts +5 -3
  179. package/dist/server/app-ssr-stream.js +29 -2
  180. package/dist/server/app-ssr-stream.js.map +1 -1
  181. package/dist/server/app-static-generation.d.ts +15 -0
  182. package/dist/server/app-static-generation.js +20 -0
  183. package/dist/server/app-static-generation.js.map +1 -0
  184. package/dist/server/csp.d.ts +1 -2
  185. package/dist/server/csp.js +1 -1
  186. package/dist/server/csp.js.map +1 -1
  187. package/dist/server/dev-module-runner.d.ts +1 -1
  188. package/dist/server/dev-module-runner.js.map +1 -1
  189. package/dist/server/dev-route-files.d.ts +7 -0
  190. package/dist/server/dev-route-files.js +73 -0
  191. package/dist/server/dev-route-files.js.map +1 -0
  192. package/dist/server/dev-server.js +4 -0
  193. package/dist/server/dev-server.js.map +1 -1
  194. package/dist/server/file-based-metadata.d.ts +17 -0
  195. package/dist/server/file-based-metadata.js +356 -0
  196. package/dist/server/file-based-metadata.js.map +1 -0
  197. package/dist/server/implicit-tags.d.ts +6 -0
  198. package/dist/server/implicit-tags.js +42 -0
  199. package/dist/server/implicit-tags.js.map +1 -0
  200. package/dist/server/instrumentation.js.map +1 -1
  201. package/dist/server/isr-cache.d.ts +20 -2
  202. package/dist/server/isr-cache.js +58 -7
  203. package/dist/server/isr-cache.js.map +1 -1
  204. package/dist/server/metadata-route-build-data.d.ts +25 -0
  205. package/dist/server/metadata-route-build-data.js +150 -0
  206. package/dist/server/metadata-route-build-data.js.map +1 -0
  207. package/dist/server/metadata-route-response.d.ts +17 -0
  208. package/dist/server/metadata-route-response.js +187 -0
  209. package/dist/server/metadata-route-response.js.map +1 -0
  210. package/dist/server/metadata-routes.d.ts +42 -4
  211. package/dist/server/metadata-routes.js +127 -11
  212. package/dist/server/metadata-routes.js.map +1 -1
  213. package/dist/server/middleware-matcher.d.ts +15 -0
  214. package/dist/server/middleware-matcher.js +102 -0
  215. package/dist/server/middleware-matcher.js.map +1 -0
  216. package/dist/server/middleware-request-headers.d.ts +1 -3
  217. package/dist/server/middleware-request-headers.js +5 -4
  218. package/dist/server/middleware-request-headers.js.map +1 -1
  219. package/dist/server/middleware-runtime.d.ts +39 -0
  220. package/dist/server/middleware-runtime.js +159 -0
  221. package/dist/server/middleware-runtime.js.map +1 -0
  222. package/dist/server/middleware.d.ts +5 -37
  223. package/dist/server/middleware.js +18 -228
  224. package/dist/server/middleware.js.map +1 -1
  225. package/dist/server/pages-api-route.d.ts +1 -1
  226. package/dist/server/pages-api-route.js.map +1 -1
  227. package/dist/server/pages-i18n.d.ts +2 -3
  228. package/dist/server/pages-i18n.js +1 -1
  229. package/dist/server/pages-i18n.js.map +1 -1
  230. package/dist/server/pages-node-compat.d.ts +1 -2
  231. package/dist/server/pages-node-compat.js +1 -1
  232. package/dist/server/pages-node-compat.js.map +1 -1
  233. package/dist/server/pages-page-data.d.ts +6 -2
  234. package/dist/server/pages-page-data.js +4 -0
  235. package/dist/server/pages-page-data.js.map +1 -1
  236. package/dist/server/pages-page-response.d.ts +1 -1
  237. package/dist/server/pages-page-response.js +2 -1
  238. package/dist/server/pages-page-response.js.map +1 -1
  239. package/dist/server/prerender-work-unit-setup.d.ts +7 -0
  240. package/dist/server/prerender-work-unit-setup.js +30 -0
  241. package/dist/server/prerender-work-unit-setup.js.map +1 -0
  242. package/dist/server/prod-server.js +12 -14
  243. package/dist/server/prod-server.js.map +1 -1
  244. package/dist/server/request-pipeline.d.ts +46 -5
  245. package/dist/server/request-pipeline.js +84 -5
  246. package/dist/server/request-pipeline.js.map +1 -1
  247. package/dist/server/rsc-stream-hints.d.ts +5 -0
  248. package/dist/server/rsc-stream-hints.js +35 -0
  249. package/dist/server/rsc-stream-hints.js.map +1 -0
  250. package/dist/server/seed-cache.js.map +1 -1
  251. package/dist/server/server-action-not-found.d.ts +9 -0
  252. package/dist/server/server-action-not-found.js +40 -0
  253. package/dist/server/server-action-not-found.js.map +1 -0
  254. package/dist/server/socket-error-backstop.d.ts +17 -0
  255. package/dist/server/socket-error-backstop.js +129 -0
  256. package/dist/server/socket-error-backstop.js.map +1 -0
  257. package/dist/server/static-file-cache.d.ts +1 -1
  258. package/dist/server/static-file-cache.js.map +1 -1
  259. package/dist/shims/cache-runtime.js +16 -3
  260. package/dist/shims/cache-runtime.js.map +1 -1
  261. package/dist/shims/cache.d.ts +27 -2
  262. package/dist/shims/cache.js +135 -24
  263. package/dist/shims/cache.js.map +1 -1
  264. package/dist/shims/error-boundary.d.ts +49 -4
  265. package/dist/shims/error-boundary.js +76 -4
  266. package/dist/shims/error-boundary.js.map +1 -1
  267. package/dist/shims/fetch-cache.d.ts +10 -1
  268. package/dist/shims/fetch-cache.js +24 -4
  269. package/dist/shims/fetch-cache.js.map +1 -1
  270. package/dist/shims/font-google-base.d.ts +21 -22
  271. package/dist/shims/font-google-base.js +86 -29
  272. package/dist/shims/font-google-base.js.map +1 -1
  273. package/dist/shims/form.js +1 -1
  274. package/dist/shims/headers.d.ts +14 -2
  275. package/dist/shims/headers.js +127 -17
  276. package/dist/shims/headers.js.map +1 -1
  277. package/dist/shims/image.js +26 -8
  278. package/dist/shims/image.js.map +1 -1
  279. package/dist/shims/internal/make-hanging-promise.d.ts +16 -0
  280. package/dist/shims/internal/make-hanging-promise.js +46 -0
  281. package/dist/shims/internal/make-hanging-promise.js.map +1 -0
  282. package/dist/shims/internal/work-unit-async-storage.d.ts +26 -3
  283. package/dist/shims/internal/work-unit-async-storage.js +6 -3
  284. package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
  285. package/dist/shims/link.js +1 -1
  286. package/dist/shims/metadata.d.ts +38 -26
  287. package/dist/shims/metadata.js +75 -45
  288. package/dist/shims/metadata.js.map +1 -1
  289. package/dist/shims/navigation.d.ts +17 -4
  290. package/dist/shims/navigation.js +29 -6
  291. package/dist/shims/navigation.js.map +1 -1
  292. package/dist/shims/navigation.react-server.d.ts +2 -2
  293. package/dist/shims/navigation.react-server.js +2 -2
  294. package/dist/shims/navigation.react-server.js.map +1 -1
  295. package/dist/shims/offline.d.ts +5 -0
  296. package/dist/shims/offline.js +17 -0
  297. package/dist/shims/offline.js.map +1 -0
  298. package/dist/shims/request-state-types.d.ts +2 -1
  299. package/dist/shims/root-params.d.ts +11 -0
  300. package/dist/shims/root-params.js +24 -0
  301. package/dist/shims/root-params.js.map +1 -0
  302. package/dist/shims/router.js +1 -1
  303. package/dist/shims/server.d.ts +5 -1
  304. package/dist/shims/server.js +101 -10
  305. package/dist/shims/server.js.map +1 -1
  306. package/dist/shims/thenable-params.d.ts +5 -0
  307. package/dist/shims/thenable-params.js +37 -0
  308. package/dist/shims/thenable-params.js.map +1 -0
  309. package/dist/shims/unified-request-context.d.ts +2 -1
  310. package/dist/shims/unified-request-context.js +4 -0
  311. package/dist/shims/unified-request-context.js.map +1 -1
  312. package/dist/shims/url-safety.d.ts +3 -1
  313. package/dist/shims/url-safety.js +5 -1
  314. package/dist/shims/url-safety.js.map +1 -1
  315. package/dist/utils/error-cause.d.ts +5 -0
  316. package/dist/utils/error-cause.js +97 -0
  317. package/dist/utils/error-cause.js.map +1 -0
  318. package/dist/utils/lazy-chunks.d.ts +1 -1
  319. package/dist/utils/lazy-chunks.js.map +1 -1
  320. package/package.json +6 -1
  321. package/dist/server/middleware-codegen.d.ts +0 -54
  322. package/dist/server/middleware-codegen.js +0 -414
  323. package/dist/server/middleware-codegen.js.map +0 -1
@@ -0,0 +1,356 @@
1
+ import { fillRoutePatternSegments, routePattern } from "../routing/route-pattern.js";
2
+ import { getMetadataImageRouteKind, getMetadataRouteKind, isValidMetadataImageId } from "./metadata-routes.js";
3
+ import { makeThenableParams } from "../shims/thenable-params.js";
4
+ //#region src/server/file-based-metadata.ts
5
+ function routeApplies(routePath, routePrefix) {
6
+ if (!routePrefix) return true;
7
+ return routePath === routePrefix || routePath.startsWith(`${routePrefix}/`);
8
+ }
9
+ function routeScore(routePrefix) {
10
+ return routePrefix.split("/").filter(Boolean).length;
11
+ }
12
+ function routeSegmentsApply(routeSegments, routePrefixSegments) {
13
+ if (routePrefixSegments.length > routeSegments.length) return false;
14
+ for (let index = 0; index < routePrefixSegments.length; index++) if (routeSegments[index] !== routePrefixSegments[index]) return false;
15
+ return true;
16
+ }
17
+ function removeParallelRouteSegments(routeSegments) {
18
+ return routeSegments.filter((segment) => !segment.startsWith("@"));
19
+ }
20
+ function routeSegmentsApplyWithParallelSlots(routeSegments, routePrefixSegments) {
21
+ if (routeSegmentsApply(routeSegments, routePrefixSegments)) return true;
22
+ const visiblePrefixSegments = removeParallelRouteSegments(routePrefixSegments);
23
+ return visiblePrefixSegments.length !== routePrefixSegments.length && routeSegmentsApply(routeSegments, visiblePrefixSegments);
24
+ }
25
+ function routeSpecificity(route) {
26
+ return route.routeSegments?.length ?? routeScore(route.routePrefix);
27
+ }
28
+ function selectDeepestRoutes(metadataRoutes, kind, routePath, params, routeSegments) {
29
+ if (!metadataRoutes || metadataRoutes.length === 0) return [];
30
+ let selectedScore = -1;
31
+ const selectedRoutes = [];
32
+ for (const route of metadataRoutes) {
33
+ if ((route.headData?.kind ?? getMetadataRouteKind(route)) !== kind) continue;
34
+ if (routeSegments && route.routeSegments) {
35
+ if (!routeSegmentsApplyWithParallelSlots(routeSegments, route.routeSegments)) continue;
36
+ const currentScore = routeSpecificity(route);
37
+ if (currentScore > selectedScore) {
38
+ selectedScore = currentScore;
39
+ selectedRoutes.length = 0;
40
+ selectedRoutes.push(route);
41
+ continue;
42
+ }
43
+ if (currentScore === selectedScore) selectedRoutes.push(route);
44
+ continue;
45
+ }
46
+ const routePrefix = route.routePrefix;
47
+ const resolvedRoutePrefix = fillRoutePatternSegments(routePrefix, params);
48
+ const normalizedRoutePrefix = routePattern(routePrefix);
49
+ if (!routeApplies(routePath, routePrefix) && !routeApplies(routePath, normalizedRoutePrefix) && (!resolvedRoutePrefix || !routeApplies(routePath, resolvedRoutePrefix))) continue;
50
+ const currentScore = routeSpecificity(route);
51
+ if (currentScore > selectedScore) {
52
+ selectedScore = currentScore;
53
+ selectedRoutes.length = 0;
54
+ selectedRoutes.push(route);
55
+ continue;
56
+ }
57
+ if (currentScore === selectedScore) selectedRoutes.push(route);
58
+ }
59
+ return selectedRoutes;
60
+ }
61
+ function isStringOrUrl(value) {
62
+ return typeof value === "string" || typeof value === "object" && value instanceof URL;
63
+ }
64
+ function normalizeIconDescriptor(value) {
65
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
66
+ const urlValue = Reflect.get(value, "url");
67
+ if (!isStringOrUrl(urlValue)) return null;
68
+ const entry = { url: urlValue };
69
+ const sizesValue = Reflect.get(value, "sizes");
70
+ if (typeof sizesValue === "string") entry.sizes = sizesValue;
71
+ const typeValue = Reflect.get(value, "type");
72
+ if (typeof typeValue === "string") entry.type = typeValue;
73
+ const mediaValue = Reflect.get(value, "media");
74
+ if (typeof mediaValue === "string") entry.media = mediaValue;
75
+ return entry;
76
+ }
77
+ function normalizeIconValue(value) {
78
+ if (isStringOrUrl(value)) return { url: value };
79
+ return normalizeIconDescriptor(value);
80
+ }
81
+ function normalizeIconValueList(values) {
82
+ const normalizedEntries = [];
83
+ for (const value of values) {
84
+ const normalizedValue = normalizeIconValue(value);
85
+ if (normalizedValue) normalizedEntries.push(normalizedValue);
86
+ }
87
+ return normalizedEntries;
88
+ }
89
+ function normalizeIconEntries(icon) {
90
+ const normalizedTopLevelValue = normalizeIconValue(icon);
91
+ if (normalizedTopLevelValue) return [normalizedTopLevelValue];
92
+ if (Array.isArray(icon)) return normalizeIconValueList(icon);
93
+ if (!isIconMap(icon)) return [];
94
+ const iconValue = icon.icon;
95
+ if (!iconValue) return [];
96
+ if (Array.isArray(iconValue)) return normalizeIconValueList(iconValue);
97
+ const normalizedValue = normalizeIconValue(iconValue);
98
+ return normalizedValue ? [normalizedValue] : [];
99
+ }
100
+ function isIconMap(value) {
101
+ if (!value || typeof value !== "object" || value instanceof URL || Array.isArray(value)) return false;
102
+ return normalizeIconValue(value) === null;
103
+ }
104
+ function cloneIconMap(value) {
105
+ if (!value) return {};
106
+ if (isIconMap(value)) return { ...value };
107
+ const iconEntries = normalizeIconEntries(value);
108
+ return iconEntries.length > 0 ? { icon: iconEntries } : {};
109
+ }
110
+ function buildIconEntry(headData) {
111
+ if (headData.kind !== "favicon" && headData.kind !== "icon") return null;
112
+ const iconEntry = { url: headData.href };
113
+ if (headData.sizes) iconEntry.sizes = headData.sizes;
114
+ if (headData.type) iconEntry.type = headData.type;
115
+ return iconEntry;
116
+ }
117
+ function buildAppleEntry(headData) {
118
+ if (headData.kind !== "apple") return null;
119
+ const appleEntry = { url: headData.href };
120
+ if (headData.sizes) appleEntry.sizes = headData.sizes;
121
+ if (headData.type) appleEntry.type = headData.type;
122
+ return appleEntry;
123
+ }
124
+ function normalizeAppleEntry(value) {
125
+ if (isStringOrUrl(value)) return { url: value };
126
+ return { ...value };
127
+ }
128
+ function buildSocialEntry(headData) {
129
+ if (headData.kind !== "openGraph" && headData.kind !== "twitter") return null;
130
+ const socialEntry = { url: headData.href };
131
+ if (headData.width !== void 0) socialEntry.width = headData.width;
132
+ if (headData.height !== void 0) socialEntry.height = headData.height;
133
+ if (headData.alt) socialEntry.alt = headData.alt;
134
+ if (headData.type) socialEntry.type = headData.type;
135
+ return socialEntry;
136
+ }
137
+ function normalizeMetadataImageId(route, id) {
138
+ const normalizedId = String(id);
139
+ if (!isValidMetadataImageId(normalizedId)) {
140
+ console.warn(`[vinext] Skipping metadata route ${route.servedUrl} image id "${normalizedId}" because metadata image ids must match /^[a-zA-Z0-9-_.]+$/.`);
141
+ return null;
142
+ }
143
+ return normalizedId;
144
+ }
145
+ function withContentHash(href, contentHash) {
146
+ if (!contentHash) return href;
147
+ return `${href}?${contentHash}`;
148
+ }
149
+ function hasOwnProperty(source, key) {
150
+ return Boolean(source && Object.prototype.hasOwnProperty.call(source, key));
151
+ }
152
+ function hasOpenGraphImages(metadata) {
153
+ return hasOwnProperty(metadata?.openGraph, "images");
154
+ }
155
+ function hasTwitterImages(metadata) {
156
+ return hasOwnProperty(metadata?.twitter, "images");
157
+ }
158
+ function hasIcons(metadata) {
159
+ return Boolean(metadata?.icons);
160
+ }
161
+ function getMetadataSourceForRoute(route, options, fallbackMetadata) {
162
+ if (!options?.metadataSources) return fallbackMetadata;
163
+ if (!route.routeSegments) return null;
164
+ for (let index = options.metadataSources.length - 1; index >= 0; index--) {
165
+ const source = options.metadataSources[index];
166
+ if (routeSegmentsApplyWithParallelSlots(source.routeSegments, route.routeSegments)) return source.metadata;
167
+ }
168
+ return null;
169
+ }
170
+ function socialRouteHasExplicitImagesAtSource(route, kind, options, fallbackMetadata) {
171
+ const sourceMetadata = getMetadataSourceForRoute(route, options, fallbackMetadata);
172
+ return kind === "openGraph" ? hasOpenGraphImages(sourceMetadata) : hasTwitterImages(sourceMetadata);
173
+ }
174
+ function iconRouteHasExplicitIconsAtSource(route, options, fallbackMetadata) {
175
+ return hasIcons(fallbackMetadata) || hasIcons(getMetadataSourceForRoute(route, options, null));
176
+ }
177
+ function readStringProperty(source, key) {
178
+ const value = Reflect.get(source, key);
179
+ return typeof value === "string" ? value : void 0;
180
+ }
181
+ function readNumberProperty(source, key) {
182
+ const value = Reflect.get(source, key);
183
+ return typeof value === "number" ? value : void 0;
184
+ }
185
+ function readStringOrNumberProperty(source, key) {
186
+ const value = Reflect.get(source, key);
187
+ if (typeof value === "string" || typeof value === "number") return value;
188
+ }
189
+ function readSizeProperty(source) {
190
+ const sizeValue = Reflect.get(source, "size");
191
+ if (typeof sizeValue !== "object" || sizeValue === null) return;
192
+ const width = readNumberProperty(sizeValue, "width");
193
+ const height = readNumberProperty(sizeValue, "height");
194
+ if (width === void 0 && height === void 0) return;
195
+ return {
196
+ width,
197
+ height
198
+ };
199
+ }
200
+ function readDynamicImageMetadataSource(source) {
201
+ return {
202
+ id: readStringOrNumberProperty(source, "id"),
203
+ alt: readStringProperty(source, "alt"),
204
+ contentType: readStringProperty(source, "contentType"),
205
+ size: readSizeProperty(source)
206
+ };
207
+ }
208
+ async function resolveDynamicImageMetadataSources(route, params) {
209
+ if (!route.module || typeof route.module !== "object") return [];
210
+ const generateImageMetadata = Reflect.get(route.module, "generateImageMetadata");
211
+ if (typeof generateImageMetadata !== "function") return [readDynamicImageMetadataSource(route.module)];
212
+ const result = await generateImageMetadata({ params: makeThenableParams(params) });
213
+ if (!Array.isArray(result)) return [];
214
+ const sources = [];
215
+ for (const entry of result) if (typeof entry === "object" && entry !== null) {
216
+ const source = readDynamicImageMetadataSource(entry);
217
+ if (source.id === void 0) {
218
+ console.warn(`[vinext] Skipping metadata route ${route.servedUrl} image metadata entry because generateImageMetadata entries must include an id.`);
219
+ continue;
220
+ }
221
+ sources.push(source);
222
+ }
223
+ return sources;
224
+ }
225
+ async function resolveRouteHeadData(route, params) {
226
+ if (!route.isDynamic || !route.module || typeof route.module !== "object") return route.headData ? [route.headData] : [];
227
+ const routeKind = getMetadataImageRouteKind(route);
228
+ if (!routeKind) return route.headData ? [route.headData] : [];
229
+ const resolvedUrl = fillRoutePatternSegments(route.servedUrl, params);
230
+ if (!resolvedUrl) {
231
+ console.warn(`[vinext] Skipping metadata route ${route.servedUrl} because params did not fill all dynamic segments.`);
232
+ return [];
233
+ }
234
+ const metadataSources = await resolveDynamicImageMetadataSources(route, params);
235
+ const resolvedHeadData = [];
236
+ for (const metadataSource of metadataSources) {
237
+ let hrefBase = resolvedUrl;
238
+ if (metadataSource.id !== void 0) {
239
+ const normalizedId = normalizeMetadataImageId(route, metadataSource.id);
240
+ if (!normalizedId) continue;
241
+ hrefBase = `${resolvedUrl}/${normalizedId}`;
242
+ }
243
+ const href = withContentHash(hrefBase, route.contentHash);
244
+ const contentType = metadataSource.contentType ?? route.contentType;
245
+ const size = metadataSource.size;
246
+ if (routeKind === "icon" || routeKind === "apple") {
247
+ let sizes;
248
+ if (size?.width !== void 0 && size.height !== void 0) sizes = `${size.width}x${size.height}`;
249
+ resolvedHeadData.push({
250
+ kind: routeKind,
251
+ href,
252
+ sizes,
253
+ type: contentType
254
+ });
255
+ continue;
256
+ }
257
+ resolvedHeadData.push({
258
+ kind: routeKind,
259
+ href,
260
+ alt: metadataSource.alt,
261
+ height: size?.height,
262
+ type: contentType,
263
+ width: size?.width
264
+ });
265
+ }
266
+ return resolvedHeadData;
267
+ }
268
+ async function resolveHeadDataList(routes, params) {
269
+ return (await Promise.all(routes.map((route) => resolveRouteHeadData(route, params)))).flat();
270
+ }
271
+ async function applyFileBasedMetadata(metadata, routePath, params, metadataRoutes, options) {
272
+ if (!metadataRoutes || metadataRoutes.length === 0) return metadata;
273
+ const routeSegments = options?.routeSegments ?? null;
274
+ const faviconRoutes = selectDeepestRoutes(metadataRoutes, "favicon", routePath, params, routeSegments);
275
+ const iconRoutes = selectDeepestRoutes(metadataRoutes, "icon", routePath, params, routeSegments).filter((route) => !iconRouteHasExplicitIconsAtSource(route, options, metadata));
276
+ const appleRoutes = selectDeepestRoutes(metadataRoutes, "apple", routePath, params, routeSegments).filter((route) => !iconRouteHasExplicitIconsAtSource(route, options, metadata));
277
+ const openGraphRoutes = selectDeepestRoutes(metadataRoutes, "openGraph", routePath, params, routeSegments).filter((route) => !socialRouteHasExplicitImagesAtSource(route, "openGraph", options, metadata));
278
+ const twitterRoutes = selectDeepestRoutes(metadataRoutes, "twitter", routePath, params, routeSegments).filter((route) => !socialRouteHasExplicitImagesAtSource(route, "twitter", options, metadata));
279
+ const manifestRoutes = selectDeepestRoutes(metadataRoutes, "manifest", routePath, params, routeSegments);
280
+ const [faviconHeadData, iconHeadData, appleHeadData, openGraphHeadData, twitterHeadData, manifestHeadData] = await Promise.all([
281
+ resolveHeadDataList(faviconRoutes, params),
282
+ resolveHeadDataList(iconRoutes, params),
283
+ resolveHeadDataList(appleRoutes, params),
284
+ resolveHeadDataList(openGraphRoutes, params),
285
+ resolveHeadDataList(twitterRoutes, params),
286
+ resolveHeadDataList(manifestRoutes, params)
287
+ ]);
288
+ if (!metadata && faviconHeadData.length === 0 && iconHeadData.length === 0 && appleHeadData.length === 0 && openGraphHeadData.length === 0 && twitterHeadData.length === 0 && manifestHeadData.length === 0) return null;
289
+ const nextMetadata = metadata ? { ...metadata } : {};
290
+ const faviconEntries = [];
291
+ for (const headData of faviconHeadData) {
292
+ const iconEntry = buildIconEntry(headData);
293
+ if (iconEntry) faviconEntries.push(iconEntry);
294
+ }
295
+ if (faviconEntries.length > 0) {
296
+ const nextIcons = cloneIconMap(nextMetadata.icons);
297
+ const normalizedIcons = normalizeIconEntries(nextIcons);
298
+ nextIcons.icon = [...faviconEntries, ...normalizedIcons];
299
+ nextMetadata.icons = nextIcons;
300
+ }
301
+ {
302
+ const nextIcons = cloneIconMap(nextMetadata.icons);
303
+ const iconEntries = [];
304
+ for (const headData of iconHeadData) {
305
+ const iconEntry = buildIconEntry(headData);
306
+ if (iconEntry) iconEntries.push(iconEntry);
307
+ }
308
+ if (iconEntries.length > 0) {
309
+ const normalizedIcons = normalizeIconEntries(nextIcons);
310
+ nextIcons.icon = [...iconEntries, ...normalizedIcons];
311
+ }
312
+ const appleEntries = [];
313
+ for (const headData of appleHeadData) {
314
+ const appleEntry = buildAppleEntry(headData);
315
+ if (appleEntry) appleEntries.push(appleEntry);
316
+ }
317
+ if (appleEntries.length > 0) {
318
+ const existingApple = nextIcons.apple;
319
+ const normalizedAppleEntries = [];
320
+ if (Array.isArray(existingApple)) for (const entry of existingApple) normalizedAppleEntries.push(normalizeAppleEntry(entry));
321
+ else if (existingApple) normalizedAppleEntries.push(normalizeAppleEntry(existingApple));
322
+ nextIcons.apple = [...appleEntries, ...normalizedAppleEntries];
323
+ }
324
+ if (iconEntries.length > 0 || appleEntries.length > 0) nextMetadata.icons = nextIcons;
325
+ }
326
+ if (openGraphHeadData.length > 0) {
327
+ const socialEntries = [];
328
+ for (const headData of openGraphHeadData) {
329
+ const socialEntry = buildSocialEntry(headData);
330
+ if (socialEntry) socialEntries.push(socialEntry);
331
+ }
332
+ if (socialEntries.length > 0) {
333
+ const nextOpenGraph = nextMetadata.openGraph ? { ...nextMetadata.openGraph } : {};
334
+ nextOpenGraph.images = socialEntries;
335
+ nextMetadata.openGraph = nextOpenGraph;
336
+ }
337
+ }
338
+ if (twitterHeadData.length > 0) {
339
+ const socialEntries = [];
340
+ for (const headData of twitterHeadData) {
341
+ const socialEntry = buildSocialEntry(headData);
342
+ if (socialEntry) socialEntries.push(socialEntry);
343
+ }
344
+ if (socialEntries.length > 0) {
345
+ const nextTwitter = nextMetadata.twitter ? { ...nextMetadata.twitter } : {};
346
+ nextTwitter.images = socialEntries;
347
+ nextMetadata.twitter = nextTwitter;
348
+ }
349
+ }
350
+ if (manifestHeadData.length > 0 && manifestHeadData[0].kind === "manifest") nextMetadata.manifest = manifestHeadData[0].href;
351
+ return nextMetadata;
352
+ }
353
+ //#endregion
354
+ export { applyFileBasedMetadata };
355
+
356
+ //# sourceMappingURL=file-based-metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-based-metadata.js","names":[],"sources":["../../src/server/file-based-metadata.ts"],"sourcesContent":["import type { Metadata } from \"vinext/shims/metadata\";\nimport { makeThenableParams } from \"vinext/shims/thenable-params\";\nimport { fillRoutePatternSegments, routePattern } from \"../routing/route-pattern.js\";\nimport {\n getMetadataImageRouteKind,\n getMetadataRouteKind,\n isValidMetadataImageId,\n type MetadataFileRoute,\n type MetadataRouteHeadData,\n} from \"./metadata-routes.js\";\n\ntype AppPageParams = Record<string, string | string[]>;\n\ntype IconEntry = {\n url: string | URL;\n sizes?: string;\n type?: string;\n media?: string;\n};\n\ntype AppleIconEntry = {\n url: string | URL;\n sizes?: string;\n type?: string;\n};\n\ntype SocialImageEntry = {\n url: string | URL;\n width?: number;\n height?: number;\n alt?: string;\n type?: string;\n};\n\ntype DynamicImageSize = {\n width?: number;\n height?: number;\n};\n\ntype DynamicImageMetadataSource = {\n id?: string | number;\n alt?: string;\n contentType?: string;\n size?: DynamicImageSize;\n};\n\ntype FileBasedMetadataSource = {\n routeSegments: readonly string[];\n metadata: Metadata | null;\n};\n\ntype FileBasedMetadataOptions = {\n routeSegments?: readonly string[] | null;\n metadataSources?: readonly FileBasedMetadataSource[] | null;\n};\n\ntype IconMap = {\n icon?: string | URL | IconEntry | IconEntry[];\n shortcut?: string | URL | Array<string | URL>;\n apple?: string | URL | AppleIconEntry | AppleIconEntry[];\n other?: Array<{ rel: string; url: string | URL; sizes?: string; type?: string }>;\n};\n\nfunction routeApplies(routePath: string, routePrefix: string): boolean {\n if (!routePrefix) {\n return true;\n }\n return routePath === routePrefix || routePath.startsWith(`${routePrefix}/`);\n}\n\nfunction routeScore(routePrefix: string): number {\n return routePrefix.split(\"/\").filter(Boolean).length;\n}\n\nfunction routeSegmentsApply(\n routeSegments: readonly string[],\n routePrefixSegments: readonly string[],\n): boolean {\n if (routePrefixSegments.length > routeSegments.length) {\n return false;\n }\n\n for (let index = 0; index < routePrefixSegments.length; index++) {\n if (routeSegments[index] !== routePrefixSegments[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction removeParallelRouteSegments(routeSegments: readonly string[]): string[] {\n return routeSegments.filter((segment) => !segment.startsWith(\"@\"));\n}\n\nfunction routeSegmentsApplyWithParallelSlots(\n routeSegments: readonly string[],\n routePrefixSegments: readonly string[],\n): boolean {\n if (routeSegmentsApply(routeSegments, routePrefixSegments)) {\n return true;\n }\n\n const visiblePrefixSegments = removeParallelRouteSegments(routePrefixSegments);\n return (\n visiblePrefixSegments.length !== routePrefixSegments.length &&\n routeSegmentsApply(routeSegments, visiblePrefixSegments)\n );\n}\n\nfunction routeSpecificity(route: MetadataFileRoute): number {\n return route.routeSegments?.length ?? routeScore(route.routePrefix);\n}\n\nfunction selectDeepestRoutes(\n metadataRoutes: readonly MetadataFileRoute[] | null | undefined,\n kind: MetadataRouteHeadData[\"kind\"],\n routePath: string,\n params: AppPageParams,\n routeSegments: readonly string[] | null | undefined,\n): MetadataFileRoute[] {\n if (!metadataRoutes || metadataRoutes.length === 0) {\n return [];\n }\n\n let selectedScore = -1;\n const selectedRoutes: MetadataFileRoute[] = [];\n\n for (const route of metadataRoutes) {\n const routeKind = route.headData?.kind ?? getMetadataRouteKind(route);\n\n if (routeKind !== kind) {\n continue;\n }\n\n if (routeSegments && route.routeSegments) {\n // Raw app-tree segments are authoritative when present. Falling back to\n // visible URL prefixes here would reintroduce route-group collisions.\n if (!routeSegmentsApplyWithParallelSlots(routeSegments, route.routeSegments)) {\n continue;\n }\n const currentScore = routeSpecificity(route);\n if (currentScore > selectedScore) {\n selectedScore = currentScore;\n selectedRoutes.length = 0;\n selectedRoutes.push(route);\n continue;\n }\n\n if (currentScore === selectedScore) {\n selectedRoutes.push(route);\n }\n continue;\n }\n\n const routePrefix = route.routePrefix;\n const resolvedRoutePrefix = fillRoutePatternSegments(routePrefix, params);\n const normalizedRoutePrefix = routePattern(routePrefix);\n if (\n !routeApplies(routePath, routePrefix) &&\n !routeApplies(routePath, normalizedRoutePrefix) &&\n (!resolvedRoutePrefix || !routeApplies(routePath, resolvedRoutePrefix))\n ) {\n continue;\n }\n\n const currentScore = routeSpecificity(route);\n if (currentScore > selectedScore) {\n selectedScore = currentScore;\n selectedRoutes.length = 0;\n selectedRoutes.push(route);\n continue;\n }\n\n if (currentScore === selectedScore) {\n selectedRoutes.push(route);\n }\n }\n\n return selectedRoutes;\n}\n\nfunction isStringOrUrl(value: unknown): value is string | URL {\n return typeof value === \"string\" || (typeof value === \"object\" && value instanceof URL);\n}\n\nfunction normalizeIconDescriptor(value: unknown): IconEntry | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return null;\n }\n\n const urlValue = Reflect.get(value, \"url\");\n if (!isStringOrUrl(urlValue)) {\n return null;\n }\n\n const entry: IconEntry = { url: urlValue };\n\n const sizesValue = Reflect.get(value, \"sizes\");\n if (typeof sizesValue === \"string\") {\n entry.sizes = sizesValue;\n }\n\n const typeValue = Reflect.get(value, \"type\");\n if (typeof typeValue === \"string\") {\n entry.type = typeValue;\n }\n\n const mediaValue = Reflect.get(value, \"media\");\n if (typeof mediaValue === \"string\") {\n entry.media = mediaValue;\n }\n\n return entry;\n}\n\nfunction normalizeIconValue(value: unknown): IconEntry | null {\n if (isStringOrUrl(value)) {\n return { url: value };\n }\n\n return normalizeIconDescriptor(value);\n}\n\nfunction normalizeIconValueList(values: readonly unknown[]): IconEntry[] {\n const normalizedEntries: IconEntry[] = [];\n for (const value of values) {\n const normalizedValue = normalizeIconValue(value);\n if (normalizedValue) {\n normalizedEntries.push(normalizedValue);\n }\n }\n return normalizedEntries;\n}\n\nfunction normalizeIconEntries(icon: NonNullable<Metadata[\"icons\"]>): IconEntry[] {\n const normalizedTopLevelValue = normalizeIconValue(icon);\n if (normalizedTopLevelValue) {\n return [normalizedTopLevelValue];\n }\n\n if (Array.isArray(icon)) {\n return normalizeIconValueList(icon);\n }\n\n if (!isIconMap(icon)) {\n return [];\n }\n\n const iconValue = icon.icon;\n if (!iconValue) {\n return [];\n }\n\n if (Array.isArray(iconValue)) {\n return normalizeIconValueList(iconValue);\n }\n\n const normalizedValue = normalizeIconValue(iconValue);\n return normalizedValue ? [normalizedValue] : [];\n}\n\nfunction isIconMap(value: Metadata[\"icons\"]): value is IconMap {\n if (!value || typeof value !== \"object\" || value instanceof URL || Array.isArray(value)) {\n return false;\n }\n return normalizeIconValue(value) === null;\n}\n\nfunction cloneIconMap(value: Metadata[\"icons\"]): IconMap {\n if (!value) {\n return {};\n }\n\n if (isIconMap(value)) {\n return { ...value };\n }\n\n const iconEntries = normalizeIconEntries(value);\n return iconEntries.length > 0 ? { icon: iconEntries } : {};\n}\n\nfunction buildIconEntry(headData: MetadataRouteHeadData): IconEntry | null {\n if (headData.kind !== \"favicon\" && headData.kind !== \"icon\") {\n return null;\n }\n\n const iconEntry: IconEntry = {\n url: headData.href,\n };\n if (headData.sizes) {\n iconEntry.sizes = headData.sizes;\n }\n if (headData.type) {\n iconEntry.type = headData.type;\n }\n return iconEntry;\n}\n\nfunction buildAppleEntry(headData: MetadataRouteHeadData): AppleIconEntry | null {\n if (headData.kind !== \"apple\") {\n return null;\n }\n\n const appleEntry: AppleIconEntry = {\n url: headData.href,\n };\n if (headData.sizes) {\n appleEntry.sizes = headData.sizes;\n }\n if (headData.type) {\n appleEntry.type = headData.type;\n }\n return appleEntry;\n}\n\nfunction normalizeAppleEntry(value: string | URL | AppleIconEntry): AppleIconEntry {\n if (isStringOrUrl(value)) {\n return { url: value };\n }\n return { ...value };\n}\n\nfunction buildSocialEntry(headData: MetadataRouteHeadData): SocialImageEntry | null {\n if (headData.kind !== \"openGraph\" && headData.kind !== \"twitter\") {\n return null;\n }\n\n const socialEntry: SocialImageEntry = {\n url: headData.href,\n };\n if (headData.width !== undefined) {\n socialEntry.width = headData.width;\n }\n if (headData.height !== undefined) {\n socialEntry.height = headData.height;\n }\n if (headData.alt) {\n socialEntry.alt = headData.alt;\n }\n if (headData.type) {\n socialEntry.type = headData.type;\n }\n return socialEntry;\n}\n\nfunction normalizeMetadataImageId(route: MetadataFileRoute, id: string | number): string | null {\n const normalizedId = String(id);\n if (!isValidMetadataImageId(normalizedId)) {\n console.warn(\n `[vinext] Skipping metadata route ${route.servedUrl} image id \"${normalizedId}\" because metadata image ids must match /^[a-zA-Z0-9-_.]+$/.`,\n );\n return null;\n }\n return normalizedId;\n}\n\nfunction withContentHash(href: string, contentHash?: string): string {\n if (!contentHash) {\n return href;\n }\n return `${href}?${contentHash}`;\n}\n\nfunction hasOwnProperty(source: object | null | undefined, key: string): boolean {\n return Boolean(source && Object.prototype.hasOwnProperty.call(source, key));\n}\n\nfunction hasOpenGraphImages(metadata: Metadata | null | undefined): boolean {\n return hasOwnProperty(metadata?.openGraph, \"images\");\n}\n\nfunction hasTwitterImages(metadata: Metadata | null | undefined): boolean {\n return hasOwnProperty(metadata?.twitter, \"images\");\n}\n\nfunction hasIcons(metadata: Metadata | null | undefined): boolean {\n return Boolean(metadata?.icons);\n}\n\nfunction getMetadataSourceForRoute(\n route: MetadataFileRoute,\n options: FileBasedMetadataOptions | undefined,\n fallbackMetadata: Metadata | null,\n): Metadata | null {\n if (!options?.metadataSources) {\n return fallbackMetadata;\n }\n\n if (!route.routeSegments) {\n return null;\n }\n\n for (let index = options.metadataSources.length - 1; index >= 0; index--) {\n const source = options.metadataSources[index];\n if (routeSegmentsApplyWithParallelSlots(source.routeSegments, route.routeSegments)) {\n return source.metadata;\n }\n }\n\n return null;\n}\n\nfunction socialRouteHasExplicitImagesAtSource(\n route: MetadataFileRoute,\n kind: \"openGraph\" | \"twitter\",\n options: FileBasedMetadataOptions | undefined,\n fallbackMetadata: Metadata | null,\n): boolean {\n const sourceMetadata = getMetadataSourceForRoute(route, options, fallbackMetadata);\n return kind === \"openGraph\"\n ? hasOpenGraphImages(sourceMetadata)\n : hasTwitterImages(sourceMetadata);\n}\n\nfunction iconRouteHasExplicitIconsAtSource(\n route: MetadataFileRoute,\n options: FileBasedMetadataOptions | undefined,\n fallbackMetadata: Metadata | null,\n): boolean {\n // Next suppresses file icon routes when any resolved icons metadata exists.\n // Social image routes stay segment-scoped instead of using this merged fallback.\n return hasIcons(fallbackMetadata) || hasIcons(getMetadataSourceForRoute(route, options, null));\n}\n\nfunction readStringProperty(source: object, key: string): string | undefined {\n const value = Reflect.get(source, key);\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction readNumberProperty(source: object, key: string): number | undefined {\n const value = Reflect.get(source, key);\n return typeof value === \"number\" ? value : undefined;\n}\n\nfunction readStringOrNumberProperty(source: object, key: string): string | number | undefined {\n const value = Reflect.get(source, key);\n if (typeof value === \"string\" || typeof value === \"number\") {\n return value;\n }\n return undefined;\n}\n\nfunction readSizeProperty(source: object): DynamicImageSize | undefined {\n const sizeValue = Reflect.get(source, \"size\");\n if (typeof sizeValue !== \"object\" || sizeValue === null) {\n return undefined;\n }\n\n const width = readNumberProperty(sizeValue, \"width\");\n const height = readNumberProperty(sizeValue, \"height\");\n if (width === undefined && height === undefined) {\n return undefined;\n }\n\n return { width, height };\n}\n\nfunction readDynamicImageMetadataSource(source: object): DynamicImageMetadataSource {\n return {\n id: readStringOrNumberProperty(source, \"id\"),\n alt: readStringProperty(source, \"alt\"),\n contentType: readStringProperty(source, \"contentType\"),\n size: readSizeProperty(source),\n };\n}\n\nasync function resolveDynamicImageMetadataSources(\n route: MetadataFileRoute,\n params: AppPageParams,\n): Promise<DynamicImageMetadataSource[]> {\n if (!route.module || typeof route.module !== \"object\") {\n return [];\n }\n\n const generateImageMetadata = Reflect.get(route.module, \"generateImageMetadata\");\n if (typeof generateImageMetadata !== \"function\") {\n return [readDynamicImageMetadataSource(route.module)];\n }\n\n const result = await generateImageMetadata({ params: makeThenableParams(params) });\n if (!Array.isArray(result)) {\n return [];\n }\n\n const sources: DynamicImageMetadataSource[] = [];\n for (const entry of result) {\n if (typeof entry === \"object\" && entry !== null) {\n const source = readDynamicImageMetadataSource(entry);\n if (source.id === undefined) {\n console.warn(\n `[vinext] Skipping metadata route ${route.servedUrl} image metadata entry because generateImageMetadata entries must include an id.`,\n );\n continue;\n }\n sources.push(source);\n }\n }\n return sources;\n}\n\nasync function resolveRouteHeadData(\n route: MetadataFileRoute,\n params: AppPageParams,\n): Promise<MetadataRouteHeadData[]> {\n if (!route.isDynamic || !route.module || typeof route.module !== \"object\") {\n return route.headData ? [route.headData] : [];\n }\n\n const routeKind = getMetadataImageRouteKind(route);\n if (!routeKind) {\n return route.headData ? [route.headData] : [];\n }\n\n // servedUrl must stay query-free here; content hashes are appended after dynamic segment filling.\n const resolvedUrl = fillRoutePatternSegments(route.servedUrl, params);\n if (!resolvedUrl) {\n console.warn(\n `[vinext] Skipping metadata route ${route.servedUrl} because params did not fill all dynamic segments.`,\n );\n return [];\n }\n const metadataSources = await resolveDynamicImageMetadataSources(route, params);\n const resolvedHeadData: MetadataRouteHeadData[] = [];\n\n for (const metadataSource of metadataSources) {\n let hrefBase = resolvedUrl;\n if (metadataSource.id !== undefined) {\n const normalizedId = normalizeMetadataImageId(route, metadataSource.id);\n if (!normalizedId) {\n continue;\n }\n hrefBase = `${resolvedUrl}/${normalizedId}`;\n }\n const href = withContentHash(hrefBase, route.contentHash);\n const contentType = metadataSource.contentType ?? route.contentType;\n const size = metadataSource.size;\n\n if (routeKind === \"icon\" || routeKind === \"apple\") {\n let sizes: string | undefined;\n if (size?.width !== undefined && size.height !== undefined) {\n sizes = `${size.width}x${size.height}`;\n }\n\n resolvedHeadData.push({\n kind: routeKind,\n href,\n sizes,\n type: contentType,\n });\n continue;\n }\n\n resolvedHeadData.push({\n kind: routeKind,\n href,\n alt: metadataSource.alt,\n height: size?.height,\n type: contentType,\n width: size?.width,\n });\n }\n\n return resolvedHeadData;\n}\n\nasync function resolveHeadDataList(\n routes: MetadataFileRoute[],\n params: AppPageParams,\n): Promise<MetadataRouteHeadData[]> {\n const headDataList = await Promise.all(\n routes.map((route) => resolveRouteHeadData(route, params)),\n );\n return headDataList.flat();\n}\n\nexport async function applyFileBasedMetadata(\n metadata: Metadata | null,\n routePath: string,\n params: AppPageParams,\n metadataRoutes: readonly MetadataFileRoute[] | null | undefined,\n options?: FileBasedMetadataOptions,\n): Promise<Metadata | null> {\n if (!metadataRoutes || metadataRoutes.length === 0) {\n return metadata;\n }\n\n const routeSegments = options?.routeSegments ?? null;\n const faviconRoutes = selectDeepestRoutes(\n metadataRoutes,\n \"favicon\",\n routePath,\n params,\n routeSegments,\n );\n const iconRoutes = selectDeepestRoutes(\n metadataRoutes,\n \"icon\",\n routePath,\n params,\n routeSegments,\n ).filter((route) => !iconRouteHasExplicitIconsAtSource(route, options, metadata));\n const appleRoutes = selectDeepestRoutes(\n metadataRoutes,\n \"apple\",\n routePath,\n params,\n routeSegments,\n ).filter((route) => !iconRouteHasExplicitIconsAtSource(route, options, metadata));\n const openGraphRoutes = selectDeepestRoutes(\n metadataRoutes,\n \"openGraph\",\n routePath,\n params,\n routeSegments,\n ).filter((route) => !socialRouteHasExplicitImagesAtSource(route, \"openGraph\", options, metadata));\n const twitterRoutes = selectDeepestRoutes(\n metadataRoutes,\n \"twitter\",\n routePath,\n params,\n routeSegments,\n ).filter((route) => !socialRouteHasExplicitImagesAtSource(route, \"twitter\", options, metadata));\n const manifestRoutes = selectDeepestRoutes(\n metadataRoutes,\n \"manifest\",\n routePath,\n params,\n routeSegments,\n );\n\n const [\n faviconHeadData,\n iconHeadData,\n appleHeadData,\n openGraphHeadData,\n twitterHeadData,\n manifestHeadData,\n ] = await Promise.all([\n resolveHeadDataList(faviconRoutes, params),\n resolveHeadDataList(iconRoutes, params),\n resolveHeadDataList(appleRoutes, params),\n resolveHeadDataList(openGraphRoutes, params),\n resolveHeadDataList(twitterRoutes, params),\n resolveHeadDataList(manifestRoutes, params),\n ]);\n\n if (\n !metadata &&\n faviconHeadData.length === 0 &&\n iconHeadData.length === 0 &&\n appleHeadData.length === 0 &&\n openGraphHeadData.length === 0 &&\n twitterHeadData.length === 0 &&\n manifestHeadData.length === 0\n ) {\n return null;\n }\n\n const nextMetadata: Metadata = metadata ? { ...metadata } : {};\n\n const faviconEntries: IconEntry[] = [];\n for (const headData of faviconHeadData) {\n const iconEntry = buildIconEntry(headData);\n if (iconEntry) {\n faviconEntries.push(iconEntry);\n }\n }\n if (faviconEntries.length > 0) {\n const nextIcons = cloneIconMap(nextMetadata.icons);\n const normalizedIcons = normalizeIconEntries(nextIcons);\n nextIcons.icon = [...faviconEntries, ...normalizedIcons];\n nextMetadata.icons = nextIcons;\n }\n\n {\n const nextIcons = cloneIconMap(nextMetadata.icons);\n\n const iconEntries: IconEntry[] = [];\n for (const headData of iconHeadData) {\n const iconEntry = buildIconEntry(headData);\n if (iconEntry) {\n iconEntries.push(iconEntry);\n }\n }\n if (iconEntries.length > 0) {\n const normalizedIcons = normalizeIconEntries(nextIcons);\n nextIcons.icon = [...iconEntries, ...normalizedIcons];\n }\n\n const appleEntries: AppleIconEntry[] = [];\n for (const headData of appleHeadData) {\n const appleEntry = buildAppleEntry(headData);\n if (appleEntry) {\n appleEntries.push(appleEntry);\n }\n }\n if (appleEntries.length > 0) {\n const existingApple = nextIcons.apple;\n const normalizedAppleEntries: AppleIconEntry[] = [];\n if (Array.isArray(existingApple)) {\n for (const entry of existingApple) {\n normalizedAppleEntries.push(normalizeAppleEntry(entry));\n }\n } else if (existingApple) {\n normalizedAppleEntries.push(normalizeAppleEntry(existingApple));\n }\n nextIcons.apple = [...appleEntries, ...normalizedAppleEntries];\n }\n\n if (iconEntries.length > 0 || appleEntries.length > 0) {\n nextMetadata.icons = nextIcons;\n }\n }\n\n if (openGraphHeadData.length > 0) {\n const socialEntries: SocialImageEntry[] = [];\n for (const headData of openGraphHeadData) {\n const socialEntry = buildSocialEntry(headData);\n if (socialEntry) {\n socialEntries.push(socialEntry);\n }\n }\n if (socialEntries.length > 0) {\n const nextOpenGraph: NonNullable<Metadata[\"openGraph\"]> = nextMetadata.openGraph\n ? { ...nextMetadata.openGraph }\n : {};\n nextOpenGraph.images = socialEntries;\n nextMetadata.openGraph = nextOpenGraph;\n }\n }\n\n if (twitterHeadData.length > 0) {\n const socialEntries: SocialImageEntry[] = [];\n for (const headData of twitterHeadData) {\n const socialEntry = buildSocialEntry(headData);\n if (socialEntry) {\n socialEntries.push(socialEntry);\n }\n }\n if (socialEntries.length > 0) {\n const nextTwitter: NonNullable<Metadata[\"twitter\"]> = nextMetadata.twitter\n ? { ...nextMetadata.twitter }\n : {};\n nextTwitter.images = socialEntries;\n nextMetadata.twitter = nextTwitter;\n }\n }\n\n if (manifestHeadData.length > 0 && manifestHeadData[0].kind === \"manifest\") {\n nextMetadata.manifest = manifestHeadData[0].href;\n }\n\n return nextMetadata;\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region src/server/implicit-tags.d.ts
2
+ type AppCacheLeafKind = "page" | "route";
3
+ declare function buildPageCacheTags(pathname: string, extraTags: string[], routeSegments: string[], leafKind: AppCacheLeafKind): string[];
4
+ //#endregion
5
+ export { buildPageCacheTags };
6
+ //# sourceMappingURL=implicit-tags.d.ts.map
@@ -0,0 +1,42 @@
1
+ //#region src/server/implicit-tags.ts
2
+ const NEXT_CACHE_IMPLICIT_TAG_ID = "_N_T_";
3
+ function appendUnique(tags, tag) {
4
+ if (!tags.includes(tag)) tags.push(tag);
5
+ }
6
+ function normalizeRouteSegment(segment) {
7
+ if (!segment || segment === "." || segment.startsWith("@")) return null;
8
+ return segment;
9
+ }
10
+ function buildRouteCachePath(routeSegments, leafKind) {
11
+ const parts = [];
12
+ for (const segment of routeSegments) {
13
+ const normalized = normalizeRouteSegment(segment);
14
+ if (normalized) parts.push(normalized);
15
+ }
16
+ parts.push(leafKind);
17
+ return `/${parts.join("/")}`;
18
+ }
19
+ function appendDerivedTags(tags, routePath) {
20
+ appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/layout`);
21
+ if (!routePath.startsWith("/")) return;
22
+ const routeParts = routePath.split("/");
23
+ const leafIndex = routeParts.length - 1;
24
+ for (let i = 1; i <= routeParts.length; i++) {
25
+ let currentPathname = routeParts.slice(0, i).join("/");
26
+ if (!currentPathname) continue;
27
+ if (!(i - 1 === leafIndex)) currentPathname = `${currentPathname}/layout`;
28
+ appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}${currentPathname}`);
29
+ }
30
+ }
31
+ function buildPageCacheTags(pathname, extraTags, routeSegments, leafKind) {
32
+ const tags = [pathname, `${NEXT_CACHE_IMPLICIT_TAG_ID}${pathname}`];
33
+ if (pathname === "/") appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/index`);
34
+ if (pathname === "/index") appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/`);
35
+ appendDerivedTags(tags, buildRouteCachePath(routeSegments, leafKind));
36
+ for (const tag of extraTags) appendUnique(tags, tag);
37
+ return tags;
38
+ }
39
+ //#endregion
40
+ export { buildPageCacheTags };
41
+
42
+ //# sourceMappingURL=implicit-tags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implicit-tags.js","names":[],"sources":["../../src/server/implicit-tags.ts"],"sourcesContent":["const NEXT_CACHE_IMPLICIT_TAG_ID = \"_N_T_\";\n\ntype AppCacheLeafKind = \"page\" | \"route\";\n\nfunction appendUnique(tags: string[], tag: string): void {\n if (!tags.includes(tag)) tags.push(tag);\n}\n\n// App route segments come from raw filesystem directories, so dynamic\n// segments are already in bracket notation such as [slug] or [...all].\nfunction normalizeRouteSegment(segment: string): string | null {\n if (!segment || segment === \".\" || segment.startsWith(\"@\")) return null;\n return segment;\n}\n\nfunction buildRouteCachePath(routeSegments: string[], leafKind: AppCacheLeafKind): string {\n const parts: string[] = [];\n for (const segment of routeSegments) {\n const normalized = normalizeRouteSegment(segment);\n if (normalized) parts.push(normalized);\n }\n parts.push(leafKind);\n return `/${parts.join(\"/\")}`;\n}\n\nfunction appendDerivedTags(tags: string[], routePath: string): void {\n appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/layout`);\n\n if (!routePath.startsWith(\"/\")) return;\n\n const routeParts = routePath.split(\"/\");\n const leafIndex = routeParts.length - 1;\n for (let i = 1; i <= routeParts.length; i++) {\n let currentPathname = routeParts.slice(0, i).join(\"/\");\n if (!currentPathname) continue;\n\n const isLeaf = i - 1 === leafIndex;\n if (!isLeaf) {\n currentPathname = `${currentPathname}/layout`;\n }\n\n appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}${currentPathname}`);\n }\n}\n\nexport function buildPageCacheTags(\n pathname: string,\n extraTags: string[],\n routeSegments: string[],\n leafKind: AppCacheLeafKind,\n): string[] {\n const tags = [pathname, `${NEXT_CACHE_IMPLICIT_TAG_ID}${pathname}`];\n if (pathname === \"/\") appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/index`);\n if (pathname === \"/index\") appendUnique(tags, `${NEXT_CACHE_IMPLICIT_TAG_ID}/`);\n appendDerivedTags(tags, buildRouteCachePath(routeSegments, leafKind));\n\n for (const tag of extraTags) {\n appendUnique(tags, tag);\n }\n\n return tags;\n}\n"],"mappings":";AAAA,MAAM,6BAA6B;AAInC,SAAS,aAAa,MAAgB,KAAmB;AACvD,KAAI,CAAC,KAAK,SAAS,IAAI,CAAE,MAAK,KAAK,IAAI;;AAKzC,SAAS,sBAAsB,SAAgC;AAC7D,KAAI,CAAC,WAAW,YAAY,OAAO,QAAQ,WAAW,IAAI,CAAE,QAAO;AACnE,QAAO;;AAGT,SAAS,oBAAoB,eAAyB,UAAoC;CACxF,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,WAAW,eAAe;EACnC,MAAM,aAAa,sBAAsB,QAAQ;AACjD,MAAI,WAAY,OAAM,KAAK,WAAW;;AAExC,OAAM,KAAK,SAAS;AACpB,QAAO,IAAI,MAAM,KAAK,IAAI;;AAG5B,SAAS,kBAAkB,MAAgB,WAAyB;AAClE,cAAa,MAAM,GAAG,2BAA2B,SAAS;AAE1D,KAAI,CAAC,UAAU,WAAW,IAAI,CAAE;CAEhC,MAAM,aAAa,UAAU,MAAM,IAAI;CACvC,MAAM,YAAY,WAAW,SAAS;AACtC,MAAK,IAAI,IAAI,GAAG,KAAK,WAAW,QAAQ,KAAK;EAC3C,IAAI,kBAAkB,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;AACtD,MAAI,CAAC,gBAAiB;AAGtB,MAAI,EADW,IAAI,MAAM,WAEvB,mBAAkB,GAAG,gBAAgB;AAGvC,eAAa,MAAM,GAAG,6BAA6B,kBAAkB;;;AAIzE,SAAgB,mBACd,UACA,WACA,eACA,UACU;CACV,MAAM,OAAO,CAAC,UAAU,GAAG,6BAA6B,WAAW;AACnE,KAAI,aAAa,IAAK,cAAa,MAAM,GAAG,2BAA2B,QAAQ;AAC/E,KAAI,aAAa,SAAU,cAAa,MAAM,GAAG,2BAA2B,GAAG;AAC/E,mBAAkB,MAAM,oBAAoB,eAAe,SAAS,CAAC;AAErE,MAAK,MAAM,OAAO,UAChB,cAAa,MAAM,IAAI;AAGzB,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/server/instrumentation.ts"],"sourcesContent":["/**\n * instrumentation.ts support\n *\n * Next.js supports an `instrumentation.ts` file at the project root that\n * exports a `register()` function. This function is called once when the\n * server starts, before any request handling. It's the recommended way to\n * set up observability tools (Sentry, Datadog, OpenTelemetry, etc.).\n *\n * Optionally, it can also export `onRequestError()` which is called when\n * an unhandled error occurs during request handling.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation\n *\n * ## App Router\n *\n * For App Router, `register()` is baked directly into the generated RSC entry\n * as a top-level `await` at module evaluation time (see `entries/app-rsc-entry.ts`\n * `generateRscEntry`). This means it runs inside the Worker process (or RSC\n * Vite environment) — the same process that handles requests — before any\n * request is served. `runInstrumentation()` is NOT called from `configureServer`\n * for App Router.\n *\n * The `onRequestError` handler is stored on `globalThis` so it is visible across\n * the RSC and SSR Vite environments (separate module graphs, same Node.js process).\n * With `@cloudflare/vite-plugin` it runs entirely inside the Worker, so\n * `globalThis` is the Worker's global — also correct.\n *\n * ## Pages Router\n *\n * Pages Router has no RSC entry, so `configureServer()` is the right place to\n * call `register()`. `runInstrumentation()` accepts a `ModuleRunner` (created\n * via `createDirectRunner()`) rather than `server.ssrLoadModule()` so it is\n * safe when `@cloudflare/vite-plugin` is present — that plugin replaces the\n * SSR environment's hot channel, causing `ssrLoadModule()` to crash with\n * `TypeError: Cannot read properties of undefined (reading 'outsideEmitter')`.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getRequestExecutionContext } from \"../shims/request-context.js\";\nimport { ValidFileMatcher } from \"../routing/file-matcher.js\";\n/**\n * Minimal duck-typed interface for the module runner passed to\n * `runInstrumentation`. Only `.import()` is used — this avoids requiring\n * callers (including tests) to provide a full `ModuleRunner` instance.\n */\nexport type ModuleImporter = {\n import(id: string): Promise<unknown>;\n};\n\n/**\n * Import a module via the runner and cast the result to `Record<string, any>`.\n *\n * Centralises the `as Record<string, any>` cast so callers don't need\n * per-call oxlint-disable comments.\n */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function importModule(\n runner: ModuleImporter,\n id: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Promise<Record<string, any>> {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return (await runner.import(id)) as Record<string, any>;\n}\n\nconst INSTRUMENTATION_LOCATIONS = [\"\", \"src/\"];\n\nfunction findInstrumentationHookFile(\n root: string,\n basename: string,\n fileMatcher: ValidFileMatcher,\n): string | null {\n for (const dir of INSTRUMENTATION_LOCATIONS) {\n for (const ext of fileMatcher.dottedExtensions) {\n const fullPath = path.join(root, dir, `${basename}${ext}`);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n }\n return null;\n}\n\n/**\n * Find the instrumentation file in the project root.\n */\nexport function findInstrumentationFile(\n root: string,\n fileMatcher: ValidFileMatcher,\n): string | null {\n return findInstrumentationHookFile(root, \"instrumentation\", fileMatcher);\n}\n\n/**\n * Find the instrumentation-client file in the project root.\n */\nexport function findInstrumentationClientFile(\n root: string,\n fileMatcher: ValidFileMatcher,\n): string | null {\n return findInstrumentationHookFile(root, \"instrumentation-client\", fileMatcher);\n}\n\n/**\n * The onRequestError handler type from Next.js instrumentation.\n *\n * Called when an unhandled error occurs during request handling.\n * Provides the error, the request info, and an error context.\n */\nexport type OnRequestErrorContext = {\n /** The route path (e.g., '/blog/[slug]') */\n routerKind: \"Pages Router\" | \"App Router\";\n /** The matched route pattern */\n routePath: string;\n /** The route type */\n routeType: \"render\" | \"route\" | \"action\" | \"middleware\";\n /** HTTP status code that will be sent */\n revalidateReason?: \"on-demand\" | \"stale\" | undefined;\n};\n\nexport type OnRequestErrorHandler = (\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n) => void | Promise<void>;\n\n/**\n * Get the registered onRequestError handler (if any).\n *\n * Reads from globalThis so it works across Vite environment boundaries.\n */\nexport function getOnRequestErrorHandler(): OnRequestErrorHandler | null {\n return globalThis.__VINEXT_onRequestErrorHandler__ ?? null;\n}\n\n/**\n * Load and execute the instrumentation file via a ModuleRunner.\n *\n * Called once during Pages Router server startup (`configureServer`). It:\n * 1. Loads the instrumentation module via `runner.import()`.\n * 2. Calls the `register()` function if exported.\n * 3. Stores the `onRequestError()` handler on `globalThis` so it is visible\n * to all Vite environment module graphs (SSR and the host process share\n * the same Node.js `globalThis`).\n *\n * **App Router** does not use this function. For App Router, `register()` is\n * emitted as a top-level `await` inside the generated RSC entry module so it\n * runs in the same Worker/environment as request handling.\n *\n * @param runner - A ModuleRunner created via `createDirectRunner()`. Must be\n * the same long-lived runner used for middleware and SSR so the module graph\n * is shared. Safe with all Vite plugin combinations, including\n * `@cloudflare/vite-plugin`, because it never touches the hot channel.\n * @param instrumentationPath - Absolute path to the instrumentation file\n */\nexport async function runInstrumentation(\n runner: ModuleImporter,\n instrumentationPath: string,\n): Promise<void> {\n try {\n const mod = (await runner.import(instrumentationPath)) as Record<string, unknown>;\n\n // Call register() if exported\n if (typeof mod.register === \"function\") {\n await mod.register();\n }\n\n // Store onRequestError handler on globalThis so environments can reach the\n // same handler.\n if (typeof mod.onRequestError === \"function\") {\n globalThis.__VINEXT_onRequestErrorHandler__ = mod.onRequestError as OnRequestErrorHandler;\n }\n } catch (err) {\n console.error(\n \"[vinext] Failed to load instrumentation:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n}\n\n/**\n * Report a request error via the instrumentation handler.\n *\n * No-op if no onRequestError handler is registered.\n *\n * Reads the handler from globalThis so this function works correctly regardless\n * of which environment it is called from.\n */\nexport function reportRequestError(\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n): Promise<void> {\n const handler = getOnRequestErrorHandler();\n if (!handler) return Promise.resolve();\n\n const promise = (async () => {\n try {\n await handler(error, request, context);\n } catch (reportErr) {\n console.error(\n \"[vinext] onRequestError handler threw:\",\n reportErr instanceof Error ? reportErr.message : String(reportErr),\n );\n }\n })();\n\n // On Cloudflare Workers, register with ctx.waitUntil() so the isolate\n // stays alive until the report completes (e.g. Sentry HTTP request).\n // On Node.js (dev or vinext start), getRequestExecutionContext() returns\n // null — fire-and-forget is fine because the process doesn't die.\n getRequestExecutionContext()?.waitUntil(promise);\n\n return promise;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,eAAsB,aACpB,QACA,IAE8B;AAE9B,QAAQ,MAAM,OAAO,OAAO,GAAG;;AAGjC,MAAM,4BAA4B,CAAC,IAAI,OAAO;AAE9C,SAAS,4BACP,MACA,UACA,aACe;AACf,MAAK,MAAM,OAAO,0BAChB,MAAK,MAAM,OAAO,YAAY,kBAAkB;EAC9C,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK,GAAG,WAAW,MAAM;AAC1D,MAAI,GAAG,WAAW,SAAS,CACzB,QAAO;;AAIb,QAAO;;;;;AAMT,SAAgB,wBACd,MACA,aACe;AACf,QAAO,4BAA4B,MAAM,mBAAmB,YAAY;;;;;AAM1E,SAAgB,8BACd,MACA,aACe;AACf,QAAO,4BAA4B,MAAM,0BAA0B,YAAY;;;;;;;AA+BjF,SAAgB,2BAAyD;AACvE,QAAO,WAAW,oCAAoC;;;;;;;;;;;;;;;;;;;;;;AAuBxD,eAAsB,mBACpB,QACA,qBACe;AACf,KAAI;EACF,MAAM,MAAO,MAAM,OAAO,OAAO,oBAAoB;AAGrD,MAAI,OAAO,IAAI,aAAa,WAC1B,OAAM,IAAI,UAAU;AAKtB,MAAI,OAAO,IAAI,mBAAmB,WAChC,YAAW,mCAAmC,IAAI;UAE7C,KAAK;AACZ,UAAQ,MACN,4CACA,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD;;;;;;;;;;;AAYL,SAAgB,mBACd,OACA,SACA,SACe;CACf,MAAM,UAAU,0BAA0B;AAC1C,KAAI,CAAC,QAAS,QAAO,QAAQ,SAAS;CAEtC,MAAM,WAAW,YAAY;AAC3B,MAAI;AACF,SAAM,QAAQ,OAAO,SAAS,QAAQ;WAC/B,WAAW;AAClB,WAAQ,MACN,0CACA,qBAAqB,QAAQ,UAAU,UAAU,OAAO,UAAU,CACnE;;KAED;AAMJ,6BAA4B,EAAE,UAAU,QAAQ;AAEhD,QAAO"}
1
+ {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/server/instrumentation.ts"],"sourcesContent":["/**\n * instrumentation.ts support\n *\n * Next.js supports an `instrumentation.ts` file at the project root that\n * exports a `register()` function. This function is called once when the\n * server starts, before any request handling. It's the recommended way to\n * set up observability tools (Sentry, Datadog, OpenTelemetry, etc.).\n *\n * Optionally, it can also export `onRequestError()` which is called when\n * an unhandled error occurs during request handling.\n *\n * References:\n * - https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation\n *\n * ## App Router\n *\n * For App Router, `register()` is baked directly into the generated RSC entry\n * as a top-level `await` at module evaluation time (see `entries/app-rsc-entry.ts`\n * `generateRscEntry`). This means it runs inside the Worker process (or RSC\n * Vite environment) — the same process that handles requests — before any\n * request is served. `runInstrumentation()` is NOT called from `configureServer`\n * for App Router.\n *\n * The `onRequestError` handler is stored on `globalThis` so it is visible across\n * the RSC and SSR Vite environments (separate module graphs, same Node.js process).\n * With `@cloudflare/vite-plugin` it runs entirely inside the Worker, so\n * `globalThis` is the Worker's global — also correct.\n *\n * ## Pages Router\n *\n * Pages Router has no RSC entry, so `configureServer()` is the right place to\n * call `register()`. `runInstrumentation()` accepts a `ModuleRunner` (created\n * via `createDirectRunner()`) rather than `server.ssrLoadModule()` so it is\n * safe when `@cloudflare/vite-plugin` is present — that plugin replaces the\n * SSR environment's hot channel, causing `ssrLoadModule()` to crash with\n * `TypeError: Cannot read properties of undefined (reading 'outsideEmitter')`.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getRequestExecutionContext } from \"vinext/shims/request-context\";\nimport { ValidFileMatcher } from \"../routing/file-matcher.js\";\n/**\n * Minimal duck-typed interface for the module runner passed to\n * `runInstrumentation`. Only `.import()` is used — this avoids requiring\n * callers (including tests) to provide a full `ModuleRunner` instance.\n */\nexport type ModuleImporter = {\n import(id: string): Promise<unknown>;\n};\n\n/**\n * Import a module via the runner and cast the result to `Record<string, any>`.\n *\n * Centralises the `as Record<string, any>` cast so callers don't need\n * per-call oxlint-disable comments.\n */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function importModule(\n runner: ModuleImporter,\n id: string,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Promise<Record<string, any>> {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return (await runner.import(id)) as Record<string, any>;\n}\n\nconst INSTRUMENTATION_LOCATIONS = [\"\", \"src/\"];\n\nfunction findInstrumentationHookFile(\n root: string,\n basename: string,\n fileMatcher: ValidFileMatcher,\n): string | null {\n for (const dir of INSTRUMENTATION_LOCATIONS) {\n for (const ext of fileMatcher.dottedExtensions) {\n const fullPath = path.join(root, dir, `${basename}${ext}`);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n }\n return null;\n}\n\n/**\n * Find the instrumentation file in the project root.\n */\nexport function findInstrumentationFile(\n root: string,\n fileMatcher: ValidFileMatcher,\n): string | null {\n return findInstrumentationHookFile(root, \"instrumentation\", fileMatcher);\n}\n\n/**\n * Find the instrumentation-client file in the project root.\n */\nexport function findInstrumentationClientFile(\n root: string,\n fileMatcher: ValidFileMatcher,\n): string | null {\n return findInstrumentationHookFile(root, \"instrumentation-client\", fileMatcher);\n}\n\n/**\n * The onRequestError handler type from Next.js instrumentation.\n *\n * Called when an unhandled error occurs during request handling.\n * Provides the error, the request info, and an error context.\n */\nexport type OnRequestErrorContext = {\n /** The route path (e.g., '/blog/[slug]') */\n routerKind: \"Pages Router\" | \"App Router\";\n /** The matched route pattern */\n routePath: string;\n /** The route type */\n routeType: \"render\" | \"route\" | \"action\" | \"middleware\";\n /** HTTP status code that will be sent */\n revalidateReason?: \"on-demand\" | \"stale\" | undefined;\n};\n\nexport type OnRequestErrorHandler = (\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n) => void | Promise<void>;\n\n/**\n * Get the registered onRequestError handler (if any).\n *\n * Reads from globalThis so it works across Vite environment boundaries.\n */\nexport function getOnRequestErrorHandler(): OnRequestErrorHandler | null {\n return globalThis.__VINEXT_onRequestErrorHandler__ ?? null;\n}\n\n/**\n * Load and execute the instrumentation file via a ModuleRunner.\n *\n * Called once during Pages Router server startup (`configureServer`). It:\n * 1. Loads the instrumentation module via `runner.import()`.\n * 2. Calls the `register()` function if exported.\n * 3. Stores the `onRequestError()` handler on `globalThis` so it is visible\n * to all Vite environment module graphs (SSR and the host process share\n * the same Node.js `globalThis`).\n *\n * **App Router** does not use this function. For App Router, `register()` is\n * emitted as a top-level `await` inside the generated RSC entry module so it\n * runs in the same Worker/environment as request handling.\n *\n * @param runner - A ModuleRunner created via `createDirectRunner()`. Must be\n * the same long-lived runner used for middleware and SSR so the module graph\n * is shared. Safe with all Vite plugin combinations, including\n * `@cloudflare/vite-plugin`, because it never touches the hot channel.\n * @param instrumentationPath - Absolute path to the instrumentation file\n */\nexport async function runInstrumentation(\n runner: ModuleImporter,\n instrumentationPath: string,\n): Promise<void> {\n try {\n const mod = (await runner.import(instrumentationPath)) as Record<string, unknown>;\n\n // Call register() if exported\n if (typeof mod.register === \"function\") {\n await mod.register();\n }\n\n // Store onRequestError handler on globalThis so environments can reach the\n // same handler.\n if (typeof mod.onRequestError === \"function\") {\n globalThis.__VINEXT_onRequestErrorHandler__ = mod.onRequestError as OnRequestErrorHandler;\n }\n } catch (err) {\n console.error(\n \"[vinext] Failed to load instrumentation:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n}\n\n/**\n * Report a request error via the instrumentation handler.\n *\n * No-op if no onRequestError handler is registered.\n *\n * Reads the handler from globalThis so this function works correctly regardless\n * of which environment it is called from.\n */\nexport function reportRequestError(\n error: Error,\n request: { path: string; method: string; headers: Record<string, string> },\n context: OnRequestErrorContext,\n): Promise<void> {\n const handler = getOnRequestErrorHandler();\n if (!handler) return Promise.resolve();\n\n const promise = (async () => {\n try {\n await handler(error, request, context);\n } catch (reportErr) {\n console.error(\n \"[vinext] onRequestError handler threw:\",\n reportErr instanceof Error ? reportErr.message : String(reportErr),\n );\n }\n })();\n\n // On Cloudflare Workers, register with ctx.waitUntil() so the isolate\n // stays alive until the report completes (e.g. Sentry HTTP request).\n // On Node.js (dev or vinext start), getRequestExecutionContext() returns\n // null — fire-and-forget is fine because the process doesn't die.\n getRequestExecutionContext()?.waitUntil(promise);\n\n return promise;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,eAAsB,aACpB,QACA,IAE8B;AAE9B,QAAQ,MAAM,OAAO,OAAO,GAAG;;AAGjC,MAAM,4BAA4B,CAAC,IAAI,OAAO;AAE9C,SAAS,4BACP,MACA,UACA,aACe;AACf,MAAK,MAAM,OAAO,0BAChB,MAAK,MAAM,OAAO,YAAY,kBAAkB;EAC9C,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK,GAAG,WAAW,MAAM;AAC1D,MAAI,GAAG,WAAW,SAAS,CACzB,QAAO;;AAIb,QAAO;;;;;AAMT,SAAgB,wBACd,MACA,aACe;AACf,QAAO,4BAA4B,MAAM,mBAAmB,YAAY;;;;;AAM1E,SAAgB,8BACd,MACA,aACe;AACf,QAAO,4BAA4B,MAAM,0BAA0B,YAAY;;;;;;;AA+BjF,SAAgB,2BAAyD;AACvE,QAAO,WAAW,oCAAoC;;;;;;;;;;;;;;;;;;;;;;AAuBxD,eAAsB,mBACpB,QACA,qBACe;AACf,KAAI;EACF,MAAM,MAAO,MAAM,OAAO,OAAO,oBAAoB;AAGrD,MAAI,OAAO,IAAI,aAAa,WAC1B,OAAM,IAAI,UAAU;AAKtB,MAAI,OAAO,IAAI,mBAAmB,WAChC,YAAW,mCAAmC,IAAI;UAE7C,KAAK;AACZ,UAAQ,MACN,4CACA,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD;;;;;;;;;;;AAYL,SAAgB,mBACd,OACA,SACA,SACe;CACf,MAAM,UAAU,0BAA0B;AAC1C,KAAI,CAAC,QAAS,QAAO,QAAQ,SAAS;CAEtC,MAAM,WAAW,YAAY;AAC3B,MAAI;AACF,SAAM,QAAQ,OAAO,SAAS,QAAQ;WAC/B,WAAW;AAClB,WAAQ,MACN,0CACA,qBAAqB,QAAQ,UAAU,UAAU,OAAO,UAAU,CACnE;;KAED;AAMJ,6BAA4B,EAAE,UAAU,QAAQ;AAEhD,QAAO"}
@@ -1,4 +1,5 @@
1
1
  import { CacheHandlerValue, CachedAppPageValue, CachedPagesValue, IncrementalCacheValue } from "../shims/cache.js";
2
+ import { OnRequestErrorContext } from "./instrumentation.js";
2
3
 
3
4
  //#region src/server/isr-cache.d.ts
4
5
  type ISRCacheEntry = {
@@ -26,8 +27,16 @@ declare function isrSet(key: string, data: IncrementalCacheValue, revalidateSeco
26
27
  * On Cloudflare Workers the regeneration promise is registered with
27
28
  * `ctx.waitUntil()` via the ALS-backed ExecutionContext, keeping the isolate
28
29
  * alive until the regeneration completes even after the Response is returned.
30
+ *
31
+ * When `errorContext` is provided and the render function fails, the error
32
+ * is reported via `reportRequestError` (instrumentation hook) with
33
+ * `revalidateReason: "stale"`.
29
34
  */
30
- declare function triggerBackgroundRegeneration(key: string, renderFn: () => Promise<void>): void;
35
+ declare function triggerBackgroundRegeneration(key: string, renderFn: () => Promise<void>, errorContext?: {
36
+ routerKind: OnRequestErrorContext["routerKind"];
37
+ routePath: string;
38
+ routeType: OnRequestErrorContext["routeType"];
39
+ }): void;
31
40
  /**
32
41
  * Build a CachedPagesValue for the Pages Router ISR cache.
33
42
  */
@@ -41,6 +50,15 @@ declare function buildAppPageCacheValue(html: string, rscData?: ArrayBuffer, sta
41
50
  * Long pathnames are hashed to stay within KV key-length limits (512 bytes).
42
51
  */
43
52
  declare function isrCacheKey(router: "pages" | "app", pathname: string, buildId?: string): string;
53
+ /**
54
+ * Normalize the App Router mounted-slot header before it participates in cache
55
+ * keys. The client can send mounted slot ids in different orders as navigation
56
+ * state changes, but equivalent slot sets must map to the same RSC cache entry.
57
+ */
58
+ declare function normalizeMountedSlotsHeader(raw: string | null | undefined): string | null;
59
+ declare function appIsrHtmlKey(pathname: string): string;
60
+ declare function appIsrRscKey(pathname: string, mountedSlotsHeader?: string | null): string;
61
+ declare function appIsrRouteKey(pathname: string): string;
44
62
  /**
45
63
  * Store the revalidate duration for a cache key.
46
64
  * Uses insertion-order LRU eviction to prevent unbounded growth.
@@ -51,5 +69,5 @@ declare function setRevalidateDuration(key: string, seconds: number): void;
51
69
  */
52
70
  declare function getRevalidateDuration(key: string): number | undefined;
53
71
  //#endregion
54
- export { ISRCacheEntry, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration };
72
+ export { ISRCacheEntry, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, getRevalidateDuration, isrCacheKey, isrGet, isrSet, normalizeMountedSlotsHeader, setRevalidateDuration, triggerBackgroundRegeneration };
55
73
  //# sourceMappingURL=isr-cache.d.ts.map