@wular/pnext 0.0.4 → 0.0.6

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 (392) hide show
  1. package/README.md +11 -11
  2. package/bin/pnext +1 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +1 -1
  8. package/reference/config.md +2 -2
  9. package/reference/css.md +13 -9
  10. package/reference/dev.md +1 -1
  11. package/reference/metadata.md +7 -7
  12. package/reference/navigation.md +35 -28
  13. package/reference/performance.md +70 -70
  14. package/reference/rendering.md +17 -17
  15. package/reference/routing.md +17 -17
  16. package/reference/typegen.md +12 -8
  17. package/src/api/cache.ts +36 -37
  18. package/src/api/client-cache.ts +2 -2
  19. package/src/api/client-navigation.ts +113 -109
  20. package/src/api/dynamic.tsx +58 -55
  21. package/src/api/link.tsx +49 -52
  22. package/src/api/navigation.ts +59 -58
  23. package/src/api/server.ts +152 -144
  24. package/src/api/suspense.ts +4 -4
  25. package/src/cli/adapters/vercel-warm.ts +126 -121
  26. package/src/cli/adapters/vercel.ts +437 -443
  27. package/src/cli/analyze.ts +333 -144
  28. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  29. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  30. package/src/cli/build.ts +976 -978
  31. package/src/cli/create.ts +71 -63
  32. package/src/cli/dev.ts +116 -116
  33. package/src/cli/index.ts +88 -85
  34. package/src/cli/migrate/package-json.ts +73 -71
  35. package/src/cli/migrate/report.ts +32 -33
  36. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  37. package/src/cli/migrate/scan.ts +46 -46
  38. package/src/cli/migrate/spinner.ts +9 -9
  39. package/src/cli/migrate/tsconfig.ts +35 -35
  40. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  41. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  42. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  43. package/src/cli/start.ts +65 -66
  44. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  45. package/src/client/build.ts +794 -768
  46. package/src/client/chunk-fold.ts +245 -240
  47. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  48. package/src/client/entry.ts +162 -147
  49. package/src/client/prebuilt.ts +231 -223
  50. package/src/client/profile.ts +29 -29
  51. package/src/client/react-compiler.ts +20 -15
  52. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  53. package/src/client/reference-stub.ts +51 -51
  54. package/src/client/reference.ts +19 -19
  55. package/src/{api → client}/router/events.ts +17 -17
  56. package/src/{api → client}/router/history.ts +16 -16
  57. package/src/{api → client}/router/hub.ts +52 -53
  58. package/src/{api/router.ts → client/router/index.ts} +61 -60
  59. package/src/{api → client}/router/policies.ts +24 -24
  60. package/src/{api → client}/router/runtime.ts +1988 -1992
  61. package/src/{api → client}/router/types.ts +93 -98
  62. package/src/compat/actions/client-plugin.ts +42 -43
  63. package/src/compat/actions/client-stub.ts +14 -14
  64. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  65. package/src/compat/actions/config.ts +63 -64
  66. package/src/compat/actions/detect.ts +68 -68
  67. package/src/compat/actions/discovery.ts +105 -105
  68. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  69. package/src/compat/actions/early-submit.ts +1 -1
  70. package/src/compat/actions/endpoint.ts +198 -198
  71. package/src/compat/actions/extensions.ts +811 -0
  72. package/src/compat/actions/flight.ts +23 -23
  73. package/src/compat/actions/form-state.ts +34 -34
  74. package/src/compat/actions/hoist.ts +382 -236
  75. package/src/compat/actions/ids.ts +7 -12
  76. package/src/compat/actions/index.ts +8 -8
  77. package/src/compat/actions/instances.ts +46 -46
  78. package/src/compat/actions/origin.ts +47 -48
  79. package/src/compat/actions/protocol.ts +22 -22
  80. package/src/compat/actions/registry.ts +19 -19
  81. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  82. package/src/compat/actions/rewrite.ts +125 -125
  83. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  84. package/src/compat/actions/serve.ts +132 -136
  85. package/src/compat/actions/server-tag.ts +4 -4
  86. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  87. package/src/compat/actions/unrecognized-error.ts +4 -4
  88. package/src/compat/{index.ts → aliases.ts} +112 -109
  89. package/src/compat/bundler/bun-externals.ts +18 -18
  90. package/src/compat/bundler/cjs-exports.ts +271 -223
  91. package/src/compat/bundler/config.ts +116 -111
  92. package/src/compat/bundler/externals.ts +12 -12
  93. package/src/compat/bundler/import-meta-url.ts +19 -19
  94. package/src/compat/bundler/modularize-imports.ts +36 -33
  95. package/src/compat/bundler/new-url-asset.ts +22 -24
  96. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  97. package/src/compat/bundler/polyfill.ts +28 -28
  98. package/src/compat/bundler/react-compiler.ts +35 -29
  99. package/src/compat/bundler/react-profiler.tsx +11 -11
  100. package/src/compat/bundler/relay-transform.ts +48 -47
  101. package/src/compat/bundler/require-context.ts +119 -113
  102. package/src/compat/bundler/resolve-extensions.ts +19 -19
  103. package/src/compat/bundler/source-cache.ts +25 -25
  104. package/src/compat/bundler/static-imports.ts +7 -7
  105. package/src/compat/bundler/symlink-imports.ts +46 -46
  106. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  107. package/src/compat/bundler/wasm.ts +58 -60
  108. package/src/compat/bundler/webpack-loaders.ts +254 -241
  109. package/src/compat/bundler/worker.ts +101 -104
  110. package/src/compat/cache/build-flags.ts +29 -29
  111. package/src/compat/cache/build-prerender-errors.ts +40 -44
  112. package/src/compat/cache/custom-handler.ts +60 -53
  113. package/src/compat/cache/fetch-patch.ts +240 -240
  114. package/src/compat/cache/handler.ts +27 -31
  115. package/src/compat/cache/modern-handler.ts +148 -126
  116. package/src/compat/cache/resume-data-cache.ts +47 -45
  117. package/src/compat/cache/revalidate.ts +233 -232
  118. package/src/compat/cache/runtime-error.ts +35 -35
  119. package/src/compat/cache/use-cache-transform.ts +442 -417
  120. package/src/compat/cache/use-cache.ts +650 -614
  121. package/src/compat/cache-control.ts +140 -142
  122. package/src/compat/client/base-path.ts +21 -20
  123. package/src/compat/client/css-order.ts +18 -18
  124. package/src/compat/client/errors/bare-boundary.ts +11 -11
  125. package/src/compat/client/errors/control-flow.ts +23 -23
  126. package/src/compat/client/errors/error-boundary.ts +61 -61
  127. package/src/compat/client/errors/global-error.ts +101 -91
  128. package/src/compat/client/errors/install.ts +71 -72
  129. package/src/compat/client/errors/lazy.ts +23 -23
  130. package/src/compat/client/errors/primitive-throw.ts +47 -45
  131. package/src/compat/client/errors/soft-refresh.ts +4 -4
  132. package/src/compat/client/link-status.ts +33 -33
  133. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  134. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  135. package/src/compat/client/navigation-scroll.ts +63 -63
  136. package/src/compat/client/optimistic-routing.ts +93 -88
  137. package/src/compat/client/prefetch-cache.ts +23 -24
  138. package/src/compat/client/route-announcer.ts +35 -35
  139. package/src/compat/client/segment-cache-policy.ts +20 -20
  140. package/src/compat/client/segment-cache.ts +309 -315
  141. package/src/compat/client/segment-prefetch.ts +127 -132
  142. package/src/compat/client/trailing-slash.ts +5 -4
  143. package/src/compat/css/chunking.ts +113 -116
  144. package/src/compat/css/inline-css.ts +21 -21
  145. package/src/compat/css/lightningcss.ts +37 -38
  146. package/src/compat/css/modules.ts +161 -175
  147. package/src/compat/css/nonce.ts +7 -7
  148. package/src/compat/css/sass-plugin.ts +18 -21
  149. package/src/compat/css/sass.ts +150 -152
  150. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  151. package/src/compat/css/styled-jsx.ts +21 -21
  152. package/src/compat/edge-runtime.ts +27 -27
  153. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  154. package/src/compat/export/client.ts +29 -31
  155. package/src/compat/export/{index.ts → emit.ts} +111 -110
  156. package/src/compat/export/standalone.ts +62 -54
  157. package/src/compat/image-optimizer/cache.ts +51 -49
  158. package/src/compat/image-optimizer/detect.ts +52 -52
  159. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  160. package/src/compat/image-optimizer/source.ts +77 -80
  161. package/src/compat/lifecycle/after-scope.ts +26 -26
  162. package/src/compat/lifecycle/after.ts +48 -45
  163. package/src/compat/lifecycle/error-funnel.ts +98 -102
  164. package/src/compat/lifecycle/error-serialize.ts +30 -26
  165. package/src/compat/lifecycle/error-ui.ts +67 -32
  166. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  167. package/src/compat/lifecycle/instrumentation.ts +85 -85
  168. package/src/compat/lifecycle/node-console.ts +11 -11
  169. package/src/compat/lifecycle/testmode.ts +160 -132
  170. package/src/compat/mdx/compile.ts +60 -57
  171. package/src/compat/mdx/plugin.ts +11 -11
  172. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  173. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  174. package/src/compat/metadata.ts +75 -81
  175. package/src/compat/next/cache.ts +68 -69
  176. package/src/compat/next/canonical-url.ts +9 -9
  177. package/src/compat/next/client-cache.ts +19 -19
  178. package/src/compat/next/client-navigation.ts +112 -116
  179. package/src/compat/next/client-only.ts +1 -1
  180. package/src/compat/next/client-script.tsx +99 -93
  181. package/src/compat/next/client-server.ts +10 -10
  182. package/src/compat/next/config-loader.ts +176 -173
  183. package/src/compat/next/config.ts +7 -7
  184. package/src/compat/next/constants.cjs +6 -6
  185. package/src/compat/next/constants.ts +6 -6
  186. package/src/compat/next/custom-server.ts +26 -24
  187. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  188. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  189. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  190. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  191. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  192. package/src/compat/next/dynamic.tsx +21 -18
  193. package/src/compat/next/error.tsx +52 -52
  194. package/src/compat/next/font/cache.ts +74 -74
  195. package/src/compat/next/font/google.ts +2 -2
  196. package/src/compat/next/font/index.ts +1 -1
  197. package/src/compat/next/font/local.ts +3 -3
  198. package/src/compat/next/font/runtime-client.ts +17 -15
  199. package/src/compat/next/font/runtime.ts +443 -408
  200. package/src/compat/next/font/shared.ts +120 -108
  201. package/src/compat/next/form.tsx +63 -63
  202. package/src/compat/next/head.tsx +2 -2
  203. package/src/compat/next/headers.ts +103 -95
  204. package/src/compat/next/image/client.tsx +220 -0
  205. package/src/compat/next/image/config.ts +56 -58
  206. package/src/compat/next/image/optimizer.ts +40 -36
  207. package/src/compat/next/image/patterns.ts +37 -40
  208. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  209. package/src/compat/next/image/shared.ts +65 -57
  210. package/src/compat/next/image/static-metadata.ts +98 -107
  211. package/src/compat/next/image/validate.ts +79 -88
  212. package/src/compat/next/image.tsx +19 -23
  213. package/src/compat/next/index.ts +1 -1
  214. package/src/compat/next/legacy-image.tsx +59 -60
  215. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  216. package/src/compat/next/link.tsx +158 -158
  217. package/src/compat/next/navigation.cjs +12 -3
  218. package/src/compat/next/navigation.ts +48 -50
  219. package/src/compat/next/offline.ts +27 -27
  220. package/src/compat/next/og.ts +121 -124
  221. package/src/compat/next/preferred-region.ts +13 -14
  222. package/src/compat/next/redirects.ts +58 -56
  223. package/src/compat/next/resource-hints.ts +73 -76
  224. package/src/compat/next/rewrites.ts +130 -133
  225. package/src/compat/next/root-params.ts +45 -45
  226. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  227. package/src/compat/next/router.cjs +4 -2
  228. package/src/compat/next/router.ts +58 -61
  229. package/src/compat/next/script.tsx +108 -108
  230. package/src/compat/next/server-only.ts +1 -1
  231. package/src/compat/next/server.ts +19 -19
  232. package/src/compat/next/svgr.ts +18 -17
  233. package/src/compat/next/telemetry.ts +24 -24
  234. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  235. package/src/compat/next/user-agent.ts +53 -49
  236. package/src/compat/next/web-vitals.ts +22 -24
  237. package/src/compat/otel/api.ts +41 -41
  238. package/src/compat/otel/client-trace-metadata.ts +25 -27
  239. package/src/compat/otel/fetch-span.ts +29 -29
  240. package/src/compat/otel/tracer.ts +331 -331
  241. package/src/compat/pages/api.ts +456 -0
  242. package/src/compat/pages/client-plugin.ts +36 -36
  243. package/src/compat/pages/router-state.ts +34 -34
  244. package/src/compat/pages/{index.ts → router.ts} +130 -135
  245. package/src/compat/ppr/io.ts +12 -12
  246. package/src/compat/ppr/missing-root-params.ts +34 -38
  247. package/src/compat/ppr/root-params-scan.ts +66 -66
  248. package/src/compat/ppr/root-params-transform.ts +24 -26
  249. package/src/compat/ppr/root-params.ts +30 -30
  250. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  251. package/src/compat/protocol.ts +71 -70
  252. package/src/compat/react/action-state.ts +47 -48
  253. package/src/compat/react/client-lite.ts +15 -15
  254. package/src/compat/react/client.ts +4 -4
  255. package/src/compat/react/compiler-runtime.ts +11 -11
  256. package/src/compat/react/dom-client.ts +44 -44
  257. package/src/compat/react/dom-react-server.ts +10 -16
  258. package/src/compat/react/dom-server.ts +10 -10
  259. package/src/compat/react/dom.ts +52 -52
  260. package/src/compat/react/hooks-extra.ts +34 -35
  261. package/src/compat/react/parity.ts +64 -61
  262. package/src/compat/react/preact.ts +81 -82
  263. package/src/compat/react/react-server.ts +28 -28
  264. package/src/compat/react/router-shim.ts +1 -1
  265. package/src/compat/react/server-component-use.ts +8 -8
  266. package/src/compat/react/server-inserted-html.ts +30 -31
  267. package/src/compat/react/server.ts +53 -55
  268. package/src/compat/react/use.ts +32 -32
  269. package/src/compat/react/view-transition.ts +20 -20
  270. package/src/compat/register/actions.ts +35 -824
  271. package/src/compat/register/boot.ts +41 -41
  272. package/src/compat/register/build-tier.ts +5 -5
  273. package/src/compat/register/build.ts +74 -70
  274. package/src/compat/register/bundler.ts +161 -156
  275. package/src/compat/register/cache.ts +20 -20
  276. package/src/compat/register/client-errors.ts +3 -3
  277. package/src/compat/register/config.ts +6 -6
  278. package/src/compat/register/css-extras.ts +30 -34
  279. package/src/compat/register/edge-runtime.ts +3 -3
  280. package/src/compat/register/errors.ts +10 -12
  281. package/src/compat/register/export.ts +16 -16
  282. package/src/compat/register/font.ts +11 -11
  283. package/src/compat/register/hooks.ts +2 -2
  284. package/src/compat/register/image.ts +40 -40
  285. package/src/compat/register/index.ts +59 -62
  286. package/src/compat/register/instrumentation-client.ts +10 -10
  287. package/src/compat/register/lifecycle.ts +25 -28
  288. package/src/compat/register/mdx.ts +10 -10
  289. package/src/compat/register/middleware.ts +226 -16
  290. package/src/compat/register/otel.ts +80 -88
  291. package/src/compat/register/pages-api.ts +10 -463
  292. package/src/compat/register/ppr.ts +16 -16
  293. package/src/compat/register/protocol.ts +17 -18
  294. package/src/compat/register/proxy.ts +49 -51
  295. package/src/compat/register/render.ts +79 -76
  296. package/src/compat/register/routing.ts +162 -159
  297. package/src/compat/register/segment.ts +24 -1897
  298. package/src/compat/register/static-image.ts +3 -3
  299. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  300. package/src/compat/register/typed-routes.ts +14 -14
  301. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  302. package/src/compat/register/validation.ts +18 -18
  303. package/src/compat/segment/loading-boundary.ts +43 -45
  304. package/src/compat/segment/page-slot.ts +69 -69
  305. package/src/compat/segment/serve.ts +1884 -0
  306. package/src/compat/segment/tree.ts +113 -112
  307. package/src/compat/segment/vary-key.ts +38 -38
  308. package/src/compat/segment/vary-params.ts +138 -142
  309. package/src/compat/static-params.ts +14 -12
  310. package/src/compat/tsconfig-defaults.ts +87 -91
  311. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  312. package/src/compat/typecheck/worker.ts +15 -12
  313. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  314. package/src/compat/typed-routes/manifest.ts +174 -170
  315. package/src/compat/typed-routes/typegen.ts +127 -110
  316. package/src/compat/validation/errors.ts +16 -19
  317. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  318. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  319. package/src/compat-bootstrap.ts +16 -16
  320. package/src/config.ts +69 -69
  321. package/src/css/build.ts +230 -227
  322. package/src/css/postcss.ts +79 -80
  323. package/src/css/worker.ts +14 -15
  324. package/src/dev/client-actions.ts +10 -10
  325. package/src/dev/client-chunk-store.ts +27 -27
  326. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  327. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  328. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  329. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  330. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  331. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  332. package/src/dev/server.ts +804 -820
  333. package/src/env.ts +46 -43
  334. package/src/extensions.ts +491 -478
  335. package/src/index.ts +8 -8
  336. package/src/internal.ts +20 -23
  337. package/src/{islands → render}/boundary-error.ts +3 -3
  338. package/src/render/hooks.ts +71 -71
  339. package/src/render/island-context.ts +14 -14
  340. package/src/render/metadata.ts +310 -310
  341. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  342. package/src/{ppr.ts → render/ppr.ts} +244 -245
  343. package/src/render/renderer.ts +2076 -2082
  344. package/src/render/resource-hints.ts +16 -17
  345. package/src/render/slots.tsx +224 -235
  346. package/src/{islands → render}/static-children.ts +9 -12
  347. package/src/{islands → render}/static-slots.ts +37 -37
  348. package/src/{cache/context.ts → request/cache.ts} +20 -20
  349. package/src/request/context.ts +107 -107
  350. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  351. package/src/resolve/engine.ts +90 -77
  352. package/src/resolve/imports.ts +475 -463
  353. package/src/resolve/scan-facts.ts +318 -296
  354. package/src/resolve/source-text.ts +37 -37
  355. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  356. package/src/routing/forwarded.ts +19 -19
  357. package/src/routing/handler.ts +84 -91
  358. package/src/routing/href.ts +69 -70
  359. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  360. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  361. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  362. package/src/routing/routes.ts +827 -815
  363. package/src/routing/slots.ts +164 -160
  364. package/src/runtime/loader.ts +952 -0
  365. package/src/{dev → runtime}/module-cache.ts +309 -287
  366. package/src/{dev → runtime}/module-generations.ts +9 -9
  367. package/src/{dev → runtime}/module-transform.ts +81 -72
  368. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  369. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  370. package/src/runtime/vendor.ts +425 -404
  371. package/src/styles.d.ts +9 -0
  372. package/src/types.ts +320 -335
  373. package/src/utils/ansi.ts +5 -5
  374. package/src/utils/{source.ts → code.ts} +15 -12
  375. package/src/utils/content-type.ts +3 -3
  376. package/src/utils/decode.ts +2 -2
  377. package/src/utils/dev-profile.ts +13 -13
  378. package/src/utils/error-log.ts +6 -6
  379. package/src/utils/esbuild.ts +18 -18
  380. package/src/utils/fs-cache.ts +13 -13
  381. package/src/utils/fs.ts +57 -49
  382. package/src/utils/html.ts +20 -24
  383. package/src/utils/native-require.ts +8 -8
  384. package/src/utils/serialize.ts +139 -146
  385. package/src/utils/verbose.ts +18 -18
  386. package/src/cli/analyze-print.ts +0 -181
  387. package/src/compat/middleware/manifest.ts +0 -210
  388. package/src/compat/next/image-client.tsx +0 -215
  389. package/src/compat/next/link-usage.ts +0 -29
  390. package/src/css/index.ts +0 -2
  391. package/src/render/index.ts +0 -1
  392. package/src/style-modules.d.ts +0 -9
@@ -11,10 +11,10 @@
11
11
  * serialization boundary), and named custom cache handler kinds fall back to
12
12
  * the default handler.
13
13
  */
14
- import { AsyncLocalStorage } from 'node:async_hooks';
15
- import { Fragment, h, type ComponentChildren } from 'preact';
16
- import { Suspense as CoreSuspense } from '../../api/suspense';
17
- import { currentParams, currentRequest, getWorkUnit } from '../../request/context';
14
+ import { AsyncLocalStorage } from 'node:async_hooks'
15
+ import { Fragment, h, type ComponentChildren } from 'preact'
16
+ import { Suspense as CoreSuspense } from '../../api/suspense'
17
+ import { currentParams, currentRequest, getWorkUnit } from '../../request/context'
18
18
  import {
19
19
  hangingPromise,
20
20
  isPrefetchShellPrerender,
@@ -24,7 +24,7 @@ import {
24
24
  PostponeError,
25
25
  realNow,
26
26
  trackPrerenderCacheFill,
27
- } from '../../ppr';
27
+ } from '../../render/ppr'
28
28
  import {
29
29
  currentRenderCacheMeta,
30
30
  currentTagRevalidationSeq,
@@ -41,7 +41,7 @@ import {
41
41
  setUseCacheRevalidateSink,
42
42
  tagsRevalidatedAfterSeq,
43
43
  tagsStaleSince,
44
- } from './revalidate';
44
+ } from './revalidate'
45
45
  import {
46
46
  hasModernCacheHandler,
47
47
  modernCacheEntry,
@@ -50,8 +50,8 @@ import {
50
50
  modernCacheKey,
51
51
  modernCacheRefreshTags,
52
52
  modernCacheSet,
53
- } from './modern-handler';
54
- import { funnelCacheRuntimeError } from './runtime-error';
53
+ } from './modern-handler'
54
+ import { funnelCacheRuntimeError } from './runtime-error'
55
55
  import {
56
56
  prerenderErrorCollectionActive,
57
57
  recordPrerenderError,
@@ -60,7 +60,7 @@ import {
60
60
  USE_CACHE_CLOSE_OVER_FUNCTION_BLOCK,
61
61
  USE_CACHE_CLOSE_OVER_FUNCTION_MESSAGE,
62
62
  USE_CACHE_HANGING_MESSAGE,
63
- } from './build-prerender-errors';
63
+ } from './build-prerender-errors'
64
64
 
65
65
  // ---------------------------------------------------------------------------
66
66
  // Build-prerender `use cache` fill timeout (E236 hanging-input detection).
@@ -70,155 +70,161 @@ import {
70
70
  // (seconds) and, on timeout, rejects with the E236 message. We mirror that only
71
71
  // while a compat build prerender is armed (see build-prerender-errors); the
72
72
  // serving runtime and pure-core builds are untouched. The value is seeded from
73
- // next.config by register-usecache at build start.
73
+ // next.config by register/use-cache at build start.
74
74
  // ---------------------------------------------------------------------------
75
75
 
76
76
  // Anchored on globalThis: begin/seed runs in the build process's copy of this
77
77
  // module, but `withFillTimeout` runs in the bundled compat-server's copy — a
78
78
  // module-local `let` would leave the bundle reading the stale default (see the
79
79
  // same duplication note in build-prerender-errors.ts).
80
- const USE_CACHE_FILL_TIMEOUT = Symbol.for('pnext.compat.useCacheFillTimeout');
80
+ const USE_CACHE_FILL_TIMEOUT = Symbol.for('pnext.compat.useCacheFillTimeout')
81
81
 
82
82
  function fillTimeoutHolder(): { ms: number } {
83
- const root = globalThis as Record<PropertyKey, unknown>;
84
- return (root[USE_CACHE_FILL_TIMEOUT] ??= { ms: 50_000 }) as { ms: number };
83
+ const root = globalThis as Record<PropertyKey, unknown>
84
+ return (root[USE_CACHE_FILL_TIMEOUT] ??= { ms: 50_000 }) as { ms: number }
85
85
  }
86
86
 
87
87
  /**
88
88
  * Seed the build-time `use cache` fill timeout (milliseconds). Next derives it
89
89
  * from `experimental.useCacheTimeout`, clamped during a build to just under
90
- * `staticPageGenerationTimeout` (register-usecache passes the already-resolved
90
+ * `staticPageGenerationTimeout` (register/use-cache passes the already-resolved
91
91
  * value). Ignored (left at the 50s default) when undefined.
92
92
  */
93
93
  export function setUseCacheFillTimeout(ms: number | undefined): void {
94
94
  if (typeof ms === 'number' && Number.isFinite(ms) && ms > 0) {
95
- fillTimeoutHolder().ms = ms;
95
+ fillTimeoutHolder().ms = ms
96
96
  }
97
97
  }
98
98
 
99
99
  interface UseCacheEntry {
100
- value: Promise<unknown>;
101
- storedAt: number;
102
- tags: string[];
103
- revalidateSeconds?: number;
104
- expireSeconds?: number;
105
- staleSeconds?: number;
100
+ value: Promise<unknown>
101
+ storedAt: number
102
+ tags: string[]
103
+ revalidateSeconds?: number
104
+ expireSeconds?: number
105
+ staleSeconds?: number
106
106
  /** A background stale-while-revalidate refresh is already in flight. */
107
- refreshing?: boolean;
108
- modernKey?: string;
107
+ refreshing?: boolean
108
+ modernKey?: string
109
109
  /** Pathname of the render that produced this entry (path-revalidation scope). */
110
- route?: string;
110
+ route?: string
111
111
  /** Hard-tag-revalidation sequence sampled when production began. */
112
- tagSeq: number;
112
+ tagSeq: number
113
113
  /**
114
114
  * The render cache-meta this entry was produced under. An on-demand
115
115
  * regeneration render (refreshFetches) re-produces each entry ONCE and then
116
116
  * reuses it, so repeated reads within the regen dedupe (Next refreshes per
117
117
  * unique cache, not per call).
118
118
  */
119
- refreshedIn?: object;
119
+ refreshedIn?: object
120
120
  /**
121
121
  * Nested `use cache` wrapper vnodes embedded in this entry's produced tree
122
122
  * that have not yet rendered. Their scopes fold tags + min cacheLife into
123
123
  * this entry when they settle (see attributeNestedCacheWrapper); persisting
124
124
  * the entry to a modern cache handler waits for the count to drain.
125
125
  */
126
- nestedPending?: number;
126
+ nestedPending?: number
127
127
  /** Invoked each time a pending nested wrapper settles. */
128
- onNestedSettled?: () => void;
128
+ onNestedSettled?: () => void
129
129
  }
130
130
 
131
- type RdcValue = null | boolean | number | string | RdcValue[] | { [key: string]: RdcValue };
131
+ type RdcValue = null | boolean | number | string | RdcValue[] | { [key: string]: RdcValue }
132
132
 
133
133
  export interface UseCacheRdcRecord {
134
- key: string;
135
- value: RdcValue;
136
- tags: string[];
137
- storedAt: number;
138
- revalidateSeconds?: number;
139
- expireSeconds?: number;
140
- staleSeconds?: number;
141
- tagSeq: number;
134
+ key: string
135
+ value: RdcValue
136
+ tags: string[]
137
+ storedAt: number
138
+ revalidateSeconds?: number
139
+ expireSeconds?: number
140
+ staleSeconds?: number
141
+ tagSeq: number
142
142
  /** Process that produced this record — `tagSeq` only means anything there. */
143
- pid?: string;
144
- route?: string;
143
+ pid?: string
144
+ route?: string
145
145
  }
146
146
 
147
147
  interface UseCacheScope {
148
- tags: Set<string>;
149
- revalidateSeconds?: number;
150
- expireSeconds?: number;
151
- staleSeconds?: number;
148
+ tags: Set<string>
149
+ revalidateSeconds?: number
150
+ expireSeconds?: number
151
+ staleSeconds?: number
152
152
  /**
153
153
  * The cached-function id producing inside this scope. Root-param reads made
154
154
  * during production attribute to this id so the id's read-set (see
155
155
  * knownRootParamsByFunctionId) widens and later calls key on those params.
156
156
  */
157
- functionId?: string;
157
+ functionId?: string
158
158
  /** Root param NAMES read (via next/root-params) while producing this entry. */
159
- rootParamsRead?: Set<string>;
159
+ rootParamsRead?: Set<string>
160
160
  }
161
161
 
162
- const USE_CACHE_ENTRIES = Symbol.for('pnext.compat.useCacheEntries');
163
- const USE_CACHE_SCOPE_STORAGE = Symbol.for('pnext.compat.useCacheScopeStorage');
164
- const USE_CACHE_KNOWN_ROOT_PARAMS = Symbol.for('pnext.compat.knownRootParamsByFunctionId');
165
- const USE_CACHE_REQUEST_TOKENS = Symbol.for('pnext.compat.useCacheRequestTokens');
162
+ const USE_CACHE_ENTRIES = Symbol.for('pnext.compat.useCacheEntries')
163
+ const USE_CACHE_SCOPE_STORAGE = Symbol.for('pnext.compat.useCacheScopeStorage')
164
+ const USE_CACHE_KNOWN_ROOT_PARAMS = Symbol.for('pnext.compat.knownRootParamsByFunctionId')
165
+ const USE_CACHE_REQUEST_TOKENS = Symbol.for('pnext.compat.useCacheRequestTokens')
166
166
 
167
167
  function useCacheGlobal<T>(key: symbol, create: () => T): T {
168
- const state = globalThis as Record<PropertyKey, unknown>;
169
- const existing = state[key] as T | undefined;
170
- if (existing !== undefined) return existing;
171
- const value = create();
172
- state[key] = value;
173
- return value;
174
- }
175
-
176
- const entries = useCacheGlobal(USE_CACHE_ENTRIES, () => new Map<string, UseCacheEntry>());
177
- const entriesLimit = 4096;
178
- const scopeStorage = useCacheGlobal(USE_CACHE_SCOPE_STORAGE, () => new AsyncLocalStorage<UseCacheScope>());
179
- const requestTokens = useCacheGlobal(USE_CACHE_REQUEST_TOKENS, () => new WeakMap<object, object>());
180
- const USE_CACHE_RDC_CAPTURE = Symbol.for('pnext.compat.useCacheRdcCapture');
181
- const USE_CACHE_RDC_RECORDS = Symbol.for('pnext.compat.useCacheRdcRecords');
168
+ const state = globalThis as Record<PropertyKey, unknown>
169
+ const existing = state[key] as T | undefined
170
+ if (existing !== undefined) return existing
171
+ const value = create()
172
+ state[key] = value
173
+ return value
174
+ }
175
+
176
+ const entries = useCacheGlobal(USE_CACHE_ENTRIES, () => new Map<string, UseCacheEntry>())
177
+ const entriesLimit = 4096
178
+ const scopeStorage = useCacheGlobal(
179
+ USE_CACHE_SCOPE_STORAGE,
180
+ () => new AsyncLocalStorage<UseCacheScope>(),
181
+ )
182
+ const requestTokens = useCacheGlobal(USE_CACHE_REQUEST_TOKENS, () => new WeakMap<object, object>())
183
+ const USE_CACHE_RDC_CAPTURE = Symbol.for('pnext.compat.useCacheRdcCapture')
184
+ const USE_CACHE_RDC_RECORDS = Symbol.for('pnext.compat.useCacheRdcRecords')
182
185
 
183
186
  function useCacheRdcRecords(): Map<string, UseCacheRdcRecord> {
184
- return useCacheGlobal(USE_CACHE_RDC_RECORDS, () => new Map<string, UseCacheRdcRecord>());
187
+ return useCacheGlobal(USE_CACHE_RDC_RECORDS, () => new Map<string, UseCacheRdcRecord>())
185
188
  }
186
189
 
187
190
  function useCacheRdcCapture(): Set<string> | undefined {
188
- return (globalThis as Record<PropertyKey, unknown>)[USE_CACHE_RDC_CAPTURE] as Set<string> | undefined;
191
+ return (globalThis as Record<PropertyKey, unknown>)[USE_CACHE_RDC_CAPTURE] as
192
+ Set<string> | undefined
189
193
  }
190
194
 
191
195
  function captureUseCacheRdcEntry(key: string) {
192
- useCacheRdcCapture()?.add(key);
196
+ useCacheRdcCapture()?.add(key)
193
197
  }
194
198
 
195
199
  export function beginUseCacheRdcCapture() {
196
- (globalThis as Record<PropertyKey, unknown>)[USE_CACHE_RDC_CAPTURE] = new Set<string>();
200
+ ;(globalThis as Record<PropertyKey, unknown>)[USE_CACHE_RDC_CAPTURE] = new Set<string>()
197
201
  }
198
202
 
199
203
  export async function collectUseCacheRdcRecords(): Promise<UseCacheRdcRecord[]> {
200
- const capture = useCacheRdcCapture();
201
- if (!capture) return [];
202
- const records: UseCacheRdcRecord[] = [];
204
+ const capture = useCacheRdcCapture()
205
+ if (!capture) return []
206
+ const records: UseCacheRdcRecord[] = []
203
207
  for (const key of capture) {
204
- const entry = entries.get(key);
205
- if (!entry) continue;
206
- const value = toRdcValue(await entry.value);
207
- if (value === undefined) continue;
208
+ const entry = entries.get(key)
209
+ if (!entry) continue
210
+ const value = toRdcValue(await entry.value)
211
+ if (value === undefined) continue
208
212
  records.push({
209
213
  key,
210
214
  value,
211
215
  tags: [...entry.tags],
212
216
  storedAt: entry.storedAt,
213
- ...(entry.revalidateSeconds !== undefined ? { revalidateSeconds: entry.revalidateSeconds } : {}),
217
+ ...(entry.revalidateSeconds !== undefined
218
+ ? { revalidateSeconds: entry.revalidateSeconds }
219
+ : {}),
214
220
  ...(entry.expireSeconds !== undefined ? { expireSeconds: entry.expireSeconds } : {}),
215
221
  ...(entry.staleSeconds !== undefined ? { staleSeconds: entry.staleSeconds } : {}),
216
222
  tagSeq: entry.tagSeq,
217
223
  pid: rdcProcessId(),
218
224
  ...(entry.route !== undefined ? { route: entry.route } : {}),
219
- });
225
+ })
220
226
  }
221
- return records;
227
+ return records
222
228
  }
223
229
 
224
230
  /**
@@ -227,29 +233,29 @@ export async function collectUseCacheRdcRecords(): Promise<UseCacheRdcRecord[]>
227
233
  * unserializable but its cacheTag()s must still reach the shell's staleness check.
228
234
  */
229
235
  export async function collectUseCacheRdcTags(): Promise<string[]> {
230
- const capture = useCacheRdcCapture();
231
- if (!capture) return [];
232
- const tags = new Set<string>();
236
+ const capture = useCacheRdcCapture()
237
+ if (!capture) return []
238
+ const tags = new Set<string>()
233
239
  for (const key of capture) {
234
- const entry = entries.get(key);
235
- if (!entry) continue;
240
+ const entry = entries.get(key)
241
+ if (!entry) continue
236
242
  // Tags are applied to the entry once its production settles.
237
- await entry.value.catch(() => undefined);
238
- for (const tag of entry.tags) tags.add(tag);
243
+ await entry.value.catch(() => undefined)
244
+ for (const tag of entry.tags) tags.add(tag)
239
245
  }
240
- return [...tags];
246
+ return [...tags]
241
247
  }
242
248
 
243
249
  export function seedUseCacheRdcRecords(records: readonly UseCacheRdcRecord[], route?: string) {
244
250
  for (const record of records) {
245
- if (!isUseCacheRdcRecord(record)) continue;
251
+ if (!isUseCacheRdcRecord(record)) continue
246
252
  // Foreign-process sequences are meaningless here — see rebaseRdcTagSeq.
247
- const tagSeq = rebaseRdcTagSeq(record);
253
+ const tagSeq = rebaseRdcTagSeq(record)
248
254
  // An RDC artifact is a snapshot: seeding it must never regress a live
249
255
  // entry produced more recently (each regen seeds the stale .rdc first).
250
- const live = entries.get(record.key);
251
- if (live !== undefined && live.storedAt > record.storedAt) continue;
252
- useCacheRdcRecords().set(record.key, { ...record, tagSeq });
256
+ const live = entries.get(record.key)
257
+ if (live !== undefined && live.storedAt > record.storedAt) continue
258
+ useCacheRdcRecords().set(record.key, { ...record, tagSeq })
253
259
  entries.set(record.key, {
254
260
  value: Promise.resolve(record.value),
255
261
  storedAt: record.storedAt,
@@ -260,14 +266,18 @@ export function seedUseCacheRdcRecords(records: readonly UseCacheRdcRecord[], ro
260
266
  ...(record.expireSeconds !== undefined ? { expireSeconds: record.expireSeconds } : {}),
261
267
  ...(record.staleSeconds !== undefined ? { staleSeconds: record.staleSeconds } : {}),
262
268
  tagSeq,
263
- ...(route !== undefined ? { route } : record.route !== undefined ? { route: record.route } : {}),
264
- });
269
+ ...(route !== undefined
270
+ ? { route }
271
+ : record.route !== undefined
272
+ ? { route: record.route }
273
+ : {}),
274
+ })
265
275
  }
266
276
  }
267
277
 
268
278
  function isUseCacheRdcRecord(value: unknown): value is UseCacheRdcRecord {
269
- if (!value || typeof value !== 'object') return false;
270
- const record = value as Partial<UseCacheRdcRecord>;
279
+ if (!value || typeof value !== 'object') return false
280
+ const record = value as Partial<UseCacheRdcRecord>
271
281
  return (
272
282
  typeof record.key === 'string' &&
273
283
  Array.isArray(record.tags) &&
@@ -277,26 +287,26 @@ function isUseCacheRdcRecord(value: unknown): value is UseCacheRdcRecord {
277
287
  typeof record.tagSeq === 'number' &&
278
288
  Number.isInteger(record.tagSeq) &&
279
289
  isRdcValue(record.value)
280
- );
290
+ )
281
291
  }
282
292
 
283
293
  function toRdcValue(value: unknown): RdcValue | undefined {
284
- if (!isRdcValue(value)) return undefined;
285
- return JSON.parse(JSON.stringify(value)) as RdcValue;
294
+ if (!isRdcValue(value)) return undefined
295
+ return JSON.parse(JSON.stringify(value)) as RdcValue
286
296
  }
287
297
 
288
298
  function isRdcValue(value: unknown, seen = new WeakSet<object>()): value is RdcValue {
289
- if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;
290
- if (typeof value === 'number') return Number.isFinite(value);
291
- if (typeof value !== 'object' || seen.has(value)) return false;
299
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') return true
300
+ if (typeof value === 'number') return Number.isFinite(value)
301
+ if (typeof value !== 'object' || seen.has(value)) return false
292
302
  if (Array.isArray(value)) {
293
- seen.add(value);
294
- return value.every(item => isRdcValue(item, seen));
303
+ seen.add(value)
304
+ return value.every(item => isRdcValue(item, seen))
295
305
  }
296
- const prototype = Object.getPrototypeOf(value) as object | null;
297
- if (prototype !== Object.prototype && prototype !== null) return false;
298
- seen.add(value);
299
- return Object.values(value).every(item => isRdcValue(item, seen));
306
+ const prototype = Object.getPrototypeOf(value) as object | null
307
+ if (prototype !== Object.prototype && prototype !== null) return false
308
+ seen.add(value)
309
+ return Object.values(value).every(item => isRdcValue(item, seen))
300
310
  }
301
311
 
302
312
  // ---------------------------------------------------------------------------
@@ -320,24 +330,23 @@ function isRdcValue(value: unknown, seen = new WeakSet<object>()): value is RdcV
320
330
  const knownRootParamsByFunctionId = useCacheGlobal(
321
331
  USE_CACHE_KNOWN_ROOT_PARAMS,
322
332
  () => new Map<string, Set<string>>(),
323
- );
333
+ )
324
334
 
325
335
  // The app's ROOT param names (segments at/above a root layout), seeded from the
326
336
  // build-time scan at registration. Anchored on globalThis so the build process
327
337
  // and the built server bundle read one copy. Only these names may join a cache
328
338
  // key: a non-root route param must never discriminate entries, and a fallback
329
339
  // prerender's unknown params must never poison the key.
330
- const USE_CACHE_ROOT_PARAM_NAMES = Symbol.for('pnext.compat.rootParamNames');
340
+ const USE_CACHE_ROOT_PARAM_NAMES = Symbol.for('pnext.compat.rootParamNames')
331
341
 
332
- /** Seed the scanned root-param name set (register-usecache, build + serve). */
342
+ /** Seed the scanned root-param name set (register/use-cache, build + serve). */
333
343
  export function setUseCacheRootParamNames(names: Iterable<string>): void {
334
- (globalThis as Record<PropertyKey, unknown>)[USE_CACHE_ROOT_PARAM_NAMES] = new Set(names);
344
+ ;(globalThis as Record<PropertyKey, unknown>)[USE_CACHE_ROOT_PARAM_NAMES] = new Set(names)
335
345
  }
336
346
 
337
347
  function rootParamNames(): Set<string> | undefined {
338
348
  return (globalThis as Record<PropertyKey, unknown>)[USE_CACHE_ROOT_PARAM_NAMES] as
339
- | Set<string>
340
- | undefined;
349
+ Set<string> | undefined
341
350
  }
342
351
 
343
352
  /**
@@ -347,21 +356,21 @@ function rootParamNames(): Set<string> | undefined {
347
356
  * participated in a cache key); false otherwise (plain request read).
348
357
  */
349
358
  export function recordUseCacheRootParamRead(name: string): boolean {
350
- const scope = scopeStorage.getStore();
351
- if (!scope?.functionId) return false;
352
- (scope.rootParamsRead ??= new Set()).add(name);
353
- let known = knownRootParamsByFunctionId.get(scope.functionId);
359
+ const scope = scopeStorage.getStore()
360
+ if (!scope?.functionId) return false
361
+ ;(scope.rootParamsRead ??= new Set()).add(name)
362
+ let known = knownRootParamsByFunctionId.get(scope.functionId)
354
363
  if (!known) {
355
- known = new Set();
356
- knownRootParamsByFunctionId.set(scope.functionId, known);
364
+ known = new Set()
365
+ knownRootParamsByFunctionId.set(scope.functionId, known)
357
366
  }
358
- known.add(name);
359
- return true;
367
+ known.add(name)
368
+ return true
360
369
  }
361
370
 
362
371
  /** True when a `use cache` producer is currently on the stack (E1140 guard). */
363
372
  export function insideUseCacheProducer(): boolean {
364
- return scopeStorage.getStore()?.functionId !== undefined;
373
+ return scopeStorage.getStore()?.functionId !== undefined
365
374
  }
366
375
 
367
376
  /**
@@ -371,57 +380,53 @@ export function insideUseCacheProducer(): boolean {
371
380
  * when the id has no known root-param reads.
372
381
  */
373
382
  function withRootParamKeyArgs(id: string, args: unknown[]): unknown[] {
374
- const known = knownRootParamsByFunctionId.get(id);
375
- const params = currentParams();
383
+ const known = knownRootParamsByFunctionId.get(id)
384
+ const params = currentParams()
376
385
  // The first call has no observed read-set yet, but it must still not seed a cross-root entry. Once
377
386
  // reads are known, narrow back to that set; until then every KNOWN (resolved) root param is a
378
387
  // conservative cache discriminator - never non-root route params (they must not split entries) and
379
388
  // never a fallback prerender's unfilled params (they must not poison the key). Over-discrimination
380
389
  // costs a duplicate entry, under-discrimination shares a value across roots.
381
- const roots = rootParamNames();
390
+ const roots = rootParamNames()
382
391
  const names = known?.size
383
392
  ? [...known]
384
- : Object.keys(params).filter(
385
- name => (!roots || roots.has(name)) && params[name] !== undefined,
386
- );
387
- if (names.length === 0) return args;
393
+ : Object.keys(params).filter(name => (!roots || roots.has(name)) && params[name] !== undefined)
394
+ if (names.length === 0) return args
388
395
  // Stable order (sorted names) so the key is deterministic regardless of the
389
396
  // order reads were discovered in.
390
- const rootParamEntries = names
391
- .sort()
392
- .map(name => [name, params[name]] as const);
393
- return [{ $pnextRootParams: rootParamEntries }, ...args];
397
+ const rootParamEntries = names.sort().map(name => [name, params[name]] as const)
398
+ return [{ $pnextRootParams: rootParamEntries }, ...args]
394
399
  }
395
400
 
396
401
  /** Test hook. */
397
402
  export function clearKnownRootParams() {
398
- knownRootParamsByFunctionId.clear();
403
+ knownRootParamsByFunctionId.clear()
399
404
  }
400
405
 
401
406
  // Next's `default` cacheLife profile, applied to a `use cache` entry that never
402
407
  // calls cacheLife(). Kept here (not read from config) so the runtime has no
403
408
  // dependency on the compat config loader; a config `expireTime`/custom default
404
409
  // override only affects explicit cacheLife() calls (see compat/next/cache.ts).
405
- const DEFAULT_REVALIDATE_SECONDS = 900;
406
- const DEFAULT_EXPIRE_SECONDS = 31536000;
407
- const DEFAULT_STALE_SECONDS = 300;
410
+ const DEFAULT_REVALIDATE_SECONDS = 900
411
+ const DEFAULT_EXPIRE_SECONDS = 31536000
412
+ const DEFAULT_STALE_SECONDS = 300
408
413
 
409
414
  // A runtime prefetch (`unstable_instant`) includes a `use cache` scope only when its effective STALE
410
415
  // window is at least this long; a shorter-lived cache stays a postponed hole. The threshold applies to
411
416
  // `stale` ONLY, never `expire` - a public cache with a short expire but a long stale is still included.
412
- const RUNTIME_PREFETCH_DYNAMIC_STALE = 30;
417
+ const RUNTIME_PREFETCH_DYNAMIC_STALE = 30
413
418
 
414
419
  // A STATIC prerender whose output the client prefetches omits a `use cache` scope that expires inside
415
420
  // this window: data that short-lived is not worth prerendering, so its boundary stays a hole the
416
421
  // navigation fills live. Note the two thresholds read DIFFERENT fields: a runtime prefetch gates on
417
422
  // `stale` (a short-expire/long-stale cache is still prefetchable), a static prefetch shell on `expire`.
418
- const PREFETCH_SHELL_DYNAMIC_EXPIRE = 300;
423
+ const PREFETCH_SHELL_DYNAMIC_EXPIRE = 300
419
424
 
420
425
  /** How the active prerender gates a short-lived `use cache` scope, if at all. */
421
426
  function prefetchCacheGate(): 'stale' | 'expire' | null {
422
- if (isRuntimePrefetchPrerender()) return 'stale';
423
- if (isPrefetchShellPrerender()) return 'expire';
424
- return null;
427
+ if (isRuntimePrefetchPrerender()) return 'stale'
428
+ if (isPrefetchShellPrerender()) return 'expire'
429
+ return null
425
430
  }
426
431
 
427
432
  /**
@@ -439,22 +444,22 @@ function gatePrefetchCacheLife<T>(
439
444
  // same way awaiting cookies()/params does (identity for the shell gate).
440
445
  return markRuntimeApiOnAwait(
441
446
  Promise.resolve(result).then(value => {
442
- const effective = life();
447
+ const effective = life()
443
448
  const short =
444
449
  gate === 'stale'
445
450
  ? (effective?.staleSeconds ?? DEFAULT_STALE_SECONDS) < RUNTIME_PREFETCH_DYNAMIC_STALE
446
- : (effective?.expireSeconds ?? DEFAULT_EXPIRE_SECONDS) < PREFETCH_SHELL_DYNAMIC_EXPIRE;
447
- if (short) return hangingPromise<T>('use cache');
448
- return value;
451
+ : (effective?.expireSeconds ?? DEFAULT_EXPIRE_SECONDS) < PREFETCH_SHELL_DYNAMIC_EXPIRE
452
+ if (short) return hangingPromise<T>('use cache')
453
+ return value
449
454
  }),
450
- );
455
+ )
451
456
  }
452
457
 
453
458
  // ---------------------------------------------------------------------------
454
459
  // cacheLife/response-header plumbing.
455
460
  //
456
461
  // A `use cache` entry's cacheLife (revalidate/expire/stale) is stashed on the
457
- // request work unit as it resolves during a render; the register-usecache
462
+ // request work unit as it resolves during a render; the register/use-cache
458
463
  // responseFinalizer reads the aggregate and emits the SWR `cache-control` +
459
464
  // `x-nextjs-stale-time` headers (mirroring how the font runtime stashes
460
465
  // preloads for a `Link` header). Only the LONGEST-lived cache scope on the
@@ -463,12 +468,12 @@ function gatePrefetchCacheLife<T>(
463
468
  // ---------------------------------------------------------------------------
464
469
 
465
470
  /** Well-known key for the per-request cache-life aggregate stash. */
466
- export const USE_CACHE_LIFE = Symbol.for('pnext.compat.useCacheLife');
471
+ export const USE_CACHE_LIFE = Symbol.for('pnext.compat.useCacheLife')
467
472
 
468
473
  export interface CacheLifeStash {
469
- revalidateSeconds?: number;
470
- expireSeconds?: number;
471
- staleSeconds?: number;
474
+ revalidateSeconds?: number
475
+ expireSeconds?: number
476
+ staleSeconds?: number
472
477
  }
473
478
 
474
479
  function stashCacheLife(life: CacheLifeStash): void {
@@ -477,121 +482,123 @@ function stashCacheLife(life: CacheLifeStash): void {
477
482
  life.expireSeconds === undefined &&
478
483
  life.staleSeconds === undefined
479
484
  ) {
480
- return;
485
+ return
481
486
  }
482
- const unit = getWorkUnit();
483
- if (!unit) return;
484
- const compat = (unit.compat ??= {});
485
- const existing = (compat[USE_CACHE_LIFE] as CacheLifeStash | undefined) ?? {};
487
+ const unit = getWorkUnit()
488
+ if (!unit) return
489
+ const compat = (unit.compat ??= {})
490
+ const existing = (compat[USE_CACHE_LIFE] as CacheLifeStash | undefined) ?? {}
486
491
  // Track the minimum revalidate (Next lowers the page revalidate to the
487
492
  // shortest cache used) but pair it with that same entry's expire so the
488
493
  // emitted SWR window is self-consistent.
489
- const nextRevalidate = minDefined(existing.revalidateSeconds, life.revalidateSeconds);
494
+ const nextRevalidate = minDefined(existing.revalidateSeconds, life.revalidateSeconds)
490
495
  const drivenByThis =
491
- life.revalidateSeconds !== undefined && life.revalidateSeconds === nextRevalidate;
496
+ life.revalidateSeconds !== undefined && life.revalidateSeconds === nextRevalidate
492
497
  // `staleSeconds` is its OWN minimum, NOT the min-revalidate entry's: the
493
498
  // client router cache is only as fresh as the shortest-lived content on the
494
499
  // page, and coupling it let whichever scope rendered first win the window
495
500
  // whenever the revalidates tied (the default case).
496
- const nextStale = minDefined(existing.staleSeconds, life.staleSeconds);
501
+ const nextStale = minDefined(existing.staleSeconds, life.staleSeconds)
497
502
  compat[USE_CACHE_LIFE] = {
498
503
  ...(nextRevalidate !== undefined ? { revalidateSeconds: nextRevalidate } : {}),
499
504
  ...(drivenByThis
500
505
  ? { ...(life.expireSeconds !== undefined ? { expireSeconds: life.expireSeconds } : {}) }
501
506
  : {
502
- ...(existing.expireSeconds !== undefined ? { expireSeconds: existing.expireSeconds } : {}),
507
+ ...(existing.expireSeconds !== undefined
508
+ ? { expireSeconds: existing.expireSeconds }
509
+ : {}),
503
510
  }),
504
511
  ...(nextStale !== undefined ? { staleSeconds: nextStale } : {}),
505
- };
512
+ }
506
513
  }
507
514
 
508
515
  /** Read + clear the cache-life aggregate stashed during the current render. */
509
516
  export function takeCacheLifeStash(): CacheLifeStash | undefined {
510
- const unit = getWorkUnit();
511
- const compat = unit?.compat;
512
- if (!compat) return undefined;
513
- const life = compat[USE_CACHE_LIFE] as CacheLifeStash | undefined;
514
- compat[USE_CACHE_LIFE] = undefined;
515
- return life;
517
+ const unit = getWorkUnit()
518
+ const compat = unit?.compat
519
+ if (!compat) return undefined
520
+ const life = compat[USE_CACHE_LIFE] as CacheLifeStash | undefined
521
+ compat[USE_CACHE_LIFE] = undefined
522
+ return life
516
523
  }
517
524
 
518
525
  function minDefined(a: number | undefined, b: number | undefined): number | undefined {
519
- if (a === undefined) return b;
520
- if (b === undefined) return a;
521
- return Math.min(a, b);
526
+ if (a === undefined) return b
527
+ if (b === undefined) return a
528
+ return Math.min(a, b)
522
529
  }
523
530
 
524
531
  // An explicit data-cache revalidate (e.g. `fetch(..., { next: { revalidate } })`
525
532
  // or a nested unstable_cache read) recorded while a `use cache` entry is
526
533
  // producing lowers the producing entry's own cacheLife, matching Next.
527
534
  setUseCacheRevalidateSink(seconds => {
528
- recordUseCacheLife(seconds);
529
- });
535
+ recordUseCacheLife(seconds)
536
+ })
530
537
 
531
538
  /** cacheTag() inside a 'use cache' function: tag the active entry. */
532
539
  export function recordUseCacheTags(tags: readonly string[]): boolean {
533
- const scope = scopeStorage.getStore();
534
- if (!scope) return false;
535
- for (const tag of tags) scope.tags.add(tag);
536
- return true;
540
+ const scope = scopeStorage.getStore()
541
+ if (!scope) return false
542
+ for (const tag of tags) scope.tags.add(tag)
543
+ return true
537
544
  }
538
545
 
539
546
  /** cacheLife() inside a 'use cache' function: bound the active entry's TTL. */
540
547
  export function recordUseCacheLife(life: number | CacheLifeStash): boolean {
541
- const scope = scopeStorage.getStore();
542
- if (!scope) return false;
543
- const revalidate = typeof life === 'number' ? life : life.revalidateSeconds;
544
- const expire = typeof life === 'number' ? undefined : life.expireSeconds;
545
- const stale = typeof life === 'number' ? undefined : life.staleSeconds;
548
+ const scope = scopeStorage.getStore()
549
+ if (!scope) return false
550
+ const revalidate = typeof life === 'number' ? life : life.revalidateSeconds
551
+ const expire = typeof life === 'number' ? undefined : life.expireSeconds
552
+ const stale = typeof life === 'number' ? undefined : life.staleSeconds
546
553
  if (revalidate !== undefined) {
547
554
  scope.revalidateSeconds =
548
555
  scope.revalidateSeconds === undefined
549
556
  ? revalidate
550
- : Math.min(scope.revalidateSeconds, revalidate);
557
+ : Math.min(scope.revalidateSeconds, revalidate)
551
558
  }
552
559
  if (expire !== undefined) {
553
560
  scope.expireSeconds =
554
- scope.expireSeconds === undefined ? expire : Math.min(scope.expireSeconds, expire);
561
+ scope.expireSeconds === undefined ? expire : Math.min(scope.expireSeconds, expire)
555
562
  }
556
563
  if (stale !== undefined) {
557
564
  scope.staleSeconds =
558
- scope.staleSeconds === undefined ? stale : Math.min(scope.staleSeconds, stale);
565
+ scope.staleSeconds === undefined ? stale : Math.min(scope.staleSeconds, stale)
559
566
  }
560
- return true;
567
+ return true
561
568
  }
562
569
 
563
570
  function entryExpired(entry: UseCacheEntry) {
564
571
  return (
565
572
  entry.revalidateSeconds !== undefined &&
566
573
  realNow() - entry.storedAt >= entry.revalidateSeconds * 1000
567
- );
574
+ )
568
575
  }
569
576
 
570
577
  /** Propagate an entry's cache meta to the surrounding render/'use cache' scopes. */
571
578
  function propagate(entry: UseCacheEntry, parent?: UseCacheScope) {
572
- const { tags, revalidateSeconds, expireSeconds, staleSeconds } = entry;
573
- recordCacheTags(tags);
574
- if (revalidateSeconds !== undefined) recordCacheRevalidate(revalidateSeconds);
579
+ const { tags, revalidateSeconds, expireSeconds, staleSeconds } = entry
580
+ recordCacheTags(tags)
581
+ if (revalidateSeconds !== undefined) recordCacheRevalidate(revalidateSeconds)
575
582
  // Also aggregate the expire/stale windows onto the render meta so a build
576
583
  // shell prerender can persist the route's SWR headers (the work-unit stash
577
584
  // below is not in scope during this async propagation — see build.ts).
578
- if (expireSeconds !== undefined) recordCacheExpire(expireSeconds);
579
- if (staleSeconds !== undefined) recordCacheStale(staleSeconds);
585
+ if (expireSeconds !== undefined) recordCacheExpire(expireSeconds)
586
+ if (staleSeconds !== undefined) recordCacheStale(staleSeconds)
580
587
  // Stash the effective cache-life on the request work unit so the finalizer
581
588
  // can emit the SWR cache-control + x-nextjs-stale-time headers.
582
589
  stashCacheLife({
583
590
  ...(revalidateSeconds !== undefined ? { revalidateSeconds } : {}),
584
591
  ...(expireSeconds !== undefined ? { expireSeconds } : {}),
585
592
  ...(staleSeconds !== undefined ? { staleSeconds } : {}),
586
- });
587
- if (parent) propagateToParent(entry, parent);
593
+ })
594
+ if (parent) propagateToParent(entry, parent)
588
595
  }
589
596
 
590
597
  function propagateToParent(entry: UseCacheEntry, parent: UseCacheScope): void {
591
- for (const tag of entry.tags) parent.tags.add(tag);
592
- parent.revalidateSeconds = minDefined(parent.revalidateSeconds, entry.revalidateSeconds);
593
- parent.expireSeconds = minDefined(parent.expireSeconds, entry.expireSeconds);
594
- parent.staleSeconds = minDefined(parent.staleSeconds, entry.staleSeconds);
598
+ for (const tag of entry.tags) parent.tags.add(tag)
599
+ parent.revalidateSeconds = minDefined(parent.revalidateSeconds, entry.revalidateSeconds)
600
+ parent.expireSeconds = minDefined(parent.expireSeconds, entry.expireSeconds)
601
+ parent.staleSeconds = minDefined(parent.staleSeconds, entry.staleSeconds)
595
602
  }
596
603
 
597
604
  export function pnextUseCache<Args extends unknown[], Result>(
@@ -599,20 +606,20 @@ export function pnextUseCache<Args extends unknown[], Result>(
599
606
  fn: (...args: Args) => Result,
600
607
  ): (...args: Args) => Result {
601
608
  const wrapper = (...args: Args): Result => {
602
- const serializedArgs = installSearchParamsTripwire(serializeCacheBoundaryArgs(args), id) as Args;
609
+ const serializedArgs = installSearchParamsTripwire(serializeCacheBoundaryArgs(args), id) as Args
603
610
  // Draft mode skips cache reads/writes but KEEPS the cache-scope rules:
604
611
  // dynamic APIs (cookies()/headers()) inside 'use cache' still throw.
605
- if (draftModeBypassActive()) return draftBypassRun(id, fn, serializedArgs);
612
+ if (draftModeBypassActive()) return draftBypassRun(id, fn, serializedArgs)
606
613
  // Element-like arguments become indexed holes: they never affect the key
607
614
  // (an element keys as an opaque token either way), the produced tree embeds
608
615
  // the hole markers, and each invocation re-instantiates it with ITS fills.
609
- const holes = extractElementHoles(serializedArgs);
610
- const keyArgs = declaredArgs(fn, serializedArgs);
611
- const key = cacheKeyFor(id, withRootParamKeyArgs(id, keyArgs));
616
+ const holes = extractElementHoles(serializedArgs)
617
+ const keyArgs = declaredArgs(fn, serializedArgs)
618
+ const key = cacheKeyFor(id, withRootParamKeyArgs(id, keyArgs))
612
619
  if (isPromiseLike(key)) {
613
620
  return key.then(
614
621
  resolvedKey => {
615
- if (resolvedKey === null) return fn(...serializedArgs);
622
+ if (resolvedKey === null) return fn(...serializedArgs)
616
623
  return readOrProduceEntry(
617
624
  resolvedKey,
618
625
  id,
@@ -620,17 +627,24 @@ export function pnextUseCache<Args extends unknown[], Result>(
620
627
  fn,
621
628
  holes.args as Args,
622
629
  holes.fills,
623
- );
630
+ )
624
631
  },
625
632
  () => fn(...serializedArgs),
626
- ) as Result;
633
+ ) as Result
627
634
  }
628
- if (key === null) return fn(...serializedArgs);
635
+ if (key === null) return fn(...serializedArgs)
629
636
 
630
- return readOrProduceEntry(key, id, argsJsonFromKey(id, key), fn, holes.args as Args, holes.fills);
631
- };
632
- (wrapper as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER] = true;
633
- return wrapper;
637
+ return readOrProduceEntry(
638
+ key,
639
+ id,
640
+ argsJsonFromKey(id, key),
641
+ fn,
642
+ holes.args as Args,
643
+ holes.fills,
644
+ )
645
+ }
646
+ ;(wrapper as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER] = true
647
+ return wrapper
634
648
  }
635
649
 
636
650
  // ---------------------------------------------------------------------------
@@ -649,9 +663,9 @@ export function pnextUseCache<Args extends unknown[], Result>(
649
663
  // ---------------------------------------------------------------------------
650
664
 
651
665
  function installSearchParamsTripwire(args: unknown[], id: string): unknown[] {
652
- if (!prerenderErrorCollectionActive()) return args;
653
- const route = routeFromCacheId(id);
654
- let changed = false;
666
+ if (!prerenderErrorCollectionActive()) return args
667
+ const route = routeFromCacheId(id)
668
+ let changed = false
655
669
  const out = args.map(arg => {
656
670
  if (
657
671
  arg !== null &&
@@ -661,15 +675,15 @@ function installSearchParamsTripwire(args: unknown[], id: string): unknown[] {
661
675
  !isElementLike(arg) &&
662
676
  isPromiseLike((arg as Record<string, unknown>).searchParams)
663
677
  ) {
664
- changed = true;
678
+ changed = true
665
679
  return {
666
680
  ...arg,
667
681
  searchParams: searchParamsTripwire(route, (arg as Record<string, unknown>).searchParams),
668
- };
682
+ }
669
683
  }
670
- return arg;
671
- });
672
- return changed ? out : args;
684
+ return arg
685
+ })
686
+ return changed ? out : args
673
687
  }
674
688
 
675
689
  function withFillTimeout(value: Promise<unknown>, route: string | undefined): Promise<unknown> {
@@ -679,14 +693,14 @@ function withFillTimeout(value: Promise<unknown>, route: string | undefined): Pr
679
693
  kind: 'hanging',
680
694
  route: route ?? '',
681
695
  consoleBlock: `Error: ${USE_CACHE_HANGING_MESSAGE}`,
682
- });
683
- reject(new Error(USE_CACHE_HANGING_MESSAGE));
684
- }, fillTimeoutHolder().ms);
696
+ })
697
+ reject(new Error(USE_CACHE_HANGING_MESSAGE))
698
+ }, fillTimeoutHolder().ms)
685
699
  // The timer must not keep the build process alive past the fill settling.
686
- if (typeof timer.unref === 'function') timer.unref();
687
- const clear = () => clearTimeout(timer);
688
- void value.then(resolve, reject).then(clear, clear);
689
- });
700
+ if (typeof timer.unref === 'function') timer.unref()
701
+ const clear = () => clearTimeout(timer)
702
+ void value.then(resolve, reject).then(clear, clear)
703
+ })
690
704
  }
691
705
 
692
706
  function searchParamsTripwire(route: string, real: unknown): PromiseLike<unknown> {
@@ -696,13 +710,13 @@ function searchParamsTripwire(route: string, real: unknown): PromiseLike<unknown
696
710
  onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
697
711
  ): PromiseLike<TResult1 | TResult2> {
698
712
  if (insideUseCacheProducer()) {
699
- const message = useCacheSearchParamsMessage(route);
700
- recordPrerenderError({ kind: 'search-params', route, consoleBlock: `Error: ${message}` });
701
- return Promise.reject(new Error(message)).then(onFulfilled, onRejected);
713
+ const message = useCacheSearchParamsMessage(route)
714
+ recordPrerenderError({ kind: 'search-params', route, consoleBlock: `Error: ${message}` })
715
+ return Promise.reject(new Error(message)).then(onFulfilled, onRejected)
702
716
  }
703
- return Promise.resolve(real).then(onFulfilled, onRejected);
717
+ return Promise.resolve(real).then(onFulfilled, onRejected)
704
718
  },
705
- };
719
+ }
706
720
  }
707
721
 
708
722
  /**
@@ -718,13 +732,13 @@ export function pnextUseCacheBound<Args extends unknown[], Result>(
718
732
  capture: () => unknown[],
719
733
  ): (...args: Args) => Result {
720
734
  const wrapper = (...args: Args): Result => {
721
- const serializedArgs = serializeCacheBoundaryArgs(args) as Args;
722
- if (draftModeBypassActive()) return draftBypassRun(id, fn, serializedArgs);
723
- let captured: unknown[];
735
+ const serializedArgs = serializeCacheBoundaryArgs(args) as Args
736
+ if (draftModeBypassActive()) return draftBypassRun(id, fn, serializedArgs)
737
+ let captured: unknown[]
724
738
  try {
725
- captured = serializeCacheBoundaryArgs(capture());
739
+ captured = serializeCacheBoundaryArgs(capture())
726
740
  } catch {
727
- return fn(...serializedArgs);
741
+ return fn(...serializedArgs)
728
742
  }
729
743
  // Close-over-function detection: a `use cache` closure that captures a non-serializable function
730
744
  // cannot cross the cache/RSC boundary. During a build prerender, reject with Next's "Functions
@@ -735,16 +749,16 @@ export function pnextUseCacheBound<Args extends unknown[], Result>(
735
749
  kind: 'close-over',
736
750
  route: routeFromCacheId(id),
737
751
  consoleBlock: USE_CACHE_CLOSE_OVER_FUNCTION_BLOCK,
738
- });
739
- throw new Error(USE_CACHE_CLOSE_OVER_FUNCTION_MESSAGE);
752
+ })
753
+ throw new Error(USE_CACHE_CLOSE_OVER_FUNCTION_MESSAGE)
740
754
  }
741
- const holes = extractElementHoles(serializedArgs);
742
- const keyArgs = [captured, ...declaredArgs(fn, serializedArgs)];
743
- const key = cacheKeyFor(id, withRootParamKeyArgs(id, keyArgs));
755
+ const holes = extractElementHoles(serializedArgs)
756
+ const keyArgs = [captured, ...declaredArgs(fn, serializedArgs)]
757
+ const key = cacheKeyFor(id, withRootParamKeyArgs(id, keyArgs))
744
758
  if (isPromiseLike(key)) {
745
759
  return key.then(
746
760
  resolvedKey => {
747
- if (resolvedKey === null) return fn(...serializedArgs);
761
+ if (resolvedKey === null) return fn(...serializedArgs)
748
762
  return readOrProduceEntry(
749
763
  resolvedKey,
750
764
  id,
@@ -752,16 +766,23 @@ export function pnextUseCacheBound<Args extends unknown[], Result>(
752
766
  fn,
753
767
  holes.args as Args,
754
768
  holes.fills,
755
- );
769
+ )
756
770
  },
757
771
  () => fn(...serializedArgs),
758
- ) as Result;
772
+ ) as Result
759
773
  }
760
- if (key === null) return fn(...serializedArgs);
761
- return readOrProduceEntry(key, id, argsJsonFromKey(id, key), fn, holes.args as Args, holes.fills);
762
- };
763
- (wrapper as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER] = true;
764
- return wrapper;
774
+ if (key === null) return fn(...serializedArgs)
775
+ return readOrProduceEntry(
776
+ key,
777
+ id,
778
+ argsJsonFromKey(id, key),
779
+ fn,
780
+ holes.args as Args,
781
+ holes.fills,
782
+ )
783
+ }
784
+ ;(wrapper as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER] = true
785
+ return wrapper
765
786
  }
766
787
 
767
788
  /**
@@ -778,42 +799,42 @@ export function pnextUseCachePrivate<Args extends unknown[], Result>(
778
799
  fn: (...args: Args) => Result,
779
800
  ): (...args: Args) => Result {
780
801
  const wrapper = (...args: Args): Result => {
781
- recordCacheNoStore();
802
+ recordCacheNoStore()
782
803
  // Private caches are EXCLUDED from prerenders: they may read per-request data, so a build/shell
783
804
  // prerender postpones the scope (a dynamic hole under the nearest Suspense boundary; a classic
784
805
  // static prerender skips the route to dynamic) and the function is evaluated per request instead.
785
806
  // EXCEPTION: a runtime prefetch samples the real request, so a private cache with a long enough
786
807
  // stale window IS included; a shorter-lived one stays a postponed hole.
787
808
  if (isPrerendering()) {
788
- if (!isRuntimePrefetchPrerender()) throw new PostponeError('use cache: private');
789
- return runtimePrefetchPrivate(id, fn, serializeCacheBoundaryArgs(args) as Args) as Result;
809
+ if (!isRuntimePrefetchPrerender()) throw new PostponeError('use cache: private')
810
+ return runtimePrefetchPrivate(id, fn, serializeCacheBoundaryArgs(args) as Args) as Result
790
811
  }
791
- const serializedArgs = serializeCacheBoundaryArgs(args) as Args;
792
- if (draftModeBypassActive()) return draftBypassRun(id, fn, serializedArgs);
812
+ const serializedArgs = serializeCacheBoundaryArgs(args) as Args
813
+ if (draftModeBypassActive()) return draftBypassRun(id, fn, serializedArgs)
793
814
  // Run the REQUEST-TIME body inside a cache scope too (the prerender path
794
815
  // already does, via runtimePrefetchPrivate): without one, a cacheLife()
795
816
  // inside a `use cache: private` body is a no-op for the response aggregate,
796
817
  // so its stale window never reaches x-nextjs-stale-time and the client
797
818
  // reuses the navigation for the page's LONGEST window instead.
798
819
  const runScoped = (call: () => Result): Result => {
799
- const scope: UseCacheScope = { tags: new Set(), functionId: id };
800
- const result = scopeStorage.run(scope, call);
820
+ const scope: UseCacheScope = { tags: new Set(), functionId: id }
821
+ const result = scopeStorage.run(scope, call)
801
822
  const settle = () => {
802
- if (scope.staleSeconds !== undefined) stashCacheLife({ staleSeconds: scope.staleSeconds });
803
- };
823
+ if (scope.staleSeconds !== undefined) stashCacheLife({ staleSeconds: scope.staleSeconds })
824
+ }
804
825
  if (isPromiseLike(result)) {
805
826
  return (result as PromiseLike<unknown>).then(value => {
806
- settle();
807
- return value;
808
- }) as Result;
827
+ settle()
828
+ return value
829
+ }) as Result
809
830
  }
810
- settle();
811
- return result;
812
- };
813
- const store = privateCacheStore();
814
- if (!store) return runScoped(() => fn(...serializedArgs));
815
- const keyArgs = declaredArgs(fn, serializedArgs);
816
- const key = cacheKeyFor(id, keyArgs);
831
+ settle()
832
+ return result
833
+ }
834
+ const store = privateCacheStore()
835
+ if (!store) return runScoped(() => fn(...serializedArgs))
836
+ const keyArgs = declaredArgs(fn, serializedArgs)
837
+ const key = cacheKeyFor(id, keyArgs)
817
838
  if (isPromiseLike(key)) {
818
839
  return key.then(
819
840
  resolvedKey =>
@@ -821,13 +842,13 @@ export function pnextUseCachePrivate<Args extends unknown[], Result>(
821
842
  ? runScoped(() => fn(...serializedArgs))
822
843
  : runScoped(() => privateMemo(store, resolvedKey, fn, serializedArgs)),
823
844
  () => runScoped(() => fn(...serializedArgs)),
824
- ) as Result;
845
+ ) as Result
825
846
  }
826
- if (key === null) return runScoped(() => fn(...serializedArgs));
827
- return runScoped(() => privateMemo(store, key, fn, serializedArgs));
828
- };
829
- (wrapper as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER] = true;
830
- return wrapper;
847
+ if (key === null) return runScoped(() => fn(...serializedArgs))
848
+ return runScoped(() => privateMemo(store, key, fn, serializedArgs))
849
+ }
850
+ ;(wrapper as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER] = true
851
+ return wrapper
831
852
  }
832
853
 
833
854
  /**
@@ -844,59 +865,59 @@ function runtimePrefetchPrivate<Args extends unknown[], Result>(
844
865
  fn: (...args: Args) => Result,
845
866
  args: Args,
846
867
  ): Promise<unknown> {
847
- const scope: UseCacheScope = { tags: new Set(), functionId: id };
848
- const produced = (async () => await scopeStorage.run(scope, () => fn(...args)))();
868
+ const scope: UseCacheScope = { tags: new Set(), functionId: id }
869
+ const produced = (async () => await scopeStorage.run(scope, () => fn(...args)))()
849
870
  // Awaiting the private cache arms the sync-IO abort (see gatePrefetchCacheLife).
850
871
  return markRuntimeApiOnAwait(
851
872
  produced.then(value => {
852
- const stale = scope.staleSeconds ?? DEFAULT_STALE_SECONDS;
853
- if (stale < RUNTIME_PREFETCH_DYNAMIC_STALE) return hangingPromise('use cache: private');
873
+ const stale = scope.staleSeconds ?? DEFAULT_STALE_SECONDS
874
+ if (stale < RUNTIME_PREFETCH_DYNAMIC_STALE) return hangingPromise('use cache: private')
854
875
  // The sampled value IS in this response, so the response is only as fresh
855
876
  // as that private window — the runtime-prefetch payload must expire with
856
877
  // it, not with the (longer) public `use cache` window baked into the
857
878
  // route's segment meta.
858
- recordCacheStale(stale);
859
- return value;
879
+ recordCacheStale(stale)
880
+ return value
860
881
  }),
861
- );
882
+ )
862
883
  }
863
884
 
864
885
  /** Well-known key for the per-request private-cache dedup store. */
865
- const PRIVATE_CACHE_STORE = Symbol.for('pnext.compat.privateCacheStore');
886
+ const PRIVATE_CACHE_STORE = Symbol.for('pnext.compat.privateCacheStore')
866
887
 
867
888
  // Fallback per-request private-cache store, used when a private `use cache`
868
889
  // resolves outside the work-unit ALS but still inside the request ALS. Keyed on
869
890
  // the request object (WeakMap → auto-evicts when the request is GC'd), anchored
870
891
  // on globalThis so the build orchestrator and the built server bundle share it.
871
- const PRIVATE_STORE_BY_REQUEST = Symbol.for('pnext.compat.privateStoreByRequest');
892
+ const PRIVATE_STORE_BY_REQUEST = Symbol.for('pnext.compat.privateStoreByRequest')
872
893
  const privateStoreByRequest = useCacheGlobal(
873
894
  PRIVATE_STORE_BY_REQUEST,
874
895
  () => new WeakMap<object, Map<string, unknown>>(),
875
- );
896
+ )
876
897
 
877
898
  function privateCacheStore(): Map<string, unknown> | undefined {
878
- const unit = getWorkUnit();
899
+ const unit = getWorkUnit()
879
900
  if (unit) {
880
- const compat = (unit.compat ??= {});
881
- let store = compat[PRIVATE_CACHE_STORE] as Map<string, unknown> | undefined;
901
+ const compat = (unit.compat ??= {})
902
+ let store = compat[PRIVATE_CACHE_STORE] as Map<string, unknown> | undefined
882
903
  if (!store) {
883
- store = new Map<string, unknown>();
884
- compat[PRIVATE_CACHE_STORE] = store;
904
+ store = new Map<string, unknown>()
905
+ compat[PRIVATE_CACHE_STORE] = store
885
906
  }
886
- return store;
907
+ return store
887
908
  }
888
909
  // Async server components may resolve outside the work-unit ALS (the renderer re-enters the request
889
910
  // ALS, but not always the work unit), and Next still dedupes identical private-cache invocations
890
911
  // WITHIN one request. Fall back to a per-request store keyed on the live request object, so two
891
912
  // placements in one render join one value while concurrent requests stay isolated.
892
- const request = currentRequest() as object | undefined;
893
- if (!request) return undefined;
894
- let store = privateStoreByRequest.get(request);
913
+ const request = currentRequest() as object | undefined
914
+ if (!request) return undefined
915
+ let store = privateStoreByRequest.get(request)
895
916
  if (!store) {
896
- store = new Map<string, unknown>();
897
- privateStoreByRequest.set(request, store);
917
+ store = new Map<string, unknown>()
918
+ privateStoreByRequest.set(request, store)
898
919
  }
899
- return store;
920
+ return store
900
921
  }
901
922
 
902
923
  function privateMemo<Args extends unknown[], Result>(
@@ -905,52 +926,54 @@ function privateMemo<Args extends unknown[], Result>(
905
926
  fn: (...args: Args) => Result,
906
927
  args: Args,
907
928
  ): Result {
908
- if (store.has(key)) return store.get(key) as Result;
909
- const value = fn(...args);
910
- store.set(key, value);
911
- return value;
929
+ if (store.has(key)) return store.get(key) as Result
930
+ const value = fn(...args)
931
+ store.set(key, value)
932
+ return value
912
933
  }
913
934
 
914
- function declaredArgs<Args extends unknown[]>(fn: (...args: Args) => unknown, args: Args): unknown[] {
915
- return args.slice(0, fn.length);
935
+ function declaredArgs<Args extends unknown[]>(
936
+ fn: (...args: Args) => unknown,
937
+ args: Args,
938
+ ): unknown[] {
939
+ return args.slice(0, fn.length)
916
940
  }
917
941
 
918
942
  function serializeCacheBoundaryArgs(args: unknown[]): unknown[] {
919
- return args.map(arg => serializeCacheBoundaryValue(arg, new WeakSet<object>()));
943
+ return args.map(arg => serializeCacheBoundaryValue(arg, new WeakSet<object>()))
920
944
  }
921
945
 
922
946
  function serializeCacheBoundaryValue(value: unknown, stack: WeakSet<object>): unknown {
923
947
  if (typeof value !== 'object' || value === null || isPromiseLike(value) || isElementLike(value)) {
924
- return value;
948
+ return value
925
949
  }
926
- if (stack.has(value)) return value;
927
- stack.add(value);
950
+ if (stack.has(value)) return value
951
+ stack.add(value)
928
952
  try {
929
- if (Array.isArray(value)) return value.map(item => serializeCacheBoundaryValue(item, stack));
930
- if (value instanceof Date) return value.toJSON();
953
+ if (Array.isArray(value)) return value.map(item => serializeCacheBoundaryValue(item, stack))
954
+ if (value instanceof Date) return value.toJSON()
931
955
  if (value instanceof Map) {
932
956
  return [...value].map(([key, nested]) => [
933
957
  serializeCacheBoundaryValue(key, stack),
934
958
  serializeCacheBoundaryValue(nested, stack),
935
- ]);
959
+ ])
936
960
  }
937
961
  if (value instanceof Set) {
938
- return [...value].map(item => serializeCacheBoundaryValue(item, stack));
962
+ return [...value].map(item => serializeCacheBoundaryValue(item, stack))
939
963
  }
940
- const iterator = (value as { [Symbol.iterator]?: unknown })[Symbol.iterator];
964
+ const iterator = (value as { [Symbol.iterator]?: unknown })[Symbol.iterator]
941
965
  if (typeof iterator === 'function') {
942
- return Array.from(value as Iterable<unknown>, item => serializeCacheBoundaryValue(item, stack));
966
+ return Array.from(value as Iterable<unknown>, item =>
967
+ serializeCacheBoundaryValue(item, stack),
968
+ )
943
969
  }
944
- const entriesForObject = Object.entries(value);
945
- if (entriesForObject.length === 0) return value;
970
+ const entriesForObject = Object.entries(value)
971
+ if (entriesForObject.length === 0) return value
946
972
  return Object.fromEntries(
947
- entriesForObject.map(([key, nested]) => [
948
- key,
949
- serializeCacheBoundaryValue(nested, stack),
950
- ]),
951
- );
973
+ entriesForObject.map(([key, nested]) => [key, serializeCacheBoundaryValue(nested, stack)]),
974
+ )
952
975
  } finally {
953
- stack.delete(value);
976
+ stack.delete(value)
954
977
  }
955
978
  }
956
979
 
@@ -962,8 +985,8 @@ function draftBypassRun<Args extends unknown[], Result>(
962
985
  fn: (...args: Args) => Result,
963
986
  args: Args,
964
987
  ): Result {
965
- const scope: UseCacheScope = { tags: new Set(), functionId: id };
966
- return runInsideDataCacheProducer(() => scopeStorage.run(scope, () => fn(...args)));
988
+ const scope: UseCacheScope = { tags: new Set(), functionId: id }
989
+ return runInsideDataCacheProducer(() => scopeStorage.run(scope, () => fn(...args)))
967
990
  }
968
991
 
969
992
  function readOrProduceEntry<Args extends unknown[], Result>(
@@ -974,9 +997,9 @@ function readOrProduceEntry<Args extends unknown[], Result>(
974
997
  args: Args,
975
998
  fills: unknown[],
976
999
  ): Result {
977
- const parent = scopeStorage.getStore();
978
- const modernKey = hasModernCacheHandler() ? modernCacheKey(id, argsJson) : undefined;
979
- const resumeRecord = useCacheRdcRecords().get(key);
1000
+ const parent = scopeStorage.getStore()
1001
+ const modernKey = hasModernCacheHandler() ? modernCacheKey(id, argsJson) : undefined
1002
+ const resumeRecord = useCacheRdcRecords().get(key)
980
1003
  if (
981
1004
  resumeRecord &&
982
1005
  !tagsRevalidatedAfterSeq(resumeRecord.tags, resumeRecord.tagSeq) &&
@@ -990,25 +1013,28 @@ function readOrProduceEntry<Args extends unknown[], Result>(
990
1013
  ...(resumeRecord.revalidateSeconds !== undefined
991
1014
  ? { revalidateSeconds: resumeRecord.revalidateSeconds }
992
1015
  : {}),
993
- ...(resumeRecord.expireSeconds !== undefined ? { expireSeconds: resumeRecord.expireSeconds } : {}),
994
- ...(resumeRecord.staleSeconds !== undefined ? { staleSeconds: resumeRecord.staleSeconds } : {}),
1016
+ ...(resumeRecord.expireSeconds !== undefined
1017
+ ? { expireSeconds: resumeRecord.expireSeconds }
1018
+ : {}),
1019
+ ...(resumeRecord.staleSeconds !== undefined
1020
+ ? { staleSeconds: resumeRecord.staleSeconds }
1021
+ : {}),
995
1022
  tagSeq: resumeRecord.tagSeq,
996
1023
  ...(resumeRecord.route !== undefined ? { route: resumeRecord.route } : {}),
997
- });
1024
+ })
998
1025
  }
999
- const existing = entries.get(key);
1000
- const meta = currentRenderCacheMeta();
1001
- const expired = existing !== undefined && entryExpired(existing);
1026
+ const existing = entries.get(key)
1027
+ const meta = currentRenderCacheMeta()
1028
+ const expired = existing !== undefined && entryExpired(existing)
1002
1029
  // Hard on-demand invalidation: a tag update (revalidateTag/updateTag) after
1003
1030
  // this entry was produced, or a revalidatePath covering the entry's owning
1004
1031
  // route. Both block and re-produce so the read observes the fresh value
1005
1032
  // (read-your-writes within an action; path-scoped revalidation).
1006
- const tagStale =
1007
- existing !== undefined && tagsRevalidatedAfterSeq(existing.tags, existing.tagSeq);
1033
+ const tagStale = existing !== undefined && tagsRevalidatedAfterSeq(existing.tags, existing.tagSeq)
1008
1034
  const pathStale =
1009
- existing !== undefined && pathScopeRevalidated(existing.route, existing.storedAt);
1010
- const invalidated = tagStale || pathStale;
1011
- const swrStale = existing !== undefined && tagsStaleSince(existing.tags, existing.storedAt);
1035
+ existing !== undefined && pathScopeRevalidated(existing.route, existing.storedAt)
1036
+ const invalidated = tagStale || pathStale
1037
+ const swrStale = existing !== undefined && tagsStaleSince(existing.tags, existing.storedAt)
1012
1038
  // On-demand revalidation re-produces each entry ONCE per render, deduped via refreshedIn - but only
1013
1039
  // entries the revalidation actually reached. A revalidation mark outlives a single render (a soft-nav
1014
1040
  // render never writes the prebuilt HTML back), so refreshing unconditionally churned every cache
@@ -1016,15 +1042,15 @@ function readOrProduceEntry<Args extends unknown[], Result>(
1016
1042
  const refresh =
1017
1043
  meta?.refreshFetches === true &&
1018
1044
  existing?.refreshedIn !== meta &&
1019
- (invalidated || swrStale || expired);
1020
- let modernGet: Promise<unknown> | undefined;
1045
+ (invalidated || swrStale || expired)
1046
+ let modernGet: Promise<unknown> | undefined
1021
1047
  if (modernKey) {
1022
- void modernCacheRefreshTags();
1048
+ void modernCacheRefreshTags()
1023
1049
  // Keep the handler read's promise: serving an existing entry awaits it, so the handler's get() -
1024
1050
  // whose logs are asserted - lands on the critical path like Next's, where a custom handler IS the
1025
1051
  // cache. A fire-and-forget read could otherwise log only after the response already flushed.
1026
- modernGet = modernCacheGet(modernKey);
1027
- if (existing && !invalidated) void modernCacheGetExpiration();
1052
+ modernGet = modernCacheGet(modernKey)
1053
+ if (existing && !invalidated) void modernCacheGetExpiration()
1028
1054
  }
1029
1055
  // A regeneration render (blockingStaleFetches) must re-produce not only
1030
1056
  // TTL-expired entries but also soft-stale ones (revalidateTag with a
@@ -1039,32 +1065,32 @@ function readOrProduceEntry<Args extends unknown[], Result>(
1039
1065
  if ((expired || swrStale) && !existing.refreshing) {
1040
1066
  // TTL expiry outside an ISR regen: serve stale while refreshing in
1041
1067
  // the background (Next's SWR data-cache semantics).
1042
- existing.refreshing = true;
1043
- void produceEntry(key, id, argsJson, fn, args, parent, existing, modernKey);
1068
+ existing.refreshing = true
1069
+ void produceEntry(key, id, argsJson, fn, args, parent, existing, modernKey)
1044
1070
  }
1045
1071
  if (modernKey && parent && existing.revalidateSeconds === undefined) {
1046
1072
  void existing.value.then(
1047
1073
  () => propagateToParent(existing, parent),
1048
1074
  () => undefined,
1049
- );
1075
+ )
1050
1076
  }
1051
- propagate(existing, parent);
1052
- captureUseCacheRdcEntry(key);
1053
- const served = withHoleFills(existing.value, fills);
1054
- const out = modernGet ? modernGet.then(() => served) : served;
1055
- const servedGate = prefetchCacheGate();
1056
- return (servedGate ? gatePrefetchCacheLife(out, servedGate, () => existing) : out) as Result;
1077
+ propagate(existing, parent)
1078
+ captureUseCacheRdcEntry(key)
1079
+ const served = withHoleFills(existing.value, fills)
1080
+ const out = modernGet ? modernGet.then(() => served) : served
1081
+ const servedGate = prefetchCacheGate()
1082
+ return (servedGate ? gatePrefetchCacheLife(out, servedGate, () => existing) : out) as Result
1057
1083
  }
1058
1084
 
1059
- const value = produceEntry(key, id, argsJson, fn, args, parent, undefined, modernKey);
1060
- captureUseCacheRdcEntry(key);
1061
- const out = withHoleFills(value, fills);
1085
+ const value = produceEntry(key, id, argsJson, fn, args, parent, undefined, modernKey)
1086
+ captureUseCacheRdcEntry(key)
1087
+ const out = withHoleFills(value, fills)
1062
1088
  // The producing entry's cacheLife is set once `value` settles (see
1063
1089
  // produceEntry); `entries.get(key)` reads it back after production.
1064
- const producedGate = prefetchCacheGate();
1090
+ const producedGate = prefetchCacheGate()
1065
1091
  return (
1066
1092
  producedGate ? gatePrefetchCacheLife(out, producedGate, () => entries.get(key)) : out
1067
- ) as Result;
1093
+ ) as Result
1068
1094
  }
1069
1095
 
1070
1096
  function produceEntry<Args extends unknown[], Result>(
@@ -1077,14 +1103,14 @@ function produceEntry<Args extends unknown[], Result>(
1077
1103
  serveStale?: UseCacheEntry,
1078
1104
  modernKey?: string,
1079
1105
  ): Promise<unknown> {
1080
- const scope: UseCacheScope = { tags: new Set(), functionId: id };
1106
+ const scope: UseCacheScope = { tags: new Set(), functionId: id }
1081
1107
  // Sample the invalidation-scope inputs at production start: the hard-tag
1082
1108
  // sequence (read-your-writes) and the producing route (path revalidation),
1083
1109
  // mirroring how the fetch/unstable_cache data caches tag entries with their
1084
1110
  // owning route.
1085
- const tagSeq = currentTagRevalidationSeq();
1086
- const producingMeta = currentRenderCacheMeta();
1087
- const route = producingMeta?.route;
1111
+ const tagSeq = currentTagRevalidationSeq()
1112
+ const producingMeta = currentRenderCacheMeta()
1113
+ const route = producingMeta?.route
1088
1114
  const entry: UseCacheEntry = {
1089
1115
  value: undefined as unknown as Promise<unknown>,
1090
1116
  storedAt: realNow(),
@@ -1092,8 +1118,12 @@ function produceEntry<Args extends unknown[], Result>(
1092
1118
  modernKey,
1093
1119
  tagSeq,
1094
1120
  ...(producingMeta !== undefined ? { refreshedIn: producingMeta } : {}),
1095
- ...(route !== undefined ? { route } : serveStale?.route !== undefined ? { route: serveStale.route } : {}),
1096
- };
1121
+ ...(route !== undefined
1122
+ ? { route }
1123
+ : serveStale?.route !== undefined
1124
+ ? { route: serveStale.route }
1125
+ : {}),
1126
+ }
1097
1127
  const rawValue = (async () =>
1098
1128
  await runInsideDataCacheProducer(() => scopeStorage.run(scope, () => fn(...args))))().then(
1099
1129
  // Render-once semantics: nested server components inside the produced tree
@@ -1103,9 +1133,9 @@ function produceEntry<Args extends unknown[], Result>(
1103
1133
  // before any caller sees it: at runtime that reshapes the error (and, in a
1104
1134
  // plain production build, logs it here and redacts what escapes).
1105
1135
  error => {
1106
- throw prerenderErrorCollectionActive() ? error : funnelCacheRuntimeError(error);
1136
+ throw prerenderErrorCollectionActive() ? error : funnelCacheRuntimeError(error)
1107
1137
  },
1108
- );
1138
+ )
1109
1139
  // E236 hanging-input guard: during a build prerender a fill that never
1110
1140
  // resolves would hang the build; race it against the configured fill timeout
1111
1141
  // and reject with Next's timeout message (recorded so the build fails). Gated
@@ -1113,31 +1143,33 @@ function produceEntry<Args extends unknown[], Result>(
1113
1143
  // render cache-meta, so we can't rely on meta.prerender here).
1114
1144
  const value = prerenderErrorCollectionActive()
1115
1145
  ? withFillTimeout(rawValue, routeFromCacheId(id))
1116
- : rawValue;
1146
+ : rawValue
1117
1147
  // A build prerender's task boundary must not cut a cache fill short: the
1118
1148
  // whole point of a cached read is that its (possibly task-settled) IO belongs
1119
1149
  // in the static shell. Register it so the boundary waits (core ppr).
1120
- void trackPrerenderCacheFill(value);
1121
- entry.value = value;
1122
- let resolveModernEntry: ((entry: ReturnType<typeof modernCacheEntry>) => void) | undefined;
1123
- let rejectModernEntry: ((reason?: unknown) => void) | undefined;
1150
+ void trackPrerenderCacheFill(value)
1151
+ entry.value = value
1152
+ let resolveModernEntry: ((entry: ReturnType<typeof modernCacheEntry>) => void) | undefined
1153
+ let rejectModernEntry: ((reason?: unknown) => void) | undefined
1124
1154
  if (modernKey) {
1125
- const pendingModernEntry = new Promise<ReturnType<typeof modernCacheEntry>>((resolve, reject) => {
1126
- resolveModernEntry = resolve;
1127
- rejectModernEntry = reject;
1128
- });
1129
- void modernCacheSet(modernKey, pendingModernEntry);
1155
+ const pendingModernEntry = new Promise<ReturnType<typeof modernCacheEntry>>(
1156
+ (resolve, reject) => {
1157
+ resolveModernEntry = resolve
1158
+ rejectModernEntry = reject
1159
+ },
1160
+ )
1161
+ void modernCacheSet(modernKey, pendingModernEntry)
1130
1162
  }
1131
1163
  if (!serveStale) {
1132
1164
  if (entries.size >= entriesLimit) {
1133
- const oldest = entries.keys().next().value;
1134
- if (oldest !== undefined) entries.delete(oldest);
1165
+ const oldest = entries.keys().next().value
1166
+ if (oldest !== undefined) entries.delete(oldest)
1135
1167
  }
1136
- entries.set(key, entry);
1168
+ entries.set(key, entry)
1137
1169
  }
1138
1170
  value.then(
1139
1171
  produced => {
1140
- entry.tags = [...scope.tags];
1172
+ entry.tags = [...scope.tags]
1141
1173
  // Conditional-read upgrade (Stage B-3): production may have read root
1142
1174
  // params that were not in this entry's key (first-ever call for the id, or
1143
1175
  // a newly-discovered branch-dependent read). Re-store the settled entry
@@ -1145,22 +1177,22 @@ function produceEntry<Args extends unknown[], Result>(
1145
1177
  // on those root params finds this value instead of re-producing. The
1146
1178
  // original key stays populated too (harmless; both point at one value).
1147
1179
  if (scope.rootParamsRead && scope.rootParamsRead.size > 0) {
1148
- const upgradedKey = cacheKeyFor(id, withRootParamKeyArgs(id, args));
1180
+ const upgradedKey = cacheKeyFor(id, withRootParamKeyArgs(id, args))
1149
1181
  if (typeof upgradedKey === 'string' && upgradedKey !== key) {
1150
- entries.set(upgradedKey, entry);
1182
+ entries.set(upgradedKey, entry)
1151
1183
  }
1152
1184
  }
1153
1185
  // A `use cache` entry with no explicit cacheLife() takes Next's `default`
1154
1186
  // profile (revalidate 900, expire one year, stale 300) so its route
1155
1187
  // emits the SWR cache-control + x-nextjs-stale-time headers.
1156
- entry.revalidateSeconds = scope.revalidateSeconds ?? DEFAULT_REVALIDATE_SECONDS;
1157
- entry.expireSeconds = scope.expireSeconds ?? DEFAULT_EXPIRE_SECONDS;
1158
- entry.staleSeconds = scope.staleSeconds ?? DEFAULT_STALE_SECONDS;
1188
+ entry.revalidateSeconds = scope.revalidateSeconds ?? DEFAULT_REVALIDATE_SECONDS
1189
+ entry.expireSeconds = scope.expireSeconds ?? DEFAULT_EXPIRE_SECONDS
1190
+ entry.staleSeconds = scope.staleSeconds ?? DEFAULT_STALE_SECONDS
1159
1191
  // Persisting to a modern cache handler waits for embedded nested `use cache` vnodes to render
1160
1192
  // (their scopes fold tags and min cacheLife into this entry - Next resolves the entry only once
1161
1193
  // the whole cached tree has serialized). A fallback timer guards against a tree that is never
1162
1194
  // rendered, so the handler's pending set cannot deadlock.
1163
- const resolveModern = resolveModernEntry;
1195
+ const resolveModern = resolveModernEntry
1164
1196
  if (resolveModern) {
1165
1197
  const finalize = () =>
1166
1198
  resolveModern(
@@ -1172,31 +1204,31 @@ function produceEntry<Args extends unknown[], Result>(
1172
1204
  revalidateSeconds: entry.revalidateSeconds ?? DEFAULT_REVALIDATE_SECONDS,
1173
1205
  storedAt: entry.storedAt,
1174
1206
  }),
1175
- );
1207
+ )
1176
1208
  if (entry.nestedPending !== undefined && entry.nestedPending > 0) {
1177
- const timer = setTimeout(finalize, 10_000);
1178
- if (typeof timer.unref === 'function') timer.unref();
1209
+ const timer = setTimeout(finalize, 10_000)
1210
+ if (typeof timer.unref === 'function') timer.unref()
1179
1211
  entry.onNestedSettled = () => {
1180
1212
  if (entry.nestedPending === 0) {
1181
- clearTimeout(timer);
1182
- finalize();
1213
+ clearTimeout(timer)
1214
+ finalize()
1183
1215
  }
1184
- };
1216
+ }
1185
1217
  } else {
1186
- finalize();
1218
+ finalize()
1187
1219
  }
1188
1220
  }
1189
1221
  // Background refresh replaces the stale entry only once settled.
1190
- if (serveStale) entries.set(key, entry);
1191
- propagate(entry, parent);
1222
+ if (serveStale) entries.set(key, entry)
1223
+ propagate(entry, parent)
1192
1224
  },
1193
1225
  () => {
1194
- rejectModernEntry?.();
1195
- if (serveStale) serveStale.refreshing = false;
1196
- else if (entries.get(key) === entry) entries.delete(key);
1226
+ rejectModernEntry?.()
1227
+ if (serveStale) serveStale.refreshing = false
1228
+ else if (entries.get(key) === entry) entries.delete(key)
1197
1229
  },
1198
- );
1199
- return value;
1230
+ )
1231
+ return value
1200
1232
  }
1201
1233
 
1202
1234
  /**
@@ -1206,34 +1238,34 @@ function produceEntry<Args extends unknown[], Result>(
1206
1238
  */
1207
1239
  function cacheKeyFor(id: string, args: unknown[]): string | Promise<string | null> | null {
1208
1240
  try {
1209
- const keyValue = resolvePromisesForKey(args, new WeakSet<object>());
1241
+ const keyValue = resolvePromisesForKey(args, new WeakSet<object>())
1210
1242
  if (isPromiseLike(keyValue)) {
1211
1243
  return Promise.resolve(keyValue).then(
1212
1244
  resolved => stringifyCacheKey(id, resolved),
1213
1245
  () => null,
1214
- );
1246
+ )
1215
1247
  }
1216
- return stringifyCacheKey(id, keyValue);
1248
+ return stringifyCacheKey(id, keyValue)
1217
1249
  } catch {
1218
- return null;
1250
+ return null
1219
1251
  }
1220
1252
  }
1221
1253
 
1222
1254
  function argsJsonFromKey(id: string, key: string): string {
1223
- return key.startsWith(`${id}:`) ? key.slice(id.length + 1) : '[]';
1255
+ return key.startsWith(`${id}:`) ? key.slice(id.length + 1) : '[]'
1224
1256
  }
1225
1257
 
1226
1258
  function stringifyCacheKey(id: string, value: unknown): string | null {
1227
1259
  try {
1228
1260
  const json = JSON.stringify(value, (_key, nested: unknown) => {
1229
- if (typeof nested === 'function' || typeof nested === 'symbol') return '$pnext:ref';
1230
- if (typeof nested === 'bigint') return `$pnext:bigint:${nested}`;
1231
- if (isElementLike(nested)) return '$pnext:element';
1232
- return nested;
1233
- });
1234
- return `${id}:${json}`;
1261
+ if (typeof nested === 'function' || typeof nested === 'symbol') return '$pnext:ref'
1262
+ if (typeof nested === 'bigint') return `$pnext:bigint:${nested}`
1263
+ if (isElementLike(nested)) return '$pnext:element'
1264
+ return nested
1265
+ })
1266
+ return `${id}:${json}`
1235
1267
  } catch {
1236
- return null;
1268
+ return null
1237
1269
  }
1238
1270
  }
1239
1271
 
@@ -1241,32 +1273,33 @@ function resolvePromisesForKey(value: unknown, stack: WeakSet<object>): unknown
1241
1273
  if (isPromiseLike(value)) {
1242
1274
  return Promise.resolve(value).then(resolved =>
1243
1275
  resolvePromisesForKey(resolved, new WeakSet<object>()),
1244
- );
1276
+ )
1245
1277
  }
1246
- if (typeof value !== 'object' || value === null || isElementLike(value)) return value;
1278
+ if (typeof value !== 'object' || value === null || isElementLike(value)) return value
1247
1279
 
1248
- if (stack.has(value)) throw new Error('cyclic cache key');
1249
- stack.add(value);
1280
+ if (stack.has(value)) throw new Error('cyclic cache key')
1281
+ stack.add(value)
1250
1282
  try {
1251
1283
  if (Array.isArray(value)) {
1252
- const items = value.map(item => resolvePromisesForKey(item, stack));
1253
- return items.some(isPromiseLike) ? Promise.all(items) : value;
1284
+ const items = value.map(item => resolvePromisesForKey(item, stack))
1285
+ return items.some(isPromiseLike) ? Promise.all(items) : value
1254
1286
  }
1255
1287
 
1256
- const entriesForObject = Object.entries(value);
1257
- if (entriesForObject.length === 0) return value;
1288
+ const entriesForObject = Object.entries(value)
1289
+ if (entriesForObject.length === 0) return value
1258
1290
 
1259
- const resolvedEntries = entriesForObject.map(([key, nested]) => [
1260
- key,
1261
- resolvePromisesForKey(nested, stack),
1262
- ] as const);
1263
- if (!resolvedEntries.some(([, nested]) => isPromiseLike(nested))) return value;
1291
+ const resolvedEntries = entriesForObject.map(
1292
+ ([key, nested]) => [key, resolvePromisesForKey(nested, stack)] as const,
1293
+ )
1294
+ if (!resolvedEntries.some(([, nested]) => isPromiseLike(nested))) return value
1264
1295
 
1265
1296
  return Promise.all(
1266
- resolvedEntries.map(([key, nested]) => Promise.resolve(nested).then(resolved => [key, resolved] as const)),
1267
- ).then(entriesList => Object.fromEntries(entriesList));
1297
+ resolvedEntries.map(([key, nested]) =>
1298
+ Promise.resolve(nested).then(resolved => [key, resolved] as const),
1299
+ ),
1300
+ ).then(entriesList => Object.fromEntries(entriesList))
1268
1301
  } finally {
1269
- stack.delete(value);
1302
+ stack.delete(value)
1270
1303
  }
1271
1304
  }
1272
1305
 
@@ -1276,7 +1309,7 @@ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
1276
1309
  value !== null &&
1277
1310
  'then' in value &&
1278
1311
  typeof (value as { then?: unknown }).then === 'function'
1279
- );
1312
+ )
1280
1313
  }
1281
1314
 
1282
1315
  // Cached element trees: argument holes + render-once subtree semantics.
@@ -1303,30 +1336,30 @@ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
1303
1336
  // renderer's existing hook-dispatcher fallback still applies.
1304
1337
 
1305
1338
  /** Marks pnextUseCache* wrappers so tree wrapping leaves them to self-memoize. */
1306
- const USE_CACHE_WRAPPER = Symbol.for('pnext.compat.useCacheWrapper');
1307
- const MEMO_WRAPPED = Symbol.for('pnext.compat.useCacheMemoWrapped');
1339
+ const USE_CACHE_WRAPPER = Symbol.for('pnext.compat.useCacheWrapper')
1340
+ const MEMO_WRAPPED = Symbol.for('pnext.compat.useCacheMemoWrapped')
1308
1341
  // Renderer component-kind marks (global symbol registry; see render/renderer.ts,
1309
1342
  // client/reference.ts, api/dynamic.tsx, render/slots.tsx).
1310
- const clientReferenceSymbol = Symbol.for('pnext.clientReference');
1311
- const dynamicReferenceSymbol = Symbol.for('pnext.dynamic');
1312
- const paramsScopeSymbol = Symbol.for('pnext.paramsScope');
1313
- const clientPageComponentSymbol = Symbol.for('pnext.clientPageComponent');
1314
- const useCacheJoinerPromiseSymbol = Symbol.for('pnext.compat.useCacheJoinerPromise');
1343
+ const clientReferenceSymbol = Symbol.for('pnext.clientReference')
1344
+ const dynamicReferenceSymbol = Symbol.for('pnext.dynamic')
1345
+ const paramsScopeSymbol = Symbol.for('pnext.paramsScope')
1346
+ const clientPageComponentSymbol = Symbol.for('pnext.clientPageComponent')
1347
+ const useCacheJoinerPromiseSymbol = Symbol.for('pnext.compat.useCacheJoinerPromise')
1315
1348
 
1316
- const HOLE_INDEX_PROP = '__pnextCacheHoleIndex';
1349
+ const HOLE_INDEX_PROP = '__pnextCacheHoleIndex'
1317
1350
 
1318
1351
  /** Inert placeholder; instantiation replaces it before it can ever render. */
1319
1352
  function CachedArgumentHole(): null {
1320
- return null;
1353
+ return null
1321
1354
  }
1322
1355
 
1323
1356
  function holeElement(index: number) {
1324
- return h(CachedArgumentHole as never, { [HOLE_INDEX_PROP]: index } as never);
1357
+ return h(CachedArgumentHole as never, { [HOLE_INDEX_PROP]: index } as never)
1325
1358
  }
1326
1359
 
1327
1360
  interface ExtractedHoles {
1328
- args: unknown[];
1329
- fills: unknown[];
1361
+ args: unknown[]
1362
+ fills: unknown[]
1330
1363
  }
1331
1364
 
1332
1365
  /**
@@ -1336,97 +1369,97 @@ interface ExtractedHoles {
1336
1369
  * returned unchanged with an empty fills list.
1337
1370
  */
1338
1371
  function extractElementHoles(args: unknown[]): ExtractedHoles {
1339
- const fills: unknown[] = [];
1340
- const stack = new WeakSet<object>();
1372
+ const fills: unknown[] = []
1373
+ const stack = new WeakSet<object>()
1341
1374
  const walk = (value: unknown): unknown => {
1342
- if (value === null || typeof value !== 'object' || isPromiseLike(value)) return value;
1375
+ if (value === null || typeof value !== 'object' || isPromiseLike(value)) return value
1343
1376
  if (isElementLike(value)) {
1344
- fills.push(value);
1345
- return holeElement(fills.length - 1);
1377
+ fills.push(value)
1378
+ return holeElement(fills.length - 1)
1346
1379
  }
1347
- if (stack.has(value)) return value;
1348
- stack.add(value);
1380
+ if (stack.has(value)) return value
1381
+ stack.add(value)
1349
1382
  try {
1350
1383
  if (Array.isArray(value)) {
1351
- let changed = false;
1384
+ let changed = false
1352
1385
  const next = value.map(item => {
1353
- const out = walk(item);
1354
- if (out !== item) changed = true;
1355
- return out;
1356
- });
1357
- return changed ? next : value;
1386
+ const out = walk(item)
1387
+ if (out !== item) changed = true
1388
+ return out
1389
+ })
1390
+ return changed ? next : value
1358
1391
  }
1359
- const proto: unknown = Object.getPrototypeOf(value);
1360
- if (proto !== Object.prototype && proto !== null) return value;
1361
- let changed = false;
1392
+ const proto: unknown = Object.getPrototypeOf(value)
1393
+ if (proto !== Object.prototype && proto !== null) return value
1394
+ let changed = false
1362
1395
  const entriesForObject = Object.entries(value).map(([key, nested]) => {
1363
- const out = walk(nested);
1364
- if (out !== nested) changed = true;
1365
- return [key, out] as const;
1366
- });
1367
- return changed ? Object.fromEntries(entriesForObject) : value;
1396
+ const out = walk(nested)
1397
+ if (out !== nested) changed = true
1398
+ return [key, out] as const
1399
+ })
1400
+ return changed ? Object.fromEntries(entriesForObject) : value
1368
1401
  } finally {
1369
- stack.delete(value);
1402
+ stack.delete(value)
1370
1403
  }
1371
- };
1372
- const out = args.map(walk);
1373
- return fills.length > 0 ? { args: out, fills } : { args, fills };
1404
+ }
1405
+ const out = args.map(walk)
1406
+ return fills.length > 0 ? { args: out, fills } : { args, fills }
1374
1407
  }
1375
1408
 
1376
1409
  /** Resolve a cached value for one invocation, substituting its hole fills. */
1377
1410
  function withHoleFills(value: Promise<unknown>, fills: unknown[]): Promise<unknown> {
1378
- if (fills.length === 0) return value;
1379
- return value.then(resolved => instantiateHoles(resolved, fills, new WeakSet<object>()));
1411
+ if (fills.length === 0) return value
1412
+ return value.then(resolved => instantiateHoles(resolved, fills, new WeakSet<object>()))
1380
1413
  }
1381
1414
 
1382
1415
  function instantiateHoles(value: unknown, fills: unknown[], stack: WeakSet<object>): unknown {
1383
- if (value === null || typeof value !== 'object' || isPromiseLike(value)) return value;
1384
- if (stack.has(value)) return value;
1385
- stack.add(value);
1416
+ if (value === null || typeof value !== 'object' || isPromiseLike(value)) return value
1417
+ if (stack.has(value)) return value
1418
+ stack.add(value)
1386
1419
  try {
1387
1420
  if (isElementLike(value)) {
1388
1421
  const vnode = value as {
1389
- type?: unknown;
1390
- props?: Record<string, unknown> | null;
1391
- key?: unknown;
1392
- ref?: unknown;
1393
- };
1422
+ type?: unknown
1423
+ props?: Record<string, unknown> | null
1424
+ key?: unknown
1425
+ ref?: unknown
1426
+ }
1394
1427
  if (vnode.type === CachedArgumentHole) {
1395
- const index = vnode.props?.[HOLE_INDEX_PROP];
1396
- return typeof index === 'number' ? (fills[index] ?? null) : null;
1428
+ const index = vnode.props?.[HOLE_INDEX_PROP]
1429
+ return typeof index === 'number' ? (fills[index] ?? null) : null
1397
1430
  }
1398
- if (!vnode.props || typeof vnode.props !== 'object') return value;
1399
- let changed = false;
1431
+ if (!vnode.props || typeof vnode.props !== 'object') return value
1432
+ let changed = false
1400
1433
  const nextProps = Object.fromEntries(
1401
1434
  Object.entries(vnode.props).map(([key, nested]) => {
1402
- const out = instantiateHoles(nested, fills, stack);
1403
- if (out !== nested) changed = true;
1404
- return [key, out] as const;
1435
+ const out = instantiateHoles(nested, fills, stack)
1436
+ if (out !== nested) changed = true
1437
+ return [key, out] as const
1405
1438
  }),
1406
- );
1407
- if (!changed) return value;
1408
- return cloneElementLike(vnode, vnode.type, nextProps);
1439
+ )
1440
+ if (!changed) return value
1441
+ return cloneElementLike(vnode, vnode.type, nextProps)
1409
1442
  }
1410
1443
  if (Array.isArray(value)) {
1411
- let changed = false;
1444
+ let changed = false
1412
1445
  const next = value.map(item => {
1413
- const out = instantiateHoles(item, fills, stack);
1414
- if (out !== item) changed = true;
1415
- return out;
1416
- });
1417
- return changed ? next : value;
1446
+ const out = instantiateHoles(item, fills, stack)
1447
+ if (out !== item) changed = true
1448
+ return out
1449
+ })
1450
+ return changed ? next : value
1418
1451
  }
1419
- const proto: unknown = Object.getPrototypeOf(value);
1420
- if (proto !== Object.prototype && proto !== null) return value;
1421
- let changed = false;
1452
+ const proto: unknown = Object.getPrototypeOf(value)
1453
+ if (proto !== Object.prototype && proto !== null) return value
1454
+ let changed = false
1422
1455
  const entriesForObject = Object.entries(value).map(([key, nested]) => {
1423
- const out = instantiateHoles(nested, fills, stack);
1424
- if (out !== nested) changed = true;
1425
- return [key, out] as const;
1426
- });
1427
- return changed ? Object.fromEntries(entriesForObject) : value;
1456
+ const out = instantiateHoles(nested, fills, stack)
1457
+ if (out !== nested) changed = true
1458
+ return [key, out] as const
1459
+ })
1460
+ return changed ? Object.fromEntries(entriesForObject) : value
1428
1461
  } finally {
1429
- stack.delete(value);
1462
+ stack.delete(value)
1430
1463
  }
1431
1464
  }
1432
1465
 
@@ -1436,21 +1469,24 @@ function cloneElementLike(
1436
1469
  type: unknown,
1437
1470
  props: Record<string, unknown>,
1438
1471
  ): unknown {
1439
- return h(type as never, {
1440
- ...props,
1441
- ...(vnode.key !== undefined && vnode.key !== null ? { key: vnode.key } : {}),
1442
- ...(vnode.ref !== undefined && vnode.ref !== null ? { ref: vnode.ref } : {}),
1443
- } as never);
1472
+ return h(
1473
+ type as never,
1474
+ {
1475
+ ...props,
1476
+ ...(vnode.key !== undefined && vnode.key !== null ? { key: vnode.key } : {}),
1477
+ ...(vnode.ref !== undefined && vnode.ref !== null ? { ref: vnode.ref } : {}),
1478
+ } as never,
1479
+ )
1444
1480
  }
1445
1481
 
1446
1482
  /** Wrap a settled cached value's server-component vnodes for render-once reuse. */
1447
1483
  function wrapCachedValue(value: unknown, scope: UseCacheScope, entry: UseCacheEntry): unknown {
1448
- return wrapCachedNode(value, scope, entry, new WeakSet<object>()).value;
1484
+ return wrapCachedNode(value, scope, entry, new WeakSet<object>()).value
1449
1485
  }
1450
1486
 
1451
1487
  interface WrapNodeResult {
1452
- value: unknown;
1453
- hasHole: boolean;
1488
+ value: unknown
1489
+ hasHole: boolean
1454
1490
  }
1455
1491
 
1456
1492
  function wrapCachedNode(
@@ -1460,36 +1496,36 @@ function wrapCachedNode(
1460
1496
  stack: WeakSet<object>,
1461
1497
  ): WrapNodeResult {
1462
1498
  if (value === null || typeof value !== 'object' || isPromiseLike(value)) {
1463
- return { value, hasHole: false };
1499
+ return { value, hasHole: false }
1464
1500
  }
1465
- if (stack.has(value)) return { value, hasHole: false };
1466
- stack.add(value);
1501
+ if (stack.has(value)) return { value, hasHole: false }
1502
+ stack.add(value)
1467
1503
  try {
1468
1504
  if (isElementLike(value)) {
1469
1505
  const vnode = value as {
1470
- type?: unknown;
1471
- props?: Record<string, unknown> | null;
1472
- key?: unknown;
1473
- ref?: unknown;
1474
- };
1475
- if (vnode.type === CachedArgumentHole) return { value, hasHole: true };
1476
- let hasHole = false;
1477
- let changed = false;
1478
- let nextProps = vnode.props ?? undefined;
1506
+ type?: unknown
1507
+ props?: Record<string, unknown> | null
1508
+ key?: unknown
1509
+ ref?: unknown
1510
+ }
1511
+ if (vnode.type === CachedArgumentHole) return { value, hasHole: true }
1512
+ let hasHole = false
1513
+ let changed = false
1514
+ let nextProps = vnode.props ?? undefined
1479
1515
  if (vnode.props && typeof vnode.props === 'object') {
1480
1516
  const entriesForProps = Object.entries(vnode.props).map(([key, nested]) => {
1481
- const result = wrapCachedNode(nested, scope, entry, stack);
1482
- hasHole ||= result.hasHole;
1483
- if (result.value !== nested) changed = true;
1484
- return [key, result.value] as const;
1485
- });
1486
- if (changed) nextProps = Object.fromEntries(entriesForProps);
1517
+ const result = wrapCachedNode(nested, scope, entry, stack)
1518
+ hasHole ||= result.hasHole
1519
+ if (result.value !== nested) changed = true
1520
+ return [key, result.value] as const
1521
+ })
1522
+ if (changed) nextProps = Object.fromEntries(entriesForProps)
1487
1523
  }
1488
1524
  // A component whose props carry a hole re-executes per invocation (its
1489
1525
  // props genuinely differ per call); everything else renders once.
1490
- const type = vnode.type;
1526
+ const type = vnode.type
1491
1527
  if (!hasHole && isUseCacheWrapperType(type)) {
1492
- entry.nestedPending = (entry.nestedPending ?? 0) + 1;
1528
+ entry.nestedPending = (entry.nestedPending ?? 0) + 1
1493
1529
  }
1494
1530
  const nextType =
1495
1531
  !hasHole && isUseCacheWrapperType(type)
@@ -1500,37 +1536,37 @@ function wrapCachedNode(
1500
1536
  )
1501
1537
  : !hasHole && shouldMemoWrapType(type)
1502
1538
  ? memoServerComponent(type as (props: Record<string, unknown>) => unknown, scope, entry)
1503
- : type;
1504
- if (!changed && nextType === type) return { value, hasHole };
1539
+ : type
1540
+ if (!changed && nextType === type) return { value, hasHole }
1505
1541
  return {
1506
1542
  value: cloneElementLike(vnode, nextType, nextProps ?? {}),
1507
1543
  hasHole,
1508
- };
1544
+ }
1509
1545
  }
1510
1546
  if (Array.isArray(value)) {
1511
- let hasHole = false;
1512
- let changed = false;
1547
+ let hasHole = false
1548
+ let changed = false
1513
1549
  const next = value.map(item => {
1514
- const result = wrapCachedNode(item, scope, entry, stack);
1515
- hasHole ||= result.hasHole;
1516
- if (result.value !== item) changed = true;
1517
- return result.value;
1518
- });
1519
- return { value: changed ? next : value, hasHole };
1550
+ const result = wrapCachedNode(item, scope, entry, stack)
1551
+ hasHole ||= result.hasHole
1552
+ if (result.value !== item) changed = true
1553
+ return result.value
1554
+ })
1555
+ return { value: changed ? next : value, hasHole }
1520
1556
  }
1521
- const proto: unknown = Object.getPrototypeOf(value);
1522
- if (proto !== Object.prototype && proto !== null) return { value, hasHole: false };
1523
- let hasHole = false;
1524
- let changed = false;
1557
+ const proto: unknown = Object.getPrototypeOf(value)
1558
+ if (proto !== Object.prototype && proto !== null) return { value, hasHole: false }
1559
+ let hasHole = false
1560
+ let changed = false
1525
1561
  const entriesForObject = Object.entries(value).map(([key, nested]) => {
1526
- const result = wrapCachedNode(nested, scope, entry, stack);
1527
- hasHole ||= result.hasHole;
1528
- if (result.value !== nested) changed = true;
1529
- return [key, result.value] as const;
1530
- });
1531
- return { value: changed ? Object.fromEntries(entriesForObject) : value, hasHole };
1562
+ const result = wrapCachedNode(nested, scope, entry, stack)
1563
+ hasHole ||= result.hasHole
1564
+ if (result.value !== nested) changed = true
1565
+ return [key, result.value] as const
1566
+ })
1567
+ return { value: changed ? Object.fromEntries(entriesForObject) : value, hasHole }
1532
1568
  } finally {
1533
- stack.delete(value);
1569
+ stack.delete(value)
1534
1570
  }
1535
1571
  }
1536
1572
 
@@ -1539,7 +1575,7 @@ function isUseCacheWrapperType(type: unknown): boolean {
1539
1575
  typeof type === 'function' &&
1540
1576
  Boolean((type as unknown as Record<symbol, unknown>)[USE_CACHE_WRAPPER]) &&
1541
1577
  !(type as unknown as Record<symbol, unknown>)[MEMO_WRAPPED]
1542
- );
1578
+ )
1543
1579
  }
1544
1580
 
1545
1581
  /**
@@ -1555,67 +1591,67 @@ function attributeNestedCacheWrapper(
1555
1591
  scope: UseCacheScope,
1556
1592
  entry: UseCacheEntry,
1557
1593
  ): (props: Record<string, unknown>) => unknown {
1558
- let settledOnce = false;
1594
+ let settledOnce = false
1559
1595
  const settle = (fold: boolean) => {
1560
- if (fold) foldScopeIntoEntry(scope, entry);
1561
- if (settledOnce) return;
1562
- settledOnce = true;
1596
+ if (fold) foldScopeIntoEntry(scope, entry)
1597
+ if (settledOnce) return
1598
+ settledOnce = true
1563
1599
  if (entry.nestedPending !== undefined && entry.nestedPending > 0) {
1564
- entry.nestedPending -= 1;
1565
- entry.onNestedSettled?.();
1600
+ entry.nestedPending -= 1
1601
+ entry.onNestedSettled?.()
1566
1602
  }
1567
- };
1603
+ }
1568
1604
  const wrapper = function (this: unknown, props: Record<string, unknown>): unknown {
1569
- if (this !== undefined) return fn.call(this, props);
1570
- const result = scopeStorage.run(scope, () => fn(props));
1605
+ if (this !== undefined) return fn.call(this, props)
1606
+ const result = scopeStorage.run(scope, () => fn(props))
1571
1607
  if (isPromiseLike(result)) {
1572
1608
  return Promise.resolve(result).then(
1573
1609
  value => {
1574
- settle(true);
1575
- return value;
1610
+ settle(true)
1611
+ return value
1576
1612
  },
1577
1613
  (error: unknown) => {
1578
- settle(false);
1579
- throw error;
1614
+ settle(false)
1615
+ throw error
1580
1616
  },
1581
- );
1617
+ )
1582
1618
  }
1583
- settle(true);
1584
- return result;
1585
- };
1586
- const marked = wrapper as unknown as Record<symbol, unknown>;
1587
- marked[MEMO_WRAPPED] = true;
1588
- marked[USE_CACHE_WRAPPER] = true;
1589
- return wrapper;
1619
+ settle(true)
1620
+ return result
1621
+ }
1622
+ const marked = wrapper as unknown as Record<symbol, unknown>
1623
+ marked[MEMO_WRAPPED] = true
1624
+ marked[USE_CACHE_WRAPPER] = true
1625
+ return wrapper
1590
1626
  }
1591
1627
 
1592
1628
  /** Fold the scope's accumulated tags + min cacheLife onto a settled entry. */
1593
1629
  function foldScopeIntoEntry(scope: UseCacheScope, entry: UseCacheEntry): void {
1594
- entry.tags = [...new Set([...entry.tags, ...scope.tags])];
1595
- entry.revalidateSeconds = minDefined(entry.revalidateSeconds, scope.revalidateSeconds);
1596
- entry.expireSeconds = minDefined(entry.expireSeconds, scope.expireSeconds);
1597
- entry.staleSeconds = minDefined(entry.staleSeconds, scope.staleSeconds);
1630
+ entry.tags = [...new Set([...entry.tags, ...scope.tags])]
1631
+ entry.revalidateSeconds = minDefined(entry.revalidateSeconds, scope.revalidateSeconds)
1632
+ entry.expireSeconds = minDefined(entry.expireSeconds, scope.expireSeconds)
1633
+ entry.staleSeconds = minDefined(entry.staleSeconds, scope.staleSeconds)
1598
1634
  }
1599
1635
 
1600
1636
  function shouldMemoWrapType(type: unknown): boolean {
1601
- if (typeof type !== 'function') return false;
1602
- if (type === Fragment || type === (CoreSuspense as unknown)) return false;
1637
+ if (typeof type !== 'function') return false
1638
+ if (type === Fragment || type === (CoreSuspense as unknown)) return false
1603
1639
  const component = type as unknown as { prototype?: { render?: unknown } } & Record<
1604
1640
  symbol,
1605
1641
  unknown
1606
- >;
1642
+ >
1607
1643
  // Class components (incl. preact/compat Suspense) need a preact instance.
1608
- if (component.prototype && typeof component.prototype.render === 'function') return false;
1609
- if (component[MEMO_WRAPPED] || component[USE_CACHE_WRAPPER]) return false;
1644
+ if (component.prototype && typeof component.prototype.render === 'function') return false
1645
+ if (component[MEMO_WRAPPED] || component[USE_CACHE_WRAPPER]) return false
1610
1646
  if (
1611
1647
  component[clientReferenceSymbol] ||
1612
1648
  component[dynamicReferenceSymbol] ||
1613
1649
  component[paramsScopeSymbol] ||
1614
1650
  component[clientPageComponentSymbol]
1615
1651
  ) {
1616
- return false;
1652
+ return false
1617
1653
  }
1618
- return true;
1654
+ return true
1619
1655
  }
1620
1656
 
1621
1657
  /**
@@ -1630,66 +1666,66 @@ function memoServerComponent(
1630
1666
  scope: UseCacheScope,
1631
1667
  entry: UseCacheEntry,
1632
1668
  ): (props: Record<string, unknown>) => unknown {
1633
- let memo: { value: unknown; requestToken?: object } | undefined;
1669
+ let memo: { value: unknown; requestToken?: object } | undefined
1634
1670
  const wrapper = function (this: unknown, props: Record<string, unknown>): unknown {
1635
- if (this !== undefined) return fn.call(this, props);
1671
+ if (this !== undefined) return fn.call(this, props)
1636
1672
  if (memo) {
1637
- const requestToken = currentUseCacheRequestToken();
1673
+ const requestToken = currentUseCacheRequestToken()
1638
1674
  if (
1639
1675
  memo.requestToken &&
1640
1676
  requestToken &&
1641
1677
  memo.requestToken !== requestToken &&
1642
1678
  isPromiseLike(memo.value)
1643
1679
  ) {
1644
- const joined = Promise.resolve(memo.value).then(value => value);
1645
- (joined as unknown as Record<symbol, unknown>)[useCacheJoinerPromiseSymbol] = true;
1646
- return joined;
1680
+ const joined = Promise.resolve(memo.value).then(value => value)
1681
+ ;(joined as unknown as Record<symbol, unknown>)[useCacheJoinerPromiseSymbol] = true
1682
+ return joined
1647
1683
  }
1648
- return memo.value;
1684
+ return memo.value
1649
1685
  }
1650
- const requestToken = currentUseCacheRequestToken();
1651
- const result = runInsideDataCacheProducer(() => scopeStorage.run(scope, () => fn(props)));
1686
+ const requestToken = currentUseCacheRequestToken()
1687
+ const result = runInsideDataCacheProducer(() => scopeStorage.run(scope, () => fn(props)))
1652
1688
  if (isPromiseLike(result)) {
1653
1689
  const settled = Promise.resolve(result).then(resolved => {
1654
1690
  // Tags declared while the subtree rendered attribute to the entry.
1655
- entry.tags = [...scope.tags];
1656
- return wrapCachedValue(resolved, scope, entry) as ComponentChildren;
1657
- });
1658
- memo = { value: settled, ...(requestToken ? { requestToken } : {}) };
1691
+ entry.tags = [...scope.tags]
1692
+ return wrapCachedValue(resolved, scope, entry) as ComponentChildren
1693
+ })
1694
+ memo = { value: settled, ...(requestToken ? { requestToken } : {}) }
1659
1695
  settled.then(undefined, () => {
1660
1696
  // Failures don't stick: the next placement re-executes.
1661
- if (memo?.value === settled) memo = undefined;
1662
- });
1663
- return settled;
1697
+ if (memo?.value === settled) memo = undefined
1698
+ })
1699
+ return settled
1664
1700
  }
1665
- entry.tags = [...scope.tags];
1666
- const wrapped = wrapCachedValue(result, scope, entry);
1667
- memo = { value: wrapped, ...(requestToken ? { requestToken } : {}) };
1668
- return wrapped;
1669
- };
1670
- (wrapper as unknown as Record<symbol, unknown>)[MEMO_WRAPPED] = true;
1671
- return wrapper;
1701
+ entry.tags = [...scope.tags]
1702
+ const wrapped = wrapCachedValue(result, scope, entry)
1703
+ memo = { value: wrapped, ...(requestToken ? { requestToken } : {}) }
1704
+ return wrapped
1705
+ }
1706
+ ;(wrapper as unknown as Record<symbol, unknown>)[MEMO_WRAPPED] = true
1707
+ return wrapper
1672
1708
  }
1673
1709
 
1674
1710
  function currentUseCacheRequestToken(): object | undefined {
1675
- const request = currentRequest() as object | undefined;
1676
- if (!request) return undefined;
1677
- let token = requestTokens.get(request);
1711
+ const request = currentRequest() as object | undefined
1712
+ if (!request) return undefined
1713
+ let token = requestTokens.get(request)
1678
1714
  if (!token) {
1679
- token = {};
1680
- requestTokens.set(request, token);
1715
+ token = {}
1716
+ requestTokens.set(request, token)
1681
1717
  }
1682
- return token;
1718
+ return token
1683
1719
  }
1684
1720
 
1685
1721
  function isElementLike(value: unknown): boolean {
1686
- if (typeof value !== 'object' || value === null) return false;
1687
- const record = value as Record<string, unknown>;
1688
- if ('$$typeof' in record) return true;
1689
- return 'type' in record && 'props' in record;
1722
+ if (typeof value !== 'object' || value === null) return false
1723
+ const record = value as Record<string, unknown>
1724
+ if ('$$typeof' in record) return true
1725
+ return 'type' in record && 'props' in record
1690
1726
  }
1691
1727
 
1692
1728
  /** Test hook. */
1693
1729
  export function clearUseCacheEntries() {
1694
- entries.clear();
1730
+ entries.clear()
1695
1731
  }