vinext 0.0.50 → 0.0.51

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 (309) hide show
  1. package/dist/build/google-fonts/fallback-metrics-data.js +14031 -0
  2. package/dist/build/google-fonts/fallback-metrics-data.js.map +1 -0
  3. package/dist/build/google-fonts/fallback-metrics.d.ts +13 -0
  4. package/dist/build/google-fonts/fallback-metrics.js +46 -0
  5. package/dist/build/google-fonts/fallback-metrics.js.map +1 -0
  6. package/dist/build/precompress.d.ts +13 -2
  7. package/dist/build/precompress.js +12 -3
  8. package/dist/build/precompress.js.map +1 -1
  9. package/dist/build/prerender.d.ts +1 -1
  10. package/dist/build/prerender.js +44 -14
  11. package/dist/build/prerender.js.map +1 -1
  12. package/dist/build/report.d.ts +5 -4
  13. package/dist/build/report.js +196 -348
  14. package/dist/build/report.js.map +1 -1
  15. package/dist/check.js +1 -0
  16. package/dist/check.js.map +1 -1
  17. package/dist/cli.js +60 -3
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client/window-next.d.ts +3 -1
  20. package/dist/client/window-next.js.map +1 -1
  21. package/dist/config/dotenv.d.ts +11 -1
  22. package/dist/config/dotenv.js.map +1 -1
  23. package/dist/config/next-config.d.ts +87 -3
  24. package/dist/config/next-config.js +222 -6
  25. package/dist/config/next-config.js.map +1 -1
  26. package/dist/config/tsconfig-paths.d.ts +13 -0
  27. package/dist/config/tsconfig-paths.js +117 -0
  28. package/dist/config/tsconfig-paths.js.map +1 -0
  29. package/dist/deploy.js +3 -2
  30. package/dist/deploy.js.map +1 -1
  31. package/dist/entries/app-browser-entry.d.ts +2 -2
  32. package/dist/entries/app-browser-entry.js +26 -1
  33. package/dist/entries/app-browser-entry.js.map +1 -1
  34. package/dist/entries/app-rsc-entry.d.ts +19 -1
  35. package/dist/entries/app-rsc-entry.js +38 -12
  36. package/dist/entries/app-rsc-entry.js.map +1 -1
  37. package/dist/entries/app-rsc-manifest.d.ts +9 -0
  38. package/dist/entries/app-rsc-manifest.js +4 -1
  39. package/dist/entries/app-rsc-manifest.js.map +1 -1
  40. package/dist/entries/pages-client-entry.js +3 -5
  41. package/dist/entries/pages-client-entry.js.map +1 -1
  42. package/dist/entries/pages-server-entry.js +19 -1
  43. package/dist/entries/pages-server-entry.js.map +1 -1
  44. package/dist/index.js +130 -37
  45. package/dist/index.js.map +1 -1
  46. package/dist/plugins/client-reference-dedup.d.ts +15 -2
  47. package/dist/plugins/client-reference-dedup.js +138 -16
  48. package/dist/plugins/client-reference-dedup.js.map +1 -1
  49. package/dist/plugins/fonts.d.ts +2 -2
  50. package/dist/plugins/fonts.js +15 -6
  51. package/dist/plugins/fonts.js.map +1 -1
  52. package/dist/plugins/sass.d.ts +34 -0
  53. package/dist/plugins/sass.js +22 -0
  54. package/dist/plugins/sass.js.map +1 -0
  55. package/dist/routing/app-route-graph.d.ts +31 -2
  56. package/dist/routing/app-route-graph.js +82 -10
  57. package/dist/routing/app-route-graph.js.map +1 -1
  58. package/dist/routing/route-pattern.d.ts +56 -1
  59. package/dist/routing/route-pattern.js +60 -1
  60. package/dist/routing/route-pattern.js.map +1 -1
  61. package/dist/server/app-browser-action-result.d.ts +27 -2
  62. package/dist/server/app-browser-action-result.js +63 -2
  63. package/dist/server/app-browser-action-result.js.map +1 -1
  64. package/dist/server/app-browser-entry.js +262 -108
  65. package/dist/server/app-browser-entry.js.map +1 -1
  66. package/dist/server/app-browser-hydration.d.ts +13 -1
  67. package/dist/server/app-browser-hydration.js +9 -1
  68. package/dist/server/app-browser-hydration.js.map +1 -1
  69. package/dist/server/app-browser-navigation-controller.d.ts +14 -1
  70. package/dist/server/app-browser-navigation-controller.js +28 -9
  71. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  72. package/dist/server/app-browser-popstate.d.ts +16 -0
  73. package/dist/server/app-browser-popstate.js +17 -0
  74. package/dist/server/app-browser-popstate.js.map +1 -0
  75. package/dist/server/app-browser-rsc-redirect.d.ts +28 -0
  76. package/dist/server/app-browser-rsc-redirect.js +37 -0
  77. package/dist/server/app-browser-rsc-redirect.js.map +1 -0
  78. package/dist/server/app-browser-state.d.ts +11 -7
  79. package/dist/server/app-browser-state.js +45 -27
  80. package/dist/server/app-browser-state.js.map +1 -1
  81. package/dist/server/app-browser-stream.d.ts +5 -4
  82. package/dist/server/app-browser-stream.js +5 -6
  83. package/dist/server/app-browser-stream.js.map +1 -1
  84. package/dist/server/app-browser-visible-commit.d.ts +5 -0
  85. package/dist/server/app-browser-visible-commit.js +38 -5
  86. package/dist/server/app-browser-visible-commit.js.map +1 -1
  87. package/dist/server/app-elements-wire.d.ts +38 -6
  88. package/dist/server/app-elements-wire.js +106 -6
  89. package/dist/server/app-elements-wire.js.map +1 -1
  90. package/dist/server/app-elements.d.ts +2 -2
  91. package/dist/server/app-elements.js +2 -2
  92. package/dist/server/app-elements.js.map +1 -1
  93. package/dist/server/app-fallback-renderer.d.ts +10 -1
  94. package/dist/server/app-fallback-renderer.js +37 -1
  95. package/dist/server/app-fallback-renderer.js.map +1 -1
  96. package/dist/server/app-history-state.d.ts +26 -0
  97. package/dist/server/app-history-state.js +53 -0
  98. package/dist/server/app-history-state.js.map +1 -0
  99. package/dist/server/app-page-boundary-render.d.ts +10 -1
  100. package/dist/server/app-page-boundary-render.js +13 -6
  101. package/dist/server/app-page-boundary-render.js.map +1 -1
  102. package/dist/server/app-page-boundary.js +3 -2
  103. package/dist/server/app-page-boundary.js.map +1 -1
  104. package/dist/server/app-page-cache.d.ts +13 -0
  105. package/dist/server/app-page-cache.js +25 -8
  106. package/dist/server/app-page-cache.js.map +1 -1
  107. package/dist/server/app-page-dispatch.d.ts +5 -0
  108. package/dist/server/app-page-dispatch.js +68 -11
  109. package/dist/server/app-page-dispatch.js.map +1 -1
  110. package/dist/server/app-page-element-builder.d.ts +7 -0
  111. package/dist/server/app-page-element-builder.js +32 -4
  112. package/dist/server/app-page-element-builder.js.map +1 -1
  113. package/dist/server/app-page-execution.js +2 -3
  114. package/dist/server/app-page-execution.js.map +1 -1
  115. package/dist/server/app-page-head.d.ts +7 -0
  116. package/dist/server/app-page-head.js +2 -1
  117. package/dist/server/app-page-head.js.map +1 -1
  118. package/dist/server/app-page-probe.d.ts +23 -1
  119. package/dist/server/app-page-probe.js +29 -1
  120. package/dist/server/app-page-probe.js.map +1 -1
  121. package/dist/server/app-page-render-observation.d.ts +35 -0
  122. package/dist/server/app-page-render-observation.js +68 -0
  123. package/dist/server/app-page-render-observation.js.map +1 -0
  124. package/dist/server/app-page-render.d.ts +5 -1
  125. package/dist/server/app-page-render.js +79 -3
  126. package/dist/server/app-page-render.js.map +1 -1
  127. package/dist/server/app-page-request.d.ts +1 -0
  128. package/dist/server/app-page-request.js.map +1 -1
  129. package/dist/server/app-page-response.js +3 -2
  130. package/dist/server/app-page-response.js.map +1 -1
  131. package/dist/server/app-page-route-wiring.d.ts +3 -1
  132. package/dist/server/app-page-route-wiring.js +42 -14
  133. package/dist/server/app-page-route-wiring.js.map +1 -1
  134. package/dist/server/app-page-stream.d.ts +2 -0
  135. package/dist/server/app-page-stream.js +1 -0
  136. package/dist/server/app-page-stream.js.map +1 -1
  137. package/dist/server/app-router-entry.js +1 -13
  138. package/dist/server/app-router-entry.js.map +1 -1
  139. package/dist/server/app-rsc-cache-busting.d.ts +19 -1
  140. package/dist/server/app-rsc-cache-busting.js +36 -1
  141. package/dist/server/app-rsc-cache-busting.js.map +1 -1
  142. package/dist/server/app-rsc-embedded-chunks.d.ts +9 -0
  143. package/dist/server/app-rsc-embedded-chunks.js +34 -0
  144. package/dist/server/app-rsc-embedded-chunks.js.map +1 -0
  145. package/dist/server/app-rsc-errors.d.ts +4 -1
  146. package/dist/server/app-rsc-errors.js +1 -1
  147. package/dist/server/app-rsc-errors.js.map +1 -1
  148. package/dist/server/app-rsc-handler.d.ts +12 -4
  149. package/dist/server/app-rsc-handler.js +6 -1
  150. package/dist/server/app-rsc-handler.js.map +1 -1
  151. package/dist/server/app-rsc-route-matching.d.ts +23 -0
  152. package/dist/server/app-rsc-route-matching.js +45 -23
  153. package/dist/server/app-rsc-route-matching.js.map +1 -1
  154. package/dist/server/app-server-action-execution.d.ts +35 -3
  155. package/dist/server/app-server-action-execution.js +87 -33
  156. package/dist/server/app-server-action-execution.js.map +1 -1
  157. package/dist/server/app-ssr-entry.d.ts +1 -0
  158. package/dist/server/app-ssr-entry.js +37 -13
  159. package/dist/server/app-ssr-entry.js.map +1 -1
  160. package/dist/server/app-ssr-error-meta.d.ts +14 -0
  161. package/dist/server/app-ssr-error-meta.js +50 -0
  162. package/dist/server/app-ssr-error-meta.js.map +1 -0
  163. package/dist/server/app-ssr-stream.d.ts +1 -1
  164. package/dist/server/app-ssr-stream.js +9 -12
  165. package/dist/server/app-ssr-stream.js.map +1 -1
  166. package/dist/server/artifact-compatibility.d.ts +12 -2
  167. package/dist/server/artifact-compatibility.js +12 -8
  168. package/dist/server/artifact-compatibility.js.map +1 -1
  169. package/dist/server/cache-proof.d.ts +124 -5
  170. package/dist/server/cache-proof.js +416 -18
  171. package/dist/server/cache-proof.js.map +1 -1
  172. package/dist/server/dev-lockfile.d.ts +110 -0
  173. package/dist/server/dev-lockfile.js +180 -0
  174. package/dist/server/dev-lockfile.js.map +1 -0
  175. package/dist/server/dev-server.js +15 -5
  176. package/dist/server/dev-server.js.map +1 -1
  177. package/dist/server/file-based-metadata.d.ts +13 -0
  178. package/dist/server/file-based-metadata.js +49 -2
  179. package/dist/server/file-based-metadata.js.map +1 -1
  180. package/dist/server/headers.d.ts +3 -1
  181. package/dist/server/headers.js +5 -2
  182. package/dist/server/headers.js.map +1 -1
  183. package/dist/server/html.js +1 -1
  184. package/dist/server/html.js.map +1 -1
  185. package/dist/server/http-error-responses.d.ts +10 -0
  186. package/dist/server/http-error-responses.js +11 -1
  187. package/dist/server/http-error-responses.js.map +1 -1
  188. package/dist/server/isr-cache.d.ts +2 -1
  189. package/dist/server/isr-cache.js +4 -2
  190. package/dist/server/isr-cache.js.map +1 -1
  191. package/dist/server/metadata-route-response.js +22 -5
  192. package/dist/server/metadata-route-response.js.map +1 -1
  193. package/dist/server/metadata-routes.js +27 -8
  194. package/dist/server/metadata-routes.js.map +1 -1
  195. package/dist/server/middleware-runtime.js +1 -0
  196. package/dist/server/middleware-runtime.js.map +1 -1
  197. package/dist/server/middleware.d.ts +12 -0
  198. package/dist/server/middleware.js +12 -0
  199. package/dist/server/middleware.js.map +1 -1
  200. package/dist/server/navigation-planner.d.ts +19 -5
  201. package/dist/server/navigation-planner.js +278 -17
  202. package/dist/server/navigation-planner.js.map +1 -1
  203. package/dist/server/navigation-trace.d.ts +8 -1
  204. package/dist/server/navigation-trace.js +7 -0
  205. package/dist/server/navigation-trace.js.map +1 -1
  206. package/dist/server/normalize-path.d.ts +2 -1
  207. package/dist/server/normalize-path.js +4 -1
  208. package/dist/server/normalize-path.js.map +1 -1
  209. package/dist/server/pages-api-route.js +1 -0
  210. package/dist/server/pages-api-route.js.map +1 -1
  211. package/dist/server/pages-page-data.d.ts +3 -2
  212. package/dist/server/pages-page-data.js +22 -3
  213. package/dist/server/pages-page-data.js.map +1 -1
  214. package/dist/server/pages-page-response.js +1 -1
  215. package/dist/server/prod-server.d.ts +28 -1
  216. package/dist/server/prod-server.js +62 -9
  217. package/dist/server/prod-server.js.map +1 -1
  218. package/dist/server/server-action-not-found.d.ts +16 -3
  219. package/dist/server/server-action-not-found.js +19 -1
  220. package/dist/server/server-action-not-found.js.map +1 -1
  221. package/dist/server/server-globals.d.ts +5 -0
  222. package/dist/server/server-globals.js +37 -0
  223. package/dist/server/server-globals.js.map +1 -0
  224. package/dist/server/static-file-cache.js +1 -1
  225. package/dist/server/static-file-cache.js.map +1 -1
  226. package/dist/shims/cache-runtime.d.ts +19 -2
  227. package/dist/shims/cache-runtime.js +67 -11
  228. package/dist/shims/cache-runtime.js.map +1 -1
  229. package/dist/shims/cache.d.ts +5 -18
  230. package/dist/shims/cache.js +2 -0
  231. package/dist/shims/cache.js.map +1 -1
  232. package/dist/shims/error-boundary.js +6 -8
  233. package/dist/shims/error-boundary.js.map +1 -1
  234. package/dist/shims/error.d.ts +18 -1
  235. package/dist/shims/error.js +56 -1
  236. package/dist/shims/error.js.map +1 -1
  237. package/dist/shims/fetch-cache.d.ts +4 -1
  238. package/dist/shims/fetch-cache.js +40 -5
  239. package/dist/shims/fetch-cache.js.map +1 -1
  240. package/dist/shims/font-google-base.d.ts +22 -8
  241. package/dist/shims/font-google-base.js +41 -71
  242. package/dist/shims/font-google-base.js.map +1 -1
  243. package/dist/shims/font-local.d.ts +3 -20
  244. package/dist/shims/font-local.js +23 -75
  245. package/dist/shims/font-local.js.map +1 -1
  246. package/dist/shims/font-utils.d.ts +51 -0
  247. package/dist/shims/font-utils.js +97 -0
  248. package/dist/shims/font-utils.js.map +1 -0
  249. package/dist/shims/hash-scroll.d.ts +7 -0
  250. package/dist/shims/hash-scroll.js +30 -0
  251. package/dist/shims/hash-scroll.js.map +1 -0
  252. package/dist/shims/headers.d.ts +8 -11
  253. package/dist/shims/headers.js +22 -2
  254. package/dist/shims/headers.js.map +1 -1
  255. package/dist/shims/image.d.ts +1 -0
  256. package/dist/shims/image.js +144 -78
  257. package/dist/shims/image.js.map +1 -1
  258. package/dist/shims/internal/app-router-context.d.ts +6 -6
  259. package/dist/shims/internal/app-router-context.js +17 -6
  260. package/dist/shims/internal/app-router-context.js.map +1 -1
  261. package/dist/shims/link-prefetch.d.ts +9 -1
  262. package/dist/shims/link-prefetch.js +11 -6
  263. package/dist/shims/link-prefetch.js.map +1 -1
  264. package/dist/shims/link.d.ts +12 -2
  265. package/dist/shims/link.js +78 -32
  266. package/dist/shims/link.js.map +1 -1
  267. package/dist/shims/metadata.d.ts +16 -30
  268. package/dist/shims/metadata.js +87 -28
  269. package/dist/shims/metadata.js.map +1 -1
  270. package/dist/shims/navigation.d.ts +158 -17
  271. package/dist/shims/navigation.js +324 -74
  272. package/dist/shims/navigation.js.map +1 -1
  273. package/dist/shims/navigation.react-server.d.ts +3 -2
  274. package/dist/shims/navigation.react-server.js +5 -2
  275. package/dist/shims/navigation.react-server.js.map +1 -1
  276. package/dist/shims/pages-router-runtime.d.ts +7 -0
  277. package/dist/shims/pages-router-runtime.js +16 -0
  278. package/dist/shims/pages-router-runtime.js.map +1 -0
  279. package/dist/shims/router.d.ts +32 -6
  280. package/dist/shims/router.js +197 -242
  281. package/dist/shims/router.js.map +1 -1
  282. package/dist/shims/script.js +110 -32
  283. package/dist/shims/script.js.map +1 -1
  284. package/dist/shims/server.js +2 -1
  285. package/dist/shims/server.js.map +1 -1
  286. package/dist/shims/slot.d.ts +1 -0
  287. package/dist/shims/slot.js +41 -1
  288. package/dist/shims/slot.js.map +1 -1
  289. package/dist/shims/unified-request-context.js +2 -0
  290. package/dist/shims/unified-request-context.js.map +1 -1
  291. package/dist/shims/unrecognized-action-error.d.ts +35 -0
  292. package/dist/shims/unrecognized-action-error.js +41 -0
  293. package/dist/shims/unrecognized-action-error.js.map +1 -0
  294. package/dist/shims/url-utils.d.ts +21 -1
  295. package/dist/shims/url-utils.js +67 -3
  296. package/dist/shims/url-utils.js.map +1 -1
  297. package/dist/utils/asset-prefix.d.ts +69 -0
  298. package/dist/utils/asset-prefix.js +91 -0
  299. package/dist/utils/asset-prefix.js.map +1 -0
  300. package/dist/utils/base-path.d.ts +7 -1
  301. package/dist/utils/base-path.js +10 -1
  302. package/dist/utils/base-path.js.map +1 -1
  303. package/dist/utils/navigation-signal.d.ts +1 -2
  304. package/dist/utils/navigation-signal.js +1 -1
  305. package/dist/utils/navigation-signal.js.map +1 -1
  306. package/dist/utils/sorted-array.d.ts +9 -0
  307. package/dist/utils/sorted-array.js +22 -0
  308. package/dist/utils/sorted-array.js.map +1 -0
  309. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"cache-proof.js","names":[],"sources":["../../src/server/cache-proof.ts"],"sourcesContent":["import type { AppRouteSemanticIds } from \"../routing/app-route-graph.js\";\nimport { fnv1a64 } from \"../utils/hash.js\";\n\nexport const CACHE_PROOF_MODEL_SCHEMA_VERSION = 0;\nexport type CacheProofModelSchemaVersion = 0;\n\nexport type CacheProofRejectionCode =\n | \"CP_MODEL_DISABLED\"\n | \"CP_DIMENSION_COUNT_EXCEEDED\"\n | \"CP_DIMENSION_NAME_MISSING\"\n | \"CP_DIMENSION_NAME_TOO_LONG\"\n | \"CP_DIMENSION_VALUE_COUNT_EXCEEDED\"\n | \"CP_DIMENSION_VALUE_TOO_LONG\"\n | \"CP_DIMENSION_VALUES_MISSING\"\n | \"CP_ENCODED_VARIANT_TOO_LONG\"\n | \"CP_INVALID_VARIANT_BUDGET\"\n | \"CP_ROUTE_VARIANT_CEILING_EXCEEDED\"\n | \"CP_UNSAFE_PUBLIC_DIMENSION\"\n | \"CP_BOUNDARY_OUTCOME_MISMATCH\"\n | \"CP_BOUNDARY_OUTCOME_UNKNOWN\";\n\nexport type CacheProofTraceFieldValue = string | number | boolean | null | readonly string[];\n\nexport type CacheProofTraceFields = Readonly<Record<string, CacheProofTraceFieldValue>>;\n\nexport type CacheProofBreakerFallbackMode = \"renderFresh\" | \"privateUncacheable\";\nexport type CacheProofFallbackScope = \"affectedOutput\" | \"route\";\n\nexport type CacheProofBreakerFallback = Readonly<{\n kind: \"breakerFallback\";\n code: CacheProofRejectionCode;\n mode: CacheProofBreakerFallbackMode;\n scope: CacheProofFallbackScope;\n fields: CacheProofTraceFields;\n}>;\n\nexport type CacheVariantBudget = Readonly<{\n maxDimensionCount: number;\n maxDimensionNameLength: number;\n maxDimensionValueLength: number;\n maxEncodedLength: number;\n maxValuesPerDimension: number;\n maxVariantsPerRoute: number;\n}>;\n\nexport const DEFAULT_CACHE_VARIANT_BUDGET = {\n maxDimensionCount: 8,\n maxDimensionNameLength: 64,\n maxDimensionValueLength: 256,\n maxEncodedLength: 1024,\n maxValuesPerDimension: 8,\n maxVariantsPerRoute: 64,\n} satisfies CacheVariantBudget;\n\nexport type CacheVariantDimensionSource =\n | \"auth\"\n | \"cookie\"\n | \"custom\"\n | \"draft-mode\"\n | \"header\"\n | \"interception\"\n | \"mounted-slots\"\n | \"params\"\n | \"route\"\n | \"search\"\n | \"session\";\n\nexport type CacheVariantDimensionPrivacy = \"internal\" | \"private\" | \"public\";\n\nexport type CacheVariantDimensionInput = Readonly<{\n name: string;\n privacy: CacheVariantDimensionPrivacy;\n source: CacheVariantDimensionSource;\n values: readonly string[];\n}>;\n\nexport type CacheVariantDimension = Readonly<{\n encoded: string;\n name: string;\n privacy: CacheVariantDimensionPrivacy;\n source: CacheVariantDimensionSource;\n valueCount: number;\n valueHashes: readonly string[];\n}>;\n\nexport type CacheProofOutputScope =\n | Readonly<{\n kind: \"app-html\";\n renderEpoch: string | null;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"app-rsc\";\n mountedSlotsFingerprint: string | null;\n renderEpoch: string | null;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"layout\";\n layoutId: string;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"page\";\n pageId: string;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"route-handler\";\n routeHandlerId: string;\n routeId: string;\n }>\n | Readonly<{\n kind: \"slot\";\n rootBoundaryId: string | null;\n routeId: string;\n slotId: string;\n }>\n | Readonly<{\n kind: \"template\";\n rootBoundaryId: string | null;\n routeId: string;\n templateId: string;\n }>;\n\nexport type CacheVariant = Readonly<{\n budget: CacheVariantBudget;\n cacheKey: string;\n dimensions: readonly CacheVariantDimension[];\n encodedLength: number;\n output: CacheProofOutputScope;\n schemaVersion: CacheProofModelSchemaVersion;\n}>;\n\nexport type BuildCacheVariantInput = Readonly<{\n budget: CacheVariantBudget;\n dimensions: readonly CacheVariantDimensionInput[];\n existingVariantCount: number;\n output: CacheProofOutputScope;\n}>;\n\nexport type BuildCacheVariantResult =\n | Readonly<{ kind: \"variant\"; variant: CacheVariant }>\n | Readonly<{ kind: \"breakerFallback\"; fallback: CacheProofBreakerFallback }>;\n\nexport type AppRouteCacheProofGraphScopeInput = Readonly<{\n ids: AppRouteSemanticIds;\n}>;\n\nexport type AppRouteCacheProofGraphScope = Readonly<{\n layoutIds: readonly string[];\n pageId: string | null;\n routeHandlerId: string | null;\n routeId: string;\n slotIds: readonly string[];\n templateIds: readonly string[];\n}>;\n\nexport type BoundaryOutcome =\n | Readonly<{ kind: \"error\"; digest?: string }>\n | Readonly<{ kind: \"forbidden\" }>\n | Readonly<{ kind: \"globalError\"; digest?: string }>\n | Readonly<{ kind: \"notFound\" }>\n | Readonly<{ kind: \"redirect\"; location: string; status: number }>\n | Readonly<{ kind: \"success\" }>\n | Readonly<{ kind: \"unauthorized\" }>\n | Readonly<{ kind: \"unknown\" }>;\n\nexport type BoundaryOutcomeCompatibility =\n | Readonly<{\n kind: \"compatible\";\n outcome: BoundaryOutcome;\n reason: \"CP_BOUNDARY_OUTCOME_MATCH\";\n }>\n | Readonly<{\n candidate: BoundaryOutcome;\n expected: BoundaryOutcome;\n fallback: CacheProofBreakerFallback;\n kind: \"incompatible\";\n }>;\n\nexport type RenderObservationCompleteness = \"complete\" | \"partial\" | \"unknown\";\nexport type RenderCacheability = \"private\" | \"public\" | \"uncacheable\" | \"unknown\";\nexport type RenderRequestApiKind =\n | \"connection\"\n | \"cookies\"\n | \"draftMode\"\n | \"headers\"\n | \"params\"\n | \"searchParams\";\nexport type RenderRequestApiStatus = \"notObserved\" | \"observed\" | \"unknown\";\n\nexport type RenderRequestApiObservation = Readonly<{\n kind: RenderRequestApiKind;\n status: RenderRequestApiStatus;\n}>;\n\nexport type RenderObservation = Readonly<{\n boundaryOutcome: BoundaryOutcome;\n cacheTags: readonly string[];\n cacheability: RenderCacheability;\n completeness: RenderObservationCompleteness;\n dynamicFetches: readonly string[];\n output: CacheProofOutputScope;\n pathTags: readonly string[];\n requestApis: readonly RenderRequestApiObservation[];\n schemaVersion: CacheProofModelSchemaVersion;\n}>;\n\nexport type BuildRenderObservationInput = Readonly<{\n boundaryOutcome: BoundaryOutcome;\n cacheTags: readonly string[];\n cacheability: RenderCacheability;\n completeness: RenderObservationCompleteness;\n dynamicFetches: readonly string[];\n output: CacheProofOutputScope;\n pathTags: readonly string[];\n requestApis: readonly RenderRequestApiObservation[];\n}>;\n\nexport type DisabledCacheProofDecision = Readonly<{\n canReuse: false;\n fallback: CacheProofBreakerFallback;\n kind: \"disabled\";\n observation: RenderObservation;\n variant: CacheVariant;\n}>;\n\nexport type CreateDisabledCacheProofDecisionInput = Readonly<{\n observation: RenderObservation;\n variant: CacheVariant;\n}>;\n\nconst PUBLIC_UNSAFE_DIMENSION_SOURCES: ReadonlySet<CacheVariantDimensionSource> = new Set([\n \"auth\",\n \"cookie\",\n \"draft-mode\",\n \"header\",\n \"session\",\n]);\n\ntype CacheVariantDimensionAccumulator = {\n name: string;\n privacy: CacheVariantDimensionPrivacy;\n source: CacheVariantDimensionSource;\n values: string[];\n};\n\ntype DimensionAccumulatorByName = Map<string, CacheVariantDimensionAccumulator>;\ntype DimensionAccumulatorByPrivacy = Map<CacheVariantDimensionPrivacy, DimensionAccumulatorByName>;\ntype DimensionAccumulatorBySource = Map<CacheVariantDimensionSource, DimensionAccumulatorByPrivacy>;\n\nfunction buildBreakerFallback(\n code: CacheProofRejectionCode,\n fields: CacheProofTraceFields = {},\n mode: CacheProofBreakerFallbackMode = \"renderFresh\",\n scope: CacheProofFallbackScope = \"affectedOutput\",\n): CacheProofBreakerFallback {\n return {\n kind: \"breakerFallback\",\n code,\n mode,\n scope,\n fields,\n };\n}\n\nfunction sortedUnique(values: readonly string[]): string[] {\n return [...new Set(values)].sort();\n}\n\nfunction normalizeDimensionName(name: string): string {\n return name.trim().toLowerCase();\n}\n\nfunction redactValue(value: string): string {\n return `h:${fnv1a64(value)}`;\n}\n\nfunction sortedUniqueRedacted(values: readonly string[]): string[] {\n return sortedUnique(sortedUnique(values).map(redactValue));\n}\n\nfunction encodeParts(parts: readonly unknown[]): string {\n return JSON.stringify(parts);\n}\n\nfunction compareDimensions(a: CacheVariantDimension, b: CacheVariantDimension): number {\n return (\n a.source.localeCompare(b.source) ||\n a.name.localeCompare(b.name) ||\n a.privacy.localeCompare(b.privacy)\n );\n}\n\nfunction encodeNullable(value: string | null): string | null {\n return value;\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unhandled cache proof variant: ${String(value)}`);\n}\n\nfunction encodeOutputScope(output: CacheProofOutputScope): string {\n switch (output.kind) {\n case \"app-html\":\n return encodeParts([\n output.kind,\n output.routeId,\n encodeNullable(output.rootBoundaryId),\n encodeNullable(output.renderEpoch),\n ]);\n case \"app-rsc\":\n return encodeParts([\n output.kind,\n output.routeId,\n encodeNullable(output.rootBoundaryId),\n encodeNullable(output.renderEpoch),\n encodeNullable(output.mountedSlotsFingerprint),\n ]);\n case \"layout\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.layoutId,\n encodeNullable(output.rootBoundaryId),\n ]);\n case \"page\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.pageId,\n encodeNullable(output.rootBoundaryId),\n ]);\n case \"route-handler\":\n return encodeParts([output.kind, output.routeId, output.routeHandlerId]);\n case \"slot\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.slotId,\n encodeNullable(output.rootBoundaryId),\n ]);\n case \"template\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.templateId,\n encodeNullable(output.rootBoundaryId),\n ]);\n default:\n return assertNever(output);\n }\n}\n\nfunction validateBudgetNumber(name: string, value: number): CacheProofBreakerFallback | null {\n if (Number.isInteger(value) && value >= 0) return null;\n return buildBreakerFallback(\"CP_INVALID_VARIANT_BUDGET\", {\n budgetField: name,\n });\n}\n\nfunction validateBudget(budget: CacheVariantBudget): CacheProofBreakerFallback | null {\n return (\n validateBudgetNumber(\"maxDimensionCount\", budget.maxDimensionCount) ??\n validateBudgetNumber(\"maxDimensionNameLength\", budget.maxDimensionNameLength) ??\n validateBudgetNumber(\"maxDimensionValueLength\", budget.maxDimensionValueLength) ??\n validateBudgetNumber(\"maxEncodedLength\", budget.maxEncodedLength) ??\n validateBudgetNumber(\"maxValuesPerDimension\", budget.maxValuesPerDimension) ??\n validateBudgetNumber(\"maxVariantsPerRoute\", budget.maxVariantsPerRoute)\n );\n}\n\nfunction buildDimension(\n input: CacheVariantDimensionInput,\n budget: CacheVariantBudget,\n): CacheVariantDimension | CacheProofBreakerFallback {\n const name = normalizeDimensionName(input.name);\n if (name.length === 0) {\n return buildBreakerFallback(\"CP_DIMENSION_NAME_MISSING\", {\n source: input.source,\n });\n }\n if (name.length > budget.maxDimensionNameLength) {\n return buildBreakerFallback(\"CP_DIMENSION_NAME_TOO_LONG\", {\n maxLength: budget.maxDimensionNameLength,\n nameHash: redactValue(name),\n source: input.source,\n });\n }\n if (input.privacy === \"public\" && PUBLIC_UNSAFE_DIMENSION_SOURCES.has(input.source)) {\n return buildBreakerFallback(\n \"CP_UNSAFE_PUBLIC_DIMENSION\",\n {\n name,\n source: input.source,\n },\n \"privateUncacheable\",\n );\n }\n\n const values = sortedUnique(input.values);\n if (values.length === 0) {\n return buildBreakerFallback(\"CP_DIMENSION_VALUES_MISSING\", {\n name,\n source: input.source,\n });\n }\n if (values.length > budget.maxValuesPerDimension) {\n return buildBreakerFallback(\"CP_DIMENSION_VALUE_COUNT_EXCEEDED\", {\n maxValues: budget.maxValuesPerDimension,\n name,\n source: input.source,\n valueCount: values.length,\n });\n }\n for (const value of values) {\n if (value.length > budget.maxDimensionValueLength) {\n return buildBreakerFallback(\"CP_DIMENSION_VALUE_TOO_LONG\", {\n maxLength: budget.maxDimensionValueLength,\n name,\n source: input.source,\n valueHash: redactValue(value),\n });\n }\n }\n\n const valueHashes = values.map(redactValue);\n const encoded = encodeParts([input.source, input.privacy, name, valueHashes]);\n\n return {\n encoded,\n name,\n privacy: input.privacy,\n source: input.source,\n valueCount: valueHashes.length,\n valueHashes,\n };\n}\n\nfunction isCacheProofBreakerFallback(\n value: CacheVariantDimension | CacheProofBreakerFallback,\n): value is CacheProofBreakerFallback {\n return \"code\" in value;\n}\n\nfunction getDimensionBucket(\n bySource: DimensionAccumulatorBySource,\n source: CacheVariantDimensionSource,\n privacy: CacheVariantDimensionPrivacy,\n): DimensionAccumulatorByName {\n const existingByPrivacy = bySource.get(source);\n const byPrivacy = existingByPrivacy ?? new Map();\n if (!existingByPrivacy) {\n bySource.set(source, byPrivacy);\n }\n\n const existingByName = byPrivacy.get(privacy);\n const byName = existingByName ?? new Map();\n if (!existingByName) {\n byPrivacy.set(privacy, byName);\n }\n\n return byName;\n}\n\nfunction mergeDimensionInputs(\n dimensions: readonly CacheVariantDimensionInput[],\n): CacheVariantDimensionInput[] {\n const bySource: DimensionAccumulatorBySource = new Map();\n const orderedDimensions: CacheVariantDimensionAccumulator[] = [];\n\n for (const dimension of dimensions) {\n const name = normalizeDimensionName(dimension.name);\n const bucket = getDimensionBucket(bySource, dimension.source, dimension.privacy);\n const existing = bucket.get(name);\n if (existing) {\n existing.values.push(...dimension.values);\n continue;\n }\n const accumulator = {\n name,\n privacy: dimension.privacy,\n source: dimension.source,\n values: [...dimension.values],\n };\n bucket.set(name, accumulator);\n orderedDimensions.push(accumulator);\n }\n\n return orderedDimensions;\n}\n\nexport function createAppRouteCacheProofGraphScope(\n route: AppRouteCacheProofGraphScopeInput,\n): AppRouteCacheProofGraphScope {\n return {\n routeId: route.ids.route,\n pageId: route.ids.page,\n routeHandlerId: route.ids.routeHandler,\n layoutIds: [...route.ids.layouts],\n templateIds: [...route.ids.templates],\n slotIds: sortedUnique(Object.values(route.ids.slots)),\n };\n}\n\nexport function buildCacheVariant(input: BuildCacheVariantInput): BuildCacheVariantResult {\n const budgetFallback = validateBudget(input.budget);\n if (budgetFallback) {\n return {\n kind: \"breakerFallback\",\n fallback: budgetFallback,\n };\n }\n if (input.existingVariantCount >= input.budget.maxVariantsPerRoute) {\n return {\n kind: \"breakerFallback\",\n fallback: buildBreakerFallback(\n \"CP_ROUTE_VARIANT_CEILING_EXCEEDED\",\n {\n existingVariantCount: input.existingVariantCount,\n maxVariantsPerRoute: input.budget.maxVariantsPerRoute,\n routeId: input.output.routeId,\n },\n \"privateUncacheable\",\n \"route\",\n ),\n };\n }\n const dimensionInputs = mergeDimensionInputs(input.dimensions);\n if (dimensionInputs.length > input.budget.maxDimensionCount) {\n return {\n kind: \"breakerFallback\",\n fallback: buildBreakerFallback(\"CP_DIMENSION_COUNT_EXCEEDED\", {\n dimensionCount: dimensionInputs.length,\n maxDimensionCount: input.budget.maxDimensionCount,\n routeId: input.output.routeId,\n }),\n };\n }\n\n const dimensions: CacheVariantDimension[] = [];\n for (const dimensionInput of dimensionInputs) {\n const dimension = buildDimension(dimensionInput, input.budget);\n if (isCacheProofBreakerFallback(dimension)) {\n return {\n kind: \"breakerFallback\",\n fallback: dimension,\n };\n }\n dimensions.push(dimension);\n }\n dimensions.sort(compareDimensions);\n\n const encoded = [\n `schema:${CACHE_PROOF_MODEL_SCHEMA_VERSION}`,\n encodeOutputScope(input.output),\n ...dimensions.map((dimension) => dimension.encoded),\n ].join(\"|\");\n\n if (encoded.length > input.budget.maxEncodedLength) {\n return {\n kind: \"breakerFallback\",\n fallback: buildBreakerFallback(\"CP_ENCODED_VARIANT_TOO_LONG\", {\n encodedHash: redactValue(encoded),\n encodedLength: encoded.length,\n maxEncodedLength: input.budget.maxEncodedLength,\n routeId: input.output.routeId,\n }),\n };\n }\n\n return {\n kind: \"variant\",\n variant: {\n schemaVersion: CACHE_PROOF_MODEL_SCHEMA_VERSION,\n cacheKey: `cp0:${fnv1a64(encoded)}`,\n output: input.output,\n dimensions,\n encodedLength: encoded.length,\n budget: { ...input.budget },\n },\n };\n}\n\nfunction boundaryOutcomesMatch(expected: BoundaryOutcome, candidate: BoundaryOutcome): boolean {\n switch (expected.kind) {\n case \"error\":\n return candidate.kind === \"error\" && (expected.digest ?? \"\") === (candidate.digest ?? \"\");\n case \"forbidden\":\n return candidate.kind === \"forbidden\";\n case \"globalError\":\n return (\n candidate.kind === \"globalError\" && (expected.digest ?? \"\") === (candidate.digest ?? \"\")\n );\n case \"notFound\":\n return candidate.kind === \"notFound\";\n case \"redirect\":\n return (\n candidate.kind === \"redirect\" &&\n expected.status === candidate.status &&\n expected.location === candidate.location\n );\n case \"success\":\n return candidate.kind === \"success\";\n case \"unauthorized\":\n return candidate.kind === \"unauthorized\";\n case \"unknown\":\n return false;\n default:\n return assertNever(expected);\n }\n}\n\nexport function buildBoundaryOutcomeCompatibility(input: {\n candidate: BoundaryOutcome;\n expected: BoundaryOutcome;\n}): BoundaryOutcomeCompatibility {\n if (input.expected.kind === \"unknown\" || input.candidate.kind === \"unknown\") {\n return {\n kind: \"incompatible\",\n expected: input.expected,\n candidate: input.candidate,\n fallback: buildBreakerFallback(\"CP_BOUNDARY_OUTCOME_UNKNOWN\", {\n candidateKind: input.candidate.kind,\n expectedKind: input.expected.kind,\n }),\n };\n }\n\n if (boundaryOutcomesMatch(input.expected, input.candidate)) {\n return {\n kind: \"compatible\",\n outcome: input.candidate,\n reason: \"CP_BOUNDARY_OUTCOME_MATCH\",\n };\n }\n\n return {\n kind: \"incompatible\",\n expected: input.expected,\n candidate: input.candidate,\n fallback: buildBreakerFallback(\"CP_BOUNDARY_OUTCOME_MISMATCH\", {\n candidateKind: input.candidate.kind,\n expectedKind: input.expected.kind,\n }),\n };\n}\n\nfunction requestApiStatusRank(status: RenderRequestApiStatus): number {\n switch (status) {\n case \"notObserved\":\n return 0;\n case \"unknown\":\n return 1;\n case \"observed\":\n return 2;\n default:\n return assertNever(status);\n }\n}\n\nfunction normalizeRequestApiObservations(\n observations: readonly RenderRequestApiObservation[],\n): RenderRequestApiObservation[] {\n const byKind = new Map<RenderRequestApiKind, RenderRequestApiStatus>();\n for (const observation of observations) {\n const current = byKind.get(observation.kind);\n if (\n current === undefined ||\n requestApiStatusRank(observation.status) > requestApiStatusRank(current)\n ) {\n byKind.set(observation.kind, observation.status);\n }\n }\n\n return [...byKind.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([kind, status]) => ({ kind, status }));\n}\n\nexport function buildRenderObservation(input: BuildRenderObservationInput): RenderObservation {\n return {\n schemaVersion: CACHE_PROOF_MODEL_SCHEMA_VERSION,\n output: input.output,\n completeness: input.completeness,\n boundaryOutcome: input.boundaryOutcome,\n requestApis: normalizeRequestApiObservations(input.requestApis),\n dynamicFetches: sortedUniqueRedacted(input.dynamicFetches),\n cacheTags: sortedUnique(input.cacheTags),\n pathTags: sortedUnique(input.pathTags),\n cacheability: input.cacheability,\n };\n}\n\nexport function hasCompleteNegativeRequestApiProof(\n observation: RenderObservation,\n requiredApis: readonly RenderRequestApiKind[],\n): boolean {\n if (observation.completeness !== \"complete\") return false;\n\n const statuses = new Map<RenderRequestApiKind, RenderRequestApiStatus>();\n for (const requestApi of observation.requestApis) {\n statuses.set(requestApi.kind, requestApi.status);\n }\n\n for (const api of requiredApis) {\n if (statuses.get(api) !== \"notObserved\") return false;\n }\n return true;\n}\n\nexport function createDisabledCacheProofDecision(\n input: CreateDisabledCacheProofDecisionInput,\n): DisabledCacheProofDecision {\n return {\n kind: \"disabled\",\n canReuse: false,\n variant: input.variant,\n observation: input.observation,\n fallback: buildBreakerFallback(\"CP_MODEL_DISABLED\"),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cache-proof.js","names":[],"sources":["../../src/server/cache-proof.ts"],"sourcesContent":["import type { AppRouteSemanticIds } from \"../routing/app-route-graph.js\";\nimport { fnv1a64 } from \"../utils/hash.js\";\nimport { findSortedStringPosition } from \"../utils/sorted-array.js\";\n\nexport const CACHE_PROOF_MODEL_SCHEMA_VERSION = 1;\nexport type CacheProofModelSchemaVersion = 1;\n\nexport type CacheProofRejectionCode =\n | \"CP_MODEL_DISABLED\"\n | \"CP_DIMENSION_COUNT_EXCEEDED\"\n | \"CP_DIMENSION_NAME_MISSING\"\n | \"CP_DIMENSION_NAME_TOO_LONG\"\n | \"CP_DIMENSION_VALUE_COUNT_EXCEEDED\"\n | \"CP_DIMENSION_VALUE_TOO_LONG\"\n | \"CP_DIMENSION_VALUES_MISSING\"\n | \"CP_ENCODED_VARIANT_TOO_LONG\"\n | \"CP_INVALID_VARIANT_BUDGET\"\n | \"CP_ROUTE_VARIANT_BUDGET_ROUTE_MISMATCH\"\n | \"CP_ROUTE_VARIANT_CEILING_EXCEEDED\"\n | \"CP_UNSAFE_PUBLIC_DIMENSION\"\n | \"CP_BOUNDARY_OUTCOME_MISMATCH\"\n | \"CP_BOUNDARY_OUTCOME_UNKNOWN\"\n | \"CP_PRIVATE_DYNAMIC_DOWNGRADE\"\n | \"CP_STATIC_LAYOUT_CANDIDATE_OUTPUT_KIND\"\n | \"CP_STATIC_LAYOUT_CURRENT_OUTPUT_KIND\"\n | \"CP_STATIC_LAYOUT_ID_MISMATCH\"\n | \"CP_STATIC_LAYOUT_OBSERVATION_OUTPUT_KIND\"\n | \"CP_STATIC_LAYOUT_OBSERVATION_OUTPUT_MISMATCH\"\n | \"CP_STATIC_LAYOUT_PRIVATE_DYNAMIC_DOWNGRADE\"\n | \"CP_STATIC_LAYOUT_PRIVATE_VARIANT_DIMENSION\"\n | \"CP_STATIC_LAYOUT_REQUEST_API_OBSERVED\"\n | \"CP_STATIC_LAYOUT_REQUEST_API_UNKNOWN\"\n | \"CP_STATIC_LAYOUT_ROOT_BOUNDARY_MISMATCH\"\n | \"CP_STATIC_LAYOUT_ROOT_BOUNDARY_UNKNOWN\";\n\nexport type CacheProofAcceptanceCode = \"CP_STATIC_LAYOUT_REUSE_PROVEN\";\n\nexport type CacheProofTraceCode = CacheProofAcceptanceCode | CacheProofRejectionCode;\n\nexport type CacheProofTraceFieldValue = string | number | boolean | null | readonly string[];\n\nexport type CacheProofTraceFields = Readonly<Record<string, CacheProofTraceFieldValue>>;\n\nexport type CacheProofBreakerFallbackMode = \"renderFresh\" | \"privateUncacheable\";\nexport type CacheProofFallbackScope = \"affectedOutput\" | \"route\";\n\nexport type CacheProofBreakerFallback = Readonly<{\n kind: \"breakerFallback\";\n code: CacheProofRejectionCode;\n mode: CacheProofBreakerFallbackMode;\n scope: CacheProofFallbackScope;\n fields: CacheProofTraceFields;\n}>;\n\nexport type CacheVariantBudget = Readonly<{\n maxDimensionCount: number;\n maxDimensionNameLength: number;\n maxDimensionValueLength: number;\n maxEncodedLength: number;\n maxValuesPerDimension: number;\n maxVariantsPerRoute: number;\n}>;\n\nexport const DEFAULT_CACHE_VARIANT_BUDGET = {\n maxDimensionCount: 8,\n maxDimensionNameLength: 64,\n maxDimensionValueLength: 256,\n maxEncodedLength: 1024,\n maxValuesPerDimension: 8,\n maxVariantsPerRoute: 64,\n} satisfies CacheVariantBudget;\n\nexport type CacheVariantDimensionSource =\n | \"auth\"\n | \"cookie\"\n | \"custom\"\n | \"draft-mode\"\n | \"header\"\n | \"interception\"\n | \"mounted-slots\"\n | \"params\"\n | \"route\"\n | \"search\"\n | \"session\";\n\nexport type CacheVariantDimensionPrivacy = \"internal\" | \"private\" | \"public\";\n\nexport type CacheVariantDimensionInput = Readonly<{\n name: string;\n privacy: CacheVariantDimensionPrivacy;\n source: CacheVariantDimensionSource;\n values: readonly string[];\n}>;\n\nexport type CacheVariantDimension = Readonly<{\n encoded: string;\n name: string;\n privacy: CacheVariantDimensionPrivacy;\n source: CacheVariantDimensionSource;\n valueCount: number;\n valueHashes: readonly string[];\n}>;\n\nexport type CacheProofOutputScope =\n | Readonly<{\n kind: \"app-html\";\n renderEpoch: string | null;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"app-rsc\";\n mountedSlotsFingerprint: string | null;\n renderEpoch: string | null;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"layout\";\n layoutId: string;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"page\";\n pageId: string;\n rootBoundaryId: string | null;\n routeId: string;\n }>\n | Readonly<{\n kind: \"route-handler\";\n routeHandlerId: string;\n routeId: string;\n }>\n | Readonly<{\n kind: \"slot\";\n rootBoundaryId: string | null;\n routeId: string;\n slotId: string;\n }>\n | Readonly<{\n kind: \"template\";\n rootBoundaryId: string | null;\n routeId: string;\n templateId: string;\n }>;\n\nexport type StaticLayoutCacheProofOutputScope = Extract<CacheProofOutputScope, { kind: \"layout\" }>;\n\nexport type CacheVariant = Readonly<{\n budget: CacheVariantBudget;\n cacheKey: string;\n dimensions: readonly CacheVariantDimension[];\n encodedLength: number;\n output: CacheProofOutputScope;\n schemaVersion: CacheProofModelSchemaVersion;\n}>;\n\nexport type BuildCacheVariantInput = Readonly<{\n budget: CacheVariantBudget;\n dimensions: readonly CacheVariantDimensionInput[];\n output: CacheProofOutputScope;\n}>;\n\nexport type BuildCacheVariantResult =\n | Readonly<{ kind: \"variant\"; variant: CacheVariant }>\n | Readonly<{ kind: \"breakerFallback\"; fallback: CacheProofBreakerFallback }>;\n\nexport type CacheVariantRouteBudget = Readonly<{\n routeId: string;\n variantCacheKeys: readonly string[];\n}>;\n\nexport type CacheVariantRouteBudgetAdmission =\n | Readonly<{\n didConsumeRouteVariantBudget: boolean;\n kind: \"variant\";\n routeBudget: CacheVariantRouteBudget;\n variant: CacheVariant;\n }>\n | Readonly<{\n fallback: CacheProofBreakerFallback;\n kind: \"breakerFallback\";\n routeBudget: CacheVariantRouteBudget | null;\n }>;\n\nexport type BuildCacheVariantWithRouteBudgetInput = BuildCacheVariantInput &\n Readonly<{\n routeBudget: CacheVariantRouteBudget | null;\n }>;\n\nexport type BuildCacheVariantWithRouteBudgetResult = CacheVariantRouteBudgetAdmission;\n\nexport type AppRouteCacheProofGraphScopeInput = Readonly<{\n ids: AppRouteSemanticIds;\n}>;\n\nexport type AppRouteCacheProofGraphScope = Readonly<{\n layoutIds: readonly string[];\n pageId: string | null;\n routeHandlerId: string | null;\n routeId: string;\n slotIds: readonly string[];\n templateIds: readonly string[];\n}>;\n\nexport type BoundaryOutcome =\n | Readonly<{ kind: \"error\"; digest?: string }>\n | Readonly<{ kind: \"forbidden\" }>\n | Readonly<{ kind: \"globalError\"; digest?: string }>\n | Readonly<{ kind: \"notFound\" }>\n | Readonly<{ kind: \"redirect\"; location: string; status: number }>\n | Readonly<{ kind: \"success\" }>\n | Readonly<{ kind: \"unauthorized\" }>\n | Readonly<{ kind: \"unknown\" }>;\n\nexport type BoundaryOutcomeCompatibility =\n | Readonly<{\n kind: \"compatible\";\n outcome: BoundaryOutcome;\n reason: \"CP_BOUNDARY_OUTCOME_MATCH\";\n }>\n | Readonly<{\n candidate: BoundaryOutcome;\n expected: BoundaryOutcome;\n fallback: CacheProofBreakerFallback;\n kind: \"incompatible\";\n }>;\n\nexport type RenderObservationCompleteness = \"complete\" | \"partial\" | \"unknown\";\nexport type RenderCacheability = \"private\" | \"public\" | \"uncacheable\" | \"unknown\";\nexport type RenderRequestApiKind =\n | \"connection\"\n | \"cookies\"\n | \"draftMode\"\n | \"headers\"\n | \"params\"\n | \"searchParams\";\nexport type RenderRequestApiStatus = \"notObserved\" | \"observed\" | \"unknown\";\n\nexport const ALL_RENDER_REQUEST_API_KINDS: readonly RenderRequestApiKind[] = [\n \"connection\",\n \"cookies\",\n \"draftMode\",\n \"headers\",\n \"params\",\n \"searchParams\",\n];\n\nexport type RenderRequestApiObservation = Readonly<{\n kind: RenderRequestApiKind;\n status: RenderRequestApiStatus;\n}>;\n\nexport type CacheProofDowngradeTarget =\n | \"freshRender\"\n | \"private\"\n | \"privateUncacheable\"\n | \"public\"\n | \"publicVariant\";\n\nexport type CacheProofDowngradeReason =\n | Readonly<{\n code: \"CP_DOWNGRADE_CACHEABILITY_PRIVATE\";\n target: \"private\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_CACHEABILITY_UNCACHEABLE\";\n target: \"privateUncacheable\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_CACHEABILITY_UNKNOWN\";\n target: \"freshRender\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_DYNAMIC_FETCH\";\n dynamicFetchCount: number;\n target: \"freshRender\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_DYNAMIC_REQUEST_API\";\n requestApi: \"connection\";\n target: \"freshRender\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_DRAFT_MODE\";\n requestApi: \"draftMode\";\n target: \"privateUncacheable\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_INCOMPLETE_OBSERVATION\";\n completeness: Exclude<RenderObservationCompleteness, \"complete\">;\n target: \"freshRender\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_PRIVATE_DIMENSION\";\n inputClass: \"auth\" | \"draft\" | \"private\" | \"session\";\n source: \"auth\" | \"cookie\" | \"draft-mode\" | \"header\" | \"session\";\n target: \"private\" | \"privateUncacheable\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_PRIVATE_REQUEST_API\";\n requestApi: \"cookies\" | \"headers\";\n target: \"private\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_PUBLIC_REQUEST_API\";\n requestApi: \"params\" | \"searchParams\";\n target: \"publicVariant\";\n }>\n | Readonly<{\n code: \"CP_DOWNGRADE_UNKNOWN_REQUEST_API\";\n requestApi: RenderRequestApiKind;\n target: \"freshRender\";\n }>;\n\nexport type CacheProofDowngradeClassification = Readonly<{\n fallback: CacheProofBreakerFallback | null;\n isPublicCacheCandidate: boolean;\n reasons: readonly CacheProofDowngradeReason[];\n target: CacheProofDowngradeTarget;\n}>;\n\nexport type ClassifyRenderObservationDowngradeInput = Readonly<{\n cacheability: RenderCacheability;\n completeness: RenderObservationCompleteness;\n dynamicFetches: readonly string[];\n requestApis: readonly RenderRequestApiObservation[];\n}>;\n\nexport type ClassifyCacheVariantDimensionDowngradeInput = Pick<\n CacheVariantDimensionInput,\n \"source\"\n>;\n\nexport type RenderObservation = Readonly<{\n boundaryOutcome: BoundaryOutcome;\n cacheTags: readonly string[];\n cacheability: RenderCacheability;\n completeness: RenderObservationCompleteness;\n downgrade: CacheProofDowngradeClassification;\n dynamicFetches: readonly string[];\n output: CacheProofOutputScope;\n pathTags: readonly string[];\n requestApis: readonly RenderRequestApiObservation[];\n schemaVersion: CacheProofModelSchemaVersion;\n}>;\n\nexport type StaticLayoutReuseProof = Readonly<{\n authorizesRuntimeReuse: false;\n candidateOutput: StaticLayoutCacheProofOutputScope;\n code: \"CP_STATIC_LAYOUT_REUSE_PROVEN\";\n currentOutput: StaticLayoutCacheProofOutputScope;\n fields: CacheProofTraceFields;\n observation: RenderObservation;\n requiredNegativeRequestApis: readonly RenderRequestApiKind[];\n reuseClass: \"static-layout\";\n variant: CacheVariant;\n}>;\n\nexport type BuildStaticLayoutReuseProofInput = Readonly<{\n candidateObservation: RenderObservation;\n candidateVariant: CacheVariant;\n currentOutput: CacheProofOutputScope;\n}>;\n\nexport type BuildStaticLayoutReuseProofResult =\n | Readonly<{ kind: \"proof\"; proof: StaticLayoutReuseProof }>\n | Readonly<{ kind: \"rejected\"; fallback: CacheProofBreakerFallback }>;\n\nexport type BuildRenderObservationInput = Readonly<{\n boundaryOutcome: BoundaryOutcome;\n cacheTags: readonly string[];\n cacheability: RenderCacheability;\n completeness: RenderObservationCompleteness;\n dynamicFetches: readonly string[];\n output: CacheProofOutputScope;\n pathTags: readonly string[];\n requestApis: readonly RenderRequestApiObservation[];\n}>;\n\nexport type BuildRenderRequestApiObservationsInput = Readonly<{\n completeness: RenderObservationCompleteness;\n observed: readonly RenderRequestApiKind[];\n}>;\n\nexport type DisabledCacheProofDecision = Readonly<{\n canReuse: false;\n fallback: CacheProofBreakerFallback;\n kind: \"disabled\";\n observation: RenderObservation;\n staticLayoutProof?: StaticLayoutReuseProof;\n variant: CacheVariant;\n}>;\n\nexport type CreateDisabledCacheProofDecisionInput = Readonly<{\n observation: RenderObservation;\n staticLayoutProof?: StaticLayoutReuseProof;\n variant: CacheVariant;\n}>;\n\nconst PUBLIC_UNSAFE_DIMENSION_SOURCES: ReadonlySet<CacheVariantDimensionSource> = new Set([\n \"auth\",\n \"cookie\",\n \"draft-mode\",\n \"header\",\n \"session\",\n]);\n\ntype CacheVariantDimensionAccumulator = {\n name: string;\n privacy: CacheVariantDimensionPrivacy;\n source: CacheVariantDimensionSource;\n values: string[];\n};\n\ntype DimensionAccumulatorByName = Map<string, CacheVariantDimensionAccumulator>;\ntype DimensionAccumulatorByPrivacy = Map<CacheVariantDimensionPrivacy, DimensionAccumulatorByName>;\ntype DimensionAccumulatorBySource = Map<CacheVariantDimensionSource, DimensionAccumulatorByPrivacy>;\n\nfunction buildBreakerFallback(\n code: CacheProofRejectionCode,\n fields: CacheProofTraceFields = {},\n mode: CacheProofBreakerFallbackMode = \"renderFresh\",\n scope: CacheProofFallbackScope = \"affectedOutput\",\n): CacheProofBreakerFallback {\n return {\n kind: \"breakerFallback\",\n code,\n mode,\n scope,\n fields,\n };\n}\n\nfunction sortedUnique(values: readonly string[]): string[] {\n return [...new Set(values)].sort();\n}\n\nfunction normalizeDimensionName(name: string): string {\n return name.trim().toLowerCase();\n}\n\nfunction redactValue(value: string): string {\n return `h:${fnv1a64(value)}`;\n}\n\nfunction sortedUniqueRedacted(values: readonly string[]): string[] {\n return sortedUnique(sortedUnique(values).map(redactValue));\n}\n\nfunction encodeParts(parts: readonly unknown[]): string {\n return JSON.stringify(parts);\n}\n\nfunction compareDimensions(a: CacheVariantDimension, b: CacheVariantDimension): number {\n return (\n a.source.localeCompare(b.source) ||\n a.name.localeCompare(b.name) ||\n a.privacy.localeCompare(b.privacy)\n );\n}\n\nfunction encodeNullable(value: string | null): string | null {\n return value;\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unhandled cache proof variant: ${String(value)}`);\n}\n\nfunction encodeOutputScope(output: CacheProofOutputScope): string {\n switch (output.kind) {\n case \"app-html\":\n return encodeParts([\n output.kind,\n output.routeId,\n encodeNullable(output.rootBoundaryId),\n encodeNullable(output.renderEpoch),\n ]);\n case \"app-rsc\":\n return encodeParts([\n output.kind,\n output.routeId,\n encodeNullable(output.rootBoundaryId),\n encodeNullable(output.renderEpoch),\n encodeNullable(output.mountedSlotsFingerprint),\n ]);\n case \"layout\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.layoutId,\n encodeNullable(output.rootBoundaryId),\n ]);\n case \"page\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.pageId,\n encodeNullable(output.rootBoundaryId),\n ]);\n case \"route-handler\":\n return encodeParts([output.kind, output.routeId, output.routeHandlerId]);\n case \"slot\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.slotId,\n encodeNullable(output.rootBoundaryId),\n ]);\n case \"template\":\n return encodeParts([\n output.kind,\n output.routeId,\n output.templateId,\n encodeNullable(output.rootBoundaryId),\n ]);\n default:\n return assertNever(output);\n }\n}\n\nfunction validateBudgetNumber(name: string, value: number): CacheProofBreakerFallback | null {\n if (Number.isInteger(value) && value >= 0) return null;\n return buildBreakerFallback(\"CP_INVALID_VARIANT_BUDGET\", {\n budgetField: name,\n });\n}\n\nfunction validateBudget(budget: CacheVariantBudget): CacheProofBreakerFallback | null {\n return (\n validateBudgetNumber(\"maxDimensionCount\", budget.maxDimensionCount) ??\n validateBudgetNumber(\"maxDimensionNameLength\", budget.maxDimensionNameLength) ??\n validateBudgetNumber(\"maxDimensionValueLength\", budget.maxDimensionValueLength) ??\n validateBudgetNumber(\"maxEncodedLength\", budget.maxEncodedLength) ??\n validateBudgetNumber(\"maxValuesPerDimension\", budget.maxValuesPerDimension) ??\n validateBudgetNumber(\"maxVariantsPerRoute\", budget.maxVariantsPerRoute)\n );\n}\n\nfunction buildDimension(\n input: CacheVariantDimensionInput,\n budget: CacheVariantBudget,\n): CacheVariantDimension | CacheProofBreakerFallback {\n const name = normalizeDimensionName(input.name);\n if (name.length === 0) {\n return buildBreakerFallback(\"CP_DIMENSION_NAME_MISSING\", {\n source: input.source,\n });\n }\n if (name.length > budget.maxDimensionNameLength) {\n return buildBreakerFallback(\"CP_DIMENSION_NAME_TOO_LONG\", {\n maxLength: budget.maxDimensionNameLength,\n nameHash: redactValue(name),\n source: input.source,\n });\n }\n if (input.privacy === \"public\" && PUBLIC_UNSAFE_DIMENSION_SOURCES.has(input.source)) {\n return buildBreakerFallback(\n \"CP_UNSAFE_PUBLIC_DIMENSION\",\n {\n name,\n source: input.source,\n },\n \"privateUncacheable\",\n );\n }\n\n const values = sortedUnique(input.values);\n if (values.length === 0) {\n return buildBreakerFallback(\"CP_DIMENSION_VALUES_MISSING\", {\n name,\n source: input.source,\n });\n }\n if (values.length > budget.maxValuesPerDimension) {\n return buildBreakerFallback(\"CP_DIMENSION_VALUE_COUNT_EXCEEDED\", {\n maxValues: budget.maxValuesPerDimension,\n name,\n source: input.source,\n valueCount: values.length,\n });\n }\n for (const value of values) {\n if (value.length > budget.maxDimensionValueLength) {\n return buildBreakerFallback(\"CP_DIMENSION_VALUE_TOO_LONG\", {\n maxLength: budget.maxDimensionValueLength,\n name,\n source: input.source,\n valueHash: redactValue(value),\n });\n }\n }\n\n const valueHashes = values.map(redactValue);\n const encoded = encodeParts([input.source, input.privacy, name, valueHashes]);\n\n return {\n encoded,\n name,\n privacy: input.privacy,\n source: input.source,\n valueCount: valueHashes.length,\n valueHashes,\n };\n}\n\nfunction isCacheProofBreakerFallback(\n value: CacheVariantDimension | CacheProofBreakerFallback,\n): value is CacheProofBreakerFallback {\n return \"code\" in value;\n}\n\nfunction getDimensionBucket(\n bySource: DimensionAccumulatorBySource,\n source: CacheVariantDimensionSource,\n privacy: CacheVariantDimensionPrivacy,\n): DimensionAccumulatorByName {\n const existingByPrivacy = bySource.get(source);\n const byPrivacy = existingByPrivacy ?? new Map();\n if (!existingByPrivacy) {\n bySource.set(source, byPrivacy);\n }\n\n const existingByName = byPrivacy.get(privacy);\n const byName = existingByName ?? new Map();\n if (!existingByName) {\n byPrivacy.set(privacy, byName);\n }\n\n return byName;\n}\n\nfunction mergeDimensionInputs(\n dimensions: readonly CacheVariantDimensionInput[],\n): CacheVariantDimensionInput[] {\n const bySource: DimensionAccumulatorBySource = new Map();\n const orderedDimensions: CacheVariantDimensionAccumulator[] = [];\n\n for (const dimension of dimensions) {\n const name = normalizeDimensionName(dimension.name);\n const bucket = getDimensionBucket(bySource, dimension.source, dimension.privacy);\n const existing = bucket.get(name);\n if (existing) {\n existing.values.push(...dimension.values);\n continue;\n }\n const accumulator = {\n name,\n privacy: dimension.privacy,\n source: dimension.source,\n values: [...dimension.values],\n };\n bucket.set(name, accumulator);\n orderedDimensions.push(accumulator);\n }\n\n return orderedDimensions;\n}\n\nexport function createAppRouteCacheProofGraphScope(\n route: AppRouteCacheProofGraphScopeInput,\n): AppRouteCacheProofGraphScope {\n return {\n routeId: route.ids.route,\n pageId: route.ids.page,\n routeHandlerId: route.ids.routeHandler,\n layoutIds: [...route.ids.layouts],\n templateIds: [...route.ids.templates],\n slotIds: sortedUnique(Object.values(route.ids.slots)),\n };\n}\n\nexport function buildCacheVariant(input: BuildCacheVariantInput): BuildCacheVariantResult {\n const budgetFallback = validateBudget(input.budget);\n if (budgetFallback) {\n return {\n kind: \"breakerFallback\",\n fallback: budgetFallback,\n };\n }\n const dimensionInputs = mergeDimensionInputs(input.dimensions);\n if (dimensionInputs.length > input.budget.maxDimensionCount) {\n return {\n kind: \"breakerFallback\",\n fallback: buildBreakerFallback(\"CP_DIMENSION_COUNT_EXCEEDED\", {\n dimensionCount: dimensionInputs.length,\n maxDimensionCount: input.budget.maxDimensionCount,\n routeId: input.output.routeId,\n }),\n };\n }\n\n const dimensions: CacheVariantDimension[] = [];\n for (const dimensionInput of dimensionInputs) {\n const dimension = buildDimension(dimensionInput, input.budget);\n if (isCacheProofBreakerFallback(dimension)) {\n return {\n kind: \"breakerFallback\",\n fallback: dimension,\n };\n }\n dimensions.push(dimension);\n }\n dimensions.sort(compareDimensions);\n\n const encoded = [\n `schema:${CACHE_PROOF_MODEL_SCHEMA_VERSION}`,\n encodeOutputScope(input.output),\n ...dimensions.map((dimension) => dimension.encoded),\n ].join(\"|\");\n\n if (encoded.length > input.budget.maxEncodedLength) {\n return {\n kind: \"breakerFallback\",\n fallback: buildBreakerFallback(\"CP_ENCODED_VARIANT_TOO_LONG\", {\n encodedHash: redactValue(encoded),\n encodedLength: encoded.length,\n maxEncodedLength: input.budget.maxEncodedLength,\n routeId: input.output.routeId,\n }),\n };\n }\n\n return {\n kind: \"variant\",\n variant: {\n schemaVersion: CACHE_PROOF_MODEL_SCHEMA_VERSION,\n cacheKey: `cp${CACHE_PROOF_MODEL_SCHEMA_VERSION}:${fnv1a64(encoded)}`,\n output: input.output,\n dimensions,\n encodedLength: encoded.length,\n budget: { ...input.budget },\n },\n };\n}\n\nfunction normalizeRouteBudget(input: CacheVariantRouteBudget): CacheVariantRouteBudget {\n return {\n routeId: input.routeId,\n variantCacheKeys: sortedUnique(input.variantCacheKeys),\n };\n}\n\nfunction buildRouteVariantCeilingFallback(\n variant: CacheVariant,\n existingVariantCount: number,\n): CacheProofBreakerFallback {\n return buildBreakerFallback(\n \"CP_ROUTE_VARIANT_CEILING_EXCEEDED\",\n {\n existingVariantCount,\n maxVariantsPerRoute: variant.budget.maxVariantsPerRoute,\n routeId: variant.output.routeId,\n },\n \"privateUncacheable\",\n \"route\",\n );\n}\n\nexport function enforceCacheVariantRouteBudget(input: {\n routeBudget: CacheVariantRouteBudget | null;\n variant: CacheVariant;\n}): CacheVariantRouteBudgetAdmission {\n if (input.routeBudget && input.routeBudget.routeId !== input.variant.output.routeId) {\n return {\n kind: \"breakerFallback\",\n routeBudget: normalizeRouteBudget(input.routeBudget),\n fallback: buildBreakerFallback(\n \"CP_ROUTE_VARIANT_BUDGET_ROUTE_MISMATCH\",\n {\n budgetRouteId: input.routeBudget.routeId,\n routeId: input.variant.output.routeId,\n },\n \"privateUncacheable\",\n \"route\",\n ),\n };\n }\n\n const routeBudget = normalizeRouteBudget(\n input.routeBudget ?? {\n routeId: input.variant.output.routeId,\n variantCacheKeys: [],\n },\n );\n const existingVariantCount = routeBudget.variantCacheKeys.length;\n const variantKeyPosition = findSortedStringPosition(\n routeBudget.variantCacheKeys,\n input.variant.cacheKey,\n );\n\n if (existingVariantCount > input.variant.budget.maxVariantsPerRoute) {\n return {\n kind: \"breakerFallback\",\n routeBudget,\n fallback: buildRouteVariantCeilingFallback(input.variant, existingVariantCount),\n };\n }\n\n if (variantKeyPosition.found) {\n return {\n kind: \"variant\",\n variant: input.variant,\n routeBudget,\n didConsumeRouteVariantBudget: false,\n };\n }\n\n if (existingVariantCount >= input.variant.budget.maxVariantsPerRoute) {\n return {\n kind: \"breakerFallback\",\n routeBudget,\n fallback: buildRouteVariantCeilingFallback(input.variant, existingVariantCount),\n };\n }\n\n return {\n kind: \"variant\",\n variant: input.variant,\n routeBudget: {\n routeId: routeBudget.routeId,\n variantCacheKeys: [\n ...routeBudget.variantCacheKeys.slice(0, variantKeyPosition.index),\n input.variant.cacheKey,\n ...routeBudget.variantCacheKeys.slice(variantKeyPosition.index),\n ],\n },\n didConsumeRouteVariantBudget: true,\n };\n}\n\nexport function buildCacheVariantWithRouteBudget(\n input: BuildCacheVariantWithRouteBudgetInput,\n): BuildCacheVariantWithRouteBudgetResult {\n const variantResult = buildCacheVariant({\n budget: input.budget,\n dimensions: input.dimensions,\n output: input.output,\n });\n\n if (variantResult.kind === \"breakerFallback\") {\n return {\n kind: \"breakerFallback\",\n routeBudget: input.routeBudget ? normalizeRouteBudget(input.routeBudget) : null,\n fallback: variantResult.fallback,\n };\n }\n\n return enforceCacheVariantRouteBudget({\n routeBudget: input.routeBudget,\n variant: variantResult.variant,\n });\n}\n\nfunction boundaryOutcomesMatch(expected: BoundaryOutcome, candidate: BoundaryOutcome): boolean {\n switch (expected.kind) {\n case \"error\":\n return candidate.kind === \"error\" && (expected.digest ?? \"\") === (candidate.digest ?? \"\");\n case \"forbidden\":\n return candidate.kind === \"forbidden\";\n case \"globalError\":\n return (\n candidate.kind === \"globalError\" && (expected.digest ?? \"\") === (candidate.digest ?? \"\")\n );\n case \"notFound\":\n return candidate.kind === \"notFound\";\n case \"redirect\":\n return (\n candidate.kind === \"redirect\" &&\n expected.status === candidate.status &&\n expected.location === candidate.location\n );\n case \"success\":\n return candidate.kind === \"success\";\n case \"unauthorized\":\n return candidate.kind === \"unauthorized\";\n case \"unknown\":\n return false;\n default:\n return assertNever(expected);\n }\n}\n\nexport function buildBoundaryOutcomeCompatibility(input: {\n candidate: BoundaryOutcome;\n expected: BoundaryOutcome;\n}): BoundaryOutcomeCompatibility {\n if (input.expected.kind === \"unknown\" || input.candidate.kind === \"unknown\") {\n return {\n kind: \"incompatible\",\n expected: input.expected,\n candidate: input.candidate,\n fallback: buildBreakerFallback(\"CP_BOUNDARY_OUTCOME_UNKNOWN\", {\n candidateKind: input.candidate.kind,\n expectedKind: input.expected.kind,\n }),\n };\n }\n\n if (boundaryOutcomesMatch(input.expected, input.candidate)) {\n return {\n kind: \"compatible\",\n outcome: input.candidate,\n reason: \"CP_BOUNDARY_OUTCOME_MATCH\",\n };\n }\n\n return {\n kind: \"incompatible\",\n expected: input.expected,\n candidate: input.candidate,\n fallback: buildBreakerFallback(\"CP_BOUNDARY_OUTCOME_MISMATCH\", {\n candidateKind: input.candidate.kind,\n expectedKind: input.expected.kind,\n }),\n };\n}\n\nfunction requestApiStatusRank(status: RenderRequestApiStatus): number {\n switch (status) {\n case \"notObserved\":\n return 0;\n case \"unknown\":\n return 1;\n case \"observed\":\n return 2;\n default:\n return assertNever(status);\n }\n}\n\nfunction normalizeRequestApiObservations(\n observations: readonly RenderRequestApiObservation[],\n): RenderRequestApiObservation[] {\n const byKind = new Map<RenderRequestApiKind, RenderRequestApiStatus>();\n for (const observation of observations) {\n const current = byKind.get(observation.kind);\n if (\n current === undefined ||\n requestApiStatusRank(observation.status) > requestApiStatusRank(current)\n ) {\n byKind.set(observation.kind, observation.status);\n }\n }\n\n return [...byKind.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([kind, status]) => ({ kind, status }));\n}\n\nfunction cacheProofDowngradeTargetRank(target: CacheProofDowngradeTarget): number {\n switch (target) {\n case \"public\":\n return 0;\n case \"publicVariant\":\n return 1;\n case \"private\":\n return 2;\n case \"privateUncacheable\":\n return 3;\n case \"freshRender\":\n return 4;\n default:\n return assertNever(target);\n }\n}\n\nfunction maxCacheProofDowngradeTarget(\n current: CacheProofDowngradeTarget,\n candidate: CacheProofDowngradeTarget,\n): CacheProofDowngradeTarget {\n return cacheProofDowngradeTargetRank(candidate) > cacheProofDowngradeTargetRank(current)\n ? candidate\n : current;\n}\n\nfunction createDowngradeFallback(\n target: CacheProofDowngradeTarget,\n reasons: readonly CacheProofDowngradeReason[],\n): CacheProofBreakerFallback | null {\n switch (target) {\n case \"public\":\n case \"publicVariant\":\n case \"private\":\n return null;\n case \"privateUncacheable\":\n return buildBreakerFallback(\n \"CP_PRIVATE_DYNAMIC_DOWNGRADE\",\n {\n reasonCodes: reasons.map((reason) => reason.code),\n target,\n },\n \"privateUncacheable\",\n );\n case \"freshRender\":\n return buildBreakerFallback(\"CP_PRIVATE_DYNAMIC_DOWNGRADE\", {\n reasonCodes: reasons.map((reason) => reason.code),\n target,\n });\n default:\n return assertNever(target);\n }\n}\n\nfunction classifyObservedRequestApiDowngrade(\n kind: RenderRequestApiKind,\n): CacheProofDowngradeReason {\n switch (kind) {\n case \"connection\":\n return {\n code: \"CP_DOWNGRADE_DYNAMIC_REQUEST_API\",\n requestApi: \"connection\",\n target: \"freshRender\",\n };\n case \"cookies\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_REQUEST_API\",\n requestApi: \"cookies\",\n target: \"private\",\n };\n case \"draftMode\":\n return {\n code: \"CP_DOWNGRADE_DRAFT_MODE\",\n requestApi: \"draftMode\",\n target: \"privateUncacheable\",\n };\n case \"headers\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_REQUEST_API\",\n requestApi: \"headers\",\n target: \"private\",\n };\n case \"params\":\n return {\n code: \"CP_DOWNGRADE_PUBLIC_REQUEST_API\",\n requestApi: \"params\",\n target: \"publicVariant\",\n };\n case \"searchParams\":\n return {\n code: \"CP_DOWNGRADE_PUBLIC_REQUEST_API\",\n requestApi: \"searchParams\",\n target: \"publicVariant\",\n };\n default:\n return assertNever(kind);\n }\n}\n\nexport function classifyCacheVariantDimensionDowngrade(\n input: ClassifyCacheVariantDimensionDowngradeInput,\n): CacheProofDowngradeReason | null {\n switch (input.source) {\n case \"auth\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_DIMENSION\",\n inputClass: \"auth\",\n source: \"auth\",\n target: \"private\",\n };\n case \"cookie\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_DIMENSION\",\n inputClass: \"private\",\n source: \"cookie\",\n target: \"private\",\n };\n case \"draft-mode\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_DIMENSION\",\n inputClass: \"draft\",\n source: \"draft-mode\",\n target: \"privateUncacheable\",\n };\n case \"header\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_DIMENSION\",\n inputClass: \"private\",\n source: \"header\",\n target: \"private\",\n };\n case \"session\":\n return {\n code: \"CP_DOWNGRADE_PRIVATE_DIMENSION\",\n inputClass: \"session\",\n source: \"session\",\n target: \"private\",\n };\n case \"custom\":\n case \"interception\":\n case \"mounted-slots\":\n case \"params\":\n case \"route\":\n case \"search\":\n return null;\n default:\n return assertNever(input.source);\n }\n}\n\nexport function classifyRenderObservationDowngrade(\n input: ClassifyRenderObservationDowngradeInput,\n): CacheProofDowngradeClassification {\n const reasons: CacheProofDowngradeReason[] = [];\n let target: CacheProofDowngradeTarget = \"public\";\n\n switch (input.cacheability) {\n case \"public\":\n break;\n case \"private\": {\n const reason = {\n code: \"CP_DOWNGRADE_CACHEABILITY_PRIVATE\",\n target: \"private\",\n } satisfies CacheProofDowngradeReason;\n reasons.push(reason);\n target = maxCacheProofDowngradeTarget(target, reason.target);\n break;\n }\n case \"uncacheable\": {\n const reason = {\n code: \"CP_DOWNGRADE_CACHEABILITY_UNCACHEABLE\",\n target: \"privateUncacheable\",\n } satisfies CacheProofDowngradeReason;\n reasons.push(reason);\n target = maxCacheProofDowngradeTarget(target, reason.target);\n break;\n }\n case \"unknown\": {\n const reason = {\n code: \"CP_DOWNGRADE_CACHEABILITY_UNKNOWN\",\n target: \"freshRender\",\n } satisfies CacheProofDowngradeReason;\n reasons.push(reason);\n target = maxCacheProofDowngradeTarget(target, reason.target);\n break;\n }\n default:\n assertNever(input.cacheability);\n }\n\n if (input.completeness !== \"complete\") {\n const reason = {\n code: \"CP_DOWNGRADE_INCOMPLETE_OBSERVATION\",\n completeness: input.completeness,\n target: \"freshRender\",\n } satisfies CacheProofDowngradeReason;\n reasons.push(reason);\n target = maxCacheProofDowngradeTarget(target, reason.target);\n }\n\n if (input.dynamicFetches.length > 0) {\n const reason = {\n code: \"CP_DOWNGRADE_DYNAMIC_FETCH\",\n dynamicFetchCount: input.dynamicFetches.length,\n target: \"freshRender\",\n } satisfies CacheProofDowngradeReason;\n reasons.push(reason);\n target = maxCacheProofDowngradeTarget(target, reason.target);\n }\n\n const requestApis = normalizeRequestApiObservations(input.requestApis);\n for (const requestApi of requestApis) {\n if (requestApi.status === \"notObserved\") continue;\n const reason =\n requestApi.status === \"unknown\"\n ? ({\n code: \"CP_DOWNGRADE_UNKNOWN_REQUEST_API\",\n requestApi: requestApi.kind,\n target: \"freshRender\",\n } satisfies CacheProofDowngradeReason)\n : classifyObservedRequestApiDowngrade(requestApi.kind);\n reasons.push(reason);\n target = maxCacheProofDowngradeTarget(target, reason.target);\n }\n\n return {\n target,\n reasons,\n fallback: createDowngradeFallback(target, reasons),\n isPublicCacheCandidate: target === \"public\" || target === \"publicVariant\",\n };\n}\n\nexport function buildRenderRequestApiObservations(\n input: BuildRenderRequestApiObservationsInput,\n): RenderRequestApiObservation[] {\n const observedKinds = new Set(input.observed);\n const absentStatus: RenderRequestApiStatus =\n input.completeness === \"complete\" ? \"notObserved\" : \"unknown\";\n\n return ALL_RENDER_REQUEST_API_KINDS.map((kind) => ({\n kind,\n status: observedKinds.has(kind) ? \"observed\" : absentStatus,\n }));\n}\n\nexport function buildRenderObservation(input: BuildRenderObservationInput): RenderObservation {\n const requestApis = normalizeRequestApiObservations(input.requestApis);\n const dynamicFetches = sortedUniqueRedacted(input.dynamicFetches);\n\n return {\n schemaVersion: CACHE_PROOF_MODEL_SCHEMA_VERSION,\n output: input.output,\n completeness: input.completeness,\n boundaryOutcome: input.boundaryOutcome,\n requestApis,\n dynamicFetches,\n cacheTags: sortedUnique(input.cacheTags),\n pathTags: sortedUnique(input.pathTags),\n cacheability: input.cacheability,\n downgrade: classifyRenderObservationDowngrade({\n cacheability: input.cacheability,\n completeness: input.completeness,\n dynamicFetches,\n requestApis,\n }),\n };\n}\n\nexport function hasCompleteNegativeRequestApiProof(\n observation: RenderObservation,\n requiredApis: readonly RenderRequestApiKind[],\n): boolean {\n if (observation.completeness !== \"complete\") return false;\n\n const statuses = new Map<RenderRequestApiKind, RenderRequestApiStatus>();\n for (const requestApi of normalizeRequestApiObservations(observation.requestApis)) {\n statuses.set(requestApi.kind, requestApi.status);\n }\n\n for (const api of requiredApis) {\n if (statuses.get(api) !== \"notObserved\") return false;\n }\n return true;\n}\n\nfunction isStaticLayoutOutputScope(\n output: CacheProofOutputScope,\n): output is StaticLayoutCacheProofOutputScope {\n return output.kind === \"layout\";\n}\n\nfunction rejectStaticLayoutReuseProof(\n code: CacheProofRejectionCode,\n fields: CacheProofTraceFields,\n mode: CacheProofBreakerFallbackMode = \"renderFresh\",\n): BuildStaticLayoutReuseProofResult {\n return {\n kind: \"rejected\",\n fallback: buildBreakerFallback(code, fields, mode),\n };\n}\n\nfunction getRequestApiStatus(\n observations: readonly RenderRequestApiObservation[],\n kind: RenderRequestApiKind,\n): RenderRequestApiStatus | \"missing\" {\n let status: RenderRequestApiStatus | null = null;\n\n for (const requestApi of observations) {\n if (requestApi.kind !== kind) continue;\n if (status === null || requestApiStatusRank(requestApi.status) > requestApiStatusRank(status)) {\n status = requestApi.status;\n }\n }\n\n return status ?? \"missing\";\n}\n\nfunction createStaticLayoutDowngradeFallback(\n downgrade: CacheProofDowngradeClassification,\n): CacheProofBreakerFallback {\n const mode: CacheProofBreakerFallbackMode =\n downgrade.target === \"privateUncacheable\" ? \"privateUncacheable\" : \"renderFresh\";\n return buildBreakerFallback(\n \"CP_STATIC_LAYOUT_PRIVATE_DYNAMIC_DOWNGRADE\",\n {\n reasonCodes: downgrade.reasons.map((reason) => reason.code),\n target: downgrade.target,\n },\n mode,\n );\n}\n\nfunction createPrivateVariantDimensionFallback(\n dimension: CacheVariantDimension,\n): CacheProofBreakerFallback | null {\n const downgrade = classifyCacheVariantDimensionDowngrade({ source: dimension.source });\n if (!downgrade && dimension.privacy !== \"private\") {\n return null;\n }\n\n const target = downgrade?.target ?? \"private\";\n const mode: CacheProofBreakerFallbackMode =\n target === \"privateUncacheable\" ? \"privateUncacheable\" : \"renderFresh\";\n return buildBreakerFallback(\n \"CP_STATIC_LAYOUT_PRIVATE_VARIANT_DIMENSION\",\n {\n dimension: dimension.name,\n privacy: dimension.privacy,\n reasonCode: downgrade?.code ?? null,\n source: dimension.source,\n target,\n },\n mode,\n );\n}\n\nfunction outputFieldMismatch(\n candidate: StaticLayoutCacheProofOutputScope,\n observation: StaticLayoutCacheProofOutputScope,\n): \"layoutId\" | \"rootBoundaryId\" | \"routeId\" | null {\n if (candidate.layoutId !== observation.layoutId) return \"layoutId\";\n if (candidate.rootBoundaryId !== observation.rootBoundaryId) return \"rootBoundaryId\";\n if (candidate.routeId !== observation.routeId) return \"routeId\";\n return null;\n}\n\nexport function buildStaticLayoutReuseProof(\n input: BuildStaticLayoutReuseProofInput,\n): BuildStaticLayoutReuseProofResult {\n if (!isStaticLayoutOutputScope(input.currentOutput)) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_CURRENT_OUTPUT_KIND\", {\n currentOutputKind: input.currentOutput.kind,\n });\n }\n\n if (!isStaticLayoutOutputScope(input.candidateVariant.output)) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_CANDIDATE_OUTPUT_KIND\", {\n candidateOutputKind: input.candidateVariant.output.kind,\n });\n }\n\n if (!isStaticLayoutOutputScope(input.candidateObservation.output)) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_OBSERVATION_OUTPUT_KIND\", {\n observationOutputKind: input.candidateObservation.output.kind,\n });\n }\n\n const currentOutput = input.currentOutput;\n const candidateOutput = input.candidateVariant.output;\n const observationOutput = input.candidateObservation.output;\n const requestApis = normalizeRequestApiObservations(input.candidateObservation.requestApis);\n const candidateObservation = {\n ...input.candidateObservation,\n requestApis,\n downgrade: classifyRenderObservationDowngrade({\n cacheability: input.candidateObservation.cacheability,\n completeness: input.candidateObservation.completeness,\n dynamicFetches: input.candidateObservation.dynamicFetches,\n requestApis,\n }),\n } satisfies RenderObservation;\n const observedOutputMismatch = outputFieldMismatch(candidateOutput, observationOutput);\n if (observedOutputMismatch) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_OBSERVATION_OUTPUT_MISMATCH\", {\n candidateLayoutId: candidateOutput.layoutId,\n candidateRootBoundaryId: candidateOutput.rootBoundaryId,\n candidateRouteId: candidateOutput.routeId,\n field: observedOutputMismatch,\n observationLayoutId: observationOutput.layoutId,\n observationRootBoundaryId: observationOutput.rootBoundaryId,\n observationRouteId: observationOutput.routeId,\n });\n }\n\n if (currentOutput.layoutId !== candidateOutput.layoutId) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_ID_MISMATCH\", {\n candidateLayoutId: candidateOutput.layoutId,\n currentLayoutId: currentOutput.layoutId,\n });\n }\n\n if (currentOutput.rootBoundaryId === null || candidateOutput.rootBoundaryId === null) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_ROOT_BOUNDARY_UNKNOWN\", {\n candidateRootBoundaryId: candidateOutput.rootBoundaryId,\n currentRootBoundaryId: currentOutput.rootBoundaryId,\n });\n }\n\n if (currentOutput.rootBoundaryId !== candidateOutput.rootBoundaryId) {\n return rejectStaticLayoutReuseProof(\"CP_STATIC_LAYOUT_ROOT_BOUNDARY_MISMATCH\", {\n candidateRootBoundaryId: candidateOutput.rootBoundaryId,\n currentRootBoundaryId: currentOutput.rootBoundaryId,\n });\n }\n\n const boundaryCompatibility = buildBoundaryOutcomeCompatibility({\n candidate: candidateObservation.boundaryOutcome,\n expected: { kind: \"success\" },\n });\n if (boundaryCompatibility.kind === \"incompatible\") {\n return {\n kind: \"rejected\",\n fallback: boundaryCompatibility.fallback,\n };\n }\n\n for (const dimension of input.candidateVariant.dimensions) {\n const fallback = createPrivateVariantDimensionFallback(dimension);\n if (fallback) {\n return {\n kind: \"rejected\",\n fallback,\n };\n }\n }\n\n if (!candidateObservation.downgrade.isPublicCacheCandidate) {\n return {\n kind: \"rejected\",\n fallback: createStaticLayoutDowngradeFallback(candidateObservation.downgrade),\n };\n }\n\n // The loop can use the shared readonly registry; the proof stores a detached evidence copy.\n const requiredNegativeRequestApis = ALL_RENDER_REQUEST_API_KINDS;\n for (const api of requiredNegativeRequestApis) {\n const status = getRequestApiStatus(candidateObservation.requestApis, api);\n if (status === \"notObserved\") continue;\n\n return rejectStaticLayoutReuseProof(\n status === \"missing\"\n ? \"CP_STATIC_LAYOUT_REQUEST_API_UNKNOWN\"\n : \"CP_STATIC_LAYOUT_REQUEST_API_OBSERVED\",\n {\n requestApi: api,\n status,\n },\n );\n }\n\n return {\n kind: \"proof\",\n proof: {\n authorizesRuntimeReuse: false,\n candidateOutput,\n code: \"CP_STATIC_LAYOUT_REUSE_PROVEN\",\n currentOutput,\n fields: {\n candidateRouteId: candidateOutput.routeId,\n currentRouteId: currentOutput.routeId,\n layoutId: currentOutput.layoutId,\n rootBoundaryId: currentOutput.rootBoundaryId,\n },\n observation: candidateObservation,\n requiredNegativeRequestApis: [...requiredNegativeRequestApis],\n reuseClass: \"static-layout\",\n variant: input.candidateVariant,\n },\n };\n}\n\nexport function createDisabledCacheProofDecision(\n input: CreateDisabledCacheProofDecisionInput,\n): DisabledCacheProofDecision {\n return {\n kind: \"disabled\",\n canReuse: false,\n variant: input.variant,\n observation: input.observation,\n ...(input.staticLayoutProof ? { staticLayoutProof: input.staticLayoutProof } : {}),\n fallback: buildBreakerFallback(\"CP_MODEL_DISABLED\"),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ //#region src/server/dev-lockfile.d.ts
2
+ /**
3
+ * Dev server lock file.
4
+ *
5
+ * Writes the running dev server's PID, port, and URL into a lock file at
6
+ * `<root>/.vinext/dev/lock.json`. When a second `vinext dev` process starts in
7
+ * the same project directory, it reads the lock file and either fails with an
8
+ * actionable error or, if the previous process is dead, takes over the lock.
9
+ *
10
+ * This is especially useful for AI coding agents, which frequently attempt to
11
+ * start `vinext dev` without knowing a server is already running.
12
+ *
13
+ * Ported behaviorally from Next.js:
14
+ * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/lockfile.ts
15
+ *
16
+ * Differences vs Next.js:
17
+ * - No native `flock()`. Next.js uses Rust SWC bindings for cross-platform
18
+ * advisory locking; vinext uses a JSON file plus a PID liveness check
19
+ * (`process.kill(pid, 0)`), which is good enough for the dev-server
20
+ * "another server is running" use case. Race conditions on lock acquisition
21
+ * are tolerated: at worst, two dev servers race and one fails to bind a port.
22
+ * - Lock file lives in `<root>/.vinext/dev/lock.json` (mirroring Next.js'
23
+ * `.next/dev/lock` layout). `.vinext/` is already used by the fonts plugin
24
+ * to cache self-hosted Google Fonts, so this re-uses the same project-local
25
+ * state directory rather than polluting `node_modules`.
26
+ */
27
+ /**
28
+ * Information about a running dev server, stored inside the lock file itself.
29
+ */
30
+ type DevServerInfo = {
31
+ pid: number;
32
+ port: number;
33
+ hostname: string;
34
+ appUrl: string;
35
+ startedAt: number; /** Project directory the server is running in. Used to detect stale entries. */
36
+ cwd: string;
37
+ };
38
+ type DevLockfile = {
39
+ /** Update the lock file contents (e.g. once the port is known after listen). */update(info: DevServerInfo): void; /** Release the lock — deletes the file. Safe to call multiple times. */
40
+ release(): void; /** Absolute path to the lock file. */
41
+ path: string;
42
+ };
43
+ /**
44
+ * Returns the absolute path to the lock file for a given project root.
45
+ */
46
+ declare function getLockfilePath(root: string): string;
47
+ /**
48
+ * Reads and parses the lock file at the given path. Returns `undefined` if the
49
+ * file doesn't exist or can't be parsed.
50
+ */
51
+ declare function readLockfile(lockfilePath: string): DevServerInfo | undefined;
52
+ /**
53
+ * Returns true if a process with the given PID is running.
54
+ *
55
+ * Uses `process.kill(pid, 0)`, which sends a null signal — it doesn't actually
56
+ * kill the process, it just checks if it exists. Throws `ESRCH` if the process
57
+ * doesn't exist, or `EPERM` if it exists but we don't have permission to
58
+ * signal it (in which case it's still running, just owned by someone else).
59
+ */
60
+ declare function isPidAlive(pid: number): boolean;
61
+ type FormatErrorOptions = {
62
+ /** Existing server info from the lock file, if readable. */existing: DevServerInfo | undefined; /** Project directory the new (failing) process is trying to run in. */
63
+ cwd: string; /** Path to the lock file. */
64
+ lockfilePath: string;
65
+ };
66
+ /**
67
+ * Format the error message printed when another dev server is already running.
68
+ *
69
+ * Matches Next.js' error layout so AI agents and CLIs can parse the same
70
+ * `- PID: ` / `- Local: ` lines.
71
+ *
72
+ * The `existing: undefined` branch below is defensive — `tryAcquireLockfile`
73
+ * currently only returns `ok: false` with a defined `existing`, but the
74
+ * formatter is exported and unit-tested separately, so it handles both shapes.
75
+ */
76
+ declare function formatAlreadyRunningError(opts: FormatErrorOptions): string;
77
+ type AcquireOptions = {
78
+ /** Project root. Lock file goes in `<root>/.vinext/dev/lock.json`. */root: string; /** Initial server info to write. Port/URL may be updated later via `update()`. */
79
+ info: DevServerInfo;
80
+ /**
81
+ * If a lock file exists but its PID is dead, take over instead of failing.
82
+ * Defaults to `true`. Set to `false` for testing.
83
+ */
84
+ takeOverStale?: boolean; /** Register `process.on('exit', release)`. Defaults to `true`. */
85
+ unlockOnExit?: boolean;
86
+ };
87
+ type AcquireSuccess = {
88
+ ok: true;
89
+ lockfile: DevLockfile;
90
+ };
91
+ type AcquireFailure = {
92
+ ok: false; /** The server info from the existing lock file, if readable. */
93
+ existing: DevServerInfo | undefined; /** Absolute path to the lock file. */
94
+ lockfilePath: string;
95
+ };
96
+ type AcquireResult = AcquireSuccess | AcquireFailure;
97
+ /**
98
+ * Try to acquire the dev lock file for the given project root.
99
+ *
100
+ * Returns `{ ok: true, lockfile }` on success — the caller should call
101
+ * `lockfile.release()` on shutdown (or rely on the exit listener registered
102
+ * via `unlockOnExit`).
103
+ *
104
+ * Returns `{ ok: false, existing, lockfilePath }` if another live dev server
105
+ * already holds the lock.
106
+ */
107
+ declare function tryAcquireLockfile(opts: AcquireOptions): AcquireResult;
108
+ //#endregion
109
+ export { DevLockfile, DevServerInfo, formatAlreadyRunningError, getLockfilePath, isPidAlive, readLockfile, tryAcquireLockfile };
110
+ //# sourceMappingURL=dev-lockfile.d.ts.map
@@ -0,0 +1,180 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ //#region src/server/dev-lockfile.ts
4
+ /**
5
+ * Dev server lock file.
6
+ *
7
+ * Writes the running dev server's PID, port, and URL into a lock file at
8
+ * `<root>/.vinext/dev/lock.json`. When a second `vinext dev` process starts in
9
+ * the same project directory, it reads the lock file and either fails with an
10
+ * actionable error or, if the previous process is dead, takes over the lock.
11
+ *
12
+ * This is especially useful for AI coding agents, which frequently attempt to
13
+ * start `vinext dev` without knowing a server is already running.
14
+ *
15
+ * Ported behaviorally from Next.js:
16
+ * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/lockfile.ts
17
+ *
18
+ * Differences vs Next.js:
19
+ * - No native `flock()`. Next.js uses Rust SWC bindings for cross-platform
20
+ * advisory locking; vinext uses a JSON file plus a PID liveness check
21
+ * (`process.kill(pid, 0)`), which is good enough for the dev-server
22
+ * "another server is running" use case. Race conditions on lock acquisition
23
+ * are tolerated: at worst, two dev servers race and one fails to bind a port.
24
+ * - Lock file lives in `<root>/.vinext/dev/lock.json` (mirroring Next.js'
25
+ * `.next/dev/lock` layout). `.vinext/` is already used by the fonts plugin
26
+ * to cache self-hosted Google Fonts, so this re-uses the same project-local
27
+ * state directory rather than polluting `node_modules`.
28
+ */
29
+ const LOCK_DIR_RELATIVE = path.join(".vinext", "dev");
30
+ const LOCK_FILE_NAME = "lock.json";
31
+ /**
32
+ * Returns the absolute path to the lock file for a given project root.
33
+ */
34
+ function getLockfilePath(root) {
35
+ return path.join(root, LOCK_DIR_RELATIVE, LOCK_FILE_NAME);
36
+ }
37
+ /**
38
+ * Reads and parses the lock file at the given path. Returns `undefined` if the
39
+ * file doesn't exist or can't be parsed.
40
+ */
41
+ function readLockfile(lockfilePath) {
42
+ let content;
43
+ try {
44
+ content = fs.readFileSync(lockfilePath, "utf-8");
45
+ } catch {
46
+ return;
47
+ }
48
+ try {
49
+ const parsed = JSON.parse(content);
50
+ if (typeof parsed.pid === "number" && typeof parsed.port === "number" && typeof parsed.hostname === "string" && typeof parsed.appUrl === "string" && typeof parsed.startedAt === "number" && typeof parsed.cwd === "string") return parsed;
51
+ return;
52
+ } catch {
53
+ return;
54
+ }
55
+ }
56
+ /**
57
+ * Returns true if a process with the given PID is running.
58
+ *
59
+ * Uses `process.kill(pid, 0)`, which sends a null signal — it doesn't actually
60
+ * kill the process, it just checks if it exists. Throws `ESRCH` if the process
61
+ * doesn't exist, or `EPERM` if it exists but we don't have permission to
62
+ * signal it (in which case it's still running, just owned by someone else).
63
+ */
64
+ function isPidAlive(pid) {
65
+ if (!Number.isInteger(pid) || pid <= 0) return false;
66
+ try {
67
+ process.kill(pid, 0);
68
+ return true;
69
+ } catch (err) {
70
+ return err.code === "EPERM";
71
+ }
72
+ }
73
+ /**
74
+ * Writes the lock file with the given content. Creates the parent directory
75
+ * if it doesn't exist.
76
+ *
77
+ * Mode `0o600` because the lock file contains a PID that, in principle, lets
78
+ * other users on the machine send signals to this user's dev server.
79
+ * Restricting reads is defense-in-depth: the PID is also discoverable via
80
+ * `ps` and the port via `netstat`/`ss`, so this isn't load-bearing.
81
+ */
82
+ function writeLockfile(lockfilePath, info) {
83
+ fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });
84
+ fs.writeFileSync(lockfilePath, JSON.stringify(info, null, 2), { mode: 384 });
85
+ }
86
+ /**
87
+ * Format the error message printed when another dev server is already running.
88
+ *
89
+ * Matches Next.js' error layout so AI agents and CLIs can parse the same
90
+ * `- PID: ` / `- Local: ` lines.
91
+ *
92
+ * The `existing: undefined` branch below is defensive — `tryAcquireLockfile`
93
+ * currently only returns `ok: false` with a defined `existing`, but the
94
+ * formatter is exported and unit-tested separately, so it handles both shapes.
95
+ */
96
+ function formatAlreadyRunningError(opts) {
97
+ const { existing, cwd, lockfilePath } = opts;
98
+ if (!existing) return [
99
+ "Another vinext dev server appears to be running in this directory.",
100
+ "",
101
+ `Stale lock file: ${path.relative(cwd, lockfilePath)}`,
102
+ "Remove it manually if no server is running, then re-run `vinext dev`."
103
+ ].join("\n");
104
+ const killCommand = process.platform === "win32" ? `taskkill /PID ${existing.pid} /F` : `kill ${existing.pid}`;
105
+ return [
106
+ "Another vinext dev server is already running.",
107
+ "",
108
+ `- Local: ${existing.appUrl}`,
109
+ `- PID: ${existing.pid}`,
110
+ `- Dir: ${existing.cwd}`,
111
+ "",
112
+ `You can access the existing server at ${existing.appUrl},`,
113
+ `or run \`${killCommand}\` to stop it and start a new one.`
114
+ ].join("\n");
115
+ }
116
+ /**
117
+ * Try to acquire the dev lock file for the given project root.
118
+ *
119
+ * Returns `{ ok: true, lockfile }` on success — the caller should call
120
+ * `lockfile.release()` on shutdown (or rely on the exit listener registered
121
+ * via `unlockOnExit`).
122
+ *
123
+ * Returns `{ ok: false, existing, lockfilePath }` if another live dev server
124
+ * already holds the lock.
125
+ */
126
+ function tryAcquireLockfile(opts) {
127
+ const { root, info, takeOverStale = true, unlockOnExit = true } = opts;
128
+ const lockfilePath = getLockfilePath(root);
129
+ const existing = readLockfile(lockfilePath);
130
+ if (existing) {
131
+ if (isPidAlive(existing.pid)) return {
132
+ ok: false,
133
+ existing,
134
+ lockfilePath
135
+ };
136
+ if (!takeOverStale) return {
137
+ ok: false,
138
+ existing,
139
+ lockfilePath
140
+ };
141
+ }
142
+ writeLockfile(lockfilePath, info);
143
+ const ownerPid = info.pid;
144
+ let released = false;
145
+ const release = () => {
146
+ if (released) return;
147
+ released = true;
148
+ try {
149
+ const current = readLockfile(lockfilePath);
150
+ if (current && current.pid === ownerPid) fs.unlinkSync(lockfilePath);
151
+ } catch {}
152
+ };
153
+ let exitListener;
154
+ if (unlockOnExit) {
155
+ exitListener = () => release();
156
+ process.on("exit", exitListener);
157
+ }
158
+ return {
159
+ ok: true,
160
+ lockfile: {
161
+ path: lockfilePath,
162
+ update(next) {
163
+ try {
164
+ writeLockfile(lockfilePath, next);
165
+ } catch {}
166
+ },
167
+ release() {
168
+ release();
169
+ if (exitListener) {
170
+ process.off("exit", exitListener);
171
+ exitListener = void 0;
172
+ }
173
+ }
174
+ }
175
+ };
176
+ }
177
+ //#endregion
178
+ export { formatAlreadyRunningError, getLockfilePath, isPidAlive, readLockfile, tryAcquireLockfile };
179
+
180
+ //# sourceMappingURL=dev-lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-lockfile.js","names":[],"sources":["../../src/server/dev-lockfile.ts"],"sourcesContent":["/**\n * Dev server lock file.\n *\n * Writes the running dev server's PID, port, and URL into a lock file at\n * `<root>/.vinext/dev/lock.json`. When a second `vinext dev` process starts in\n * the same project directory, it reads the lock file and either fails with an\n * actionable error or, if the previous process is dead, takes over the lock.\n *\n * This is especially useful for AI coding agents, which frequently attempt to\n * start `vinext dev` without knowing a server is already running.\n *\n * Ported behaviorally from Next.js:\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/build/lockfile.ts\n *\n * Differences vs Next.js:\n * - No native `flock()`. Next.js uses Rust SWC bindings for cross-platform\n * advisory locking; vinext uses a JSON file plus a PID liveness check\n * (`process.kill(pid, 0)`), which is good enough for the dev-server\n * \"another server is running\" use case. Race conditions on lock acquisition\n * are tolerated: at worst, two dev servers race and one fails to bind a port.\n * - Lock file lives in `<root>/.vinext/dev/lock.json` (mirroring Next.js'\n * `.next/dev/lock` layout). `.vinext/` is already used by the fonts plugin\n * to cache self-hosted Google Fonts, so this re-uses the same project-local\n * state directory rather than polluting `node_modules`.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst LOCK_DIR_RELATIVE = path.join(\".vinext\", \"dev\");\nconst LOCK_FILE_NAME = \"lock.json\";\n\n/**\n * Information about a running dev server, stored inside the lock file itself.\n */\nexport type DevServerInfo = {\n pid: number;\n port: number;\n hostname: string;\n appUrl: string;\n startedAt: number;\n /** Project directory the server is running in. Used to detect stale entries. */\n cwd: string;\n};\n\nexport type DevLockfile = {\n /** Update the lock file contents (e.g. once the port is known after listen). */\n update(info: DevServerInfo): void;\n /** Release the lock — deletes the file. Safe to call multiple times. */\n release(): void;\n /** Absolute path to the lock file. */\n path: string;\n};\n\n/**\n * Returns the absolute path to the lock file for a given project root.\n */\nexport function getLockfilePath(root: string): string {\n return path.join(root, LOCK_DIR_RELATIVE, LOCK_FILE_NAME);\n}\n\n/**\n * Reads and parses the lock file at the given path. Returns `undefined` if the\n * file doesn't exist or can't be parsed.\n */\nexport function readLockfile(lockfilePath: string): DevServerInfo | undefined {\n let content: string;\n try {\n content = fs.readFileSync(lockfilePath, \"utf-8\");\n } catch {\n return undefined;\n }\n try {\n const parsed = JSON.parse(content) as DevServerInfo;\n if (\n typeof parsed.pid === \"number\" &&\n typeof parsed.port === \"number\" &&\n typeof parsed.hostname === \"string\" &&\n typeof parsed.appUrl === \"string\" &&\n typeof parsed.startedAt === \"number\" &&\n typeof parsed.cwd === \"string\"\n ) {\n return parsed;\n }\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns true if a process with the given PID is running.\n *\n * Uses `process.kill(pid, 0)`, which sends a null signal — it doesn't actually\n * kill the process, it just checks if it exists. Throws `ESRCH` if the process\n * doesn't exist, or `EPERM` if it exists but we don't have permission to\n * signal it (in which case it's still running, just owned by someone else).\n */\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n // EPERM means the process exists but we lack permission — still alive.\n return code === \"EPERM\";\n }\n}\n\n/**\n * Writes the lock file with the given content. Creates the parent directory\n * if it doesn't exist.\n *\n * Mode `0o600` because the lock file contains a PID that, in principle, lets\n * other users on the machine send signals to this user's dev server.\n * Restricting reads is defense-in-depth: the PID is also discoverable via\n * `ps` and the port via `netstat`/`ss`, so this isn't load-bearing.\n */\nfunction writeLockfile(lockfilePath: string, info: DevServerInfo): void {\n fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });\n fs.writeFileSync(lockfilePath, JSON.stringify(info, null, 2), { mode: 0o600 });\n}\n\ntype FormatErrorOptions = {\n /** Existing server info from the lock file, if readable. */\n existing: DevServerInfo | undefined;\n /** Project directory the new (failing) process is trying to run in. */\n cwd: string;\n /** Path to the lock file. */\n lockfilePath: string;\n};\n\n/**\n * Format the error message printed when another dev server is already running.\n *\n * Matches Next.js' error layout so AI agents and CLIs can parse the same\n * `- PID: ` / `- Local: ` lines.\n *\n * The `existing: undefined` branch below is defensive — `tryAcquireLockfile`\n * currently only returns `ok: false` with a defined `existing`, but the\n * formatter is exported and unit-tested separately, so it handles both shapes.\n */\nexport function formatAlreadyRunningError(opts: FormatErrorOptions): string {\n const { existing, cwd, lockfilePath } = opts;\n\n if (!existing) {\n // Defensive fallback. Not reachable from tryAcquireLockfile today.\n return [\n \"Another vinext dev server appears to be running in this directory.\",\n \"\",\n `Stale lock file: ${path.relative(cwd, lockfilePath)}`,\n \"Remove it manually if no server is running, then re-run `vinext dev`.\",\n ].join(\"\\n\");\n }\n\n const killCommand =\n process.platform === \"win32\" ? `taskkill /PID ${existing.pid} /F` : `kill ${existing.pid}`;\n\n return [\n \"Another vinext dev server is already running.\",\n \"\",\n `- Local: ${existing.appUrl}`,\n `- PID: ${existing.pid}`,\n `- Dir: ${existing.cwd}`,\n \"\",\n `You can access the existing server at ${existing.appUrl},`,\n `or run \\`${killCommand}\\` to stop it and start a new one.`,\n ].join(\"\\n\");\n}\n\ntype AcquireOptions = {\n /** Project root. Lock file goes in `<root>/.vinext/dev/lock.json`. */\n root: string;\n /** Initial server info to write. Port/URL may be updated later via `update()`. */\n info: DevServerInfo;\n /**\n * If a lock file exists but its PID is dead, take over instead of failing.\n * Defaults to `true`. Set to `false` for testing.\n */\n takeOverStale?: boolean;\n /** Register `process.on('exit', release)`. Defaults to `true`. */\n unlockOnExit?: boolean;\n};\n\ntype AcquireSuccess = {\n ok: true;\n lockfile: DevLockfile;\n};\n\ntype AcquireFailure = {\n ok: false;\n /** The server info from the existing lock file, if readable. */\n existing: DevServerInfo | undefined;\n /** Absolute path to the lock file. */\n lockfilePath: string;\n};\n\ntype AcquireResult = AcquireSuccess | AcquireFailure;\n\n/**\n * Try to acquire the dev lock file for the given project root.\n *\n * Returns `{ ok: true, lockfile }` on success — the caller should call\n * `lockfile.release()` on shutdown (or rely on the exit listener registered\n * via `unlockOnExit`).\n *\n * Returns `{ ok: false, existing, lockfilePath }` if another live dev server\n * already holds the lock.\n */\nexport function tryAcquireLockfile(opts: AcquireOptions): AcquireResult {\n const { root, info, takeOverStale = true, unlockOnExit = true } = opts;\n const lockfilePath = getLockfilePath(root);\n\n const existing = readLockfile(lockfilePath);\n if (existing) {\n const alive = isPidAlive(existing.pid);\n if (alive) {\n return { ok: false, existing, lockfilePath };\n }\n if (!takeOverStale) {\n return { ok: false, existing, lockfilePath };\n }\n // Existing entry is stale (dead PID). Fall through and overwrite.\n }\n\n // NB: there is a small TOCTOU window between readLockfile() above and\n // writeLockfile() here. Two processes starting simultaneously can both\n // pass the check and both write the lock file. This is intentionally\n // tolerated — the loser will fail to bind its port, producing a clear\n // error. A native flock() (the approach Next.js takes via Rust bindings)\n // would close the window, but it's not worth the complexity for a\n // dev-ergonomics feature.\n writeLockfile(lockfilePath, info);\n\n // Capture the owner PID once so release() always asks \"is the file still\n // mine?\" against the same identity, regardless of what update() writes\n // later. In practice the PID never changes between acquire and release,\n // but this makes the intent explicit and decouples release from update.\n const ownerPid = info.pid;\n\n let released = false;\n const release = () => {\n if (released) return;\n released = true;\n try {\n // Only delete if the file still points at us. If another process took\n // over the lock (e.g. after a crash), don't delete their entry.\n const current = readLockfile(lockfilePath);\n if (current && current.pid === ownerPid) {\n fs.unlinkSync(lockfilePath);\n }\n } catch {\n // Best-effort cleanup.\n }\n };\n\n // The \"exit\" event fires once Node.js is about to exit — either gracefully\n // (event loop drained, explicit process.exit(), or after the default\n // SIGINT/SIGTERM handlers terminate the process). It does NOT fire on\n // uncaught exceptions or hard crashes (SIGKILL), which is fine: the next\n // `vinext dev` will detect the dead PID and take over the stale lock.\n //\n // If a future caller installs a custom signal handler that swallows\n // SIGINT/SIGTERM without exiting, the lock would leak — also fine, same\n // recovery path applies.\n let exitListener: NodeJS.ExitListener | undefined;\n if (unlockOnExit) {\n exitListener = () => release();\n process.on(\"exit\", exitListener);\n }\n\n const lockfile: DevLockfile = {\n path: lockfilePath,\n update(next: DevServerInfo): void {\n try {\n writeLockfile(lockfilePath, next);\n } catch {\n // Best-effort; not fatal.\n }\n },\n release(): void {\n release();\n if (exitListener) {\n process.off(\"exit\", exitListener);\n exitListener = undefined;\n }\n },\n };\n\n return { ok: true, lockfile };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,oBAAoB,KAAK,KAAK,WAAW,MAAM;AACrD,MAAM,iBAAiB;;;;AA2BvB,SAAgB,gBAAgB,MAAsB;CACpD,OAAO,KAAK,KAAK,MAAM,mBAAmB,eAAe;;;;;;AAO3D,SAAgB,aAAa,cAAiD;CAC5E,IAAI;CACJ,IAAI;EACF,UAAU,GAAG,aAAa,cAAc,QAAQ;SAC1C;EACN;;CAEF,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,QAAQ;EAClC,IACE,OAAO,OAAO,QAAQ,YACtB,OAAO,OAAO,SAAS,YACvB,OAAO,OAAO,aAAa,YAC3B,OAAO,OAAO,WAAW,YACzB,OAAO,OAAO,cAAc,YAC5B,OAAO,OAAO,QAAQ,UAEtB,OAAO;EAET;SACM;EACN;;;;;;;;;;;AAYJ,SAAgB,WAAW,KAAsB;CAC/C,IAAI,CAAC,OAAO,UAAU,IAAI,IAAI,OAAO,GAAG,OAAO;CAC/C,IAAI;EACF,QAAQ,KAAK,KAAK,EAAE;EACpB,OAAO;UACA,KAAK;EAGZ,OAFc,IAA8B,SAE5B;;;;;;;;;;;;AAapB,SAAS,cAAc,cAAsB,MAA2B;CACtE,GAAG,UAAU,KAAK,QAAQ,aAAa,EAAE,EAAE,WAAW,MAAM,CAAC;CAC7D,GAAG,cAAc,cAAc,KAAK,UAAU,MAAM,MAAM,EAAE,EAAE,EAAE,MAAM,KAAO,CAAC;;;;;;;;;;;;AAsBhF,SAAgB,0BAA0B,MAAkC;CAC1E,MAAM,EAAE,UAAU,KAAK,iBAAiB;CAExC,IAAI,CAAC,UAEH,OAAO;EACL;EACA;EACA,oBAAoB,KAAK,SAAS,KAAK,aAAa;EACpD;EACD,CAAC,KAAK,KAAK;CAGd,MAAM,cACJ,QAAQ,aAAa,UAAU,iBAAiB,SAAS,IAAI,OAAO,QAAQ,SAAS;CAEvF,OAAO;EACL;EACA;EACA,mBAAmB,SAAS;EAC5B,mBAAmB,SAAS;EAC5B,mBAAmB,SAAS;EAC5B;EACA,yCAAyC,SAAS,OAAO;EACzD,YAAY,YAAY;EACzB,CAAC,KAAK,KAAK;;;;;;;;;;;;AA0Cd,SAAgB,mBAAmB,MAAqC;CACtE,MAAM,EAAE,MAAM,MAAM,gBAAgB,MAAM,eAAe,SAAS;CAClE,MAAM,eAAe,gBAAgB,KAAK;CAE1C,MAAM,WAAW,aAAa,aAAa;CAC3C,IAAI,UAAU;EAEZ,IADc,WAAW,SAAS,IACzB,EACP,OAAO;GAAE,IAAI;GAAO;GAAU;GAAc;EAE9C,IAAI,CAAC,eACH,OAAO;GAAE,IAAI;GAAO;GAAU;GAAc;;CAYhD,cAAc,cAAc,KAAK;CAMjC,MAAM,WAAW,KAAK;CAEtB,IAAI,WAAW;CACf,MAAM,gBAAgB;EACpB,IAAI,UAAU;EACd,WAAW;EACX,IAAI;GAGF,MAAM,UAAU,aAAa,aAAa;GAC1C,IAAI,WAAW,QAAQ,QAAQ,UAC7B,GAAG,WAAW,aAAa;UAEvB;;CAcV,IAAI;CACJ,IAAI,cAAc;EAChB,qBAAqB,SAAS;EAC9B,QAAQ,GAAG,QAAQ,aAAa;;CAqBlC,OAAO;EAAE,IAAI;EAAM,UAAA;GAjBjB,MAAM;GACN,OAAO,MAA2B;IAChC,IAAI;KACF,cAAc,cAAc,KAAK;YAC3B;;GAIV,UAAgB;IACd,SAAS;IACT,IAAI,cAAc;KAChB,QAAQ,IAAI,QAAQ,aAAa;KACjC,eAAe,KAAA;;;GAKM;EAAE"}
@@ -3,6 +3,7 @@ import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-
3
3
  import { patternToNextFormat } from "../routing/route-validation.js";
4
4
  import { matchRoute } from "../routing/pages-router.js";
5
5
  import { VINEXT_CACHE_HEADER } from "./headers.js";
6
+ import { normalizeStaticPathname } from "../routing/route-pattern.js";
6
7
  import { importModule, reportRequestError } from "./instrumentation.js";
7
8
  import { _runWithCacheState } from "../shims/cache.js";
8
9
  import { buildPagesCacheValue, getRevalidateDuration, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
@@ -216,11 +217,18 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
216
217
  defaultLocale: currentDefaultLocale ?? ""
217
218
  });
218
219
  if ((pathsResult?.fallback ?? false) === false) {
219
- if (!(pathsResult?.paths ?? []).some((p) => Object.entries(p.params).every(([key, val]) => {
220
- const actual = params[key];
221
- if (Array.isArray(val)) return Array.isArray(actual) && val.join("/") === actual.join("/");
222
- return String(val) === String(actual);
223
- }))) {
220
+ const paths = pathsResult?.paths ?? [];
221
+ const currentPathname = normalizeStaticPathname(url);
222
+ if (!paths.some((p) => {
223
+ if (typeof p === "string") return normalizeStaticPathname(p) === currentPathname;
224
+ const entryParams = p.params;
225
+ if (entryParams === void 0 || entryParams === null) return false;
226
+ return Object.entries(entryParams).every(([key, val]) => {
227
+ const actual = params[key];
228
+ if (Array.isArray(val)) return Array.isArray(actual) && val.join("/") === actual.join("/");
229
+ return String(val) === String(actual);
230
+ });
231
+ })) {
224
232
  await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher);
225
233
  return;
226
234
  }
@@ -453,6 +461,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
453
461
  import "vinext/instrumentation-client";
454
462
  import React from "react";
455
463
  import { hydrateRoot } from "react-dom/client";
464
+ import { installPagesRouterRuntime } from "vinext/pages-router-runtime";
456
465
  import { wrapWithRouterContext } from "next/router";
457
466
 
458
467
  const nextData = window.__NEXT_DATA__;
@@ -473,6 +482,7 @@ async function hydrate() {
473
482
  element = wrapWithRouterContext(element);
474
483
  const root = hydrateRoot(document.getElementById("__next"), element);
475
484
  window.__VINEXT_ROOT__ = root;
485
+ installPagesRouterRuntime();
476
486
  window.__VINEXT_HYDRATED_AT = performance.now();
477
487
  }
478
488
  hydrate();