@thomas.f/prnext 0.1.17-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.
- package/README.md +26 -869
- package/build/actions.mjs +1 -1
- package/build/analyze.mjs +180 -24
- package/build/app-config.mjs +13 -0
- package/build/app-static.mjs +34 -12
- package/build/app-styles.mjs +79 -0
- package/build/app.mjs +81 -26
- package/build/bundle-runtime.mjs +22 -9
- package/build/cache-components.mjs +1 -1
- package/build/cache-handlers.mjs +1 -1
- package/build/compact-css.mjs +53 -0
- package/build/compilation-cache.mjs +269 -0
- package/build/compilation-store.mjs +142 -0
- package/build/compiler.mjs +31 -8
- package/build/config.mjs +30 -1
- package/build/create.mjs +239 -0
- package/build/css-modules.mjs +56 -12
- package/build/deployment-assets.mjs +89 -0
- package/build/deployment-config.mjs +18 -0
- package/build/dev-css-update.mjs +4 -2
- package/build/dev-dependencies.mjs +1 -1
- package/build/dev-discovery.mjs +3 -3
- package/build/dev.mjs +15 -8
- package/build/dynamic.mjs +1 -1
- package/build/edge.mjs +3 -3
- package/build/fonts.mjs +1 -1
- package/build/framework-imports.mjs +15 -5
- package/build/html-only-config.mjs +53 -0
- package/build/html-only-graph.mjs +67 -0
- package/build/index.mjs +59 -26
- package/build/instrumentation.mjs +30 -0
- package/build/metadata-routes.mjs +1 -1
- package/build/migration-audit.mjs +1 -1
- package/build/native-type-config.mjs +37 -0
- package/build/persistent-cache.mjs +18 -3
- package/build/precompress.mjs +1 -1
- package/build/prerender-cache.mjs +70 -0
- package/build/prerender-config.mjs +29 -0
- package/build/prerender-pages.mjs +19 -9
- package/build/prerender-pool.mjs +165 -0
- package/build/prerender-process-worker.mjs +58 -0
- package/build/prerender-worker.mjs +5 -5
- package/build/production-builder.mjs +58 -9
- package/build/react-compiler-report.mjs +225 -0
- package/build/react-compiler.mjs +139 -0
- package/build/runtime-contract.mjs +2 -2
- package/build/static-export.mjs +4 -4
- package/build/styles.mjs +6 -4
- package/build/transform.mjs +1 -1
- package/build/typed-routes.mjs +4 -0
- package/build/typescript-dev.mjs +35 -8
- package/build/typescript-native-worker.mjs +55 -0
- package/build/typescript-native.mjs +18 -0
- package/build/typescript-toolchain.mjs +19 -0
- package/build/typescript.mjs +6 -4
- package/build/webpack-graph.mjs +6 -3
- package/build/worker-memory.mjs +9 -0
- package/cli.mjs +60 -4
- package/compat/activity-cache.cjs +26 -0
- package/compat/after.cjs +6 -4
- package/compat/app-layout-context.cjs +52 -2
- package/compat/base64.cjs +10 -0
- package/compat/cache-handlers.cjs +12 -2
- package/compat/cache-wire.cjs +28 -0
- package/compat/cache.cjs +2 -1
- package/compat/cache.d.cts +4 -0
- package/compat/cookies.cjs +21 -5
- package/compat/data-cache.cjs +12 -8
- package/compat/flight-delta.cjs +150 -0
- package/compat/headers.cjs +4 -2
- package/compat/incremental-cache.cjs +20 -7
- package/compat/index.d.ts +68 -3
- package/compat/link.cjs +12 -9
- package/compat/navigation-authorization.cjs +9 -0
- package/compat/navigation-stages.cjs +19 -0
- package/compat/offline-store.cjs +11 -0
- package/compat/offline.cjs +5 -0
- package/compat/offline.d.cts +2 -0
- package/compat/redis-cache.cjs +201 -0
- package/compat/redis-cache.d.cts +37 -0
- package/compat/server-primitives.cjs +31 -11
- package/compat/server.d.cts +15 -2
- package/compat/static-generation.cjs +7 -0
- package/compat/use-cache.cjs +3 -0
- package/docs/ai/compatibility.md +171 -2
- package/docs/ai/data.md +165 -5
- package/docs/ai/deployment.md +158 -0
- package/docs/ai/quickstart.md +29 -4
- package/docs/ai/routing.md +82 -0
- package/docs/ai/troubleshooting.md +37 -2
- package/package.json +26 -7
- package/runtime/action-client.mjs +10 -4
- package/runtime/action-server.mjs +2 -0
- package/runtime/app-client.mjs +212 -65
- package/runtime/app-content.mjs +4 -3
- package/runtime/app-partial-client.mjs +3 -0
- package/runtime/app-partial.mjs +1 -1
- package/runtime/app-prefetch-constants.mjs +4 -0
- package/runtime/app-prefetch.mjs +2 -3
- package/runtime/app-render-key.mjs +5 -0
- package/runtime/app-render.mjs +88 -28
- package/runtime/app-routing.mjs +22 -9
- package/runtime/app-static.mjs +18 -8
- package/runtime/app-stream-html.mjs +10 -53
- package/runtime/app-style-selection.mjs +34 -0
- package/runtime/app-styles.mjs +154 -0
- package/runtime/cache-components-rsc.mjs +4 -1
- package/runtime/cache-request.mjs +2 -1
- package/runtime/deployment-client.mjs +19 -0
- package/runtime/dev-client.mjs +7 -2
- package/runtime/dev-compiler-broker.mjs +42 -0
- package/runtime/dev-indicator.mjs +8 -3
- package/runtime/dev-lazy-tool.mjs +16 -0
- package/runtime/development.mjs +29 -12
- package/runtime/document.mjs +1 -1
- package/runtime/edge.mjs +6 -5
- package/runtime/fetch-cache.mjs +10 -8
- package/runtime/flight-delta.mjs +50 -0
- package/runtime/flight-tap.mjs +28 -0
- package/runtime/html-only.mjs +27 -0
- package/runtime/http.mjs +2 -0
- package/runtime/incremental-cache.mjs +25 -4
- package/runtime/instrumentation.mjs +49 -0
- package/runtime/navigation-recovery.mjs +36 -0
- package/runtime/navigation.mjs +7 -0
- package/runtime/node-html-stream.mjs +47 -0
- package/runtime/pages-client.mjs +14 -7
- package/runtime/pages-navigation.mjs +48 -63
- package/runtime/prefetch-scheduler.mjs +110 -0
- package/runtime/profile.mjs +14 -0
- package/runtime/profiles.json +2 -2
- package/runtime/render.mjs +3 -0
- package/runtime/route-pattern.mjs +44 -0
- package/runtime/rsc-worker.mjs +34 -12
- package/runtime/trace.mjs +78 -0
- package/runtime/watch-project.mjs +19 -15
- package/runtime/worker.mjs +70 -26
package/docs/ai/data.md
CHANGED
|
@@ -114,13 +114,173 @@ enable them to silence a request-context error or assume their complete upstream
|
|
|
114
114
|
behavior. Keep a working `unstable_cache` or fetch solution unless the project
|
|
115
115
|
needs a different model.
|
|
116
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
|
+
|
|
117
273
|
## Background work and durable data
|
|
118
274
|
|
|
119
|
-
`after()` from `next/server` is supported in Node
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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.
|
|
124
284
|
|
|
125
285
|
Tracked callbacks are bounded by the route's `maxDuration` and shutdown
|
|
126
286
|
deadlines; forced termination can interrupt them. Use an external durable queue
|
package/docs/ai/deployment.md
CHANGED
|
@@ -20,6 +20,22 @@ run `next start` on a PRNext build or hand-edit its manifests to bypass runtime
|
|
|
20
20
|
compatibility checks. PRNext's native server handles HTTP while React rendering
|
|
21
21
|
uses Node workers. Published native components must match the framework version.
|
|
22
22
|
|
|
23
|
+
Each production artifact also has its own deployment identity, even when
|
|
24
|
+
`generateBuildId` returns a constant. App navigation, Pages data and JavaScript
|
|
25
|
+
Server Actions check this identity before decoding a response. A stale GET can
|
|
26
|
+
fall back to a document navigation. A stale action fails without executing or
|
|
27
|
+
automatically replaying the mutation; preserve the user's work and ask them to
|
|
28
|
+
reload before retrying it explicitly.
|
|
29
|
+
|
|
30
|
+
When rebuilding into the same output directory, PRNext can carry forward public
|
|
31
|
+
hashed assets from two previous artifacts for up to one hour, within a 64 MiB
|
|
32
|
+
budget. This does not retain old server code, HTML, data responses or sessions.
|
|
33
|
+
It does not coordinate independent servers or a CDN: deploy matching artifacts
|
|
34
|
+
and configure the proxy to preserve the deployment headers and cache variation.
|
|
35
|
+
Use `deploymentRetention: false` to disable carry-over, or configure
|
|
36
|
+
`deploymentRetention: { maxAge: 3600, maxGenerations: 2, maxBytes: 67108864 }`;
|
|
37
|
+
`maxAge` is in seconds. A new output directory has no previous assets to retain.
|
|
38
|
+
|
|
23
39
|
For an existing reverse proxy, bind the application to loopback and configure
|
|
24
40
|
the proxy according to the deployment environment. Set required production
|
|
25
41
|
environment variables and exercise a real health route before switching
|
|
@@ -35,6 +51,141 @@ relative files. Build for the target OS/architecture and retain compatible Node;
|
|
|
35
51
|
a standalone bundle is not a universal executable. Read the shipped
|
|
36
52
|
[build output contract](../../build/ADAPTERS.md) for artifact boundaries.
|
|
37
53
|
|
|
54
|
+
## HTML rendering paths
|
|
55
|
+
|
|
56
|
+
Buffered App HTML decodes UTF-8 chunks incrementally, avoiding a second aggregate
|
|
57
|
+
binary copy. The existing 16 MiB response limit still applies, including to large
|
|
58
|
+
chunks and multi-byte text.
|
|
59
|
+
|
|
60
|
+
The default remains React's Web stream renderer. To evaluate the experimental
|
|
61
|
+
Node pipeable renderer, set `PRNEXT_SSR_STREAM=node` in the server environment.
|
|
62
|
+
It uses bounded backpressure and cancellation for ordinary Node App rendering.
|
|
63
|
+
Partial/PPR continuations keep the Web API and their original resume state;
|
|
64
|
+
Edge rendering is unchanged. Unset the variable to restore the default.
|
|
65
|
+
This is a workload comparison option, not a promised performance multiplier:
|
|
66
|
+
small-page CPU and larger-page timings can move in different directions.
|
|
67
|
+
|
|
68
|
+
## Readiness and first-request latency
|
|
69
|
+
|
|
70
|
+
`prnext start --prewarm` initializes the framework renderers before accepting
|
|
71
|
+
HTTP requests. It warms one render host and its RSC isolate; additional hosts
|
|
72
|
+
remain lazy. It does not call application pages, API handlers or data loaders.
|
|
73
|
+
Normal instrumentation initialization still runs. Existing `workerPreload`
|
|
74
|
+
configuration, if present, separately imports the application modules selected
|
|
75
|
+
by that configuration. Prewarming failure prevents HTTP readiness.
|
|
76
|
+
|
|
77
|
+
For a supervisor, add `--readiness-file /private/run/instance-ready.json`.
|
|
78
|
+
Create its parent directory first, and use a separate path per application or
|
|
79
|
+
independent instance. The atomically replaced JSON contains `ready`, `pid`,
|
|
80
|
+
`address`, `generation`, and `render: 'prewarmed' | 'lazy'`. HTTP readiness alone
|
|
81
|
+
does not mean a lazy React renderer is loaded. Replacement and shutdown
|
|
82
|
+
coordinate through a private sidecar lock; normal shutdown removes only its
|
|
83
|
+
own generation. A forced termination can leave a stale marker, so the supervisor
|
|
84
|
+
must also verify the process identity and a real application health check.
|
|
85
|
+
|
|
86
|
+
Idle retirement saves memory at the cost of the next render's wake latency.
|
|
87
|
+
For an application that must keep its used hosts alive, explicitly pass
|
|
88
|
+
`--worker-idle-seconds 0`. Prewarming is a startup operation, not periodic
|
|
89
|
+
synthetic traffic, and does not silently disable retirement. Measure the first
|
|
90
|
+
dynamic page as well as startup time; prewarming moves work earlier rather
|
|
91
|
+
than eliminating all cold work.
|
|
92
|
+
|
|
93
|
+
## Profiles under resource limits
|
|
94
|
+
|
|
95
|
+
Balanced remains the default. On Linux, effective policies consider CPU quotas
|
|
96
|
+
and memory ceilings at visible cgroup-v2 ancestors, as well as available CPUs.
|
|
97
|
+
Speed uses a smaller nursery and admission/buffer budgets when limited to one
|
|
98
|
+
CPU or at most 1 GiB memory. Balanced also lowers its budgets below 512 MiB.
|
|
99
|
+
The native launcher passes its effective policy to the RSC isolate so both use
|
|
100
|
+
the same resource decision. Explicit `NODE_OPTIONS` heap flags keep their precedence.
|
|
101
|
+
|
|
102
|
+
Memory favors a smaller nursery and earlier idle retirement, without forcing
|
|
103
|
+
V8's optimize-for-size policy. It can still trade CPU and latency for RAM.
|
|
104
|
+
Classic preserves the historical policy. These settings are advisory budgets,
|
|
105
|
+
not total RSS caps; use an OS/container limit for hard isolation. Explicit
|
|
106
|
+
worker counts are not automatically increased to match the host CPU count.
|
|
107
|
+
Compare throughput, CPU per correct response, p95 and memory together before
|
|
108
|
+
choosing Speed or adding workers.
|
|
109
|
+
|
|
110
|
+
## Production worker memory and recovery
|
|
111
|
+
|
|
112
|
+
A production Node host can be drained when its RSS stays above an explicit soft
|
|
113
|
+
budget for three one-second samples:
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
export default { workerMemory: { maxRssMb: 512 } };
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This is opt-in (64–65536 MiB), separate from the build's prerender budget. It
|
|
120
|
+
observes process RSS, including rendering threads; it does not cap V8's heap or
|
|
121
|
+
force garbage collection. Existing responses and tracked work finish on the old
|
|
122
|
+
host while new work uses a replacement. At most two process generations per
|
|
123
|
+
host group can overlap, including child reaping; further replacement waits for
|
|
124
|
+
capacity. This is not an OS memory sandbox: a busy draining host can exceed the
|
|
125
|
+
soft target. Serial maintenance workers check their budget after requests.
|
|
126
|
+
Development keeps its existing worker behavior.
|
|
127
|
+
|
|
128
|
+
Repeated failed Node starts and unexpected host exits use a shared startup
|
|
129
|
+
cooldown, from 100 ms up to 30 seconds. A sustained healthy period of 60 seconds
|
|
130
|
+
resets the circuit. Queued callers keep their original deadlines; no failed
|
|
131
|
+
request, POST or Server Action is automatically replayed. Ordinary client
|
|
132
|
+
cancellations and planned memory/idle retirements do not count as crashes.
|
|
133
|
+
|
|
134
|
+
The private local data cache uses a versioned binary value transport when
|
|
135
|
+
negotiated by the native worker launcher. Existing JSON clients remain accepted.
|
|
136
|
+
Authentication, limits, leases, tag/path invalidation and cache policy are
|
|
137
|
+
unchanged. This internal transport is not a public application API.
|
|
138
|
+
|
|
139
|
+
## Production prerendering
|
|
140
|
+
|
|
141
|
+
App and Pages static paths can render in separate Node processes during a
|
|
142
|
+
production build. Path discovery runs once; results are published in a stable
|
|
143
|
+
order. Small batches and `workers: 1` use the local renderer. Defaults account
|
|
144
|
+
for available CPUs and memory. To set explicit ceilings:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
export default {
|
|
148
|
+
prerender: { workers: 4, maxMemoryMb: 2048 },
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`workers` accepts 1–8; `maxMemoryMb` accepts 256–16384 MiB and covers the sum of
|
|
153
|
+
observed child-process RSS, including their rendering threads. This is a sampled
|
|
154
|
+
admission budget, not an OS memory limit; it does not include the build
|
|
155
|
+
coordinator. A path error or exhausted budget fails the build, stops pending
|
|
156
|
+
work and closes the workers. Paths are not automatically retried: keep static
|
|
157
|
+
data loaders free of mutations and independent of execution order.
|
|
158
|
+
|
|
159
|
+
Build workers share a temporary, bounded cache for eligible App reads which the
|
|
160
|
+
application explicitly elects to cache. It is discarded after the build and
|
|
161
|
+
never uses the production cache. `no-store` requests and `Set-Cookie` responses
|
|
162
|
+
are not coalesced. Authentication does not opt a read into caching; cached reads
|
|
163
|
+
retain request-derived keys. Pages loaders retain their existing cache behavior.
|
|
164
|
+
|
|
165
|
+
## Selected pages without browser JavaScript
|
|
166
|
+
|
|
167
|
+
Use the PRNext-specific `htmlOnly` option for fully static, noninteractive App
|
|
168
|
+
pages. Select registered route patterns exactly, or use a final `/**` subtree:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
export default {
|
|
172
|
+
htmlOnly: ['/about', '/articles/[slug]', '/manual/**'],
|
|
173
|
+
};
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Build validation requires Node App routes with complete static output. A
|
|
177
|
+
dynamic pattern needs `dynamicParams = false` and a nonempty
|
|
178
|
+
`generateStaticParams` result. Client Components, Server Actions, interactive
|
|
179
|
+
`next/link`, scripts, ISR, PPR, `cacheComponents`, Edge and advanced parallel or
|
|
180
|
+
intercepting routes are rejected for selected pages. Use ordinary `<a>` links.
|
|
181
|
+
Unknown patterns fail the production build rather than silently doing nothing.
|
|
182
|
+
|
|
183
|
+
HTML keeps CSS, fonts, metadata and resolved Suspense content. It has no
|
|
184
|
+
bootstrap scripts or Flight payload; entering it performs document navigation.
|
|
185
|
+
Unknown generated parameters return 404. The mode is enforced in production,
|
|
186
|
+
including static export and standalone output; development retains its normal
|
|
187
|
+
JavaScript tools. It is an explicit PRNext extension, not a Next.js config key.
|
|
188
|
+
|
|
38
189
|
## Static browser output
|
|
39
190
|
|
|
40
191
|
PRNext can serve a plain HTML/CSS/JavaScript directory:
|
|
@@ -103,6 +254,13 @@ an explicit choice, not a reason to print credentials or copy `.env` into assets
|
|
|
103
254
|
|
|
104
255
|
## Observe before tuning
|
|
105
256
|
|
|
257
|
+
Production rendering, API and regeneration admission use bounded queues grouped
|
|
258
|
+
by resolved route pattern. Different parameter values share one pattern's
|
|
259
|
+
waiting budget. Waiting patterns take turns as capacity becomes available;
|
|
260
|
+
a single pattern can still use all execution slots. Excess requests receive
|
|
261
|
+
503 with `Retry-After`; an expensive request already executing is not preempted.
|
|
262
|
+
This limits backlog monopolization, not all CPU use or application-level quotas.
|
|
263
|
+
|
|
106
264
|
Use `prnext start --inspect` and `prnext inspect --json` for sampled route/cache
|
|
107
265
|
and runtime observations. `prnext analyze --json` describes built browser
|
|
108
266
|
JavaScript; it is not an SSR latency measurement. A production profile can be
|
package/docs/ai/quickstart.md
CHANGED
|
@@ -19,11 +19,36 @@ dependencies enabled. Consumers do not need Rust; building PRNext from its sourc
|
|
|
19
19
|
repository is a different workflow. Do not install a platform package separately
|
|
20
20
|
or combine package versions to work around an installation failure.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Create a new application
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
```sh
|
|
25
|
+
npx @thomas.f/prnext create my-app
|
|
26
|
+
cd my-app
|
|
27
|
+
npm run dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`create` writes a minimal App Router application and runs `npm install`. The
|
|
31
|
+
default template uses TypeScript and includes the application components, CSS,
|
|
32
|
+
scripts, TypeScript configuration and an `AGENTS.md` pointer to the installed
|
|
33
|
+
PRNext documentation. It pins PRNext to the version running the command and the
|
|
34
|
+
matching React peer versions.
|
|
35
|
+
|
|
36
|
+
Use an empty directory or a new path. Quote paths containing spaces. Options:
|
|
37
|
+
|
|
38
|
+
- `--js` creates JavaScript components without TypeScript configuration or type dependencies.
|
|
39
|
+
- `--no-install` writes the files without installing dependencies; run `npm install` in the new application before starting it.
|
|
40
|
+
- `--json` returns a structured creation report for automation.
|
|
41
|
+
|
|
42
|
+
`npm i @thomas.f/prnext` only installs the package in the current project. It does
|
|
43
|
+
not create application files, scripts or configuration. For an existing
|
|
44
|
+
application, preserve its package manager and follow
|
|
45
|
+
[Compatibility](compatibility.md) for a Next.js migration.
|
|
46
|
+
|
|
47
|
+
## Set up an application manually
|
|
48
|
+
|
|
49
|
+
To set up a new application manually, start with the manifest below, then install
|
|
50
|
+
dependencies. For an existing application, merge only the necessary scripts and
|
|
51
|
+
preserve its other fields.
|
|
27
52
|
|
|
28
53
|
### `package.json`
|
|
29
54
|
|
package/docs/ai/routing.md
CHANGED
|
@@ -20,6 +20,14 @@ segments and `pages/api/`. Existing `getServerSideProps`, `getStaticProps` and
|
|
|
20
20
|
belong in an App Router page. Check a specific advanced routing feature against
|
|
21
21
|
the installed implementation rather than assuming complete Next.js parity.
|
|
22
22
|
|
|
23
|
+
App styles follow the page and its layout scopes instead of loading every
|
|
24
|
+
page's stylesheet globally. Shared layout sheets are reused, and navigation
|
|
25
|
+
loads the next page's sheets before committing its content. Keep shared global
|
|
26
|
+
rules in the common layout and use CSS Modules for component-specific selectors.
|
|
27
|
+
Do not rely on a sibling page's CSS being present. Loading/error/default and
|
|
28
|
+
parallel-route branches can share a route's style plan; test their cascade as
|
|
29
|
+
well as ordinary forward and back navigation in a production build.
|
|
30
|
+
|
|
23
31
|
## Dynamic segments
|
|
24
32
|
|
|
25
33
|
`app/products/[slug]/page.tsx` matches `/products/example`. App `params` and
|
|
@@ -94,6 +102,47 @@ Client Component. Pages `useRouter` comes from `next/router`; they are different
|
|
|
94
102
|
contracts. Server code can call `redirect()` or `notFound()` from
|
|
95
103
|
`next/navigation`; let their control-flow exceptions reach the framework.
|
|
96
104
|
|
|
105
|
+
Production link prefetching prioritizes keyboard focus, hover and touch over
|
|
106
|
+
links entering the viewport. Both routers bound speculative work; an actual
|
|
107
|
+
navigation takes priority. Data saver, offline/2G connections and hidden tabs
|
|
108
|
+
disable speculation, while slower connections restrict it to intent. Browsers
|
|
109
|
+
without connection information retain bounded prefetching. `prefetch={false}`
|
|
110
|
+
disables Link speculation, including hover and focus. None of these policies
|
|
111
|
+
blocks a real click or changes authorization and private-data checks. App Link
|
|
112
|
+
automatic prefetching retains its existing partial-rendering requirement.
|
|
113
|
+
|
|
114
|
+
### Connection loss during App navigation
|
|
115
|
+
|
|
116
|
+
App navigation retries transport failures of its GET request within a 30-second
|
|
117
|
+
budget and at most three attempts. While disconnected, the current page and its
|
|
118
|
+
mounted state remain visible. A newer navigation cancels the older attempt.
|
|
119
|
+
Reconnection issues a fresh request with the current cookies; it does not serve
|
|
120
|
+
a saved private response. Application errors and deployment mismatches retain
|
|
121
|
+
their existing handling. Speculative prefetches are not queued offline.
|
|
122
|
+
|
|
123
|
+
For an optional application-owned connection indicator:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
'use client';
|
|
127
|
+
import { useOffline } from '@thomas.f/prnext/offline';
|
|
128
|
+
|
|
129
|
+
export default function ConnectionStatus() {
|
|
130
|
+
const offline = useOffline();
|
|
131
|
+
return offline ? <p role="status">Waiting for a connection…</p> : null;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The hook reports browser offline status and pending navigation recovery, not a
|
|
136
|
+
guarantee that a server is reachable. Exhausted network recovery preserves the
|
|
137
|
+
page and dispatches `prnext:navigation-error` on `window`, with `detail.message`
|
|
138
|
+
for an application-owned retry notice. This recovery applies to App navigation;
|
|
139
|
+
it does not add offline Pages routing, a service worker or persistent storage.
|
|
140
|
+
|
|
141
|
+
Server Actions and other POSTs are **never automatically replayed**. If a mutation
|
|
142
|
+
loses its response, it may already have run: keep the user's input and verify its
|
|
143
|
+
result before offering an explicit retry. A full reload still loses ordinary
|
|
144
|
+
in-memory state; persist drafts deliberately if they must survive reloads.
|
|
145
|
+
|
|
97
146
|
With `typedRoutes: true`, `prnext typegen` writes a discovered route registry.
|
|
98
147
|
For the dynamic page above, this materializes an encoded URL:
|
|
99
148
|
|
|
@@ -112,6 +161,39 @@ parameters, separators and dot segments. Add query strings through
|
|
|
112
161
|
Generated route checking is PRNext's registry, not the Next.js language-service
|
|
113
162
|
plugin.
|
|
114
163
|
|
|
164
|
+
## Retained App pages
|
|
165
|
+
|
|
166
|
+
App layout branches retain inactive UI through React Activity. Effects stop
|
|
167
|
+
while hidden, and returning to an authorized destination restores its mounted
|
|
168
|
+
form state. The shared default budget is four inactive branches for two minutes,
|
|
169
|
+
not four at every nesting level. It is an entry/time limit, not a byte-level RAM
|
|
170
|
+
guarantee. It applies within a mounted layout; a new root layout loads a document.
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
export default {
|
|
174
|
+
navigationCache: { maxEntries: 3, ttlMs: 60000, authCookies: ['session'] },
|
|
175
|
+
};
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Use `navigationCache: false` to disable retention. Server Actions, `router.refresh()`
|
|
179
|
+
and observed cookie/authorization changes discard inactive branches while keeping
|
|
180
|
+
the current page's client state. Back/forward navigation requests fresh Flight
|
|
181
|
+
before revealing a retained destination. Cache hits do not bypass authorization.
|
|
182
|
+
After a custom authentication request outside Server Actions, call
|
|
183
|
+
`router.refresh()`. A browser integration may also dispatch
|
|
184
|
+
`window.dispatchEvent(new Event('prnext:invalidate-navigation'))` to refresh and
|
|
185
|
+
clear retained/prefetched state. This is in-memory UI retention, not offline
|
|
186
|
+
storage or mutation replay.
|
|
187
|
+
|
|
188
|
+
By default, cookie changes invalidate inactive branches while ordinary refresh
|
|
189
|
+
keeps the active component state. Set `authCookies` to the application's actual
|
|
190
|
+
session cookie names to also reset active branches when those credentials change.
|
|
191
|
+
Unrelated preference cookies then do not affect retention. Authorization headers
|
|
192
|
+
remain part of the identity. Identity is checked in each branch's authorized
|
|
193
|
+
request context, including restored parallel slots. In development, the private
|
|
194
|
+
identity key remains stable across Fast Refresh; build-time action-key rotation
|
|
195
|
+
does not discard the active editor state.
|
|
196
|
+
|
|
115
197
|
## Route Handlers
|
|
116
198
|
|
|
117
199
|
An App Route Handler exports HTTP methods and returns a Web `Response` or
|
|
@@ -59,6 +59,13 @@ config/environment, custom Document or incompatible module changes can reload
|
|
|
59
59
|
the document. Production prerendering and compression are not development HMR.
|
|
60
60
|
An unused route need not rebuild until requested.
|
|
61
61
|
|
|
62
|
+
Middleware and rewrites can select a cold destination after routing. PRNext
|
|
63
|
+
compiles that destination through its private development compiler and continues
|
|
64
|
+
the same request. It does not send an upload or action through middleware again.
|
|
65
|
+
Retained parallel branches are compiled together when their source is needed.
|
|
66
|
+
An error in an unrelated unopened route should not force that route into the
|
|
67
|
+
active graph. Safe mode remains available for full-graph comparisons.
|
|
68
|
+
|
|
62
69
|
For a controlled diagnosis, stop only the development process you own and try:
|
|
63
70
|
|
|
64
71
|
```sh
|
|
@@ -66,12 +73,26 @@ prnext dev --safe
|
|
|
66
73
|
```
|
|
67
74
|
|
|
68
75
|
Safe mode uses full builds and fresh workers. `PRNEXT_BUILD_CACHE=0` disables
|
|
69
|
-
PRNext's compiler/build caches when set in the current process environment; use
|
|
76
|
+
PRNext's persistent compiler/build caches when set in the current process environment; use
|
|
70
77
|
the syntax appropriate to the user's shell and restore the previous setting
|
|
71
|
-
after the comparison.
|
|
78
|
+
after the comparison. In-memory compiler/transform reuse is a separate layer.
|
|
79
|
+
Optional cache-storage failures should fall back to work,
|
|
72
80
|
while genuine loader errors remain build failures. Do not delete every cache or
|
|
73
81
|
lockfile as a first response, especially with another server running.
|
|
74
82
|
|
|
83
|
+
Production builds can reuse eligible compiled JavaScript graphs and CSS Module
|
|
84
|
+
outputs from the default CSS engine across processes through the private
|
|
85
|
+
`.prnext-cache` directory. The successful artifact's `compilation-cache-report.json` and the
|
|
86
|
+
programmatic build result's `compilationCache` report actual hits, misses and
|
|
87
|
+
bypasses. These are compilation diagnostics, not response-cache or latency
|
|
88
|
+
measurements. App graphs that discover actions or client boundaries, development
|
|
89
|
+
graphs, the explicit PostCSS engine and custom loader/PostCSS pipelines still
|
|
90
|
+
execute their compiler work.
|
|
91
|
+
HTML, Flight responses and private rendered data are not stored in this cache.
|
|
92
|
+
Changes to tracked dependencies, resolution candidates, options, environment
|
|
93
|
+
or framework identity invalidate eligible entries. Corrupt or unavailable cache
|
|
94
|
+
records fall back to compilation; `PRNEXT_BUILD_CACHE=0` disables their persistent reuse.
|
|
95
|
+
|
|
75
96
|
For production observations:
|
|
76
97
|
|
|
77
98
|
```sh
|
|
@@ -85,6 +106,20 @@ exclude the full streaming body, and missing/dropped samples are not zero.
|
|
|
85
106
|
`prnext analyze`, after building, rather than interpreting request time as
|
|
86
107
|
compiler performance.
|
|
87
108
|
|
|
109
|
+
Initial-JS analysis includes required hydration imports and SSR-used dynamic
|
|
110
|
+
chunks. Schema 2 reports per-prerendered-path samples and the maximum size for
|
|
111
|
+
each compression metric; a route's file list can be the union of its samples.
|
|
112
|
+
Request-dependent or incomplete graphs are lower bounds. Their size budgets
|
|
113
|
+
fail as `initial-unknown`, even if that lower bound is small. Do not present it
|
|
114
|
+
as a complete measurement or a size saving. Rebuild old schema-1 baselines before
|
|
115
|
+
comparing them with schema 2. An opaque webpack graph is also marked incomplete.
|
|
116
|
+
|
|
117
|
+
For memoization questions, run `prnext compiler --json` after a successful build
|
|
118
|
+
with `reactCompiler` enabled. Inspect the affected function and its skip reason,
|
|
119
|
+
the reported graph coverage and cached-transform counts. An absent function in
|
|
120
|
+
an observed-transforms report is not proof that it was optimized or rejected.
|
|
121
|
+
Keep application behavior and hydration checks alongside this diagnostic.
|
|
122
|
+
|
|
88
123
|
## Connect the existing development MCP
|
|
89
124
|
|
|
90
125
|
Start `prnext dev` in the application, then configure a compatible assistant to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thomas.f/prnext",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18-alpha",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Experimental Rust-powered runtime for Next.js apps, React and npm",
|
|
6
6
|
"homepage": "https://prnext.dev",
|
|
@@ -115,6 +115,14 @@
|
|
|
115
115
|
"./headers": "./compat/headers.cjs",
|
|
116
116
|
"./server": "./compat/server.cjs",
|
|
117
117
|
"./cache": "./compat/cache.cjs",
|
|
118
|
+
"./cache/redis": {
|
|
119
|
+
"types": "./compat/redis-cache.d.cts",
|
|
120
|
+
"default": "./compat/redis-cache.cjs"
|
|
121
|
+
},
|
|
122
|
+
"./offline": {
|
|
123
|
+
"types": "./compat/offline.d.cts",
|
|
124
|
+
"default": "./compat/offline.cjs"
|
|
125
|
+
},
|
|
118
126
|
"./constants": "./compat/constants.cjs"
|
|
119
127
|
},
|
|
120
128
|
"dependencies": {
|
|
@@ -124,13 +132,17 @@
|
|
|
124
132
|
"@babel/traverse": "^7.29.0",
|
|
125
133
|
"@babel/types": "^7.29.0",
|
|
126
134
|
"@edge-runtime/vm": "5.0.0",
|
|
135
|
+
"@jridgewell/trace-mapping": "0.3.31",
|
|
127
136
|
"@vercel/nft": "1.11.0",
|
|
137
|
+
"babel-plugin-react-compiler": "1.0.0",
|
|
128
138
|
"browserslist": "4.29.0",
|
|
129
139
|
"dotenv": "16.6.1",
|
|
130
140
|
"dotenv-expand": "11.0.7",
|
|
131
141
|
"esbuild": "^0.28.2",
|
|
132
142
|
"fontkit": "2.0.4",
|
|
143
|
+
"get-tsconfig": "4.14.3",
|
|
133
144
|
"html-to-image": "1.11.13",
|
|
145
|
+
"jsonc-parser": "3.3.1",
|
|
134
146
|
"lightningcss": "1.31.0",
|
|
135
147
|
"loader-runner": "4.3.2",
|
|
136
148
|
"oxc-parser": "0.155.0",
|
|
@@ -139,23 +151,30 @@
|
|
|
139
151
|
"postcss": "8.5.28",
|
|
140
152
|
"postcss-flexbugs-fixes": "5.0.2",
|
|
141
153
|
"postcss-preset-env": "11.5.3",
|
|
154
|
+
"postcss-selector-parser": "7.1.6",
|
|
142
155
|
"postcss-value-parser": "4.2.0",
|
|
143
156
|
"react-refresh": "0.19.0",
|
|
144
157
|
"schema-utils": "4.3.3",
|
|
145
158
|
"webpack": "5.111.1"
|
|
146
159
|
},
|
|
147
160
|
"peerDependencies": {
|
|
161
|
+
"@redis/client": "^6.3.0",
|
|
148
162
|
"react": "19.3.1",
|
|
149
163
|
"react-dom": "19.3.1",
|
|
150
164
|
"react-server-dom-webpack": "19.3.1"
|
|
151
165
|
},
|
|
166
|
+
"peerDependenciesMeta": {
|
|
167
|
+
"@redis/client": {
|
|
168
|
+
"optional": true
|
|
169
|
+
}
|
|
170
|
+
},
|
|
152
171
|
"optionalDependencies": {
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"prnext-
|
|
156
|
-
"prnext-
|
|
157
|
-
"prnext-
|
|
158
|
-
"prnext-
|
|
172
|
+
"prnext-darwin-arm64": "0.1.18-alpha",
|
|
173
|
+
"prnext-darwin-x64": "0.1.18-alpha",
|
|
174
|
+
"prnext-linux-arm64-gnu": "0.1.18-alpha",
|
|
175
|
+
"prnext-linux-x64-gnu": "0.1.18-alpha",
|
|
176
|
+
"@thomas.f/prnext-win32-x64-msvc": "0.1.18-alpha",
|
|
177
|
+
"@thomas.f/prnext-win32-arm64-msvc": "0.1.18-alpha"
|
|
159
178
|
},
|
|
160
179
|
"engines": {
|
|
161
180
|
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
|