vinext 0.0.45 → 0.0.47

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 (311) hide show
  1. package/README.md +7 -5
  2. package/dist/build/prerender.d.ts +2 -1
  3. package/dist/build/prerender.js +80 -17
  4. package/dist/build/prerender.js.map +1 -1
  5. package/dist/build/report.d.ts +1 -1
  6. package/dist/build/route-classification-injector.d.ts +35 -0
  7. package/dist/build/route-classification-injector.js +61 -0
  8. package/dist/build/route-classification-injector.js.map +1 -0
  9. package/dist/build/route-classification-manifest.d.ts +1 -1
  10. package/dist/build/standalone.js +4 -3
  11. package/dist/build/standalone.js.map +1 -1
  12. package/dist/build/static-export.d.ts +1 -1
  13. package/dist/check.js +30 -18
  14. package/dist/check.js.map +1 -1
  15. package/dist/cli-args.d.ts +31 -0
  16. package/dist/cli-args.js +104 -0
  17. package/dist/cli-args.js.map +1 -0
  18. package/dist/cli.js +6 -19
  19. package/dist/cli.js.map +1 -1
  20. package/dist/cloudflare/kv-cache-handler.js +29 -9
  21. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  22. package/dist/config/config-matchers.js +1 -0
  23. package/dist/config/config-matchers.js.map +1 -1
  24. package/dist/config/next-config.d.ts +42 -4
  25. package/dist/config/next-config.js +27 -0
  26. package/dist/config/next-config.js.map +1 -1
  27. package/dist/deploy.js +18 -23
  28. package/dist/deploy.js.map +1 -1
  29. package/dist/entries/app-rsc-entry.d.ts +4 -3
  30. package/dist/entries/app-rsc-entry.js +435 -2317
  31. package/dist/entries/app-rsc-entry.js.map +1 -1
  32. package/dist/entries/app-rsc-manifest.d.ts +24 -0
  33. package/dist/entries/app-rsc-manifest.js +155 -0
  34. package/dist/entries/app-rsc-manifest.js.map +1 -0
  35. package/dist/entries/pages-server-entry.js +18 -105
  36. package/dist/entries/pages-server-entry.js.map +1 -1
  37. package/dist/index.js +82 -85
  38. package/dist/index.js.map +1 -1
  39. package/dist/plugins/fonts.js +54 -32
  40. package/dist/plugins/fonts.js.map +1 -1
  41. package/dist/plugins/rsc-client-shim-excludes.d.ts +6 -0
  42. package/dist/plugins/rsc-client-shim-excludes.js +28 -0
  43. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -0
  44. package/dist/routing/app-route-graph.d.ts +109 -0
  45. package/dist/routing/app-route-graph.js +819 -0
  46. package/dist/routing/app-route-graph.js.map +1 -0
  47. package/dist/routing/app-router.d.ts +2 -79
  48. package/dist/routing/app-router.js +7 -621
  49. package/dist/routing/app-router.js.map +1 -1
  50. package/dist/routing/route-pattern.d.ts +9 -0
  51. package/dist/routing/route-pattern.js +90 -0
  52. package/dist/routing/route-pattern.js.map +1 -0
  53. package/dist/routing/route-trie.js +10 -11
  54. package/dist/routing/route-trie.js.map +1 -1
  55. package/dist/server/app-browser-entry.js +94 -232
  56. package/dist/server/app-browser-entry.js.map +1 -1
  57. package/dist/server/app-browser-error.d.ts +3 -4
  58. package/dist/server/app-browser-error.js +8 -4
  59. package/dist/server/app-browser-error.js.map +1 -1
  60. package/dist/server/app-browser-navigation-controller.d.ts +73 -0
  61. package/dist/server/app-browser-navigation-controller.js +282 -0
  62. package/dist/server/app-browser-navigation-controller.js.map +1 -0
  63. package/dist/server/app-browser-state.d.ts +1 -1
  64. package/dist/server/app-browser-state.js.map +1 -1
  65. package/dist/server/app-elements.js +1 -5
  66. package/dist/server/app-elements.js.map +1 -1
  67. package/dist/server/app-fallback-renderer.d.ts +57 -0
  68. package/dist/server/app-fallback-renderer.js +79 -0
  69. package/dist/server/app-fallback-renderer.js.map +1 -0
  70. package/dist/server/app-hook-warning-suppression.d.ts +7 -0
  71. package/dist/server/app-hook-warning-suppression.js +12 -0
  72. package/dist/server/app-hook-warning-suppression.js.map +1 -0
  73. package/dist/server/app-middleware.d.ts +32 -0
  74. package/dist/server/app-middleware.js +147 -0
  75. package/dist/server/app-middleware.js.map +1 -0
  76. package/dist/server/app-mounted-slots-header.d.ts +17 -0
  77. package/dist/server/app-mounted-slots-header.js +21 -0
  78. package/dist/server/app-mounted-slots-header.js.map +1 -0
  79. package/dist/server/app-page-boundary-render.d.ts +4 -2
  80. package/dist/server/app-page-boundary-render.js +50 -30
  81. package/dist/server/app-page-boundary-render.js.map +1 -1
  82. package/dist/server/app-page-boundary.d.ts +12 -1
  83. package/dist/server/app-page-boundary.js +27 -12
  84. package/dist/server/app-page-boundary.js.map +1 -1
  85. package/dist/server/app-page-cache.d.ts +22 -5
  86. package/dist/server/app-page-cache.js +90 -11
  87. package/dist/server/app-page-cache.js.map +1 -1
  88. package/dist/server/app-page-dispatch.d.ts +123 -0
  89. package/dist/server/app-page-dispatch.js +348 -0
  90. package/dist/server/app-page-dispatch.js.map +1 -0
  91. package/dist/server/app-page-element-builder.d.ts +61 -0
  92. package/dist/server/app-page-element-builder.js +139 -0
  93. package/dist/server/app-page-element-builder.js.map +1 -0
  94. package/dist/server/app-page-execution.d.ts +4 -3
  95. package/dist/server/app-page-execution.js +5 -8
  96. package/dist/server/app-page-execution.js.map +1 -1
  97. package/dist/server/app-page-head.d.ts +55 -0
  98. package/dist/server/app-page-head.js +196 -0
  99. package/dist/server/app-page-head.js.map +1 -0
  100. package/dist/server/app-page-method.d.ts +16 -0
  101. package/dist/server/app-page-method.js +30 -0
  102. package/dist/server/app-page-method.js.map +1 -0
  103. package/dist/server/app-page-params.d.ts +8 -0
  104. package/dist/server/app-page-params.js +28 -0
  105. package/dist/server/app-page-params.js.map +1 -0
  106. package/dist/server/app-page-render.d.ts +7 -2
  107. package/dist/server/app-page-render.js +131 -32
  108. package/dist/server/app-page-render.js.map +1 -1
  109. package/dist/server/app-page-request.d.ts +23 -8
  110. package/dist/server/app-page-request.js +51 -6
  111. package/dist/server/app-page-request.js.map +1 -1
  112. package/dist/server/app-page-response.d.ts +1 -0
  113. package/dist/server/app-page-response.js +3 -7
  114. package/dist/server/app-page-response.js.map +1 -1
  115. package/dist/server/app-page-route-wiring.d.ts +29 -5
  116. package/dist/server/app-page-route-wiring.js +30 -8
  117. package/dist/server/app-page-route-wiring.js.map +1 -1
  118. package/dist/server/app-page-stream.d.ts +10 -0
  119. package/dist/server/app-page-stream.js +5 -1
  120. package/dist/server/app-page-stream.js.map +1 -1
  121. package/dist/server/app-post-middleware-context.d.ts +16 -0
  122. package/dist/server/app-post-middleware-context.js +28 -0
  123. package/dist/server/app-post-middleware-context.js.map +1 -0
  124. package/dist/server/app-prerender-endpoints.d.ts +19 -0
  125. package/dist/server/app-prerender-endpoints.js +96 -0
  126. package/dist/server/app-prerender-endpoints.js.map +1 -0
  127. package/dist/server/app-prerender-static-params.d.ts +16 -0
  128. package/dist/server/app-prerender-static-params.js +14 -0
  129. package/dist/server/app-prerender-static-params.js.map +1 -0
  130. package/dist/server/app-request-context.d.ts +22 -0
  131. package/dist/server/app-request-context.js +30 -0
  132. package/dist/server/app-request-context.js.map +1 -0
  133. package/dist/server/app-route-handler-cache.d.ts +4 -0
  134. package/dist/server/app-route-handler-cache.js +11 -3
  135. package/dist/server/app-route-handler-cache.js.map +1 -1
  136. package/dist/server/app-route-handler-dispatch.d.ts +43 -0
  137. package/dist/server/app-route-handler-dispatch.js +149 -0
  138. package/dist/server/app-route-handler-dispatch.js.map +1 -0
  139. package/dist/server/app-route-handler-execution.d.ts +8 -3
  140. package/dist/server/app-route-handler-execution.js +25 -4
  141. package/dist/server/app-route-handler-execution.js.map +1 -1
  142. package/dist/server/app-route-handler-response.d.ts +6 -3
  143. package/dist/server/app-route-handler-response.js +52 -11
  144. package/dist/server/app-route-handler-response.js.map +1 -1
  145. package/dist/server/app-route-handler-runtime.d.ts +4 -1
  146. package/dist/server/app-route-handler-runtime.js +107 -1
  147. package/dist/server/app-route-handler-runtime.js.map +1 -1
  148. package/dist/server/app-router-entry.js.map +1 -1
  149. package/dist/server/app-rsc-error-handler.d.ts +21 -0
  150. package/dist/server/app-rsc-error-handler.js +30 -0
  151. package/dist/server/app-rsc-error-handler.js.map +1 -0
  152. package/dist/server/app-rsc-errors.d.ts +27 -0
  153. package/dist/server/app-rsc-errors.js +42 -0
  154. package/dist/server/app-rsc-errors.js.map +1 -0
  155. package/dist/server/app-rsc-handler.d.ts +117 -0
  156. package/dist/server/app-rsc-handler.js +260 -0
  157. package/dist/server/app-rsc-handler.js.map +1 -0
  158. package/dist/server/app-rsc-request-normalization.d.ts +40 -0
  159. package/dist/server/app-rsc-request-normalization.js +63 -0
  160. package/dist/server/app-rsc-request-normalization.js.map +1 -0
  161. package/dist/server/app-rsc-response-finalizer.d.ts +30 -0
  162. package/dist/server/app-rsc-response-finalizer.js +38 -0
  163. package/dist/server/app-rsc-response-finalizer.js.map +1 -0
  164. package/dist/server/app-rsc-route-matching.d.ts +40 -0
  165. package/dist/server/app-rsc-route-matching.js +66 -0
  166. package/dist/server/app-rsc-route-matching.js.map +1 -0
  167. package/dist/server/app-segment-config.d.ts +33 -0
  168. package/dist/server/app-segment-config.js +86 -0
  169. package/dist/server/app-segment-config.js.map +1 -0
  170. package/dist/server/app-server-action-execution.d.ts +88 -1
  171. package/dist/server/app-server-action-execution.js +257 -5
  172. package/dist/server/app-server-action-execution.js.map +1 -1
  173. package/dist/server/app-ssr-entry.d.ts +7 -0
  174. package/dist/server/app-ssr-entry.js +30 -9
  175. package/dist/server/app-ssr-entry.js.map +1 -1
  176. package/dist/server/app-ssr-stream.d.ts +4 -2
  177. package/dist/server/app-ssr-stream.js +29 -2
  178. package/dist/server/app-ssr-stream.js.map +1 -1
  179. package/dist/server/app-static-generation.d.ts +15 -0
  180. package/dist/server/app-static-generation.js +20 -0
  181. package/dist/server/app-static-generation.js.map +1 -0
  182. package/dist/server/cache-control.d.ts +24 -0
  183. package/dist/server/cache-control.js +33 -0
  184. package/dist/server/cache-control.js.map +1 -0
  185. package/dist/server/dev-error-overlay-store.d.ts +23 -0
  186. package/dist/server/dev-error-overlay-store.js +67 -0
  187. package/dist/server/dev-error-overlay-store.js.map +1 -0
  188. package/dist/server/dev-error-overlay.d.ts +15 -0
  189. package/dist/server/dev-error-overlay.js +548 -0
  190. package/dist/server/dev-error-overlay.js.map +1 -0
  191. package/dist/server/dev-route-files.d.ts +7 -0
  192. package/dist/server/dev-route-files.js +73 -0
  193. package/dist/server/dev-route-files.js.map +1 -0
  194. package/dist/server/dev-server.js +4 -0
  195. package/dist/server/dev-server.js.map +1 -1
  196. package/dist/server/file-based-metadata.d.ts +17 -0
  197. package/dist/server/file-based-metadata.js +356 -0
  198. package/dist/server/file-based-metadata.js.map +1 -0
  199. package/dist/server/implicit-tags.d.ts +6 -0
  200. package/dist/server/implicit-tags.js +42 -0
  201. package/dist/server/implicit-tags.js.map +1 -0
  202. package/dist/server/instrumentation-runtime.d.ts +44 -0
  203. package/dist/server/instrumentation-runtime.js +29 -0
  204. package/dist/server/instrumentation-runtime.js.map +1 -0
  205. package/dist/server/instrumentation.js.map +1 -1
  206. package/dist/server/isr-cache.d.ts +16 -3
  207. package/dist/server/isr-cache.js +56 -8
  208. package/dist/server/isr-cache.js.map +1 -1
  209. package/dist/server/metadata-route-build-data.d.ts +25 -0
  210. package/dist/server/metadata-route-build-data.js +150 -0
  211. package/dist/server/metadata-route-build-data.js.map +1 -0
  212. package/dist/server/metadata-route-response.d.ts +17 -0
  213. package/dist/server/metadata-route-response.js +187 -0
  214. package/dist/server/metadata-route-response.js.map +1 -0
  215. package/dist/server/metadata-routes.d.ts +42 -4
  216. package/dist/server/metadata-routes.js +127 -11
  217. package/dist/server/metadata-routes.js.map +1 -1
  218. package/dist/server/middleware-matcher.d.ts +15 -0
  219. package/dist/server/middleware-matcher.js +102 -0
  220. package/dist/server/middleware-matcher.js.map +1 -0
  221. package/dist/server/middleware-request-headers.js +2 -1
  222. package/dist/server/middleware-request-headers.js.map +1 -1
  223. package/dist/server/middleware-runtime.d.ts +39 -0
  224. package/dist/server/middleware-runtime.js +159 -0
  225. package/dist/server/middleware-runtime.js.map +1 -0
  226. package/dist/server/middleware.d.ts +4 -36
  227. package/dist/server/middleware.js +18 -228
  228. package/dist/server/middleware.js.map +1 -1
  229. package/dist/server/pages-page-data.d.ts +7 -2
  230. package/dist/server/pages-page-data.js +10 -5
  231. package/dist/server/pages-page-data.js.map +1 -1
  232. package/dist/server/pages-page-response.d.ts +2 -1
  233. package/dist/server/pages-page-response.js +5 -3
  234. package/dist/server/pages-page-response.js.map +1 -1
  235. package/dist/server/prerender-work-unit-setup.d.ts +7 -0
  236. package/dist/server/prerender-work-unit-setup.js +30 -0
  237. package/dist/server/prerender-work-unit-setup.js.map +1 -0
  238. package/dist/server/prod-server.js +10 -14
  239. package/dist/server/prod-server.js.map +1 -1
  240. package/dist/server/request-pipeline.d.ts +46 -5
  241. package/dist/server/request-pipeline.js +84 -5
  242. package/dist/server/request-pipeline.js.map +1 -1
  243. package/dist/server/rsc-stream-hints.d.ts +7 -0
  244. package/dist/server/rsc-stream-hints.js +38 -0
  245. package/dist/server/rsc-stream-hints.js.map +1 -0
  246. package/dist/server/seed-cache.js +19 -8
  247. package/dist/server/seed-cache.js.map +1 -1
  248. package/dist/server/server-action-not-found.d.ts +9 -0
  249. package/dist/server/server-action-not-found.js +40 -0
  250. package/dist/server/server-action-not-found.js.map +1 -0
  251. package/dist/shims/cache-runtime.js +28 -11
  252. package/dist/shims/cache-runtime.js.map +1 -1
  253. package/dist/shims/cache.d.ts +39 -4
  254. package/dist/shims/cache.js +93 -16
  255. package/dist/shims/cache.js.map +1 -1
  256. package/dist/shims/error-boundary.d.ts +66 -5
  257. package/dist/shims/error-boundary.js +106 -4
  258. package/dist/shims/error-boundary.js.map +1 -1
  259. package/dist/shims/fetch-cache.d.ts +4 -1
  260. package/dist/shims/fetch-cache.js +55 -13
  261. package/dist/shims/fetch-cache.js.map +1 -1
  262. package/dist/shims/font-google-base.d.ts +5 -4
  263. package/dist/shims/font-google-base.js +61 -13
  264. package/dist/shims/font-google-base.js.map +1 -1
  265. package/dist/shims/headers.d.ts +14 -2
  266. package/dist/shims/headers.js +127 -17
  267. package/dist/shims/headers.js.map +1 -1
  268. package/dist/shims/image.js +116 -10
  269. package/dist/shims/image.js.map +1 -1
  270. package/dist/shims/internal/make-hanging-promise.d.ts +16 -0
  271. package/dist/shims/internal/make-hanging-promise.js +46 -0
  272. package/dist/shims/internal/make-hanging-promise.js.map +1 -0
  273. package/dist/shims/internal/work-unit-async-storage.d.ts +26 -3
  274. package/dist/shims/internal/work-unit-async-storage.js +6 -3
  275. package/dist/shims/internal/work-unit-async-storage.js.map +1 -1
  276. package/dist/shims/metadata.d.ts +38 -26
  277. package/dist/shims/metadata.js +75 -45
  278. package/dist/shims/metadata.js.map +1 -1
  279. package/dist/shims/navigation.d.ts +10 -1
  280. package/dist/shims/navigation.js +18 -1
  281. package/dist/shims/navigation.js.map +1 -1
  282. package/dist/shims/navigation.react-server.d.ts +2 -2
  283. package/dist/shims/navigation.react-server.js +2 -2
  284. package/dist/shims/navigation.react-server.js.map +1 -1
  285. package/dist/shims/offline.d.ts +5 -0
  286. package/dist/shims/offline.js +17 -0
  287. package/dist/shims/offline.js.map +1 -0
  288. package/dist/shims/request-state-types.d.ts +3 -2
  289. package/dist/shims/root-params.d.ts +11 -0
  290. package/dist/shims/root-params.js +24 -0
  291. package/dist/shims/root-params.js.map +1 -0
  292. package/dist/shims/router.js +1 -1
  293. package/dist/shims/server.d.ts +3 -1
  294. package/dist/shims/server.js +83 -5
  295. package/dist/shims/server.js.map +1 -1
  296. package/dist/shims/thenable-params.d.ts +5 -0
  297. package/dist/shims/thenable-params.js +37 -0
  298. package/dist/shims/thenable-params.js.map +1 -0
  299. package/dist/shims/unified-request-context.d.ts +3 -2
  300. package/dist/shims/unified-request-context.js +3 -0
  301. package/dist/shims/unified-request-context.js.map +1 -1
  302. package/dist/shims/use-merged-ref.d.ts +7 -0
  303. package/dist/shims/use-merged-ref.js +40 -0
  304. package/dist/shims/use-merged-ref.js.map +1 -0
  305. package/dist/utils/cache-control-metadata.d.ts +6 -0
  306. package/dist/utils/cache-control-metadata.js +16 -0
  307. package/dist/utils/cache-control-metadata.js.map +1 -0
  308. package/package.json +6 -1
  309. package/dist/server/middleware-codegen.d.ts +0 -54
  310. package/dist/server/middleware-codegen.js +0 -414
  311. package/dist/server/middleware-codegen.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-route-response.js","names":[],"sources":["../../src/server/metadata-route-response.ts"],"sourcesContent":["import {\n isValidMetadataImageId,\n manifestToJson,\n matchMetadataRoutePattern,\n robotsToText,\n sitemapToXml,\n type ManifestConfig,\n type MetadataFileRoute,\n type RobotsConfig,\n type SitemapEntry,\n} from \"./metadata-routes.js\";\n\ntype AppPageParams = Record<string, string | string[]>;\ntype MetadataRouteFunction = (props: Record<string, unknown>) => unknown;\ntype MakeThenableParams = (params: AppPageParams) => unknown;\n\ntype MetadataRuntimeRoute = MetadataFileRoute & {\n fileDataBase64?: string;\n};\n\ntype MetadataRouteRequestOptions = {\n metadataRoutes: readonly MetadataRuntimeRoute[];\n cleanPathname: string;\n makeThenableParams: MakeThenableParams;\n};\n\ntype MatchedMetadataRoute = {\n params: AppPageParams | null;\n imageId: string | null;\n};\n\ntype MetadataRouteFunctions = {\n defaultExport: MetadataRouteFunction | null;\n generateImageMetadata: MetadataRouteFunction | null;\n generateSitemaps: MetadataRouteFunction | null;\n hasGeneratedImageMetadata: boolean;\n};\n\nconst routeFunctionCache = new WeakMap<MetadataRuntimeRoute, MetadataRouteFunctions>();\n\nfunction isObject(value: unknown): value is object {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction readFunction(\n module: Record<string, unknown> | undefined,\n key: string,\n): MetadataRouteFunction | null {\n if (!module) {\n return null;\n }\n const value = Reflect.get(module, key);\n if (typeof value !== \"function\") {\n return null;\n }\n return (props) => Reflect.apply(value, module, [props]);\n}\n\nfunction isSitemapEntries(value: unknown): value is SitemapEntry[] {\n return Array.isArray(value);\n}\n\nfunction isRobotsConfig(value: unknown): value is RobotsConfig {\n return isObject(value) && !Array.isArray(value);\n}\n\nfunction isManifestConfig(value: unknown): value is ManifestConfig {\n return isObject(value) && !Array.isArray(value);\n}\n\nfunction isImageMetadataRoute(route: MetadataRuntimeRoute): boolean {\n return (\n route.type === \"icon\" ||\n route.type === \"apple-icon\" ||\n route.type === \"opengraph-image\" ||\n route.type === \"twitter-image\"\n );\n}\n\nfunction getMetadataRouteFunctions(route: MetadataRuntimeRoute): MetadataRouteFunctions {\n const cached = routeFunctionCache.get(route);\n if (cached) {\n return cached;\n }\n\n const generateImageMetadata =\n route.isDynamic && isImageMetadataRoute(route)\n ? readFunction(route.module, \"generateImageMetadata\")\n : null;\n const functions = {\n defaultExport: route.isDynamic ? readFunction(route.module, \"default\") : null,\n generateImageMetadata,\n generateSitemaps:\n route.type === \"sitemap\" && route.isDynamic\n ? readFunction(route.module, \"generateSitemaps\")\n : null,\n hasGeneratedImageMetadata:\n route.isDynamic && isImageMetadataRoute(route) && Boolean(generateImageMetadata),\n };\n routeFunctionCache.set(route, functions);\n return functions;\n}\n\nfunction matchMetadataRoute(\n route: MetadataRuntimeRoute,\n cleanPathname: string,\n functions: MetadataRouteFunctions,\n): MatchedMetadataRoute | null {\n if (route.patternParts) {\n const urlParts = cleanPathname.split(\"/\").filter(Boolean);\n if (functions.hasGeneratedImageMetadata && urlParts.length > 0) {\n const params = matchMetadataRoutePattern(urlParts.slice(0, -1), route.patternParts);\n if (params) {\n return {\n params,\n imageId: urlParts[urlParts.length - 1],\n };\n }\n }\n\n const params = matchMetadataRoutePattern(urlParts, route.patternParts);\n return params ? { params, imageId: null } : null;\n }\n\n if (functions.hasGeneratedImageMetadata && cleanPathname.startsWith(`${route.servedUrl}/`)) {\n const imageSuffix = cleanPathname.slice(route.servedUrl.length + 1);\n if (!imageSuffix || imageSuffix.includes(\"/\")) {\n return null;\n }\n return { params: Object.create(null), imageId: imageSuffix };\n }\n\n return cleanPathname === route.servedUrl ? { params: null, imageId: null } : null;\n}\n\nfunction findGeneratedSitemapId(entries: unknown, rawId: string): string | null {\n if (!Array.isArray(entries)) {\n return null;\n }\n\n for (const entry of entries) {\n if (!isObject(entry) || Reflect.get(entry, \"id\") == null) {\n throw new Error(\"id property is required for every item returned from generateSitemaps\");\n }\n const id = Reflect.get(entry, \"id\");\n if (String(id) === rawId) {\n return rawId;\n }\n }\n\n return null;\n}\n\nfunction makeThenableMetadataRouteId(id: string) {\n return Object.assign(Promise.resolve(id), {\n toString() {\n return id;\n },\n valueOf() {\n return id;\n },\n [Symbol.toPrimitive]() {\n return id;\n },\n });\n}\n\nasync function handleGeneratedSitemap(\n route: MetadataRuntimeRoute,\n cleanPathname: string,\n functions: MetadataRouteFunctions,\n): Promise<Response | null> {\n if (!functions.generateSitemaps || !functions.defaultExport) {\n return null;\n }\n\n const sitemapPrefix = route.servedUrl.slice(0, -4);\n if (!cleanPathname.startsWith(`${sitemapPrefix}/`) || !cleanPathname.endsWith(\".xml\")) {\n return null;\n }\n\n const rawId = cleanPathname.slice(sitemapPrefix.length + 1, -4);\n if (rawId.includes(\"/\")) {\n return null;\n }\n\n const matchedId = findGeneratedSitemapId(await functions.generateSitemaps({}), rawId);\n if (!matchedId) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const result = await functions.defaultExport({\n id: makeThenableMetadataRouteId(matchedId),\n });\n if (result instanceof Response) {\n return result;\n }\n if (!isSitemapEntries(result)) {\n throw new TypeError(\"Metadata sitemap routes must return an array.\");\n }\n return new Response(sitemapToXml(result), {\n headers: {\n \"Content-Type\": route.contentType,\n \"Cache-Control\": \"public, max-age=0, must-revalidate\",\n },\n });\n}\n\nfunction findGeneratedImageId(\n imageMetadata: unknown,\n imageId: string,\n servedUrl: string,\n): string | null {\n if (!Array.isArray(imageMetadata)) {\n return null;\n }\n\n for (const item of imageMetadata) {\n if (!isObject(item) || Reflect.get(item, \"id\") == null) {\n throw new Error(\"id property is required for every item returned from generateImageMetadata\");\n }\n\n const itemId = String(Reflect.get(item, \"id\"));\n if (!isValidMetadataImageId(itemId)) {\n console.warn(\n `[vinext] Skipping metadata route ${servedUrl} image id \"${itemId}\" because metadata image ids must match /^[a-zA-Z0-9-_.]+$/.`,\n );\n continue;\n }\n if (itemId === imageId) {\n return itemId;\n }\n }\n\n return null;\n}\n\nasync function callDynamicMetadataRoute(\n route: MetadataRuntimeRoute,\n match: MatchedMetadataRoute,\n makeThenableParams: MakeThenableParams,\n functions: MetadataRouteFunctions,\n): Promise<Response> {\n if (!functions.defaultExport) {\n console.warn(`[vinext] Dynamic metadata route ${route.servedUrl} has no default export.`);\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const paramsThenable = makeThenableParams(match.params ?? {});\n let result: unknown;\n if (functions.hasGeneratedImageMetadata) {\n if (match.imageId === null || !isValidMetadataImageId(match.imageId)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n if (!functions.generateImageMetadata) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const matchedImageId = findGeneratedImageId(\n await functions.generateImageMetadata({ params: paramsThenable }),\n match.imageId,\n route.servedUrl,\n );\n if (!matchedImageId) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n result = await functions.defaultExport({\n params: paramsThenable,\n id: makeThenableMetadataRouteId(matchedImageId),\n });\n } else {\n result = await functions.defaultExport({ params: paramsThenable });\n }\n\n if (result instanceof Response) {\n return result;\n }\n\n let body: string;\n if (route.type === \"sitemap\") {\n if (!isSitemapEntries(result)) {\n throw new TypeError(\"Metadata sitemap routes must return an array.\");\n }\n body = sitemapToXml(result);\n } else if (route.type === \"robots\") {\n if (!isRobotsConfig(result)) {\n throw new TypeError(\"Metadata robots routes must return an object.\");\n }\n body = robotsToText(result);\n } else if (route.type === \"manifest\") {\n if (!isManifestConfig(result)) {\n throw new TypeError(\"Metadata manifest routes must return an object.\");\n }\n body = manifestToJson(result);\n } else if (isImageMetadataRoute(route)) {\n throw new TypeError(\n `Dynamic metadata ${route.type} route ${route.servedUrl} must return a Response.`,\n );\n } else {\n body = JSON.stringify(result);\n }\n\n return new Response(body, {\n headers: {\n \"Content-Type\": route.contentType,\n \"Cache-Control\": \"public, max-age=0, must-revalidate\",\n },\n });\n}\n\nfunction serveStaticMetadataRoute(route: MetadataRuntimeRoute): Response {\n if (typeof route.fileDataBase64 !== \"string\") {\n throw new Error(\n `[vinext] Static metadata route ${route.servedUrl} is missing embedded file data.`,\n );\n }\n\n try {\n const binary = atob(route.fileDataBase64);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index++) {\n bytes[index] = binary.charCodeAt(index);\n }\n return new Response(bytes, {\n headers: {\n \"Content-Type\": route.contentType,\n \"Cache-Control\": \"public, max-age=0, must-revalidate\",\n },\n });\n } catch (error) {\n const reason = error instanceof Error && error.message ? `: ${error.message}` : \"\";\n throw new Error(\n `[vinext] Failed to decode embedded metadata route file data for ${route.servedUrl}${reason}`,\n { cause: error },\n );\n }\n}\n\nexport async function handleMetadataRouteRequest(\n options: MetadataRouteRequestOptions,\n): Promise<Response | null> {\n for (const route of options.metadataRoutes) {\n const functions = getMetadataRouteFunctions(route);\n if (route.type === \"sitemap\" && route.isDynamic) {\n if (functions.generateSitemaps) {\n const generatedSitemapResponse = await handleGeneratedSitemap(\n route,\n options.cleanPathname,\n functions,\n );\n if (generatedSitemapResponse) {\n return generatedSitemapResponse;\n }\n\n // Next.js serves only generated sitemap children when generateSitemaps()\n // exists, so the base /sitemap.xml route should not fall through.\n continue;\n }\n }\n\n const match = matchMetadataRoute(route, options.cleanPathname, functions);\n if (!match) {\n continue;\n }\n\n return route.isDynamic\n ? callDynamicMetadataRoute(route, match, options.makeThenableParams, functions)\n : serveStaticMetadataRoute(route);\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -85,17 +85,55 @@ declare function robotsToText(config: RobotsConfig): string;
85
85
  * Convert a manifest config to JSON string.
86
86
  */
87
87
  declare function manifestToJson(config: ManifestConfig): string;
88
+ /**
89
+ * Compute the static URL for a metadata file given its app directory
90
+ * parent path and filename.
91
+ *
92
+ * Example:
93
+ * fillStaticMetadataSegment("/", "favicon.ico") -> "/favicon.ico"
94
+ * fillStaticMetadataSegment("/blog/[slug]", "favicon.ico") -> "/blog/-/favicon.ico"
95
+ * fillStaticMetadataSegment("/(group)/group", "icon.png") -> "/group/icon-131tc6.png"
96
+ */
97
+ declare function fillStaticMetadataSegment(appDirPath: string, lastSegment: string): string;
88
98
  type MetadataFileRoute = {
89
99
  /** Type of metadata file */type: string; /** Whether this is a dynamic (code-generated) route */
90
- isDynamic: boolean; /** Absolute file path */
91
- filePath: string; /** URL path this file is served at */
100
+ isDynamic: boolean; /** Imported dynamic module for code-generated metadata routes. */
101
+ module?: Record<string, unknown>; /** Absolute file path */
102
+ filePath: string; /** Route prefix where this metadata applies, preserving dynamic segment names. */
103
+ routePrefix: string; /** Raw app tree segments where this metadata file is colocated. */
104
+ routeSegments?: string[]; /** Pattern parts for matching dynamic metadata routes at request time. */
105
+ patternParts?: string[]; /** URL path this file is served at */
92
106
  servedUrl: string; /** Content type for the response */
93
- contentType: string;
107
+ contentType: string; /** Optional metadata used to inject file-based routes into <head>. */
108
+ headData?: MetadataRouteHeadData; /** Optional content hash for cache-busting metadata links. */
109
+ contentHash?: string; /** Sibling .alt.txt file for static social image metadata routes. */
110
+ altFilePath?: string;
111
+ };
112
+ type MetadataRouteHeadData = {
113
+ kind: "favicon" | "icon" | "apple";
114
+ href: string;
115
+ type?: string;
116
+ sizes?: string;
117
+ } | {
118
+ kind: "openGraph" | "twitter";
119
+ href: string;
120
+ type?: string;
121
+ width?: number;
122
+ height?: number;
123
+ alt?: string;
124
+ } | {
125
+ kind: "manifest";
126
+ href: string;
94
127
  };
128
+ declare function getMetadataRouteKind(route: Pick<MetadataFileRoute, "type">): MetadataRouteHeadData["kind"] | null;
129
+ declare function getMetadataImageRouteKind(route: Pick<MetadataFileRoute, "type">): Extract<MetadataRouteHeadData["kind"], "icon" | "apple" | "openGraph" | "twitter"> | null;
130
+ declare function isValidMetadataImageId(id: string): boolean;
131
+ declare function matchMetadataRoutePattern(urlParts: string[], patternParts: string[]): Record<string, string | string[]> | null;
132
+ declare function matchMetadataFileBaseName(metaType: string, baseName: string): string | null;
95
133
  /**
96
134
  * Scan an app directory for metadata files.
97
135
  */
98
136
  declare function scanMetadataFiles(appDir: string): MetadataFileRoute[];
99
137
  //#endregion
100
- export { METADATA_FILE_MAP, ManifestConfig, MetadataFileRoute, RobotsConfig, RobotsRule, SitemapEntry, manifestToJson, robotsToText, scanMetadataFiles, sitemapToXml };
138
+ export { METADATA_FILE_MAP, ManifestConfig, MetadataFileRoute, MetadataRouteHeadData, RobotsConfig, RobotsRule, SitemapEntry, fillStaticMetadataSegment, getMetadataImageRouteKind, getMetadataRouteKind, isValidMetadataImageId, manifestToJson, matchMetadataFileBaseName, matchMetadataRoutePattern, robotsToText, scanMetadataFiles, sitemapToXml };
101
139
  //# sourceMappingURL=metadata-routes.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { matchRoutePattern } from "../routing/route-pattern.js";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  //#region src/server/metadata-routes.ts
@@ -213,19 +214,124 @@ function manifestToJson(config) {
213
214
  function serializeDate(value) {
214
215
  return value instanceof Date ? value.toISOString() : value;
215
216
  }
216
- function metadataRouteSuffix(parentSegments, metaType) {
217
- if (metaType === "sitemap" || metaType === "robots" || metaType === "manifest") return "";
218
- if (!parentSegments.some((segment) => segment.startsWith("(") && segment.endsWith(")") || segment.startsWith("@") && segment !== "@children")) return "";
219
- const parentPath = `/${parentSegments.join("/")}`;
217
+ /**
218
+ * Regular expression pattern used to match route parameters.
219
+ * Matches both single parameters and parameter groups.
220
+ * Examples:
221
+ * - `[[...slug]]` matches parameter group with key 'slug', repeat: true, optional: true
222
+ * - `[...slug]` matches parameter group with key 'slug', repeat: true, optional: false
223
+ * - `[[foo]]` matches parameter with key 'foo', repeat: false, optional: true
224
+ * - `[bar]` matches parameter with key 'bar', repeat: false, optional: false
225
+ */
226
+ const PARAMETER_PATTERN = /^([^[]*)\[((?:\[[^\]]*\])|[^\]]+)\](.*)$/;
227
+ function isGroupSegment(segment) {
228
+ return segment.startsWith("(") && segment.endsWith(")");
229
+ }
230
+ function isParallelRouteSegment(segment) {
231
+ return segment.startsWith("@") && segment !== "@children";
232
+ }
233
+ function normalizeStaticMetadataRouteSegment(segment) {
234
+ let normalizedSegment = segment;
235
+ let match = normalizedSegment.match(PARAMETER_PATTERN);
236
+ while (match) {
237
+ normalizedSegment = `${match[1]}-${match[3]}`;
238
+ match = normalizedSegment.match(PARAMETER_PATTERN);
239
+ }
240
+ return normalizedSegment;
241
+ }
242
+ function getStaticMetadataRoute(appDirPath) {
243
+ const segments = appDirPath.split("/").filter(Boolean);
244
+ const normalizedSegments = [];
245
+ for (const seg of segments) {
246
+ if (isGroupSegment(seg) || seg.startsWith("@")) continue;
247
+ normalizedSegments.push(normalizeStaticMetadataRouteSegment(seg));
248
+ }
249
+ return normalizedSegments.length > 0 ? `/${normalizedSegments.join("/")}` : "";
250
+ }
251
+ function hashMetadataRouteParentPath(parentPathname) {
220
252
  let hash = 5381;
221
- for (let i = 0; i < parentPath.length; i++) hash = (hash << 5) + hash + parentPath.charCodeAt(i) & 4294967295;
253
+ for (let i = 0; i < parentPathname.length; i++) hash = (hash << 5) + hash + parentPathname.charCodeAt(i) & 4294967295;
222
254
  return (hash >>> 0).toString(36).slice(0, 6);
223
255
  }
256
+ function getMetadataRouteSuffix(page) {
257
+ const lastSlash = page.lastIndexOf("/");
258
+ const parentPathname = lastSlash > 0 ? page.slice(0, lastSlash) : "";
259
+ if (page.endsWith("/sitemap") || page.endsWith("/sitemap.xml")) return "";
260
+ if (!parentPathname.split("/").some((seg) => isGroupSegment(seg) || isParallelRouteSegment(seg))) return "";
261
+ return hashMetadataRouteParentPath(parentPathname);
262
+ }
263
+ function computeMetadataRouteSuffix(appDirPath, leafName) {
264
+ return {
265
+ route: getStaticMetadataRoute(appDirPath),
266
+ suffix: getMetadataRouteSuffix(appDirPath === "" || appDirPath === "/" ? `/${leafName}` : `${appDirPath}/${leafName}`)
267
+ };
268
+ }
269
+ function getMetadataRouteFilename(appDirPath, lastSegment) {
270
+ const ext = path.posix.extname(lastSegment);
271
+ const name = lastSegment.slice(0, -ext.length || void 0);
272
+ const { suffix } = computeMetadataRouteSuffix(appDirPath, name);
273
+ return `${name}${suffix ? `-${suffix}` : ""}${ext}`;
274
+ }
275
+ /**
276
+ * Compute the static URL for a metadata file given its app directory
277
+ * parent path and filename.
278
+ *
279
+ * Example:
280
+ * fillStaticMetadataSegment("/", "favicon.ico") -> "/favicon.ico"
281
+ * fillStaticMetadataSegment("/blog/[slug]", "favicon.ico") -> "/blog/-/favicon.ico"
282
+ * fillStaticMetadataSegment("/(group)/group", "icon.png") -> "/group/icon-131tc6.png"
283
+ */
284
+ function fillStaticMetadataSegment(appDirPath, lastSegment) {
285
+ const route = getStaticMetadataRoute(appDirPath);
286
+ const filename = getMetadataRouteFilename(appDirPath, lastSegment);
287
+ return route === "" ? `/${filename}` : `${route}/${filename}`;
288
+ }
289
+ function getMetadataRouteKind(route) {
290
+ if (route.type === "favicon") return "favicon";
291
+ if (route.type === "icon") return "icon";
292
+ if (route.type === "apple-icon") return "apple";
293
+ if (route.type === "opengraph-image") return "openGraph";
294
+ if (route.type === "twitter-image") return "twitter";
295
+ if (route.type === "manifest") return "manifest";
296
+ return null;
297
+ }
298
+ function getMetadataImageRouteKind(route) {
299
+ const kind = getMetadataRouteKind(route);
300
+ if (kind === "icon" || kind === "apple" || kind === "openGraph" || kind === "twitter") return kind;
301
+ return null;
302
+ }
303
+ const metadataImageIdPattern = /^[a-zA-Z0-9-_.]+$/;
304
+ function isValidMetadataImageId(id) {
305
+ return metadataImageIdPattern.test(id);
306
+ }
307
+ function matchMetadataRoutePattern(urlParts, patternParts) {
308
+ return matchRoutePattern(urlParts, patternParts);
309
+ }
310
+ function metadataRouteSuffix(parentSegments, metaType) {
311
+ if (metaType === "sitemap") return "";
312
+ if (!parentSegments.some((segment) => segment.startsWith("(") && segment.endsWith(")") || segment.startsWith("@") && segment !== "@children")) return "";
313
+ return hashMetadataRouteParentPath(`/${parentSegments.join("/")}`);
314
+ }
224
315
  function withMetadataSuffix(urlPath, suffix) {
225
316
  if (!suffix) return urlPath;
226
317
  const parsed = path.posix.parse(urlPath);
227
318
  return path.posix.join(parsed.dir || "/", `${parsed.name}-${suffix}${parsed.ext}`);
228
319
  }
320
+ function getMetadataServedUrl(metaType, config, ext, isDynamic, suffix, routeBaseName) {
321
+ if (isDynamic && (metaType === "icon" || metaType === "apple-icon" || metaType === "opengraph-image" || metaType === "twitter-image")) return withMetadataSuffix(`/${routeBaseName}`, suffix);
322
+ if (isDynamic) return withMetadataSuffix(config.urlPath, suffix);
323
+ if (metaType === "manifest") return withMetadataSuffix(`/${routeBaseName}${ext}`, suffix);
324
+ if (metaType === "icon" || metaType === "apple-icon" || metaType === "opengraph-image" || metaType === "twitter-image") return withMetadataSuffix(`/${routeBaseName}${ext}`, suffix);
325
+ return withMetadataSuffix(config.urlPath, suffix);
326
+ }
327
+ function matchMetadataFileBaseName(metaType, baseName) {
328
+ if (baseName === metaType) return baseName;
329
+ if (metaType === "icon" || metaType === "apple-icon" || metaType === "opengraph-image" || metaType === "twitter-image") {
330
+ const suffix = baseName.slice(metaType.length);
331
+ if (/^\d$/.test(suffix)) return baseName;
332
+ }
333
+ return null;
334
+ }
229
335
  /**
230
336
  * Scan an app directory for metadata files.
231
337
  */
@@ -248,19 +354,25 @@ function scanMetadataFiles(appDir) {
248
354
  const baseName = fileName.replace(/\.[^.]+$/, "");
249
355
  const ext = fileName.slice(baseName.length);
250
356
  for (const [metaType, config] of Object.entries(METADATA_FILE_MAP)) {
251
- if (baseName !== metaType) continue;
357
+ const routeBaseName = matchMetadataFileBaseName(metaType, baseName);
358
+ if (!routeBaseName) continue;
252
359
  if (!config.nestable && urlPrefix !== "") continue;
253
360
  const isStatic = config.staticExtensions.includes(ext);
254
361
  const isDynamic = config.dynamicExtensions.includes(ext);
255
362
  if (!isStatic && !isDynamic) continue;
256
- const suffix = metadataRouteSuffix(parentSegments, metaType);
257
- const urlPath = withMetadataSuffix(config.urlPath, suffix);
363
+ const appDirPath = parentSegments.length > 0 ? `/${parentSegments.join("/")}` : "";
364
+ const urlPath = getMetadataServedUrl(metaType, config, ext, isDynamic, metadataRouteSuffix(parentSegments, metaType), routeBaseName);
365
+ const servedUrl = isStatic ? fillStaticMetadataSegment(appDirPath, `${routeBaseName}${ext}`) : urlPrefix === "" ? urlPath : `${urlPrefix}${urlPath}`;
366
+ const altFilePath = isStatic && (metaType === "opengraph-image" || metaType === "twitter-image") ? resolveStaticMetadataAltFilePath(dir, baseName) : void 0;
258
367
  routes.push({
259
368
  type: metaType,
260
369
  isDynamic,
261
370
  filePath: path.join(dir, fileName),
262
- servedUrl: urlPrefix === "" ? urlPath : `${urlPrefix}${urlPath}`,
263
- contentType: isStatic ? getStaticContentType(ext, config.contentType) : config.contentType
371
+ routePrefix: urlPrefix,
372
+ routeSegments: parentSegments,
373
+ servedUrl,
374
+ contentType: isStatic && metaType === "manifest" ? config.contentType : isStatic ? getStaticContentType(ext, config.contentType) : config.contentType,
375
+ altFilePath
264
376
  });
265
377
  }
266
378
  }
@@ -274,6 +386,10 @@ function scanMetadataFiles(appDir) {
274
386
  }
275
387
  return Array.from(byUrl.values());
276
388
  }
389
+ function resolveStaticMetadataAltFilePath(dir, baseName) {
390
+ const altPath = path.join(dir, `${baseName}.alt.txt`);
391
+ return fs.existsSync(altPath) ? altPath : void 0;
392
+ }
277
393
  function getStaticContentType(ext, fallback) {
278
394
  return {
279
395
  ".xml": "application/xml",
@@ -289,6 +405,6 @@ function getStaticContentType(ext, fallback) {
289
405
  }[ext] ?? fallback;
290
406
  }
291
407
  //#endregion
292
- export { METADATA_FILE_MAP, manifestToJson, robotsToText, scanMetadataFiles, sitemapToXml };
408
+ export { METADATA_FILE_MAP, fillStaticMetadataSegment, getMetadataImageRouteKind, getMetadataRouteKind, isValidMetadataImageId, manifestToJson, matchMetadataFileBaseName, matchMetadataRoutePattern, robotsToText, scanMetadataFiles, sitemapToXml };
293
409
 
294
410
  //# sourceMappingURL=metadata-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata-routes.js","names":[],"sources":["../../src/server/metadata-routes.ts"],"sourcesContent":["/**\n * File-based metadata route handling.\n *\n * Next.js supports special files in the app/ directory that auto-generate\n * metadata routes:\n * - sitemap.ts/.xml → /sitemap.xml (application/xml)\n * - robots.ts/.txt → /robots.txt (text/plain)\n * - manifest.ts/.json/.webmanifest → /manifest.webmanifest (application/manifest+json)\n * - icon.tsx/.png → /icon (image/*)\n * - opengraph-image.tsx/.png → /opengraph-image (image/*)\n * - twitter-image.tsx/.png → /twitter-image (image/*)\n * - apple-icon.tsx/.png → /apple-icon (image/*)\n * - favicon.ico → /favicon.ico (image/x-icon)\n *\n * Dynamic versions (ts/tsx/js) export a default function that returns the data.\n * Static versions (xml/txt/json/png/etc.) are served as-is.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n// -------------------------------------------------------------------\n// Types matching Next.js MetadataRoute\n// -------------------------------------------------------------------\n\nexport type SitemapEntry = {\n url: string;\n lastModified?: string | Date;\n changeFrequency?: \"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\";\n priority?: number;\n alternates?: {\n languages?: Record<string, string>;\n };\n images?: string[];\n videos?: Array<{\n title: string;\n thumbnail_loc: string;\n description: string;\n content_loc?: string;\n player_loc?: string;\n duration?: number;\n expiration_date?: string | Date;\n rating?: number;\n view_count?: number;\n publication_date?: string | Date;\n family_friendly?: \"yes\" | \"no\";\n restriction?: { relationship: \"allow\" | \"deny\"; content: string };\n platform?: { relationship: \"allow\" | \"deny\"; content: string };\n requires_subscription?: \"yes\" | \"no\";\n uploader?: {\n info?: string;\n content?: string;\n };\n live?: \"yes\" | \"no\";\n tag?: string;\n }>;\n};\n\nexport type RobotsRule = {\n userAgent?: string | string[];\n allow?: string | string[];\n disallow?: string | string[];\n crawlDelay?: number;\n};\n\nexport type RobotsConfig = {\n rules: RobotsRule | RobotsRule[];\n sitemap?: string | string[];\n host?: string;\n};\n\nexport type ManifestConfig = {\n name?: string;\n short_name?: string;\n description?: string;\n start_url?: string;\n display?: \"fullscreen\" | \"standalone\" | \"minimal-ui\" | \"browser\";\n background_color?: string;\n theme_color?: string;\n icons?: Array<{\n src: string;\n sizes?: string;\n type?: string;\n purpose?: string;\n }>;\n [key: string]: unknown;\n};\n\n// -------------------------------------------------------------------\n// Known metadata file patterns\n// -------------------------------------------------------------------\n\n/** Map of metadata file base names to their URL path and content type. */\nexport const METADATA_FILE_MAP: Record<\n string,\n {\n /** URL path this file is served at */\n urlPath: string;\n /** Content type for the response */\n contentType: string;\n /** Whether this can be dynamic (.ts/.tsx/.js) */\n canBeDynamic: boolean;\n /** File extensions for static variants */\n staticExtensions: string[];\n /** File extensions for dynamic variants */\n dynamicExtensions: string[];\n /** Whether this can be nested in sub-segments */\n nestable: boolean;\n }\n> = {\n sitemap: {\n urlPath: \"/sitemap.xml\",\n contentType: \"application/xml\",\n canBeDynamic: true,\n staticExtensions: [\".xml\"],\n dynamicExtensions: [\".ts\", \".js\"],\n nestable: true,\n },\n robots: {\n urlPath: \"/robots.txt\",\n contentType: \"text/plain\",\n canBeDynamic: true,\n staticExtensions: [\".txt\"],\n dynamicExtensions: [\".ts\", \".js\"],\n nestable: false,\n },\n manifest: {\n urlPath: \"/manifest.webmanifest\",\n contentType: \"application/manifest+json\",\n canBeDynamic: true,\n staticExtensions: [\".json\", \".webmanifest\"],\n dynamicExtensions: [\".ts\", \".js\"],\n nestable: false,\n },\n favicon: {\n urlPath: \"/favicon.ico\",\n contentType: \"image/x-icon\",\n canBeDynamic: false,\n staticExtensions: [\".ico\"],\n dynamicExtensions: [],\n nestable: false,\n },\n icon: {\n urlPath: \"/icon\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".ico\", \".jpg\", \".jpeg\", \".png\", \".svg\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n \"opengraph-image\": {\n urlPath: \"/opengraph-image\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".jpg\", \".jpeg\", \".png\", \".gif\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n \"twitter-image\": {\n urlPath: \"/twitter-image\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".jpg\", \".jpeg\", \".png\", \".gif\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n \"apple-icon\": {\n urlPath: \"/apple-icon\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".jpg\", \".jpeg\", \".png\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n};\n\n// -------------------------------------------------------------------\n// Serializers\n// -------------------------------------------------------------------\n\n/** Escape the five XML special characters in text content and attribute values. */\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\n/**\n * Convert a sitemap array to XML string.\n */\nexport function sitemapToXml(entries: SitemapEntry[]): string {\n const hasAlternates = entries.some((entry) => Object.keys(entry.alternates ?? {}).length > 0);\n const hasImages = entries.some((entry) => Boolean(entry.images?.length));\n const hasVideos = entries.some((entry) => Boolean(entry.videos?.length));\n let content = \"\";\n\n content += '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n';\n content += '<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"';\n if (hasImages) {\n content += ' xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"';\n }\n if (hasVideos) {\n content += ' xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"';\n }\n if (hasAlternates) {\n content += ' xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n';\n } else {\n content += \">\\n\";\n }\n\n for (const entry of entries) {\n content += \"<url>\\n\";\n content += `<loc>${escapeXml(entry.url)}</loc>\\n`;\n\n const languages = entry.alternates?.languages;\n if (languages && Object.keys(languages).length) {\n for (const language in languages) {\n content += `<xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(language)}\" href=\"${escapeXml(languages[language])}\" />\\n`;\n }\n }\n\n if (entry.images?.length) {\n for (const image of entry.images) {\n content += `<image:image>\\n<image:loc>${escapeXml(image)}</image:loc>\\n</image:image>\\n`;\n }\n }\n\n if (entry.videos?.length) {\n for (const video of entry.videos) {\n const videoFields = [\n \"<video:video>\",\n `<video:title>${escapeXml(String(video.title))}</video:title>`,\n `<video:thumbnail_loc>${escapeXml(String(video.thumbnail_loc))}</video:thumbnail_loc>`,\n `<video:description>${escapeXml(String(video.description))}</video:description>`,\n video.content_loc &&\n `<video:content_loc>${escapeXml(String(video.content_loc))}</video:content_loc>`,\n video.player_loc &&\n `<video:player_loc>${escapeXml(String(video.player_loc))}</video:player_loc>`,\n video.duration && `<video:duration>${video.duration}</video:duration>`,\n video.view_count && `<video:view_count>${video.view_count}</video:view_count>`,\n video.tag && `<video:tag>${escapeXml(String(video.tag))}</video:tag>`,\n video.rating && `<video:rating>${video.rating}</video:rating>`,\n video.expiration_date &&\n `<video:expiration_date>${escapeXml(String(video.expiration_date))}</video:expiration_date>`,\n video.publication_date &&\n `<video:publication_date>${escapeXml(String(video.publication_date))}</video:publication_date>`,\n video.family_friendly &&\n `<video:family_friendly>${video.family_friendly}</video:family_friendly>`,\n video.requires_subscription &&\n `<video:requires_subscription>${video.requires_subscription}</video:requires_subscription>`,\n video.live && `<video:live>${video.live}</video:live>`,\n video.restriction &&\n `<video:restriction relationship=\"${escapeXml(String(video.restriction.relationship))}\">${escapeXml(String(video.restriction.content))}</video:restriction>`,\n video.platform &&\n `<video:platform relationship=\"${escapeXml(String(video.platform.relationship))}\">${escapeXml(String(video.platform.content))}</video:platform>`,\n video.uploader &&\n `<video:uploader${video.uploader.info ? ` info=\"${escapeXml(String(video.uploader.info))}\"` : \"\"}>${escapeXml(String(video.uploader.content))}</video:uploader>`,\n \"</video:video>\\n\",\n ].filter(Boolean);\n content += videoFields.join(\"\\n\");\n }\n }\n\n if (entry.lastModified) {\n content += `<lastmod>${serializeDate(entry.lastModified)}</lastmod>\\n`;\n }\n if (entry.changeFrequency) {\n content += `<changefreq>${entry.changeFrequency}</changefreq>\\n`;\n }\n if (typeof entry.priority === \"number\") {\n content += `<priority>${entry.priority}</priority>\\n`;\n }\n content += \"</url>\\n\";\n }\n\n content += \"</urlset>\\n\";\n return content;\n}\n\n/**\n * Convert a robots config to text format.\n */\nexport function robotsToText(config: RobotsConfig): string {\n const lines: string[] = [];\n const rules = Array.isArray(config.rules) ? config.rules : [config.rules];\n\n for (const rule of rules) {\n const agents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent ?? \"*\"];\n\n for (const agent of agents) {\n lines.push(`User-Agent: ${agent}`);\n }\n\n if (rule.allow) {\n const allows = Array.isArray(rule.allow) ? rule.allow : [rule.allow];\n for (const allow of allows) {\n lines.push(`Allow: ${allow}`);\n }\n }\n\n if (rule.disallow) {\n const disallows = Array.isArray(rule.disallow) ? rule.disallow : [rule.disallow];\n for (const disallow of disallows) {\n lines.push(`Disallow: ${disallow}`);\n }\n }\n\n if (rule.crawlDelay !== undefined) {\n lines.push(`Crawl-delay: ${rule.crawlDelay}`);\n }\n\n lines.push(\"\");\n }\n\n if (config.sitemap) {\n const sitemaps = Array.isArray(config.sitemap) ? config.sitemap : [config.sitemap];\n for (const sitemap of sitemaps) {\n lines.push(`Sitemap: ${sitemap}`);\n }\n }\n\n if (config.host) {\n lines.push(`Host: ${config.host}`);\n }\n\n return lines.join(\"\\n\").trim() + \"\\n\";\n}\n\n/**\n * Convert a manifest config to JSON string.\n */\nexport function manifestToJson(config: ManifestConfig): string {\n return JSON.stringify(config, null, 2);\n}\n\nfunction serializeDate(value: string | Date): string {\n return value instanceof Date ? value.toISOString() : value;\n}\n\n// -------------------------------------------------------------------\n// Metadata route discovery\n// -------------------------------------------------------------------\n\nexport type MetadataFileRoute = {\n /** Type of metadata file */\n type: string;\n /** Whether this is a dynamic (code-generated) route */\n isDynamic: boolean;\n /** Absolute file path */\n filePath: string;\n /** URL path this file is served at */\n servedUrl: string;\n /** Content type for the response */\n contentType: string;\n};\n\nfunction metadataRouteSuffix(parentSegments: string[], metaType: string): string {\n if (metaType === \"sitemap\" || metaType === \"robots\" || metaType === \"manifest\") {\n // Sitemap is exempt per Next.js. Robots and manifest are also safe to\n // exempt because they are root-only in vinext, so invisible parents never apply.\n return \"\";\n }\n\n const hasInvisibleParent = parentSegments.some(\n (segment) =>\n (segment.startsWith(\"(\") && segment.endsWith(\")\")) ||\n (segment.startsWith(\"@\") && segment !== \"@children\"),\n );\n if (!hasInvisibleParent) return \"\";\n\n const parentPath = `/${parentSegments.join(\"/\")}`;\n let hash = 5381;\n for (let i = 0; i < parentPath.length; i++) {\n hash = ((hash << 5) + hash + parentPath.charCodeAt(i)) & 0xffffffff;\n }\n return (hash >>> 0).toString(36).slice(0, 6);\n}\n\nfunction withMetadataSuffix(urlPath: string, suffix: string): string {\n if (!suffix) return urlPath;\n const parsed = path.posix.parse(urlPath);\n return path.posix.join(parsed.dir || \"/\", `${parsed.name}-${suffix}${parsed.ext}`);\n}\n\n/**\n * Scan an app directory for metadata files.\n */\nexport function scanMetadataFiles(appDir: string): MetadataFileRoute[] {\n const routes: MetadataFileRoute[] = [];\n\n // Scan the app directory recursively\n function scan(dir: string, urlPrefix: string, parentSegments: string[]): void {\n if (!fs.existsSync(dir)) return;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const dirName = entry.name;\n if (dirName.startsWith(\"_\")) continue;\n\n const isRouteGroup = dirName.startsWith(\"(\") && dirName.endsWith(\")\");\n const isParallelRoute = dirName.startsWith(\"@\");\n const nextUrlPrefix =\n isRouteGroup || isParallelRoute ? urlPrefix : `${urlPrefix}/${dirName}`;\n scan(path.join(dir, dirName), nextUrlPrefix, [...parentSegments, dirName]);\n continue;\n }\n\n // Check each metadata file pattern\n const fileName = entry.name;\n const baseName = fileName.replace(/\\.[^.]+$/, \"\");\n const ext = fileName.slice(baseName.length);\n\n for (const [metaType, config] of Object.entries(METADATA_FILE_MAP)) {\n // Check if the base name matches\n if (baseName !== metaType) continue;\n\n // Check nestability — non-nestable types only at root\n if (!config.nestable && urlPrefix !== \"\") continue;\n\n // Check if this is a static or dynamic variant\n const isStatic = config.staticExtensions.includes(ext);\n const isDynamic = config.dynamicExtensions.includes(ext);\n\n if (!isStatic && !isDynamic) continue;\n const suffix = metadataRouteSuffix(parentSegments, metaType);\n const urlPath = withMetadataSuffix(config.urlPath, suffix);\n\n routes.push({\n type: metaType,\n isDynamic,\n filePath: path.join(dir, fileName),\n servedUrl: urlPrefix === \"\" ? urlPath : `${urlPrefix}${urlPath}`,\n contentType: isStatic\n ? getStaticContentType(ext, config.contentType)\n : config.contentType,\n });\n }\n }\n }\n\n scan(appDir, \"\", []);\n\n // Deduplicate: if both dynamic and static variants exist at the same URL,\n // keep only the dynamic one (matches Next.js behavior).\n const byUrl = new Map<string, MetadataFileRoute>();\n for (const route of routes) {\n const existing = byUrl.get(route.servedUrl);\n if (!existing) {\n byUrl.set(route.servedUrl, route);\n } else if (route.isDynamic && !existing.isDynamic) {\n // Dynamic takes priority over static\n byUrl.set(route.servedUrl, route);\n }\n // If both are static or both dynamic, keep the first one found\n }\n return Array.from(byUrl.values());\n}\n\nfunction getStaticContentType(ext: string, fallback: string): string {\n const map: Record<string, string> = {\n \".xml\": \"application/xml\",\n \".txt\": \"text/plain\",\n \".json\": \"application/json\",\n \".webmanifest\": \"application/manifest+json\",\n \".ico\": \"image/x-icon\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".svg\": \"image/svg+xml\",\n };\n return map[ext] ?? fallback;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"metadata-routes.js","names":[],"sources":["../../src/server/metadata-routes.ts"],"sourcesContent":["/**\n * File-based metadata route handling.\n *\n * Next.js supports special files in the app/ directory that auto-generate\n * metadata routes:\n * - sitemap.ts/.xml → /sitemap.xml (application/xml)\n * - robots.ts/.txt → /robots.txt (text/plain)\n * - manifest.ts/.json/.webmanifest → /manifest.webmanifest (application/manifest+json)\n * - icon.tsx/.png → /icon (image/*)\n * - opengraph-image.tsx/.png → /opengraph-image (image/*)\n * - twitter-image.tsx/.png → /twitter-image (image/*)\n * - apple-icon.tsx/.png → /apple-icon (image/*)\n * - favicon.ico → /favicon.ico (image/x-icon)\n *\n * Dynamic versions (ts/tsx/js) export a default function that returns the data.\n * Static versions (xml/txt/json/png/etc.) are served as-is.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { matchRoutePattern } from \"../routing/route-pattern.js\";\n\n// -------------------------------------------------------------------\n// Types matching Next.js MetadataRoute\n// -------------------------------------------------------------------\n\nexport type SitemapEntry = {\n url: string;\n lastModified?: string | Date;\n changeFrequency?: \"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\";\n priority?: number;\n alternates?: {\n languages?: Record<string, string>;\n };\n images?: string[];\n videos?: Array<{\n title: string;\n thumbnail_loc: string;\n description: string;\n content_loc?: string;\n player_loc?: string;\n duration?: number;\n expiration_date?: string | Date;\n rating?: number;\n view_count?: number;\n publication_date?: string | Date;\n family_friendly?: \"yes\" | \"no\";\n restriction?: { relationship: \"allow\" | \"deny\"; content: string };\n platform?: { relationship: \"allow\" | \"deny\"; content: string };\n requires_subscription?: \"yes\" | \"no\";\n uploader?: {\n info?: string;\n content?: string;\n };\n live?: \"yes\" | \"no\";\n tag?: string;\n }>;\n};\n\nexport type RobotsRule = {\n userAgent?: string | string[];\n allow?: string | string[];\n disallow?: string | string[];\n crawlDelay?: number;\n};\n\nexport type RobotsConfig = {\n rules: RobotsRule | RobotsRule[];\n sitemap?: string | string[];\n host?: string;\n};\n\nexport type ManifestConfig = {\n name?: string;\n short_name?: string;\n description?: string;\n start_url?: string;\n display?: \"fullscreen\" | \"standalone\" | \"minimal-ui\" | \"browser\";\n background_color?: string;\n theme_color?: string;\n icons?: Array<{\n src: string;\n sizes?: string;\n type?: string;\n purpose?: string;\n }>;\n [key: string]: unknown;\n};\n\n// -------------------------------------------------------------------\n// Known metadata file patterns\n// -------------------------------------------------------------------\n\n/** Map of metadata file base names to their URL path and content type. */\nexport const METADATA_FILE_MAP: Record<\n string,\n {\n /** URL path this file is served at */\n urlPath: string;\n /** Content type for the response */\n contentType: string;\n /** Whether this can be dynamic (.ts/.tsx/.js) */\n canBeDynamic: boolean;\n /** File extensions for static variants */\n staticExtensions: string[];\n /** File extensions for dynamic variants */\n dynamicExtensions: string[];\n /** Whether this can be nested in sub-segments */\n nestable: boolean;\n }\n> = {\n sitemap: {\n urlPath: \"/sitemap.xml\",\n contentType: \"application/xml\",\n canBeDynamic: true,\n staticExtensions: [\".xml\"],\n dynamicExtensions: [\".ts\", \".js\"],\n nestable: true,\n },\n robots: {\n urlPath: \"/robots.txt\",\n contentType: \"text/plain\",\n canBeDynamic: true,\n staticExtensions: [\".txt\"],\n dynamicExtensions: [\".ts\", \".js\"],\n nestable: false,\n },\n manifest: {\n urlPath: \"/manifest.webmanifest\",\n contentType: \"application/manifest+json\",\n canBeDynamic: true,\n staticExtensions: [\".json\", \".webmanifest\"],\n dynamicExtensions: [\".ts\", \".js\"],\n nestable: false,\n },\n favicon: {\n urlPath: \"/favicon.ico\",\n contentType: \"image/x-icon\",\n canBeDynamic: false,\n staticExtensions: [\".ico\"],\n dynamicExtensions: [],\n nestable: false,\n },\n icon: {\n urlPath: \"/icon\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".ico\", \".jpg\", \".jpeg\", \".png\", \".svg\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n \"opengraph-image\": {\n urlPath: \"/opengraph-image\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".jpg\", \".jpeg\", \".png\", \".gif\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n \"twitter-image\": {\n urlPath: \"/twitter-image\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".jpg\", \".jpeg\", \".png\", \".gif\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n \"apple-icon\": {\n urlPath: \"/apple-icon\",\n contentType: \"image/png\",\n canBeDynamic: true,\n staticExtensions: [\".jpg\", \".jpeg\", \".png\"],\n dynamicExtensions: [\".ts\", \".tsx\", \".js\"],\n nestable: true,\n },\n};\n\n// -------------------------------------------------------------------\n// Serializers\n// -------------------------------------------------------------------\n\n/** Escape the five XML special characters in text content and attribute values. */\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\n/**\n * Convert a sitemap array to XML string.\n */\nexport function sitemapToXml(entries: SitemapEntry[]): string {\n const hasAlternates = entries.some((entry) => Object.keys(entry.alternates ?? {}).length > 0);\n const hasImages = entries.some((entry) => Boolean(entry.images?.length));\n const hasVideos = entries.some((entry) => Boolean(entry.videos?.length));\n let content = \"\";\n\n content += '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n';\n content += '<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"';\n if (hasImages) {\n content += ' xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"';\n }\n if (hasVideos) {\n content += ' xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"';\n }\n if (hasAlternates) {\n content += ' xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n';\n } else {\n content += \">\\n\";\n }\n\n for (const entry of entries) {\n content += \"<url>\\n\";\n content += `<loc>${escapeXml(entry.url)}</loc>\\n`;\n\n const languages = entry.alternates?.languages;\n if (languages && Object.keys(languages).length) {\n for (const language in languages) {\n content += `<xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(language)}\" href=\"${escapeXml(languages[language])}\" />\\n`;\n }\n }\n\n if (entry.images?.length) {\n for (const image of entry.images) {\n content += `<image:image>\\n<image:loc>${escapeXml(image)}</image:loc>\\n</image:image>\\n`;\n }\n }\n\n if (entry.videos?.length) {\n for (const video of entry.videos) {\n const videoFields = [\n \"<video:video>\",\n `<video:title>${escapeXml(String(video.title))}</video:title>`,\n `<video:thumbnail_loc>${escapeXml(String(video.thumbnail_loc))}</video:thumbnail_loc>`,\n `<video:description>${escapeXml(String(video.description))}</video:description>`,\n video.content_loc &&\n `<video:content_loc>${escapeXml(String(video.content_loc))}</video:content_loc>`,\n video.player_loc &&\n `<video:player_loc>${escapeXml(String(video.player_loc))}</video:player_loc>`,\n video.duration && `<video:duration>${video.duration}</video:duration>`,\n video.view_count && `<video:view_count>${video.view_count}</video:view_count>`,\n video.tag && `<video:tag>${escapeXml(String(video.tag))}</video:tag>`,\n video.rating && `<video:rating>${video.rating}</video:rating>`,\n video.expiration_date &&\n `<video:expiration_date>${escapeXml(String(video.expiration_date))}</video:expiration_date>`,\n video.publication_date &&\n `<video:publication_date>${escapeXml(String(video.publication_date))}</video:publication_date>`,\n video.family_friendly &&\n `<video:family_friendly>${video.family_friendly}</video:family_friendly>`,\n video.requires_subscription &&\n `<video:requires_subscription>${video.requires_subscription}</video:requires_subscription>`,\n video.live && `<video:live>${video.live}</video:live>`,\n video.restriction &&\n `<video:restriction relationship=\"${escapeXml(String(video.restriction.relationship))}\">${escapeXml(String(video.restriction.content))}</video:restriction>`,\n video.platform &&\n `<video:platform relationship=\"${escapeXml(String(video.platform.relationship))}\">${escapeXml(String(video.platform.content))}</video:platform>`,\n video.uploader &&\n `<video:uploader${video.uploader.info ? ` info=\"${escapeXml(String(video.uploader.info))}\"` : \"\"}>${escapeXml(String(video.uploader.content))}</video:uploader>`,\n \"</video:video>\\n\",\n ].filter(Boolean);\n content += videoFields.join(\"\\n\");\n }\n }\n\n if (entry.lastModified) {\n content += `<lastmod>${serializeDate(entry.lastModified)}</lastmod>\\n`;\n }\n if (entry.changeFrequency) {\n content += `<changefreq>${entry.changeFrequency}</changefreq>\\n`;\n }\n if (typeof entry.priority === \"number\") {\n content += `<priority>${entry.priority}</priority>\\n`;\n }\n content += \"</url>\\n\";\n }\n\n content += \"</urlset>\\n\";\n return content;\n}\n\n/**\n * Convert a robots config to text format.\n */\nexport function robotsToText(config: RobotsConfig): string {\n const lines: string[] = [];\n const rules = Array.isArray(config.rules) ? config.rules : [config.rules];\n\n for (const rule of rules) {\n const agents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent ?? \"*\"];\n\n for (const agent of agents) {\n lines.push(`User-Agent: ${agent}`);\n }\n\n if (rule.allow) {\n const allows = Array.isArray(rule.allow) ? rule.allow : [rule.allow];\n for (const allow of allows) {\n lines.push(`Allow: ${allow}`);\n }\n }\n\n if (rule.disallow) {\n const disallows = Array.isArray(rule.disallow) ? rule.disallow : [rule.disallow];\n for (const disallow of disallows) {\n lines.push(`Disallow: ${disallow}`);\n }\n }\n\n if (rule.crawlDelay !== undefined) {\n lines.push(`Crawl-delay: ${rule.crawlDelay}`);\n }\n\n lines.push(\"\");\n }\n\n if (config.sitemap) {\n const sitemaps = Array.isArray(config.sitemap) ? config.sitemap : [config.sitemap];\n for (const sitemap of sitemaps) {\n lines.push(`Sitemap: ${sitemap}`);\n }\n }\n\n if (config.host) {\n lines.push(`Host: ${config.host}`);\n }\n\n return lines.join(\"\\n\").trim() + \"\\n\";\n}\n\n/**\n * Convert a manifest config to JSON string.\n */\nexport function manifestToJson(config: ManifestConfig): string {\n return JSON.stringify(config, null, 2);\n}\n\nfunction serializeDate(value: string | Date): string {\n return value instanceof Date ? value.toISOString() : value;\n}\n\n// -------------------------------------------------------------------\n// Static metadata URL resolution\n//\n// Ported from Next.js: packages/next/src/lib/metadata/get-metadata-route.ts\n// https://github.com/vercel/next.js/blob/7873aea/packages/next/src/lib/metadata/get-metadata-route.ts\n//\n// Static metadata files (like favicon.ico, icon.png) under dynamic parents\n// get a fixed URL with \"-\" placeholders instead of literal \"[param]\" segments.\n// Route groups and parallel route parents trigger a unique hash suffix to\n// avoid collisions.\n// -------------------------------------------------------------------\n\n/**\n * Regular expression pattern used to match route parameters.\n * Matches both single parameters and parameter groups.\n * Examples:\n * - `[[...slug]]` matches parameter group with key 'slug', repeat: true, optional: true\n * - `[...slug]` matches parameter group with key 'slug', repeat: true, optional: false\n * - `[[foo]]` matches parameter with key 'foo', repeat: false, optional: true\n * - `[bar]` matches parameter with key 'bar', repeat: false, optional: false\n */\nconst PARAMETER_PATTERN = /^([^[]*)\\[((?:\\[[^\\]]*\\])|[^\\]]+)\\](.*)$/;\n\nfunction isGroupSegment(segment: string): boolean {\n return segment.startsWith(\"(\") && segment.endsWith(\")\");\n}\n\nfunction isParallelRouteSegment(segment: string): boolean {\n return segment.startsWith(\"@\") && segment !== \"@children\";\n}\n\nfunction normalizeStaticMetadataRouteSegment(segment: string): string {\n let normalizedSegment = segment;\n let match = normalizedSegment.match(PARAMETER_PATTERN);\n while (match) {\n normalizedSegment = `${match[1]}-${match[3]}`;\n match = normalizedSegment.match(PARAMETER_PATTERN);\n }\n return normalizedSegment;\n}\n\nfunction getStaticMetadataRoute(appDirPath: string): string {\n const segments = appDirPath.split(\"/\").filter(Boolean);\n const normalizedSegments: string[] = [];\n for (const seg of segments) {\n // Strip route groups and all parallel route slots (including @children)\n // from the URL path. The @children slot is the default parallel route\n // and must also be invisible in the URL, matching Next.js behavior.\n if (isGroupSegment(seg) || seg.startsWith(\"@\")) continue;\n normalizedSegments.push(normalizeStaticMetadataRouteSegment(seg));\n }\n return normalizedSegments.length > 0 ? `/${normalizedSegments.join(\"/\")}` : \"\";\n}\n\nfunction hashMetadataRouteParentPath(parentPathname: string): string {\n let hash = 5381;\n for (let i = 0; i < parentPathname.length; i++) {\n hash = ((hash << 5) + hash + parentPathname.charCodeAt(i)) & 0xffffffff;\n }\n return (hash >>> 0).toString(36).slice(0, 6);\n}\n\nfunction getMetadataRouteSuffix(page: string): string {\n const lastSlash = page.lastIndexOf(\"/\");\n const parentPathname = lastSlash > 0 ? page.slice(0, lastSlash) : \"\";\n if (page.endsWith(\"/sitemap\") || page.endsWith(\"/sitemap.xml\")) return \"\";\n const segments = parentPathname.split(\"/\");\n const hasInvisibleParent = segments.some(\n (seg) => isGroupSegment(seg) || isParallelRouteSegment(seg),\n );\n if (!hasInvisibleParent) return \"\";\n return hashMetadataRouteParentPath(parentPathname);\n}\n\nfunction computeMetadataRouteSuffix(\n appDirPath: string,\n leafName: string,\n): { route: string; suffix: string } {\n const route = getStaticMetadataRoute(appDirPath);\n const pagePath =\n appDirPath === \"\" || appDirPath === \"/\" ? `/${leafName}` : `${appDirPath}/${leafName}`;\n const suffix = getMetadataRouteSuffix(pagePath);\n return { route, suffix };\n}\n\nfunction getMetadataRouteFilename(appDirPath: string, lastSegment: string): string {\n const ext = path.posix.extname(lastSegment);\n const name = lastSegment.slice(0, -ext.length || undefined);\n const { suffix } = computeMetadataRouteSuffix(appDirPath, name);\n const routeSuffix = suffix ? `-${suffix}` : \"\";\n return `${name}${routeSuffix}${ext}`;\n}\n\n/**\n * Compute the static URL for a metadata file given its app directory\n * parent path and filename.\n *\n * Example:\n * fillStaticMetadataSegment(\"/\", \"favicon.ico\") -> \"/favicon.ico\"\n * fillStaticMetadataSegment(\"/blog/[slug]\", \"favicon.ico\") -> \"/blog/-/favicon.ico\"\n * fillStaticMetadataSegment(\"/(group)/group\", \"icon.png\") -> \"/group/icon-131tc6.png\"\n */\nexport function fillStaticMetadataSegment(appDirPath: string, lastSegment: string): string {\n const route = getStaticMetadataRoute(appDirPath);\n const filename = getMetadataRouteFilename(appDirPath, lastSegment);\n return route === \"\" ? `/${filename}` : `${route}/${filename}`;\n}\n\n// -------------------------------------------------------------------\n// Metadata route discovery\n// -------------------------------------------------------------------\n\nexport type MetadataFileRoute = {\n /** Type of metadata file */\n type: string;\n /** Whether this is a dynamic (code-generated) route */\n isDynamic: boolean;\n /** Imported dynamic module for code-generated metadata routes. */\n module?: Record<string, unknown>;\n /** Absolute file path */\n filePath: string;\n /** Route prefix where this metadata applies, preserving dynamic segment names. */\n routePrefix: string;\n /** Raw app tree segments where this metadata file is colocated. */\n routeSegments?: string[];\n /** Pattern parts for matching dynamic metadata routes at request time. */\n patternParts?: string[];\n /** URL path this file is served at */\n servedUrl: string;\n /** Content type for the response */\n contentType: string;\n /** Optional metadata used to inject file-based routes into <head>. */\n headData?: MetadataRouteHeadData;\n /** Optional content hash for cache-busting metadata links. */\n contentHash?: string;\n /** Sibling .alt.txt file for static social image metadata routes. */\n altFilePath?: string;\n};\n\nexport type MetadataRouteHeadData =\n | {\n kind: \"favicon\" | \"icon\" | \"apple\";\n href: string;\n type?: string;\n sizes?: string;\n }\n | {\n kind: \"openGraph\" | \"twitter\";\n href: string;\n type?: string;\n width?: number;\n height?: number;\n alt?: string;\n }\n | {\n kind: \"manifest\";\n href: string;\n };\n\nexport function getMetadataRouteKind(\n route: Pick<MetadataFileRoute, \"type\">,\n): MetadataRouteHeadData[\"kind\"] | null {\n if (route.type === \"favicon\") return \"favicon\";\n if (route.type === \"icon\") return \"icon\";\n if (route.type === \"apple-icon\") return \"apple\";\n if (route.type === \"opengraph-image\") return \"openGraph\";\n if (route.type === \"twitter-image\") return \"twitter\";\n if (route.type === \"manifest\") return \"manifest\";\n return null;\n}\n\nexport function getMetadataImageRouteKind(\n route: Pick<MetadataFileRoute, \"type\">,\n): Extract<MetadataRouteHeadData[\"kind\"], \"icon\" | \"apple\" | \"openGraph\" | \"twitter\"> | null {\n const kind = getMetadataRouteKind(route);\n if (kind === \"icon\" || kind === \"apple\" || kind === \"openGraph\" || kind === \"twitter\") {\n return kind;\n }\n return null;\n}\n\nconst metadataImageIdPattern = /^[a-zA-Z0-9-_.]+$/;\n\nexport function isValidMetadataImageId(id: string): boolean {\n return metadataImageIdPattern.test(id);\n}\n\nexport function matchMetadataRoutePattern(\n urlParts: string[],\n patternParts: string[],\n): Record<string, string | string[]> | null {\n return matchRoutePattern(urlParts, patternParts);\n}\n\nfunction metadataRouteSuffix(parentSegments: string[], metaType: string): string {\n if (metaType === \"sitemap\") {\n // Sitemap is exempt per Next.js (robots/manifest are root-only, so\n // invisible parents never apply — but we keep the exemption list\n // matching getMetadataRouteSuffix for defensive consistency).\n return \"\";\n }\n\n const hasInvisibleParent = parentSegments.some(\n (segment) =>\n (segment.startsWith(\"(\") && segment.endsWith(\")\")) ||\n (segment.startsWith(\"@\") && segment !== \"@children\"),\n );\n if (!hasInvisibleParent) return \"\";\n\n return hashMetadataRouteParentPath(`/${parentSegments.join(\"/\")}`);\n}\n\nfunction withMetadataSuffix(urlPath: string, suffix: string): string {\n if (!suffix) return urlPath;\n const parsed = path.posix.parse(urlPath);\n return path.posix.join(parsed.dir || \"/\", `${parsed.name}-${suffix}${parsed.ext}`);\n}\n\nfunction getMetadataServedUrl(\n metaType: string,\n config: { urlPath: string },\n ext: string,\n isDynamic: boolean,\n suffix: string,\n routeBaseName: string,\n): string {\n if (\n isDynamic &&\n (metaType === \"icon\" ||\n metaType === \"apple-icon\" ||\n metaType === \"opengraph-image\" ||\n metaType === \"twitter-image\")\n ) {\n return withMetadataSuffix(`/${routeBaseName}`, suffix);\n }\n\n if (isDynamic) {\n return withMetadataSuffix(config.urlPath, suffix);\n }\n\n if (metaType === \"manifest\") {\n return withMetadataSuffix(`/${routeBaseName}${ext}`, suffix);\n }\n\n if (\n metaType === \"icon\" ||\n metaType === \"apple-icon\" ||\n metaType === \"opengraph-image\" ||\n metaType === \"twitter-image\"\n ) {\n return withMetadataSuffix(`/${routeBaseName}${ext}`, suffix);\n }\n\n return withMetadataSuffix(config.urlPath, suffix);\n}\n\nexport function matchMetadataFileBaseName(metaType: string, baseName: string): string | null {\n if (baseName === metaType) {\n return baseName;\n }\n\n if (\n metaType === \"icon\" ||\n metaType === \"apple-icon\" ||\n metaType === \"opengraph-image\" ||\n metaType === \"twitter-image\"\n ) {\n const suffix = baseName.slice(metaType.length);\n if (/^\\d$/.test(suffix)) {\n return baseName;\n }\n }\n\n return null;\n}\n\n/**\n * Scan an app directory for metadata files.\n */\nexport function scanMetadataFiles(appDir: string): MetadataFileRoute[] {\n const routes: MetadataFileRoute[] = [];\n\n // Scan the app directory recursively\n function scan(dir: string, urlPrefix: string, parentSegments: string[]): void {\n if (!fs.existsSync(dir)) return;\n\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const dirName = entry.name;\n if (dirName.startsWith(\"_\")) continue;\n\n const isRouteGroup = dirName.startsWith(\"(\") && dirName.endsWith(\")\");\n const isParallelRoute = dirName.startsWith(\"@\");\n const nextUrlPrefix =\n isRouteGroup || isParallelRoute ? urlPrefix : `${urlPrefix}/${dirName}`;\n scan(path.join(dir, dirName), nextUrlPrefix, [...parentSegments, dirName]);\n continue;\n }\n\n // Check each metadata file pattern\n const fileName = entry.name;\n const baseName = fileName.replace(/\\.[^.]+$/, \"\");\n const ext = fileName.slice(baseName.length);\n\n for (const [metaType, config] of Object.entries(METADATA_FILE_MAP)) {\n const routeBaseName = matchMetadataFileBaseName(metaType, baseName);\n if (!routeBaseName) continue;\n\n // Check nestability — non-nestable types only at root\n if (!config.nestable && urlPrefix !== \"\") continue;\n\n // Check if this is a static or dynamic variant\n const isStatic = config.staticExtensions.includes(ext);\n const isDynamic = config.dynamicExtensions.includes(ext);\n\n if (!isStatic && !isDynamic) continue;\n const appDirPath = parentSegments.length > 0 ? `/${parentSegments.join(\"/\")}` : \"\";\n const suffix = metadataRouteSuffix(parentSegments, metaType);\n const urlPath = getMetadataServedUrl(\n metaType,\n config,\n ext,\n isDynamic,\n suffix,\n routeBaseName,\n );\n const servedUrl = isStatic\n ? fillStaticMetadataSegment(appDirPath, `${routeBaseName}${ext}`)\n : urlPrefix === \"\"\n ? urlPath\n : `${urlPrefix}${urlPath}`;\n const altFilePath =\n isStatic && (metaType === \"opengraph-image\" || metaType === \"twitter-image\")\n ? resolveStaticMetadataAltFilePath(dir, baseName)\n : undefined;\n\n routes.push({\n type: metaType,\n isDynamic,\n filePath: path.join(dir, fileName),\n routePrefix: urlPrefix,\n routeSegments: parentSegments,\n servedUrl,\n contentType:\n isStatic && metaType === \"manifest\"\n ? config.contentType\n : isStatic\n ? getStaticContentType(ext, config.contentType)\n : config.contentType,\n altFilePath,\n });\n }\n }\n }\n\n scan(appDir, \"\", []);\n\n // Deduplicate: if both dynamic and static variants exist at the same URL,\n // keep only the dynamic one (matches Next.js behavior).\n const byUrl = new Map<string, MetadataFileRoute>();\n for (const route of routes) {\n const existing = byUrl.get(route.servedUrl);\n if (!existing) {\n byUrl.set(route.servedUrl, route);\n } else if (route.isDynamic && !existing.isDynamic) {\n // Dynamic takes priority over static\n byUrl.set(route.servedUrl, route);\n }\n // If both are static or both dynamic, keep the first one found\n }\n return Array.from(byUrl.values());\n}\n\nfunction resolveStaticMetadataAltFilePath(dir: string, baseName: string): string | undefined {\n const altPath = path.join(dir, `${baseName}.alt.txt`);\n return fs.existsSync(altPath) ? altPath : undefined;\n}\n\nfunction getStaticContentType(ext: string, fallback: string): string {\n const map: Record<string, string> = {\n \".xml\": \"application/xml\",\n \".txt\": \"text/plain\",\n \".json\": \"application/json\",\n \".webmanifest\": \"application/manifest+json\",\n \".ico\": \"image/x-icon\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".svg\": \"image/svg+xml\",\n };\n return map[ext] ?? fallback;\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { HasCondition, NextI18nConfig } from "../config/next-config.js";
2
+
3
+ //#region src/server/middleware-matcher.d.ts
4
+ type MiddlewareMatcherObject = {
5
+ source: string;
6
+ locale?: false;
7
+ has?: HasCondition[];
8
+ missing?: HasCondition[];
9
+ };
10
+ type MatcherConfig = string | Array<string | MiddlewareMatcherObject>;
11
+ declare function matchesMiddleware(pathname: string, matcher: MatcherConfig | undefined, request?: Request, i18nConfig?: NextI18nConfig | null): boolean;
12
+ declare function matchPattern(pathname: string, pattern: string): boolean;
13
+ //#endregion
14
+ export { MatcherConfig, MiddlewareMatcherObject, matchPattern, matchesMiddleware };
15
+ //# sourceMappingURL=middleware-matcher.d.ts.map
@@ -0,0 +1,102 @@
1
+ import { checkHasConditions, requestContextFromRequest, safeRegExp } from "../config/config-matchers.js";
2
+ //#region src/server/middleware-matcher.ts
3
+ const EMPTY_MIDDLEWARE_REQUEST_CONTEXT = {
4
+ headers: new Headers(),
5
+ cookies: {},
6
+ query: new URLSearchParams(),
7
+ host: ""
8
+ };
9
+ const _mwPatternCache = /* @__PURE__ */ new Map();
10
+ function matchesMiddleware(pathname, matcher, request, i18nConfig) {
11
+ if (!matcher) return true;
12
+ if (typeof matcher === "string") return matchMatcherPattern(pathname, matcher, i18nConfig);
13
+ if (!Array.isArray(matcher)) return false;
14
+ const requestContext = request ? requestContextFromRequest(request) : EMPTY_MIDDLEWARE_REQUEST_CONTEXT;
15
+ for (const m of matcher) {
16
+ if (typeof m === "string") {
17
+ if (matchMatcherPattern(pathname, m, i18nConfig)) return true;
18
+ continue;
19
+ }
20
+ if (isValidMiddlewareMatcherObject(m)) {
21
+ if (!matchObjectMatcher(pathname, m, i18nConfig)) continue;
22
+ if (!checkHasConditions(m.has, m.missing, requestContext)) continue;
23
+ return true;
24
+ }
25
+ }
26
+ return false;
27
+ }
28
+ function isValidMiddlewareMatcherObject(value) {
29
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
30
+ if (!("source" in value) || typeof value.source !== "string") return false;
31
+ for (const key of Object.keys(value)) if (key !== "source" && key !== "locale" && key !== "has" && key !== "missing") return false;
32
+ if ("locale" in value && value.locale !== void 0 && value.locale !== false) return false;
33
+ if ("has" in value && value.has !== void 0 && !Array.isArray(value.has)) return false;
34
+ if ("missing" in value && value.missing !== void 0 && !Array.isArray(value.missing)) return false;
35
+ return true;
36
+ }
37
+ function matchMatcherPattern(pathname, pattern, i18nConfig) {
38
+ if (!i18nConfig) return matchPattern(pathname, pattern);
39
+ return matchPattern(stripLocalePrefix(pathname, i18nConfig) ?? pathname, pattern);
40
+ }
41
+ function matchObjectMatcher(pathname, matcher, i18nConfig) {
42
+ return matcher.locale === false ? matchPattern(pathname, matcher.source) : matchMatcherPattern(pathname, matcher.source, i18nConfig);
43
+ }
44
+ function stripLocalePrefix(pathname, i18nConfig) {
45
+ if (pathname === "/") return null;
46
+ const segments = pathname.split("/");
47
+ const firstSegment = segments[1];
48
+ if (!firstSegment || !i18nConfig.locales.includes(firstSegment)) return null;
49
+ const stripped = "/" + segments.slice(2).join("/");
50
+ return stripped === "/" ? "/" : stripped.replace(/\/+$/, "") || "/";
51
+ }
52
+ function matchPattern(pathname, pattern) {
53
+ let cached = _mwPatternCache.get(pattern);
54
+ if (cached === void 0) {
55
+ cached = compileMatcherPattern(pattern);
56
+ _mwPatternCache.set(pattern, cached);
57
+ }
58
+ if (cached === null) return pathname === pattern;
59
+ return cached.test(pathname);
60
+ }
61
+ function extractConstraint(str, re) {
62
+ if (str[re.lastIndex] !== "(") return null;
63
+ const start = re.lastIndex + 1;
64
+ let depth = 1;
65
+ let i = start;
66
+ while (i < str.length && depth > 0) {
67
+ if (str[i] === "(") depth++;
68
+ else if (str[i] === ")") depth--;
69
+ i++;
70
+ }
71
+ if (depth !== 0) return null;
72
+ re.lastIndex = i;
73
+ return str.slice(start, i - 1);
74
+ }
75
+ function compileMatcherPattern(pattern) {
76
+ const hasConstraints = /:[\w-]+[*+]?\(/.test(pattern);
77
+ if (!hasConstraints && (pattern.includes("(") || pattern.includes("\\"))) return safeRegExp("^" + pattern + "$");
78
+ let regexStr = "";
79
+ const tokenRe = /\/:([\w-]+)\*|\/:([\w-]+)\+|:([\w-]+)|[.]|[^/:.]+|./g;
80
+ let tok;
81
+ while ((tok = tokenRe.exec(pattern)) !== null) if (tok[1] !== void 0) {
82
+ const constraint = hasConstraints ? extractConstraint(pattern, tokenRe) : null;
83
+ regexStr += constraint !== null ? `(?:/(${constraint}))?` : "(?:/.*)?";
84
+ } else if (tok[2] !== void 0) {
85
+ const constraint = hasConstraints ? extractConstraint(pattern, tokenRe) : null;
86
+ regexStr += constraint !== null ? `(?:/(${constraint}))` : "(?:/.+)";
87
+ } else if (tok[3] !== void 0) {
88
+ const constraint = hasConstraints ? extractConstraint(pattern, tokenRe) : null;
89
+ const isOptional = pattern[tokenRe.lastIndex] === "?";
90
+ if (isOptional) tokenRe.lastIndex += 1;
91
+ const group = constraint !== null ? `(${constraint})` : "([^/]+)";
92
+ if (isOptional && regexStr.endsWith("/")) regexStr = regexStr.slice(0, -1) + `(?:/${group})?`;
93
+ else if (isOptional) regexStr += `${group}?`;
94
+ else regexStr += group;
95
+ } else if (tok[0] === ".") regexStr += "\\.";
96
+ else regexStr += tok[0];
97
+ return safeRegExp("^" + regexStr + "$");
98
+ }
99
+ //#endregion
100
+ export { matchPattern, matchesMiddleware };
101
+
102
+ //# sourceMappingURL=middleware-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware-matcher.js","names":[],"sources":["../../src/server/middleware-matcher.ts"],"sourcesContent":["import {\n checkHasConditions,\n requestContextFromRequest,\n safeRegExp,\n type RequestContext,\n} from \"../config/config-matchers.js\";\nimport type { HasCondition, NextI18nConfig } from \"../config/next-config.js\";\n\nexport type MiddlewareMatcherObject = {\n source: string;\n locale?: false;\n has?: HasCondition[];\n missing?: HasCondition[];\n};\n\nexport type MatcherConfig = string | Array<string | MiddlewareMatcherObject>;\n\nconst EMPTY_MIDDLEWARE_REQUEST_CONTEXT: RequestContext = {\n headers: new Headers(),\n cookies: {},\n query: new URLSearchParams(),\n host: \"\",\n};\n\nconst _mwPatternCache = new Map<string, RegExp | null>();\n\nexport function matchesMiddleware(\n pathname: string,\n matcher: MatcherConfig | undefined,\n request?: Request,\n i18nConfig?: NextI18nConfig | null,\n): boolean {\n if (!matcher) {\n return true;\n }\n\n if (typeof matcher === \"string\") {\n return matchMatcherPattern(pathname, matcher, i18nConfig);\n }\n if (!Array.isArray(matcher)) {\n return false;\n }\n\n const requestContext = request\n ? requestContextFromRequest(request)\n : EMPTY_MIDDLEWARE_REQUEST_CONTEXT;\n\n for (const m of matcher) {\n if (typeof m === \"string\") {\n if (matchMatcherPattern(pathname, m, i18nConfig)) {\n return true;\n }\n continue;\n }\n\n if (isValidMiddlewareMatcherObject(m)) {\n if (!matchObjectMatcher(pathname, m, i18nConfig)) {\n continue;\n }\n\n if (!checkHasConditions(m.has, m.missing, requestContext)) {\n continue;\n }\n\n return true;\n }\n }\n\n return false;\n}\n\nfunction isValidMiddlewareMatcherObject(value: unknown): value is MiddlewareMatcherObject {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\n if (!(\"source\" in value) || typeof value.source !== \"string\") return false;\n\n for (const key of Object.keys(value)) {\n if (key !== \"source\" && key !== \"locale\" && key !== \"has\" && key !== \"missing\") {\n return false;\n }\n }\n\n if (\"locale\" in value && value.locale !== undefined && value.locale !== false) return false;\n if (\"has\" in value && value.has !== undefined && !Array.isArray(value.has)) return false;\n if (\"missing\" in value && value.missing !== undefined && !Array.isArray(value.missing)) {\n return false;\n }\n\n return true;\n}\n\nfunction matchMatcherPattern(\n pathname: string,\n pattern: string,\n i18nConfig?: NextI18nConfig | null,\n): boolean {\n if (!i18nConfig) return matchPattern(pathname, pattern);\n\n const localeStrippedPathname = stripLocalePrefix(pathname, i18nConfig);\n return matchPattern(localeStrippedPathname ?? pathname, pattern);\n}\n\nfunction matchObjectMatcher(\n pathname: string,\n matcher: MiddlewareMatcherObject,\n i18nConfig?: NextI18nConfig | null,\n): boolean {\n return matcher.locale === false\n ? matchPattern(pathname, matcher.source)\n : matchMatcherPattern(pathname, matcher.source, i18nConfig);\n}\n\nfunction stripLocalePrefix(pathname: string, i18nConfig: NextI18nConfig): string | null {\n if (pathname === \"/\") return null;\n\n const segments = pathname.split(\"/\");\n const firstSegment = segments[1];\n if (!firstSegment || !i18nConfig.locales.includes(firstSegment)) {\n return null;\n }\n\n const stripped = \"/\" + segments.slice(2).join(\"/\");\n return stripped === \"/\" ? \"/\" : stripped.replace(/\\/+$/, \"\") || \"/\";\n}\n\nexport function matchPattern(pathname: string, pattern: string): boolean {\n let cached = _mwPatternCache.get(pattern);\n if (cached === undefined) {\n cached = compileMatcherPattern(pattern);\n _mwPatternCache.set(pattern, cached);\n }\n if (cached === null) return pathname === pattern;\n return cached.test(pathname);\n}\n\nfunction extractConstraint(str: string, re: RegExp): string | null {\n if (str[re.lastIndex] !== \"(\") return null;\n const start = re.lastIndex + 1;\n let depth = 1;\n let i = start;\n while (i < str.length && depth > 0) {\n if (str[i] === \"(\") depth++;\n else if (str[i] === \")\") depth--;\n i++;\n }\n if (depth !== 0) return null;\n re.lastIndex = i;\n return str.slice(start, i - 1);\n}\n\nfunction compileMatcherPattern(pattern: string): RegExp | null {\n const hasConstraints = /:[\\w-]+[*+]?\\(/.test(pattern);\n\n if (!hasConstraints && (pattern.includes(\"(\") || pattern.includes(\"\\\\\"))) {\n return safeRegExp(\"^\" + pattern + \"$\");\n }\n\n let regexStr = \"\";\n const tokenRe = /\\/:([\\w-]+)\\*|\\/:([\\w-]+)\\+|:([\\w-]+)|[.]|[^/:.]+|./g;\n let tok: RegExpExecArray | null;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) {\n const constraint = hasConstraints ? extractConstraint(pattern, tokenRe) : null;\n regexStr += constraint !== null ? `(?:/(${constraint}))?` : \"(?:/.*)?\";\n } else if (tok[2] !== undefined) {\n const constraint = hasConstraints ? extractConstraint(pattern, tokenRe) : null;\n regexStr += constraint !== null ? `(?:/(${constraint}))` : \"(?:/.+)\";\n } else if (tok[3] !== undefined) {\n const constraint = hasConstraints ? extractConstraint(pattern, tokenRe) : null;\n const isOptional = pattern[tokenRe.lastIndex] === \"?\";\n if (isOptional) tokenRe.lastIndex += 1;\n\n const group = constraint !== null ? `(${constraint})` : \"([^/]+)\";\n\n if (isOptional && regexStr.endsWith(\"/\")) {\n regexStr = regexStr.slice(0, -1) + `(?:/${group})?`;\n } else if (isOptional) {\n regexStr += `${group}?`;\n } else {\n regexStr += group;\n }\n } else if (tok[0] === \".\") {\n regexStr += \"\\\\.\";\n } else {\n regexStr += tok[0];\n }\n }\n\n return safeRegExp(\"^\" + regexStr + \"$\");\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  //#region src/server/middleware-request-headers.ts
2
2
  const MIDDLEWARE_REQUEST_HEADER_PREFIX = "x-middleware-request-";
3
3
  const MIDDLEWARE_OVERRIDE_HEADERS = "x-middleware-override-headers";
4
+ const MIDDLEWARE_SET_COOKIE_HEADER = "x-middleware-set-cookie";
4
5
  function getMiddlewareHeaderValue(source, key) {
5
6
  if (source instanceof Headers) return source.get(key);
6
7
  const value = source[key];
@@ -51,7 +52,7 @@ function buildRequestHeadersFromMiddlewareResponse(baseHeaders, middlewareHeader
51
52
  return nextHeaders;
52
53
  }
53
54
  function shouldKeepMiddlewareHeader(key) {
54
- return key === MIDDLEWARE_OVERRIDE_HEADERS || key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX);
55
+ return key === MIDDLEWARE_OVERRIDE_HEADERS || key === MIDDLEWARE_SET_COOKIE_HEADER || key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX);
55
56
  }
56
57
  //#endregion
57
58
  export { buildRequestHeadersFromMiddlewareResponse, encodeMiddlewareRequestHeaders, shouldKeepMiddlewareHeader };
@@ -1 +1 @@
1
- {"version":3,"file":"middleware-request-headers.js","names":[],"sources":["../../src/server/middleware-request-headers.ts"],"sourcesContent":["const MIDDLEWARE_REQUEST_HEADER_PREFIX = \"x-middleware-request-\";\nconst MIDDLEWARE_OVERRIDE_HEADERS = \"x-middleware-override-headers\";\n\ntype MiddlewareHeaderValue = string | string[];\ntype MiddlewareHeaderSource = Headers | Record<string, MiddlewareHeaderValue>;\n\nfunction getMiddlewareHeaderValue(source: MiddlewareHeaderSource, key: string): string | null {\n if (source instanceof Headers) {\n return source.get(key);\n }\n\n const value = source[key];\n if (value === undefined) return null;\n return Array.isArray(value) ? (value[0] ?? null) : value;\n}\n\nfunction getOverrideHeaderNames(source: MiddlewareHeaderSource): string[] | null {\n const rawValue = getMiddlewareHeaderValue(source, MIDDLEWARE_OVERRIDE_HEADERS);\n if (rawValue === null) return null;\n return rawValue\n .split(\",\")\n .map((key) => key.trim())\n .filter(Boolean);\n}\n\nfunction getForwardedRequestHeaders(source: MiddlewareHeaderSource): Map<string, string> {\n const forwardedHeaders = new Map<string, string>();\n\n if (source instanceof Headers) {\n for (const [key, value] of source.entries()) {\n if (key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX)) {\n forwardedHeaders.set(key.slice(MIDDLEWARE_REQUEST_HEADER_PREFIX.length), value);\n }\n }\n return forwardedHeaders;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (!key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX)) continue;\n const normalizedValue = Array.isArray(value) ? (value[0] ?? \"\") : value;\n forwardedHeaders.set(key.slice(MIDDLEWARE_REQUEST_HEADER_PREFIX.length), normalizedValue);\n }\n\n return forwardedHeaders;\n}\n\nfunction cloneHeaders(source: Headers): Headers {\n const cloned = new Headers();\n for (const [key, value] of source.entries()) {\n cloned.append(key, value);\n }\n return cloned;\n}\n\nexport function encodeMiddlewareRequestHeaders(\n targetHeaders: Headers,\n requestHeaders: Headers,\n): void {\n const overrideHeaderNames = [...requestHeaders.keys()];\n targetHeaders.set(MIDDLEWARE_OVERRIDE_HEADERS, overrideHeaderNames.join(\",\"));\n\n for (const [key, value] of requestHeaders.entries()) {\n targetHeaders.set(`${MIDDLEWARE_REQUEST_HEADER_PREFIX}${key}`, value);\n }\n}\n\nexport function buildRequestHeadersFromMiddlewareResponse(\n baseHeaders: Headers,\n middlewareHeaders: MiddlewareHeaderSource,\n): Headers | null {\n const overrideHeaderNames = getOverrideHeaderNames(middlewareHeaders);\n const forwardedHeaders = getForwardedRequestHeaders(middlewareHeaders);\n\n if (overrideHeaderNames === null && forwardedHeaders.size === 0) {\n return null;\n }\n\n const nextHeaders = overrideHeaderNames === null ? cloneHeaders(baseHeaders) : new Headers();\n\n if (overrideHeaderNames === null) {\n for (const [key, value] of forwardedHeaders) {\n nextHeaders.set(key, value);\n }\n return nextHeaders;\n }\n\n for (const key of overrideHeaderNames) {\n const value = forwardedHeaders.get(key);\n if (value !== undefined) {\n nextHeaders.set(key, value);\n }\n }\n\n return nextHeaders;\n}\n\nexport function shouldKeepMiddlewareHeader(key: string): boolean {\n return key === MIDDLEWARE_OVERRIDE_HEADERS || key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX);\n}\n"],"mappings":";AAAA,MAAM,mCAAmC;AACzC,MAAM,8BAA8B;AAKpC,SAAS,yBAAyB,QAAgC,KAA4B;AAC5F,KAAI,kBAAkB,QACpB,QAAO,OAAO,IAAI,IAAI;CAGxB,MAAM,QAAQ,OAAO;AACrB,KAAI,UAAU,KAAA,EAAW,QAAO;AAChC,QAAO,MAAM,QAAQ,MAAM,GAAI,MAAM,MAAM,OAAQ;;AAGrD,SAAS,uBAAuB,QAAiD;CAC/E,MAAM,WAAW,yBAAyB,QAAQ,4BAA4B;AAC9E,KAAI,aAAa,KAAM,QAAO;AAC9B,QAAO,SACJ,MAAM,IAAI,CACV,KAAK,QAAQ,IAAI,MAAM,CAAC,CACxB,OAAO,QAAQ;;AAGpB,SAAS,2BAA2B,QAAqD;CACvF,MAAM,mCAAmB,IAAI,KAAqB;AAElD,KAAI,kBAAkB,SAAS;AAC7B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,CACzC,KAAI,IAAI,WAAW,iCAAiC,CAClD,kBAAiB,IAAI,IAAI,MAAM,GAAwC,EAAE,MAAM;AAGnF,SAAO;;AAGT,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;AACjD,MAAI,CAAC,IAAI,WAAW,iCAAiC,CAAE;EACvD,MAAM,kBAAkB,MAAM,QAAQ,MAAM,GAAI,MAAM,MAAM,KAAM;AAClE,mBAAiB,IAAI,IAAI,MAAM,GAAwC,EAAE,gBAAgB;;AAG3F,QAAO;;AAGT,SAAS,aAAa,QAA0B;CAC9C,MAAM,SAAS,IAAI,SAAS;AAC5B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,CACzC,QAAO,OAAO,KAAK,MAAM;AAE3B,QAAO;;AAGT,SAAgB,+BACd,eACA,gBACM;CACN,MAAM,sBAAsB,CAAC,GAAG,eAAe,MAAM,CAAC;AACtD,eAAc,IAAI,6BAA6B,oBAAoB,KAAK,IAAI,CAAC;AAE7E,MAAK,MAAM,CAAC,KAAK,UAAU,eAAe,SAAS,CACjD,eAAc,IAAI,GAAG,mCAAmC,OAAO,MAAM;;AAIzE,SAAgB,0CACd,aACA,mBACgB;CAChB,MAAM,sBAAsB,uBAAuB,kBAAkB;CACrE,MAAM,mBAAmB,2BAA2B,kBAAkB;AAEtE,KAAI,wBAAwB,QAAQ,iBAAiB,SAAS,EAC5D,QAAO;CAGT,MAAM,cAAc,wBAAwB,OAAO,aAAa,YAAY,GAAG,IAAI,SAAS;AAE5F,KAAI,wBAAwB,MAAM;AAChC,OAAK,MAAM,CAAC,KAAK,UAAU,iBACzB,aAAY,IAAI,KAAK,MAAM;AAE7B,SAAO;;AAGT,MAAK,MAAM,OAAO,qBAAqB;EACrC,MAAM,QAAQ,iBAAiB,IAAI,IAAI;AACvC,MAAI,UAAU,KAAA,EACZ,aAAY,IAAI,KAAK,MAAM;;AAI/B,QAAO;;AAGT,SAAgB,2BAA2B,KAAsB;AAC/D,QAAO,QAAQ,+BAA+B,IAAI,WAAW,iCAAiC"}
1
+ {"version":3,"file":"middleware-request-headers.js","names":[],"sources":["../../src/server/middleware-request-headers.ts"],"sourcesContent":["const MIDDLEWARE_REQUEST_HEADER_PREFIX = \"x-middleware-request-\";\nconst MIDDLEWARE_OVERRIDE_HEADERS = \"x-middleware-override-headers\";\nconst MIDDLEWARE_SET_COOKIE_HEADER = \"x-middleware-set-cookie\";\n\ntype MiddlewareHeaderValue = string | string[];\ntype MiddlewareHeaderSource = Headers | Record<string, MiddlewareHeaderValue>;\n\nfunction getMiddlewareHeaderValue(source: MiddlewareHeaderSource, key: string): string | null {\n if (source instanceof Headers) {\n return source.get(key);\n }\n\n const value = source[key];\n if (value === undefined) return null;\n return Array.isArray(value) ? (value[0] ?? null) : value;\n}\n\nfunction getOverrideHeaderNames(source: MiddlewareHeaderSource): string[] | null {\n const rawValue = getMiddlewareHeaderValue(source, MIDDLEWARE_OVERRIDE_HEADERS);\n if (rawValue === null) return null;\n return rawValue\n .split(\",\")\n .map((key) => key.trim())\n .filter(Boolean);\n}\n\nfunction getForwardedRequestHeaders(source: MiddlewareHeaderSource): Map<string, string> {\n const forwardedHeaders = new Map<string, string>();\n\n if (source instanceof Headers) {\n for (const [key, value] of source.entries()) {\n if (key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX)) {\n forwardedHeaders.set(key.slice(MIDDLEWARE_REQUEST_HEADER_PREFIX.length), value);\n }\n }\n return forwardedHeaders;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (!key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX)) continue;\n const normalizedValue = Array.isArray(value) ? (value[0] ?? \"\") : value;\n forwardedHeaders.set(key.slice(MIDDLEWARE_REQUEST_HEADER_PREFIX.length), normalizedValue);\n }\n\n return forwardedHeaders;\n}\n\nfunction cloneHeaders(source: Headers): Headers {\n const cloned = new Headers();\n for (const [key, value] of source.entries()) {\n cloned.append(key, value);\n }\n return cloned;\n}\n\nexport function encodeMiddlewareRequestHeaders(\n targetHeaders: Headers,\n requestHeaders: Headers,\n): void {\n const overrideHeaderNames = [...requestHeaders.keys()];\n targetHeaders.set(MIDDLEWARE_OVERRIDE_HEADERS, overrideHeaderNames.join(\",\"));\n\n for (const [key, value] of requestHeaders.entries()) {\n targetHeaders.set(`${MIDDLEWARE_REQUEST_HEADER_PREFIX}${key}`, value);\n }\n}\n\nexport function buildRequestHeadersFromMiddlewareResponse(\n baseHeaders: Headers,\n middlewareHeaders: MiddlewareHeaderSource,\n): Headers | null {\n const overrideHeaderNames = getOverrideHeaderNames(middlewareHeaders);\n const forwardedHeaders = getForwardedRequestHeaders(middlewareHeaders);\n\n if (overrideHeaderNames === null && forwardedHeaders.size === 0) {\n return null;\n }\n\n const nextHeaders = overrideHeaderNames === null ? cloneHeaders(baseHeaders) : new Headers();\n\n if (overrideHeaderNames === null) {\n for (const [key, value] of forwardedHeaders) {\n nextHeaders.set(key, value);\n }\n return nextHeaders;\n }\n\n for (const key of overrideHeaderNames) {\n const value = forwardedHeaders.get(key);\n if (value !== undefined) {\n nextHeaders.set(key, value);\n }\n }\n\n return nextHeaders;\n}\n\nexport function shouldKeepMiddlewareHeader(key: string): boolean {\n return (\n key === MIDDLEWARE_OVERRIDE_HEADERS ||\n key === MIDDLEWARE_SET_COOKIE_HEADER ||\n key.startsWith(MIDDLEWARE_REQUEST_HEADER_PREFIX)\n );\n}\n"],"mappings":";AAAA,MAAM,mCAAmC;AACzC,MAAM,8BAA8B;AACpC,MAAM,+BAA+B;AAKrC,SAAS,yBAAyB,QAAgC,KAA4B;AAC5F,KAAI,kBAAkB,QACpB,QAAO,OAAO,IAAI,IAAI;CAGxB,MAAM,QAAQ,OAAO;AACrB,KAAI,UAAU,KAAA,EAAW,QAAO;AAChC,QAAO,MAAM,QAAQ,MAAM,GAAI,MAAM,MAAM,OAAQ;;AAGrD,SAAS,uBAAuB,QAAiD;CAC/E,MAAM,WAAW,yBAAyB,QAAQ,4BAA4B;AAC9E,KAAI,aAAa,KAAM,QAAO;AAC9B,QAAO,SACJ,MAAM,IAAI,CACV,KAAK,QAAQ,IAAI,MAAM,CAAC,CACxB,OAAO,QAAQ;;AAGpB,SAAS,2BAA2B,QAAqD;CACvF,MAAM,mCAAmB,IAAI,KAAqB;AAElD,KAAI,kBAAkB,SAAS;AAC7B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,CACzC,KAAI,IAAI,WAAW,iCAAiC,CAClD,kBAAiB,IAAI,IAAI,MAAM,GAAwC,EAAE,MAAM;AAGnF,SAAO;;AAGT,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;AACjD,MAAI,CAAC,IAAI,WAAW,iCAAiC,CAAE;EACvD,MAAM,kBAAkB,MAAM,QAAQ,MAAM,GAAI,MAAM,MAAM,KAAM;AAClE,mBAAiB,IAAI,IAAI,MAAM,GAAwC,EAAE,gBAAgB;;AAG3F,QAAO;;AAGT,SAAS,aAAa,QAA0B;CAC9C,MAAM,SAAS,IAAI,SAAS;AAC5B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,CACzC,QAAO,OAAO,KAAK,MAAM;AAE3B,QAAO;;AAGT,SAAgB,+BACd,eACA,gBACM;CACN,MAAM,sBAAsB,CAAC,GAAG,eAAe,MAAM,CAAC;AACtD,eAAc,IAAI,6BAA6B,oBAAoB,KAAK,IAAI,CAAC;AAE7E,MAAK,MAAM,CAAC,KAAK,UAAU,eAAe,SAAS,CACjD,eAAc,IAAI,GAAG,mCAAmC,OAAO,MAAM;;AAIzE,SAAgB,0CACd,aACA,mBACgB;CAChB,MAAM,sBAAsB,uBAAuB,kBAAkB;CACrE,MAAM,mBAAmB,2BAA2B,kBAAkB;AAEtE,KAAI,wBAAwB,QAAQ,iBAAiB,SAAS,EAC5D,QAAO;CAGT,MAAM,cAAc,wBAAwB,OAAO,aAAa,YAAY,GAAG,IAAI,SAAS;AAE5F,KAAI,wBAAwB,MAAM;AAChC,OAAK,MAAM,CAAC,KAAK,UAAU,iBACzB,aAAY,IAAI,KAAK,MAAM;AAE7B,SAAO;;AAGT,MAAK,MAAM,OAAO,qBAAqB;EACrC,MAAM,QAAQ,iBAAiB,IAAI,IAAI;AACvC,MAAI,UAAU,KAAA,EACZ,aAAY,IAAI,KAAK,MAAM;;AAI/B,QAAO;;AAGT,SAAgB,2BAA2B,KAAsB;AAC/D,QACE,QAAQ,+BACR,QAAQ,gCACR,IAAI,WAAW,iCAAiC"}