@thomas.f/prnext 0.1.16-alpha → 0.1.18-alpha

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 (215) hide show
  1. package/AI.md +63 -0
  2. package/README.md +35 -503
  3. package/build/ADAPTERS.md +27 -2
  4. package/build/actions.mjs +6 -1
  5. package/build/analyze.mjs +340 -0
  6. package/build/app-config.mjs +20 -8
  7. package/build/app-static.mjs +41 -14
  8. package/build/app-styles.mjs +79 -0
  9. package/build/app.mjs +99 -31
  10. package/build/build-lock.mjs +6 -6
  11. package/build/bundle-runtime.mjs +39 -11
  12. package/build/cache-components.mjs +1 -1
  13. package/build/cache-handlers.mjs +1 -1
  14. package/build/check.mjs +56 -11
  15. package/build/compact-css.mjs +53 -0
  16. package/build/compilation-cache.mjs +269 -0
  17. package/build/compilation-store.mjs +142 -0
  18. package/build/compiler.mjs +51 -4
  19. package/build/concurrent.mjs +12 -0
  20. package/build/config.mjs +50 -13
  21. package/build/create.mjs +239 -0
  22. package/build/css-modules.mjs +65 -10
  23. package/build/deployment-assets.mjs +89 -0
  24. package/build/deployment-config.mjs +18 -0
  25. package/build/dev-compiler-health.mjs +11 -0
  26. package/build/dev-css-update.mjs +85 -0
  27. package/build/dev-dependencies.mjs +44 -0
  28. package/build/dev-discovery.mjs +28 -0
  29. package/build/dev-worker.mjs +14 -5
  30. package/build/dev.mjs +64 -6
  31. package/build/dynamic.mjs +1 -1
  32. package/build/edge.mjs +5 -4
  33. package/build/fonts.mjs +1 -1
  34. package/build/framework-imports.mjs +50 -0
  35. package/build/html-only-config.mjs +53 -0
  36. package/build/html-only-graph.mjs +67 -0
  37. package/build/incremental.mjs +8 -4
  38. package/build/index.mjs +140 -112
  39. package/build/inspect.mjs +3 -0
  40. package/build/inspection-summary.mjs +37 -4
  41. package/build/instrumentation.mjs +30 -0
  42. package/build/loader-cache.mjs +53 -26
  43. package/build/metadata-routes.mjs +3 -4
  44. package/build/migrate.mjs +60 -57
  45. package/build/migration-audit.mjs +1 -1
  46. package/build/native-type-config.mjs +37 -0
  47. package/build/npm-packages.mjs +3 -18
  48. package/build/package-manager.mjs +308 -0
  49. package/build/persistent-cache.mjs +112 -0
  50. package/build/precompress.mjs +62 -30
  51. package/build/prerender-cache.mjs +70 -0
  52. package/build/prerender-config.mjs +29 -0
  53. package/build/prerender-pages.mjs +100 -0
  54. package/build/prerender-pool.mjs +165 -0
  55. package/build/prerender-process-worker.mjs +58 -0
  56. package/build/prerender-worker.mjs +10 -0
  57. package/build/production-builder.mjs +129 -0
  58. package/build/react-compiler-report.mjs +225 -0
  59. package/build/react-compiler.mjs +139 -0
  60. package/build/runtime-contract.mjs +2 -2
  61. package/build/scan.mjs +4 -4
  62. package/build/source-analysis.mjs +42 -0
  63. package/build/static-adapter.mjs +13 -5
  64. package/build/static-export.mjs +4 -4
  65. package/build/static-input.mjs +48 -0
  66. package/build/styles.mjs +17 -3
  67. package/build/transform-cache.mjs +37 -4
  68. package/build/transform.mjs +1 -1
  69. package/build/type-command.mjs +41 -0
  70. package/build/typed-routes.mjs +95 -0
  71. package/build/typescript-dev-worker.mjs +18 -0
  72. package/build/typescript-dev.mjs +49 -0
  73. package/build/typescript-native-worker.mjs +55 -0
  74. package/build/typescript-native.mjs +18 -0
  75. package/build/typescript-toolchain.mjs +19 -0
  76. package/build/typescript-watch.mjs +22 -0
  77. package/build/typescript.mjs +11 -8
  78. package/build/webpack-graph.mjs +170 -30
  79. package/build/worker-memory.mjs +9 -0
  80. package/build/worker-preload.mjs +9 -0
  81. package/cli.mjs +162 -122
  82. package/compat/activity-cache.cjs +26 -0
  83. package/compat/after.cjs +6 -4
  84. package/compat/app-layout-context.cjs +52 -2
  85. package/compat/base64.cjs +10 -0
  86. package/compat/cache-handlers.cjs +12 -2
  87. package/compat/cache-wire.cjs +28 -0
  88. package/compat/cache.cjs +2 -1
  89. package/compat/cache.d.cts +4 -0
  90. package/compat/cookies.cjs +21 -5
  91. package/compat/data-cache.cjs +12 -8
  92. package/compat/flight-delta.cjs +150 -0
  93. package/compat/headers.cjs +4 -2
  94. package/compat/incremental-cache.cjs +20 -7
  95. package/compat/index.d.ts +89 -10
  96. package/compat/link.cjs +12 -9
  97. package/compat/link.d.cts +4 -3
  98. package/compat/navigation-authorization.cjs +9 -0
  99. package/compat/navigation-stages.cjs +19 -0
  100. package/compat/navigation.d.cts +4 -3
  101. package/compat/offline-store.cjs +11 -0
  102. package/compat/offline.cjs +5 -0
  103. package/compat/offline.d.cts +2 -0
  104. package/compat/redis-cache.cjs +201 -0
  105. package/compat/redis-cache.d.cts +37 -0
  106. package/compat/router.d.cts +4 -3
  107. package/compat/server-primitives.cjs +31 -11
  108. package/compat/server.d.cts +15 -2
  109. package/compat/static-generation.cjs +7 -0
  110. package/compat/typed-route.d.cts +17 -0
  111. package/compat/use-cache.cjs +3 -0
  112. package/docs/ai/compatibility.md +275 -0
  113. package/docs/ai/data.md +288 -0
  114. package/docs/ai/deployment.md +269 -0
  115. package/docs/ai/manifest.json +11 -0
  116. package/docs/ai/quickstart.md +171 -0
  117. package/docs/ai/routing.md +214 -0
  118. package/docs/ai/troubleshooting.md +155 -0
  119. package/index.mjs +2 -0
  120. package/native/diagnostics.mjs +34 -0
  121. package/native/resolve.mjs +7 -2
  122. package/package.json +44 -7
  123. package/release/ai-docs.mjs +48 -0
  124. package/release/verify.mjs +2 -0
  125. package/runtime/action-client.mjs +10 -4
  126. package/runtime/action-server.mjs +2 -0
  127. package/runtime/ai-docs.mjs +202 -0
  128. package/runtime/app-client.mjs +226 -68
  129. package/runtime/app-content.mjs +4 -3
  130. package/runtime/app-partial-client.mjs +3 -0
  131. package/runtime/app-partial.mjs +1 -1
  132. package/runtime/app-prefetch-constants.mjs +4 -0
  133. package/runtime/app-prefetch.mjs +2 -3
  134. package/runtime/app-render-key.mjs +5 -0
  135. package/runtime/app-render.mjs +100 -28
  136. package/runtime/app-routing.mjs +22 -9
  137. package/runtime/app-static.mjs +18 -8
  138. package/runtime/app-stream-html.mjs +10 -53
  139. package/runtime/app-style-selection.mjs +34 -0
  140. package/runtime/app-styles.mjs +154 -0
  141. package/runtime/cache-components-rsc.mjs +4 -1
  142. package/runtime/cache-request.mjs +2 -1
  143. package/runtime/cloud/archive.mjs +5 -6
  144. package/runtime/compile-cache.mjs +8 -0
  145. package/runtime/deployment-client.mjs +19 -0
  146. package/runtime/dev-ai-brands.mjs +26 -0
  147. package/runtime/dev-ai-style.mjs +99 -0
  148. package/runtime/dev-build-reasons.mjs +25 -0
  149. package/runtime/dev-cache-control.mjs +53 -0
  150. package/runtime/dev-client.mjs +245 -46
  151. package/runtime/dev-compiler-broker.mjs +42 -0
  152. package/runtime/dev-console-copy.mjs +19 -0
  153. package/runtime/dev-console.mjs +106 -0
  154. package/runtime/dev-dependency-graph.mjs +40 -0
  155. package/runtime/dev-element-picker-style.mjs +27 -0
  156. package/runtime/dev-element-picker.mjs +213 -0
  157. package/runtime/dev-error-overlay-style.mjs +41 -0
  158. package/runtime/dev-error-overlay.mjs +167 -0
  159. package/runtime/dev-error-report.mjs +121 -0
  160. package/runtime/dev-file-changes.mjs +1 -1
  161. package/runtime/dev-gateway.mjs +44 -0
  162. package/runtime/dev-host-relay.mjs +66 -0
  163. package/runtime/dev-indicator.mjs +191 -0
  164. package/runtime/dev-issue-motion.mjs +214 -0
  165. package/runtime/dev-lazy-tool.mjs +34 -0
  166. package/runtime/dev-logo.mjs +2 -0
  167. package/runtime/dev-notification.mjs +81 -0
  168. package/runtime/dev-popout.mjs +34 -0
  169. package/runtime/dev-position.mjs +107 -0
  170. package/runtime/dev-preview-touch.mjs +122 -0
  171. package/runtime/dev-runtime-pool.mjs +45 -0
  172. package/runtime/dev-runtime-spare.mjs +57 -0
  173. package/runtime/dev-tools-client.mjs +155 -0
  174. package/runtime/dev-tools-mcp.mjs +126 -0
  175. package/runtime/dev-tools-metrics.mjs +31 -0
  176. package/runtime/dev-tools-model.mjs +42 -0
  177. package/runtime/dev-tools-panel.mjs +341 -0
  178. package/runtime/dev-tools-result.mjs +37 -0
  179. package/runtime/dev-tools-server.mjs +256 -0
  180. package/runtime/dev-tools-source.mjs +104 -0
  181. package/runtime/dev-tooltip.mjs +94 -0
  182. package/runtime/dev-ui-icons.mjs +8 -0
  183. package/runtime/dev-workbench-style.mjs +106 -0
  184. package/runtime/dev-workbench.mjs +428 -0
  185. package/runtime/dev-worker-logs.mjs +31 -0
  186. package/runtime/development.mjs +324 -0
  187. package/runtime/document.mjs +1 -1
  188. package/runtime/edge.mjs +6 -5
  189. package/runtime/env.mjs +2 -1
  190. package/runtime/fetch-cache.mjs +10 -8
  191. package/runtime/flight-delta.mjs +50 -0
  192. package/runtime/flight-tap.mjs +28 -0
  193. package/runtime/html-only.mjs +27 -0
  194. package/runtime/http.mjs +2 -0
  195. package/runtime/incremental-cache.mjs +25 -4
  196. package/runtime/instrumentation.mjs +49 -0
  197. package/runtime/navigation-recovery.mjs +36 -0
  198. package/runtime/navigation.mjs +7 -0
  199. package/runtime/node-html-stream.mjs +47 -0
  200. package/runtime/pages-client.mjs +24 -8
  201. package/runtime/pages-navigation.mjs +54 -64
  202. package/runtime/persistent/client.mjs +6 -5
  203. package/runtime/persistent/daemon.mjs +66 -56
  204. package/runtime/persistent/native-proxy.mjs +63 -0
  205. package/runtime/prefetch-scheduler.mjs +110 -0
  206. package/runtime/preload.mjs +12 -0
  207. package/runtime/profile.mjs +14 -0
  208. package/runtime/profiles.json +2 -2
  209. package/runtime/render.mjs +3 -0
  210. package/runtime/route-pattern.mjs +44 -0
  211. package/runtime/rsc-worker.mjs +41 -12
  212. package/runtime/trace.mjs +78 -0
  213. package/runtime/typed-route.mjs +19 -0
  214. package/runtime/watch-project.mjs +29 -18
  215. package/runtime/worker.mjs +102 -24
@@ -0,0 +1,275 @@
1
+ # Compatibility and migration
2
+
3
+ [AI documentation index](../../AI.md)
4
+
5
+ ## Treat compatibility as a checked contract
6
+
7
+ PRNext adapts many public Next.js conventions; it is not a drop-in implementation
8
+ of every Next.js version. Package installation, a recognized import or a green
9
+ source audit does not prove an application works. The installed package's
10
+ metadata, declarations, diagnostics and executable behavior are the authority
11
+ for this release. Do not use an old repository checklist to override them.
12
+
13
+ Common supported areas include App/Pages routing, React Server Components,
14
+ Server Actions, request APIs, navigation, route metadata, supported cache APIs,
15
+ CSS Modules and the project's PostCSS pipeline. Scope varies by runtime and
16
+ option. Advanced parallel/intercepting routing, custom webpack/loaders, package
17
+ transpilation, image/font tooling and caching deserve targeted build and browser
18
+ tests. Support for one configuration shape does not imply support for all
19
+ upstream plugin behavior.
20
+
21
+ ## Instrumentation and tracing
22
+
23
+ Use a single `instrumentation.ts`, `.js`, `.mjs` or `.cjs` at the project root
24
+ or in `src`. Export an optional async `register()` for runtime initialization
25
+ and an optional async `onRequestError(error, request, context)` for reporting.
26
+ The `Instrumentation.onRequestError` type is exported from PRNext's root API.
27
+ `process.env.NEXT_RUNTIME` distinguishes `nodejs` and `edge`; conditional imports
28
+ keep Node SDKs out of an Edge bundle. Registration runs once per server realm,
29
+ including the separate RSC realm, before application requests. A production
30
+ prerender does not run the runtime startup hook. Request-time ISR and PPR
31
+ regeneration initialize their server realm and report failures through the same
32
+ hooks. Repeated propagation of an RSC error to the HTML worker does not report
33
+ the same error again.
34
+
35
+ ```ts
36
+ import type { Instrumentation } from '@thomas.f/prnext'
37
+
38
+ export async function register() {
39
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
40
+ await import('./observability.node')
41
+ }
42
+ }
43
+
44
+ export const onRequestError: Instrumentation.onRequestError = async (
45
+ error, request, context
46
+ ) => {
47
+ await reportError(error, request, context)
48
+ }
49
+ ```
50
+
51
+ `reportError` above is an application function, not a PRNext API. Initialization
52
+ has a 30-second budget. Each error hook has a five-second budget; reporting
53
+ failure does not replace the application failure. Redirect/not-found control
54
+ signals are excluded. Hooks receive request information: the application owns
55
+ redaction before sending that information to a third party.
56
+
57
+ For built-in correlated traces, explicitly set
58
+ `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` to an OTLP HTTP collector's complete traces
59
+ URL and `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/json`. Alternatively,
60
+ `OTEL_EXPORTER_OTLP_ENDPOINT` supplies a base URL with `/v1/traces` appended.
61
+ `OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_TRACES_HEADERS` (or general
62
+ `OTEL_EXPORTER_OTLP_HEADERS`) and `OTEL_SDK_DISABLED=true` are supported. Keep
63
+ exporter credentials in the server environment. This built-in path supports
64
+ HTTP JSON, not gRPC or HTTP protobuf; use your own SDK for other protocols.
65
+
66
+ Native HTTP spans cover static and dynamic responses through body completion
67
+ or cancellation. Node, RSC and fetch spans share W3C trace context. Export queues
68
+ are bounded and telemetry is best effort; an unavailable collector does not
69
+ block a site response. Built-in span attributes exclude bodies, cookie values,
70
+ query strings and authorization headers. An application-installed
71
+ `@opentelemetry/api` can use the same parent context when its SDK installs a
72
+ context manager in `register`. No SDK is installed automatically, and this does
73
+ not certify every vendor integration.
74
+
75
+ ## Production bytes and navigation
76
+
77
+ Production CSS Modules use deterministic compact class and animation names.
78
+ Global classes and declared composition exports retain their meaning; development
79
+ keeps readable names. Internal JavaScript exports are shortened by the selected
80
+ compiler, while public exports and dynamic imports keep their interfaces. Asset
81
+ hashes and source maps remain compiler-owned.
82
+
83
+ App navigation can negotiate binary differential Flight responses. Every request
84
+ still runs fresh rendering and access checks. The server retains at most 64
85
+ completed bases, 32 MiB in total, with a two-MiB per-base limit and 30-second
86
+ expiry. Bases are keyed by deployment and credentials; an unknown, expired or
87
+ incompatible base produces ordinary Flight. The browser retains one completed
88
+ base, streams reconstructed bytes to React and can retry one failed differential
89
+ navigation as a full GET. Server Actions are never replayed through this path.
90
+ Per worker, at most eight captures and two encoders run together. Additional
91
+ requests use ordinary Flight, keeping transient transport work bounded too.
92
+
93
+ Configuration uses one `next.config.*` or `prnext.config.*` file and rejects
94
+ unknown keys rather than ignoring them. React Compiler is available through an
95
+ explicit `reactCompiler` option described below. `typedRoutes: true` is
96
+ implemented through PRNext's generated route registry. Do not copy a full
97
+ Next.js configuration template into a new application.
98
+
99
+ The repository's reference tests build and run matching applications against
100
+ Next.js 16.4.3, including mounted App navigation, form Actions and cookies,
101
+ metadata, Pages locale routing, redirects, APIs and selected webpack loader
102
+ contracts. These executable examples cover specific behavior; they do not
103
+ certify every Next.js application or plugin. `NextRequest.nextUrl` understands
104
+ data-route build IDs and locales, and request-cookie mutations synchronize the
105
+ request header. Request cookies have different mutation results from response
106
+ cookies; do not use them interchangeably.
107
+
108
+ A locked real-application fixture additionally exercises Better Auth 1.7.14
109
+ (email/password, session cookies and logout), Drizzle ORM 0.45.4 over SQLite
110
+ through its proxy driver, next-intl 4.14.9 (server and client translations),
111
+ and the MDX 3.1.1 webpack loader against Next.js 16.4.3. It covers authenticated
112
+ Server Actions, user-owned rows, cookie/locale isolation, client navigation and
113
+ Server Component `after()` callbacks. This is evidence for those workflows,
114
+ not a guarantee for every plugin, provider or deployment configuration.
115
+
116
+ PRNext-specific options such as `prerender` and `htmlOnly` are described in
117
+ [Build and deployment](deployment.md). Do not pass them to an upstream Next.js
118
+ build when comparing the two implementations.
119
+
120
+ ## TypeScript 7 and earlier compilers
121
+
122
+ PRNext uses the application's installed TypeScript. TypeScript 7 runs through its
123
+ official native `tsc` command for production checks, `prnext typecheck` and live
124
+ development diagnostics. Install TypeScript normally, including its platform
125
+ optional dependencies. PRNext does not download a compiler during a build.
126
+ TypeScript 5 and 6 retain their compiler API integration.
127
+
128
+ Checks use `--noEmit` and keep incremental compiler state in an owned temporary
129
+ directory. Development reports updated errors and clears fixed diagnostics;
130
+ new source files and configuration changes restart the native watch generation.
131
+ `typedRoutes` also supports native TypeScript and preserves JSONC comments when
132
+ adding its generated declaration to the config. Compiler support does not imply
133
+ that every application is ready for TypeScript 7's own language/config changes.
134
+
135
+ ## React Compiler
136
+
137
+ Enable the bundled official React Compiler in the project's existing config:
138
+
139
+ ```js
140
+ export default {
141
+ reactCompiler: true,
142
+ };
143
+ ```
144
+
145
+ No separate compiler installation is required. The default is disabled;
146
+ `reactCompiler: false` preserves the uncompiled component path. Enabled builds
147
+ optimize project-owned browser components in both Pages and App applications,
148
+ in development and production. App Client Components also use the transform in
149
+ their isolated server-rendering graph. The Pages server graph stays uncompiled,
150
+ including APIs, custom Document code and data-loader utilities, so ordinary
151
+ server functions never acquire client-only memoization hooks. React Server
152
+ Components, Server Action modules, PRNext internals, generated entrypoints and
153
+ npm packages are not compiled. Keep the matching React 19 peer versions required
154
+ by the installed framework.
155
+
156
+ Ordinary `.mts`/`.cts` hook helpers can be optimized; server/client directives
157
+ in those extensions remain unsupported. Decorated `.mts`/`.cts` utility modules
158
+ keep their native compilation path while importing React components can still
159
+ be optimized.
160
+
161
+ For gradual adoption, use `reactCompiler: { compilationMode: 'annotation' }`, then
162
+ put `"use memo";` at the beginning of each component or hook to optimize. In
163
+ either mode, `"use no memo";` excludes an individual function. Supported object
164
+ options are `compilationMode: 'infer' | 'annotation'`,
165
+ `panicThreshold: 'none' | 'critical_errors' | 'all_errors'` and `target: '19'`.
166
+ Defaults are `'infer'`, `'none'` and `'19'`; other fields are rejected.
167
+
168
+ After a successful build, inspect the compiler's actual results:
169
+
170
+ ```sh
171
+ prnext compiler
172
+ prnext compiler --json
173
+ ```
174
+
175
+ The report names observed functions, their project-relative files, optimization
176
+ or skip status, and the compiler's reasons. These are transform decisions;
177
+ server-code removal and tree shaking can later omit a function from the browser
178
+ bundle. Cached transforms retain their
179
+ diagnostics and are identified as reused. Browser and server graphs do not
180
+ inflate the function count. An incremental build can reuse graph inputs without
181
+ observing every transform; the report states that scope rather than claiming
182
+ complete application coverage. Disabled compilation is reported explicitly.
183
+ The report describes the last successful build, stays outside public assets and
184
+ does not include source code. A failed build preserves the previous report.
185
+
186
+ The compiler can skip code that cannot be safely optimized. Follow the Rules of
187
+ React and check actual behavior rather than assuming every component was
188
+ compiled. It reduces repeated component work; it does not guarantee faster
189
+ server responses, smaller JavaScript or a specific speed multiplier. Validate
190
+ hydration, interactions and Fast Refresh in the application, then compare a
191
+ production build with the option enabled and disabled. Compilation adds build
192
+ work; PRNext caches pure transforms with their compiler version and settings.
193
+
194
+ See the [official React Compiler documentation](https://react.dev/learn/react-compiler).
195
+
196
+ Only explicit private Next.js import adapters are supported. Do not add arbitrary
197
+ `next/dist/...` imports or install another Next runtime to satisfy one: isolate
198
+ or replace the incompatible dependency, retain the original framework where
199
+ needed, and describe the limitation honestly.
200
+
201
+ ## Migrate conservatively
202
+
203
+ Use the project's existing package manager. For npm, install the intended
204
+ published PRNext release and its matching React peers, then inspect the dry run:
205
+
206
+ ```sh
207
+ npm i --save-exact @thomas.f/prnext react@19.3.1 react-dom@19.3.1 react-server-dom-webpack@19.3.1
208
+ npx --no-install prnext migrate --dry-run
209
+ npx --no-install prnext migrate --check --json
210
+ ```
211
+
212
+ Review scripts, manager/workspace detection, configuration and reported source
213
+ conflicts. When the plan is correct:
214
+
215
+ ```sh
216
+ npx --no-install prnext migrate
217
+ npx --no-install prnext check --json
218
+ npx --no-install prnext typecheck
219
+ npx --no-install prnext build
220
+ ```
221
+
222
+ Migration changes selected scripts/dependencies, backs up package files and
223
+ existing lockfiles, and keeps Next.js and application source. Public `next/*`
224
+ imports can remain. Do not remove Next, replace all imports or recreate the
225
+ application without a separate need.
226
+
227
+ Installation honors npm, pnpm and Yarn Classic/Berry and the owning workspace
228
+ root. Conflicting managers, unsafe filesystem entries and unlisted workspaces
229
+ can block automatic install. `migrate --no-install` prepares the manifest for
230
+ manual manager execution. Custom shell scripts may require a manual edit.
231
+ An installation failure attempts safe manifest/lockfile restoration; it cannot
232
+ roll back every dependency directory or manager cache. Never solve peer conflicts
233
+ by silently adding `--force` or `--legacy-peer-deps`.
234
+
235
+ `migrate --check` audits without applying a migration. Configuration evaluation
236
+ can run project code, and installed custom loaders still require a real build.
237
+ The audit is not a sandbox for untrusted projects.
238
+
239
+ ## Compare running applications
240
+
241
+ Start the original application and the candidate on separate available ports.
242
+ Create `paths.json` with representative concrete URLs:
243
+
244
+ ```json
245
+ ["/", "/products/demo", "/api/health"]
246
+ ```
247
+
248
+ Then compare public GET responses:
249
+
250
+ ```sh
251
+ prnext migrate --check --against http://localhost:3000 --candidate http://localhost:3001 --routes paths.json
252
+ ```
253
+
254
+ The comparison does not authenticate or follow redirects. It compares response
255
+ samples, including status, content type and selected body semantics; it does not
256
+ prove hydration, styling, actions or all dynamic routes. Visit important routes
257
+ in a real browser, test authorized and unauthorized states, and run the existing
258
+ application tests. Keep the original setup available until the candidate is
259
+ actually verified. Timings from this comparison are not controlled benchmarks.
260
+
261
+ ## Boundaries that often matter
262
+
263
+ React server/client directives in `.mts`/`.cts` are rejected; use `.ts`/`.tsx`.
264
+ App Route Handlers use Web Request/Response, while Pages APIs retain their own
265
+ request/response contract. App and Pages router hooks are not interchangeable.
266
+ Edge code cannot assume Node APIs or Node-only package behavior.
267
+
268
+ HTTP/1 WebSockets can tunnel through supported external HTTP(S) rewrites and
269
+ hosting gateways; App/Pages handlers do not gain a WebSocket server API. Static
270
+ imports accept completed public browser output, not another framework's SSR
271
+ server. See [Deployment](deployment.md) before choosing an adapter.
272
+
273
+ When a feature is missing, give its exact failing API/option and a supported
274
+ alternative. Do not claim full compatibility, invent a replacement method or
275
+ mask the error with an empty shim.
@@ -0,0 +1,288 @@
1
+ # Request data, actions and caching
2
+
3
+ [AI documentation index](../../AI.md)
4
+
5
+ ## Request context
6
+
7
+ Use server request APIs only while handling a PRNext request. Await `headers()`
8
+ and `cookies()` from `next/headers`. Headers are read-only; cookie writes belong
9
+ in a Server Action or Route Handler before response headers commit. Do not read
10
+ the current request at module initialization or stash request data in a
11
+ process-global variable.
12
+
13
+ The following complete example stores a non-sensitive display preference. It is
14
+ not an authentication or session implementation.
15
+
16
+ ### `app/preferences/actions.ts`
17
+
18
+ ```ts
19
+ 'use server';
20
+
21
+ import { cookies } from 'next/headers';
22
+ import { revalidatePath } from 'next/cache';
23
+
24
+ export async function saveTheme(form: FormData) {
25
+ const theme = form.get('theme');
26
+ if (theme !== 'light' && theme !== 'dark') throw new Error('Invalid theme');
27
+ const store = await cookies();
28
+ store.set('theme', theme, { httpOnly: true, sameSite: 'lax', path: '/' });
29
+ revalidatePath('/preferences');
30
+ }
31
+ ```
32
+
33
+ ### `app/preferences/page.tsx`
34
+
35
+ ```tsx
36
+ import { cookies } from 'next/headers';
37
+ import { saveTheme } from './actions';
38
+
39
+ export default async function Preferences() {
40
+ const store = await cookies();
41
+ const theme = store.get('theme')?.value === 'dark' ? 'dark' : 'light';
42
+ return <main>
43
+ <h1>Preferences</h1>
44
+ <p>Current theme: {theme}</p>
45
+ <form action={saveTheme}>
46
+ <select name="theme" defaultValue={theme} aria-label="Theme">
47
+ <option value="light">Light</option>
48
+ <option value="dark">Dark</option>
49
+ </select>
50
+ <button type="submit">Save</button>
51
+ </form>
52
+ </main>;
53
+ }
54
+ ```
55
+
56
+ Server Actions accept untrusted input even when invoked through a form you
57
+ rendered. Check authentication, authorization and input on every protected
58
+ mutation. For real session cookies, use the application's session library and
59
+ production HTTPS policy, including appropriate secure cookie settings.
60
+
61
+ ## Fetch deliberately
62
+
63
+ Choose freshness explicitly. Request-specific or sensitive reads normally need
64
+ `fetch(url, { cache: 'no-store' })`. Shared public data can opt into
65
+ `cache: 'force-cache'` and `next: { revalidate: seconds, tags: [...] }`. The
66
+ `next` extension belongs to PRNext's server fetch integration, not browser fetch.
67
+ Check `response.ok` and validate the returned data before using it.
68
+
69
+ Under the default route configuration, an ordinary fetch or `next.tags` alone
70
+ does not request persistent caching. Route configuration can change defaults;
71
+ inspect it before diagnosing a cache. Avoid conflicting `cache` and `revalidate`
72
+ options. A successful fetch cache hit is different from cached HTML, a browser
73
+ router cache hit or a compiler cache hit.
74
+
75
+ `unstable_cache` caches JSON-serializable function results. A runnable public-data
76
+ example:
77
+
78
+ ### `lib/catalog.ts`
79
+
80
+ ```ts
81
+ import { unstable_cache } from 'next/cache';
82
+
83
+ export const readCatalog = unstable_cache(
84
+ async () => [{ id: 'demo', name: 'Demo product' }],
85
+ ['public-catalog-v1'],
86
+ { tags: ['catalog'], revalidate: 60 },
87
+ );
88
+ ```
89
+
90
+ The static array stands in for a real server-side data read. Do not perform
91
+ mutations in the cached callback. Read cookies/headers outside `unstable_cache`
92
+ and pass required values as arguments. Include tenant/user identity in any
93
+ appropriate cache key and authorize before access; never treat a shared cache as
94
+ a session store. Avoid caching private data unless its isolation is deliberate
95
+ and tested. Changes in closed-over values need explicit key parts or arguments.
96
+
97
+ ## Invalidate in the mutation
98
+
99
+ `revalidatePath('/preferences')` invalidates a concrete path. A dynamic pattern
100
+ requires its kind, such as `revalidatePath('/products/[slug]', 'page')`.
101
+ `revalidateTag('catalog', 'max')` marks tagged data stale for revalidation;
102
+ `updateTag('catalog')` expires it for read-your-own-writes in a Server Action.
103
+ `updateTag` is action-only; path/tag revalidation also supports Route Handlers.
104
+ Perform the underlying successful write before invalidating its cache.
105
+
106
+ Invalidation APIs are rejected during rendering, inside a cached callback and
107
+ after response headers have been sent. They do not delete unrelated build caches
108
+ or restart the server. Custom cache handlers can delegate these semantics; do
109
+ not infer their hit rate from PRNext's native-cache diagnostics.
110
+
111
+ The installed implementation also has `cacheComponents`, `'use cache'`,
112
+ `cacheLife` and `cacheTag` support. These are additional cache contracts; do not
113
+ enable them to silence a request-context error or assume their complete upstream
114
+ behavior. Keep a working `unstable_cache` or fetch solution unless the project
115
+ needs a different model.
116
+
117
+ ## Navigation stages
118
+
119
+ Enable `cacheComponents: true` and `partialPrefetching: true` to separate runtime
120
+ App Shell work from per-link prefetch work and actual navigation work:
121
+
122
+ ```jsx
123
+ import { Suspense } from 'react';
124
+ import { navigation, prefetch } from 'next/cache';
125
+
126
+ async function Recommendations() {
127
+ await prefetch();
128
+ return <div>{await getCachedRecommendations()}</div>;
129
+ }
130
+
131
+ async function ExpensiveReport() {
132
+ await navigation();
133
+ return <div>{await getCachedReport()}</div>;
134
+ }
135
+
136
+ export default function Page() {
137
+ return <>
138
+ <Suspense fallback={<p>Loading recommendations</p>}><Recommendations /></Suspense>
139
+ <Suspense fallback={<p>Loading report</p>}><ExpensiveReport /></Suspense>
140
+ </>;
141
+ }
142
+ ```
143
+
144
+ Define and authorize the application-specific data functions above. Call gates
145
+ before invoking a cached function, never inside its cached scope. Both gates
146
+ require Cache Components. Static prerenders continue through them, so static
147
+ output can already contain the result. Without partial prefetching, `prefetch()`
148
+ has no effect and an explicit `Link prefetch={true}` requests the full route.
149
+
150
+ A default Link requests the shell. `prefetch={true}` permits per-link work;
151
+ content below `navigation()` waits for the navigation. Runtime previews are
152
+ private, size-bounded and tied to the request's authorization identity. They do
153
+ not authorize a subsequent navigation.
154
+ Uncached runtime fetches and `connection()` remain deferred during speculative
155
+ rendering. Put reusable I/O in a cached function to make it eligible for an
156
+ earlier stage; a gate does not itself cache data.
157
+
158
+ Pages and layouts may export `ensureStatic = 'shell'`, `'prefetch'` or
159
+ `'navigation'`. Earlier stages are included in stricter levels. A descendant
160
+ cannot weaken a parent; `false` conflicts with a static requirement elsewhere
161
+ in the route. `'auto'` adds no requirement. Client Component exports are rejected.
162
+ `'shell'` and `'prefetch'` request static output for those stages without adding
163
+ a separate full-route build check. `'navigation'` validates complete static
164
+ server output during build and development, including request APIs beneath
165
+ Suspense, metadata and complete dynamic parameter sets. It requires nonempty,
166
+ complete `generateStaticParams()` results for dynamic routes. All levels require
167
+ Cache Components; shell/prefetch levels need partial prefetching to select stages.
168
+
169
+ ## Shared Redis Cache Components
170
+
171
+ The optional first-party Redis handler shares `'use cache'` results across Node
172
+ processes. Install `@redis/client@^6.3.0` only in applications using it; a normal PRNext
173
+ installation and generated project do not require Redis.
174
+
175
+ ### `redis-handler.mjs`
176
+
177
+ ```js
178
+ import { createRedisCache } from '@thomas.f/prnext/cache/redis';
179
+
180
+ export default createRedisCache({
181
+ namespace: 'my-app-production-v1',
182
+ urlEnv: 'REDIS_URL',
183
+ });
184
+ ```
185
+
186
+ ### `prnext.config.mjs`
187
+
188
+ ```js
189
+ export default {
190
+ cacheComponents: true,
191
+ cacheHandlers: { remote: './redis-handler.mjs' },
192
+ };
193
+ ```
194
+
195
+ ### `app/catalog/read.ts`
196
+
197
+ ```ts
198
+ import { cacheLife, cacheTag } from 'next/cache';
199
+
200
+ export async function readCatalog() {
201
+ 'use cache: remote';
202
+ cacheLife('hours');
203
+ cacheTag('catalog');
204
+ return [{ id: 'demo', name: 'Demo product' }];
205
+ }
206
+ ```
207
+
208
+ Set the Redis URL in the server's runtime environment. It is not embedded in the
209
+ handler bundle. Replicas sharing results need the same namespace and compatible
210
+ build output. Use separate namespaces for unrelated apps, environments and
211
+ incompatible deployments. This factory handles Cache Components. The separate
212
+ incremental factory below handles native HTML/ISR, fetch and `unstable_cache`.
213
+
214
+ Cache fills use a bounded cross-process lease. Atomic tag generations prevent a
215
+ fill invalidated while running, or an expired lease owner, from publishing old
216
+ data. Soft tag invalidation retains stale-while-revalidate behavior; immediate
217
+ expiration prevents another replica from reading the old entry. Losing the tag
218
+ registry invalidates existing entries rather than resurrecting them.
219
+
220
+ Values are limited to 2 MiB, metadata to 64 KiB and each entry to 256 tags.
221
+ The tag registry is bounded; excess cardinality conservatively invalidates the
222
+ namespace. Default retention is at most seven days. Defaults use a 30-second
223
+ lease, a 2.5-second fill wait and one-second command timeout. Reads can fall back
224
+ to fresh computation on cache failure; invalidation failures remain errors.
225
+ Cached callbacks must be safe to execute more than once after timeout or outage:
226
+ leases reduce duplicate work, not provide exactly-once business operations.
227
+
228
+ An already connected node-redis client can be supplied as `client`; its lifecycle
229
+ remains the application's responsibility. Otherwise the handler connects lazily
230
+ and owns its client. Use Redis appropriate for the application's availability,
231
+ access controls and data sensitivity; a shared cache is not an authorization
232
+ boundary or a durable database.
233
+
234
+ ## Shared Redis pages and request data
235
+
236
+ Configure `cacheHandler` with the constructor returned by the incremental factory:
237
+
238
+ ```js
239
+ // incremental-redis.mjs
240
+ import { createRedisIncrementalCache } from '@thomas.f/prnext/cache/redis';
241
+ export default createRedisIncrementalCache({ namespace: 'my-app-production' });
242
+ ```
243
+
244
+ ```js
245
+ // prnext.config.mjs
246
+ export default {
247
+ cacheHandler: './incremental-redis.mjs',
248
+ cacheMaxMemorySize: 0,
249
+ };
250
+ ```
251
+
252
+ Set `REDIS_URL` at runtime and install the optional `@redis/client@^6.3.0` client.
253
+ The same configuration works for App and Pages HTML/ISR, static Route Handlers,
254
+ cacheable `fetch` and `unstable_cache`. It does not cache private dynamic pages.
255
+ Replicas must use compatible builds and share an application-specific namespace.
256
+ Cache Components still select their own `cacheHandlers`; both factories may use
257
+ the same namespace to share tag invalidations.
258
+
259
+ The runtime acquires a shared generation lease before rendering, checks for a
260
+ winner's result after waiting and carries that lease through publication. An
261
+ invalidation or expired lease rejects late writes atomically. Runtime reads and
262
+ publication remain bounded; unavailable Redis can prevent a page regeneration
263
+ from succeeding. A shared cache is not a durable database or an exactly-once
264
+ execution mechanism. Keep producers safe to retry.
265
+
266
+ Incremental entries preserve Buffer and Map values, have a 16 MiB serialized
267
+ limit and use V8-major-specific keys. The default generation lease is 60 seconds;
268
+ `leaseMs` can be configured up to 120 seconds. Configure it for the expected
269
+ generation duration. The default maximum storage age remains seven days.
270
+ New cacheHandler builds include a native generation-lease capability check;
271
+ use the matching PRNext native binary and rebuild existing output.
272
+
273
+ ## Background work and durable data
274
+
275
+ `after()` from `next/server` is supported in Node Server Components (including
276
+ layouts and metadata), Route Handlers and Server Actions. It runs tracked
277
+ callbacks after the response completes or disconnects; static renders execute
278
+ them during the build. Edge lifecycles remain unsupported. In a Server Component,
279
+ read request data before registering the callback and capture the needed values:
280
+ `headers()`, `cookies()` and `draftMode()` inside that callback are rejected.
281
+ Route Handler and Action callbacks can read their retained request context.
282
+ Cookie writes and path/tag invalidation are rejected after the response;
283
+ invalidate before returning from the mutation.
284
+
285
+ Tracked callbacks are bounded by the route's `maxDuration` and shutdown
286
+ deadlines; forced termination can interrupt them. Use an external durable queue
287
+ for work that must survive a restart. Module globals, local runtime caches and
288
+ temporary files are not substitutes for a database or durable job store.