@thomas.f/prnext 0.1.19-alpha → 0.1.20-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/build/app.mjs +46 -78
- package/build/build-lock.mjs +2 -71
- package/build/build-retention.mjs +119 -0
- package/build/bundle-runtime.mjs +9 -8
- package/build/cache-command.mjs +31 -0
- package/build/check.mjs +41 -3
- package/build/compilation-cache.mjs +32 -12
- package/build/compilation-storage.mjs +190 -0
- package/build/compiler.mjs +3 -3
- package/build/config.mjs +23 -2
- package/build/create.mjs +1 -1
- package/build/css-modules.mjs +4 -0
- package/build/dev-action-graph.mjs +21 -0
- package/build/font-cache.mjs +9 -6
- package/build/font-files.mjs +1 -1
- package/build/index.mjs +30 -13
- package/build/native-ppr.mjs +32 -0
- package/build/og.mjs +121 -1
- package/build/parallel-plan.mjs +55 -0
- package/build/persistent-cache.mjs +29 -24
- package/build/production-check.mjs +34 -0
- package/build/react-compiler-report.mjs +31 -8
- package/build/react-compiler.mjs +101 -10
- package/build/server-config.mjs +35 -0
- package/build/source-analysis.mjs +54 -4
- package/build/static-adapter.mjs +7 -3
- package/build/transform.mjs +38 -6
- package/build/typescript-dev.mjs +3 -1
- package/cli.mjs +14 -5
- package/compat/app-layout-context.cjs +7 -3
- package/compat/flight-delta.cjs +47 -12
- package/compat/index.d.ts +24 -1
- package/compat/navigation-authorization.cjs +8 -1
- package/docs/ai/compatibility.md +110 -7
- package/docs/ai/deployment.md +141 -0
- package/docs/ai/quickstart.md +5 -0
- package/package.json +12 -9
- package/release/fontkit.mjs +31 -0
- package/release/verify.mjs +3 -0
- package/runtime/api.mjs +5 -2
- package/runtime/app-client.mjs +150 -26
- package/runtime/app-partial.mjs +2 -10
- package/runtime/app-prefetch.mjs +2 -2
- package/runtime/app-render.mjs +11 -7
- package/runtime/app-stream-channel.mjs +3 -2
- package/runtime/app-styles.mjs +22 -4
- package/runtime/compile-cache-retention.mjs +53 -0
- package/runtime/compile-cache.mjs +28 -1
- package/runtime/dev-client.mjs +6 -3
- package/runtime/dev-gateway.mjs +3 -2
- package/runtime/dev-route-selection.mjs +64 -0
- package/runtime/dev-runtime-pool.mjs +3 -2
- package/runtime/dev-runtime-spare.mjs +1 -1
- package/runtime/dev-session-retention.mjs +154 -0
- package/runtime/development.mjs +27 -8
- package/runtime/env.mjs +9 -0
- package/runtime/flight-delta.mjs +31 -5
- package/runtime/native-ppr.mjs +35 -0
- package/runtime/og/NOTICE.txt +1 -1
- package/runtime/og/index.node.mjs +76 -31216
- package/runtime/og/upstream.json +115 -0
- package/runtime/persistent/client.mjs +4 -2
- package/runtime/persistent/daemon.mjs +3 -11
- package/runtime/persistent/health.mjs +58 -0
- package/runtime/project-lock.mjs +71 -0
- package/runtime/request-transport.mjs +112 -32
- package/runtime/storage-path.mjs +36 -0
- package/runtime/trace.mjs +44 -4
- package/runtime/worker.mjs +9 -4
- package/vendor/fontkit/NOTICE.txt +5 -0
- package/vendor/fontkit/SOURCE-NOTICES.txt +209 -0
- package/vendor/fontkit/index.mjs +3 -0
- package/vendor/fontkit/licenses/@swc__helpers/LICENSE +201 -0
- package/vendor/fontkit/licenses/base64-js/LICENSE +21 -0
- package/vendor/fontkit/licenses/brotli/readme.md +61 -0
- package/vendor/fontkit/licenses/clone/LICENSE +18 -0
- package/vendor/fontkit/licenses/dfa/README.md +83 -0
- package/vendor/fontkit/licenses/fast-deep-equal/LICENSE +21 -0
- package/vendor/fontkit/licenses/fontkit/README.md +266 -0
- package/vendor/fontkit/licenses/restructure/LICENSE +21 -0
- package/vendor/fontkit/licenses/tiny-inflate/LICENSE +21 -0
- package/vendor/fontkit/licenses/tslib/LICENSE.txt +12 -0
- package/vendor/fontkit/licenses/unicode-properties/LICENSE +8 -0
- package/vendor/fontkit/licenses/unicode-trie/LICENSE +8 -0
- package/vendor/fontkit/upstream.json +215 -0
package/compat/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface InstantSample {
|
|
|
14
14
|
export type InstantConfig = boolean | { level?: 'warning' | 'experimental-error'; unstable_samples?: InstantSample[]; unstable_disableValidation?: true; unstable_disableDevValidation?: true; unstable_disableBuildValidation?: true };
|
|
15
15
|
|
|
16
16
|
export interface ReactCompilerOptions {
|
|
17
|
+
/** Babel by default. The native Oxc backend is experimental and opt-in. */
|
|
18
|
+
backend?: 'babel' | 'oxc';
|
|
17
19
|
compilationMode?: 'infer' | 'annotation';
|
|
18
20
|
panicThreshold?: 'none' | 'critical_errors' | 'all_errors';
|
|
19
21
|
/** PRNext requires matching React 19 peers. */
|
|
@@ -121,6 +123,8 @@ export interface Rewrite extends CustomRoute { destination: string }
|
|
|
121
123
|
export type ConfigRedirect = CustomRoute & Redirect;
|
|
122
124
|
/** Implemented project options. Unsupported behavior-changing options fail the build. */
|
|
123
125
|
export interface NextConfig {
|
|
126
|
+
/** Public origin and transport peers permitted to set X-Forwarded-Host/Proto. */
|
|
127
|
+
server?: {publicOrigin?: string; trustedProxies?: readonly string[]};
|
|
124
128
|
/** PRNext only. CLI > environment > build configuration > profile.
|
|
125
129
|
* 0 disables the corresponding retirement; keepWarm retains one lane per used host.
|
|
126
130
|
*/
|
|
@@ -131,6 +135,10 @@ export interface NextConfig {
|
|
|
131
135
|
workerPreload?: false | { routes: string[]; maxRssMb?: number; timeoutMs?: number };
|
|
132
136
|
/** Keep bounded previous public hashed assets. false disables carry-over. Defaults: one hour, two generations, 64 MiB. */
|
|
133
137
|
deploymentRetention?: false | {maxAge?: number; maxGenerations?: number; maxBytes?: number};
|
|
138
|
+
/** Common disk budget for regenerable compiler caches, in bytes (default 256 MiB).
|
|
139
|
+
* Applied after builds; active dev sessions/workers can defer reclamation.
|
|
140
|
+
* Runtime data, invalidations, page and image caches are excluded. */
|
|
141
|
+
compilationCache?: {maxDiskSize?: number};
|
|
134
142
|
/** PRNext production prerender processes; maxMemoryMb bounds their combined observed memory. */
|
|
135
143
|
prerender?: {workers?: number; maxMemoryMb?: number};
|
|
136
144
|
/** Explicit App route patterns that must produce complete static HTML without browser JavaScript. */
|
|
@@ -167,7 +175,20 @@ export interface NextConfig {
|
|
|
167
175
|
implementation?: 'sass' | 'sass-embedded';
|
|
168
176
|
[option: string]: unknown;
|
|
169
177
|
};
|
|
170
|
-
experimental?: {
|
|
178
|
+
experimental?: {
|
|
179
|
+
nextScriptWorkers?: boolean;
|
|
180
|
+
/** App Router development only; compile literal client imports on first use. */
|
|
181
|
+
lazyDynamicImports?: boolean;
|
|
182
|
+
/** Start App Route Handlers before upload EOF; mutations must handle aborted uploads. */
|
|
183
|
+
streamingRequestBodies?: boolean;
|
|
184
|
+
/** Serve certified public PPR document shells before starting Node. Requires cacheComponents. */
|
|
185
|
+
nativePprShell?: boolean;
|
|
186
|
+
/** Bound independent production CSS builds after graph discovery. Disabled by default. */
|
|
187
|
+
parallelBuilds?: boolean | {maxWorkers?: number; maxMemoryMb?: number};
|
|
188
|
+
optimizePackageImports?: string[];
|
|
189
|
+
proxyClientMaxBodySize?: number | string;
|
|
190
|
+
serverActions?: true | {allowedOrigins?: string[]; bodySizeLimit?: number | string};
|
|
191
|
+
};
|
|
171
192
|
trailingSlash?: boolean;
|
|
172
193
|
skipTrailingSlashRedirect?: boolean;
|
|
173
194
|
skipMiddlewareUrlNormalize?: boolean;
|
|
@@ -216,6 +237,7 @@ export interface StaticBuildOptions {
|
|
|
216
237
|
}
|
|
217
238
|
export interface ReactCompilerReport {
|
|
218
239
|
version: 1;
|
|
240
|
+
backend?: 'babel' | 'oxc';
|
|
219
241
|
compilerVersion: string | null;
|
|
220
242
|
enabled: boolean;
|
|
221
243
|
buildId: string | null;
|
|
@@ -227,6 +249,7 @@ export interface ReactCompilerReport {
|
|
|
227
249
|
summary: {files: number; functions: number; optimized: number; skipped: number; errors: number; diagnostics: number; mixed: number; transforms: number; cacheReuses: number};
|
|
228
250
|
files: Array<{
|
|
229
251
|
file: string; transforms: number; cacheReuses: number; targets: string[]; notes: string[];
|
|
252
|
+
diagnostics?: Array<{kind: 'CompileError' | 'CompileDiagnostic'; status: 'error' | 'diagnostic'; reason: string | null; category: string | null; targets: Array<'browser' | 'node' | 'edge' | 'unspecified'>}>;
|
|
230
253
|
functions: Array<{
|
|
231
254
|
name: string | null;
|
|
232
255
|
start: {line: number; column: number} | null;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const { createHmac } = require('node:crypto');
|
|
3
|
+
function developmentNavigationSecret(actionKey) {
|
|
4
|
+
if (typeof actionKey !== 'string' || !actionKey) return undefined;
|
|
5
|
+
// The compiler retains this private key across generations of one dev
|
|
6
|
+
// session. Derive a separate purpose key so worker replacement cannot look
|
|
7
|
+
// like an account change, without publishing or reusing the encryption key.
|
|
8
|
+
return createHmac('sha256', actionKey).update('prnext:development:navigation-authorization:v1').digest('hex');
|
|
9
|
+
}
|
|
3
10
|
function navigationAuthorization(context, secret, cookieNames) {
|
|
4
11
|
if (!secret || context?.staticState) return undefined;
|
|
5
12
|
const names = cookieNames === undefined ? null : new Set(cookieNames);
|
|
6
13
|
const cookies = context.cookies.getAll().filter(cookie => !names || names.has(cookie.name)).map(({ name, value }) => [name, value]).sort((a, b) => a[0].localeCompare(b[0]));
|
|
7
14
|
return createHmac('sha256', secret).update(JSON.stringify([cookies, context.headers.get('authorization') || ''])).digest('hex');
|
|
8
15
|
}
|
|
9
|
-
module.exports = { navigationAuthorization };
|
|
16
|
+
module.exports = { navigationAuthorization, developmentNavigationSecret };
|
package/docs/ai/compatibility.md
CHANGED
|
@@ -18,6 +18,13 @@ transpilation, image/font tooling and caching deserve targeted build and browser
|
|
|
18
18
|
tests. Support for one configuration shape does not imply support for all
|
|
19
19
|
upstream plugin behavior.
|
|
20
20
|
|
|
21
|
+
When moving from 0.1.19-alpha, use the installed
|
|
22
|
+
[upgrade checklist](deployment.md#upgrading-from-0119-alpha): rebuild matching
|
|
23
|
+
artifacts, configure the public HTTPS origin and proxy trust, verify strict
|
|
24
|
+
account transitions and review saved persistent health routes. Storage cleanup
|
|
25
|
+
must preserve runtime data and invalidation history. Experimental performance
|
|
26
|
+
options remain independent and opt-in.
|
|
27
|
+
|
|
21
28
|
## Instrumentation and tracing
|
|
22
29
|
|
|
23
30
|
Use a single `instrumentation.ts`, `.js`, `.mjs` or `.cjs` at the project root
|
|
@@ -132,6 +139,66 @@ new source files and configuration changes restart the native watch generation.
|
|
|
132
139
|
adding its generated declaration to the config. Compiler support does not imply
|
|
133
140
|
that every application is ready for TypeScript 7's own language/config changes.
|
|
134
141
|
|
|
142
|
+
## Experimental performance paths
|
|
143
|
+
|
|
144
|
+
These options are independent and disabled by default. Enable them in the
|
|
145
|
+
application's existing configuration, then qualify its actual workflows:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
export default {
|
|
149
|
+
cacheComponents: true,
|
|
150
|
+
experimental: {
|
|
151
|
+
nativePprShell: true,
|
|
152
|
+
streamingRequestBodies: true,
|
|
153
|
+
parallelBuilds: {maxWorkers: 4, maxMemoryMb: 1024},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`nativePprShell` lets Rust send the exact certified build shell before starting
|
|
159
|
+
Node, then resume its React continuation. The initial implementation covers
|
|
160
|
+
concrete App PPR document GETs without query strings, cookies, authorization,
|
|
161
|
+
middleware, rewrites, base paths, router-state/Flight/prefetch headers or CSP
|
|
162
|
+
nonces. Generic parameter shells, Edge, external caches and unsupported requests
|
|
163
|
+
keep the ordinary Node path. Prefixes and artifacts have checked hashes and
|
|
164
|
+
bounded sizes; continuations use a private transport field, never an HTTP flag.
|
|
165
|
+
Responses remain private and no-store. The cache generation must still be zero
|
|
166
|
+
and the build artifact unexpired: any persisted invalidation disables this build
|
|
167
|
+
snapshot optimization, including across server restarts. It does not disable
|
|
168
|
+
PPR itself or invalidate an already committed response. Requests already using
|
|
169
|
+
a snapshot finish with that same artifact when an invalidation races them.
|
|
170
|
+
|
|
171
|
+
`streamingRequestBodies` makes `Request.body` available to Node App Route
|
|
172
|
+
Handlers before upload EOF. It negotiates its own protocol capability and sends
|
|
173
|
+
at most one 64 KiB frame per reader credit. Unsupported workers use the existing
|
|
174
|
+
buffered path. Server Actions, Pages APIs and middleware-buffered requests retain
|
|
175
|
+
their previous buffering behavior. Streaming handlers can begin application
|
|
176
|
+
effects before a truncated or excessive upload is rejected: validate the input
|
|
177
|
+
and complete authorization before committing a mutation. The total request limit
|
|
178
|
+
remains 32 MiB. Existing ingress byte reservations are preserved; this feature
|
|
179
|
+
does not promise a smaller V8 heap or higher memory admission concurrency. A
|
|
180
|
+
cancelled or early-returned upload closes its single-use lane without replaying
|
|
181
|
+
the mutation, while retaining the healthy Node host.
|
|
182
|
+
|
|
183
|
+
`parallelBuilds` freezes App stylesheet plans after dependency discovery,
|
|
184
|
+
deduplicates outputs and emits independent jobs with deterministic linking.
|
|
185
|
+
Graph discovery itself remains ordered. Concurrency is bounded by CPU count,
|
|
186
|
+
available memory and the configured worker/budget limits. `maxWorkers` accepts
|
|
187
|
+
1–8; `maxMemoryMb` accepts 256–8192 and is an admission estimate, not a hard RSS
|
|
188
|
+
limit for third-party code. Custom webpack/loaders, custom PostCSS pipelines and
|
|
189
|
+
Sass callbacks retain serial execution. A failed job stops new admission and
|
|
190
|
+
settles started jobs before the staging build is discarded; the last published
|
|
191
|
+
build is preserved. An enabled build records its actual stylesheet job count
|
|
192
|
+
and selected concurrency in `manifest.buildParallelism`.
|
|
193
|
+
|
|
194
|
+
Development refreshes can also target the routes that import a changed Server
|
|
195
|
+
Action or its transitive dependencies. Certification uses both dependency
|
|
196
|
+
graphs before and after a change. An unrelated tab still applies a deferred
|
|
197
|
+
generation before navigating to a changed route. Unknown or incomplete graphs,
|
|
198
|
+
binding/runtime/route changes, global configuration, rewrites/middleware,
|
|
199
|
+
internationalized or parallel/intercepting routing retain a global refresh.
|
|
200
|
+
Server generations still publish as a whole; pending mutations are never replayed.
|
|
201
|
+
|
|
135
202
|
## React Compiler
|
|
136
203
|
|
|
137
204
|
Enable the bundled official React Compiler in the project's existing config:
|
|
@@ -142,7 +209,7 @@ export default {
|
|
|
142
209
|
};
|
|
143
210
|
```
|
|
144
211
|
|
|
145
|
-
|
|
212
|
+
The default Babel backend needs no separate compiler installation. The default is disabled;
|
|
146
213
|
`reactCompiler: false` preserves the uncompiled component path. Enabled builds
|
|
147
214
|
optimize project-owned browser components in both Pages and App applications,
|
|
148
215
|
in development and production. App Client Components also use the transform in
|
|
@@ -153,17 +220,43 @@ Components, Server Action modules, PRNext internals, generated entrypoints and
|
|
|
153
220
|
npm packages are not compiled. Keep the matching React 19 peer versions required
|
|
154
221
|
by the installed framework.
|
|
155
222
|
|
|
156
|
-
|
|
157
|
-
in those extensions remain unsupported.
|
|
223
|
+
With the default Babel backend, ordinary `.mts`/`.cts` hook helpers can be
|
|
224
|
+
optimized; server/client directives in those extensions remain unsupported.
|
|
225
|
+
Decorated `.mts`/`.cts` utility modules
|
|
158
226
|
keep their native compilation path while importing React components can still
|
|
159
227
|
be optimized.
|
|
160
228
|
|
|
161
229
|
For gradual adoption, use `reactCompiler: { compilationMode: 'annotation' }`, then
|
|
162
230
|
put `"use memo";` at the beginning of each component or hook to optimize. In
|
|
163
231
|
either mode, `"use no memo";` excludes an individual function. Supported object
|
|
164
|
-
options are `compilationMode: 'infer' | 'annotation'`,
|
|
232
|
+
options are `backend: 'babel' | 'oxc'`, `compilationMode: 'infer' | 'annotation'`,
|
|
165
233
|
`panicThreshold: 'none' | 'critical_errors' | 'all_errors'` and `target: '19'`.
|
|
166
|
-
Defaults are `'infer'`, `'none'` and `'19'`; other fields are rejected.
|
|
234
|
+
Defaults are Babel, `'infer'`, `'none'` and `'19'`; other fields are rejected.
|
|
235
|
+
|
|
236
|
+
The experimental native Rust backend is an explicit alternative:
|
|
237
|
+
|
|
238
|
+
```sh
|
|
239
|
+
npm install --save-dev oxc-transform-react@0.157.0
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
export default {
|
|
244
|
+
reactCompiler: { backend: 'oxc' },
|
|
245
|
+
};
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Oxc is an optional peer, so ordinary Babel builds do not install its compiler,
|
|
249
|
+
WASM distribution or native binding. Run the installation above in the application
|
|
250
|
+
project on the build machine, with optional platform dependencies enabled. A
|
|
251
|
+
global CLI also resolves this project-local peer. This option
|
|
252
|
+
requires the native binding; an absent or incompatible compiler, missing binding
|
|
253
|
+
or compilation failure fails explicitly. PRNext never silently changes the backend. JSX remains available
|
|
254
|
+
to the existing graph compiler, while this backend removes TypeScript syntax.
|
|
255
|
+
App Client Component server rendering uses Oxc's SSR output; browser graphs use
|
|
256
|
+
its client output. The same project boundaries and Server Action exclusions
|
|
257
|
+
apply. Source maps, loader maps and the existing Fast Refresh pipeline remain
|
|
258
|
+
part of the compilation path. Unsupported options are rejected rather than
|
|
259
|
+
passed through to the native compiler.
|
|
167
260
|
|
|
168
261
|
After a successful build, inspect the compiler's actual results:
|
|
169
262
|
|
|
@@ -172,8 +265,8 @@ prnext compiler
|
|
|
172
265
|
prnext compiler --json
|
|
173
266
|
```
|
|
174
267
|
|
|
175
|
-
|
|
176
|
-
or skip status, and the compiler's reasons. These are transform decisions;
|
|
268
|
+
For Babel, the report names observed functions, their project-relative files,
|
|
269
|
+
optimization or skip status, and the compiler's reasons. These are transform decisions;
|
|
177
270
|
server-code removal and tree shaking can later omit a function from the browser
|
|
178
271
|
bundle. Cached transforms retain their
|
|
179
272
|
diagnostics and are identified as reused. Browser and server graphs do not
|
|
@@ -183,6 +276,12 @@ complete application coverage. Disabled compilation is reported explicitly.
|
|
|
183
276
|
The report describes the last successful build, stays outside public assets and
|
|
184
277
|
does not include source code. A failed build preserves the previous report.
|
|
185
278
|
|
|
279
|
+
The Oxc report identifies the backend and package version, records native
|
|
280
|
+
diagnostics at file scope, and states that function optimization totals are
|
|
281
|
+
unavailable: its API does not expose Babel's function-success events. This is
|
|
282
|
+
not a count of zero optimized functions. Cached native diagnostics are replayed;
|
|
283
|
+
cache identities include the backend, compiler version and client/SSR mode.
|
|
284
|
+
|
|
186
285
|
The compiler can skip code that cannot be safely optimized. Follow the Rules of
|
|
187
286
|
React and check actual behavior rather than assuming every component was
|
|
188
287
|
compiled. It reduces repeated component work; it does not guarantee faster
|
|
@@ -192,6 +291,10 @@ production build with the option enabled and disabled. Compilation adds build
|
|
|
192
291
|
work; PRNext caches pure transforms with their compiler version and settings.
|
|
193
292
|
|
|
194
293
|
See the [official React Compiler documentation](https://react.dev/learn/react-compiler).
|
|
294
|
+
The [Oxc React Compiler guide](https://oxc.rs/docs/guide/usage/transformer/react-compiler.html)
|
|
295
|
+
describes the experimental backend. Measure compilation and application behavior
|
|
296
|
+
with the selected backend before adopting it; selecting Rust alone is not a
|
|
297
|
+
measured speedup for a project.
|
|
195
298
|
|
|
196
299
|
Only explicit private Next.js import adapters are supported. Do not add arbitrary
|
|
197
300
|
`next/dist/...` imports or install another Next runtime to satisfy one: isolate
|
package/docs/ai/deployment.md
CHANGED
|
@@ -8,12 +8,90 @@ Build with the installed PRNext and run the resulting trusted output with its
|
|
|
8
8
|
matching runtime:
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
+
prnext check --production
|
|
11
12
|
prnext typecheck
|
|
12
13
|
prnext build
|
|
13
14
|
prnext output --json
|
|
14
15
|
prnext start --hostname 127.0.0.1 --port 3000
|
|
15
16
|
```
|
|
16
17
|
|
|
18
|
+
### Upgrading from 0.1.19-alpha
|
|
19
|
+
|
|
20
|
+
Install matching framework/native versions, then rebuild and restart the complete
|
|
21
|
+
application or standalone output. Keep Node and the React protocol peers aligned
|
|
22
|
+
with this package's metadata. Existing compiler entries are reused only when
|
|
23
|
+
their identities match; never delete `data.sqlite3` or invalidation history to
|
|
24
|
+
perform an upgrade. Read the storage section below before pruning compiler caches.
|
|
25
|
+
|
|
26
|
+
Persistent apps need a supervisor restart too. In a maintenance window for the
|
|
27
|
+
whole `PRNEXT_PM_HOME`, run `prnext pdown` with that same home before replacing
|
|
28
|
+
packages. It stops all managed apps and preserves saved settings and enabled
|
|
29
|
+
entries; `prestart` alone keeps the already loaded supervisor code. Update and
|
|
30
|
+
rebuild the installations that must return with 0.1.20, then launch `pstart` or
|
|
31
|
+
`prestart` through the new CLI. It also restores all enabled entries, with their
|
|
32
|
+
saved binary paths and health routes. Check `pstatus` and real health responses;
|
|
33
|
+
a global CLI update does not replace each app's package or binary. Keep independent
|
|
34
|
+
lifecycles in separate homes.
|
|
35
|
+
|
|
36
|
+
Set `server.publicOrigin` to the external HTTPS origin and `server.trustedProxies`
|
|
37
|
+
to the immediate proxy's IP/CIDR addresses. The configured origin wins; forwarded
|
|
38
|
+
host/protocol headers are trusted only from those peers. Rebuild after config
|
|
39
|
+
changes and check redirects, secure cookies and authentication callbacks over
|
|
40
|
+
the actual public URL. This does not establish a forwarded client-IP chain.
|
|
41
|
+
|
|
42
|
+
Run production preflight, then test account A → sign out → account B with the
|
|
43
|
+
actual `navigationCache.authCookies`, including shared layouts and browser
|
|
44
|
+
history. Changed or unknown strict identities require a fresh document; cached
|
|
45
|
+
PPR previews wait for fresh identity. Authorize protected reads and writes in
|
|
46
|
+
the application as before.
|
|
47
|
+
|
|
48
|
+
Use a dedicated complete HTTP 200 application health response for database/service
|
|
49
|
+
readiness. `/_prnext/health` checks native readiness only; redirects no longer
|
|
50
|
+
pass persistent health checks. Previously saved applications keep their health
|
|
51
|
+
path, so a redirecting `/` must be replaced in the saved persistent settings.
|
|
52
|
+
Observe the post-switch rollback window and exercise an explicit idempotent
|
|
53
|
+
retry; a lost response can follow a successful write, and mutations are never
|
|
54
|
+
automatically replayed. Keep schema changes compatible with the previous server.
|
|
55
|
+
|
|
56
|
+
### Production preflight
|
|
57
|
+
|
|
58
|
+
Run `prnext check --production --json` to collect deployment diagnostics without
|
|
59
|
+
building or rewriting the application. It evaluates the production config and
|
|
60
|
+
environment files, restores the calling process environment afterward, and checks
|
|
61
|
+
installed dependencies, route conventions, the public origin, proxy trust,
|
|
62
|
+
authentication retention settings and active experimental options. JSON diagnostics
|
|
63
|
+
include `code`, `path`, `severity` and an action where one is needed. Errors fail
|
|
64
|
+
the command; warnings require review and do not change its exit status.
|
|
65
|
+
|
|
66
|
+
If a build already exists, preflight also compares its public origin, proxy and
|
|
67
|
+
authentication retention settings with the current config and rejects a development artifact. Rebuild after
|
|
68
|
+
changing deployment settings: `prnext start` reads the selected artifact and does
|
|
69
|
+
not reevaluate the source configuration. Missing output is informational before
|
|
70
|
+
the first build; unreadable existing metadata requires review.
|
|
71
|
+
|
|
72
|
+
For authenticated App Router applications, explicitly list every session cookie
|
|
73
|
+
name, including HTTPS variants used by the authentication library:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
export default {
|
|
77
|
+
navigationCache: {
|
|
78
|
+
authCookies: ['session-cookie-name', '__Secure-session-cookie-name'],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Replace these example names with the application's actual cookies; PRNext does
|
|
84
|
+
not infer authentication from cookie names. This enables strict retained-page
|
|
85
|
+
state resets when the server's authorization identity changes. Test account A →
|
|
86
|
+
sign out → account B with both active and retained pages, and confirm that private
|
|
87
|
+
drafts disappear. Public applications can review the guidance or disable page
|
|
88
|
+
retention with `navigationCache: false`.
|
|
89
|
+
|
|
90
|
+
A successful preflight checks configuration, not external services or application
|
|
91
|
+
authorization. Qualify the final build over public HTTPS, including authentication,
|
|
92
|
+
health checks, failure recovery and rollback. Experimental warnings identify the
|
|
93
|
+
options that need workload-specific validation; they are not performance claims.
|
|
94
|
+
|
|
17
95
|
The default build directory is `.prnext`; `distDir` can change it. The private
|
|
18
96
|
project output selector is `.prnext-output.json`. Do not assume `.next` layout,
|
|
19
97
|
run `next start` on a PRNext build or hand-edit its manifests to bypass runtime
|
|
@@ -254,6 +332,69 @@ an explicit choice, not a reason to print credentials or copy `.env` into assets
|
|
|
254
332
|
|
|
255
333
|
## Observe before tuning
|
|
256
334
|
|
|
335
|
+
### Installation and disk storage
|
|
336
|
+
|
|
337
|
+
The default installation includes Babel React Compiler support. The Oxc React
|
|
338
|
+
Compiler backend is an optional peer installed explicitly; read the
|
|
339
|
+
[compatibility guide](compatibility.md) before selecting it. Keep optional native
|
|
340
|
+
platform dependencies enabled. The framework's internal Fontkit bundle includes
|
|
341
|
+
its provenance and dependency licenses under `vendor/fontkit`; applications do
|
|
342
|
+
not need a separate Fontkit dependency for `next/font`.
|
|
343
|
+
|
|
344
|
+
Production builds retain one OpenGraph renderer when needed. A bounded analysis
|
|
345
|
+
of the compiled server graph may omit it only when the application can be
|
|
346
|
+
certified not to use `ImageResponse`. Development, Edge, unknown external modules
|
|
347
|
+
and computed loading keep it. No configuration change is needed. Deploy the
|
|
348
|
+
standalone output alone when using `output: 'standalone'`; copying the complete
|
|
349
|
+
ordinary build beside it duplicates those artifacts.
|
|
350
|
+
|
|
351
|
+
Compiler caches share a **256 MiB** retention budget per project:
|
|
352
|
+
|
|
353
|
+
```js
|
|
354
|
+
export default {
|
|
355
|
+
compilationCache: {maxDiskSize: 128 * 1024 * 1024}, // bytes, 0–4 GiB
|
|
356
|
+
};
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
PRNext applies the policy after a build, including failures, and after a clean
|
|
360
|
+
development shutdown. It is a retention target, not a hard filesystem quota:
|
|
361
|
+
active development, pinned V8 workers, busy files or an incomplete bounded scan
|
|
362
|
+
can defer reclamation. Existing per-cache limits still apply. Eviction can make
|
|
363
|
+
the next build slower; evicted downloaded fonts need network access again.
|
|
364
|
+
Large JSON entries use bounded, checksummed compression; old raw records remain
|
|
365
|
+
readable, and corrupt/incompatible records are cache misses.
|
|
366
|
+
|
|
367
|
+
Inspect and preview changes without executing application configuration:
|
|
368
|
+
|
|
369
|
+
```sh
|
|
370
|
+
prnext cache --json
|
|
371
|
+
prnext cache --prune --dry-run --max-bytes 67108864 --json
|
|
372
|
+
prnext cache --prune --max-bytes 67108864
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
The CLI reads the policy saved by the last build. `--max-bytes` overrides only
|
|
376
|
+
that purge; `0` removes eligible compiler records. Statistics cover recognized
|
|
377
|
+
framework cache files, excluding unknown paths; `scanComplete: false` means a
|
|
378
|
+
lower bound and prevents purging from an incomplete snapshot. Stop development
|
|
379
|
+
before purging. Concurrent builds are protected by the publisher lock, active
|
|
380
|
+
managed workers pin their namespace, and failed deletions can be retried.
|
|
381
|
+
|
|
382
|
+
These commands never remove runtime data, `data.sqlite3`, page/image indexes,
|
|
383
|
+
invalidation generations, tags or fill leases. Never delete/reset the data
|
|
384
|
+
database to reclaim compiler storage: that can lose invalidation history. The
|
|
385
|
+
runtime instead performs small incremental SQLite reclamation steps and bounded
|
|
386
|
+
orphan-file maintenance on its own timer. Existing SQLite databases without
|
|
387
|
+
incremental auto-vacuum retain their existing format; no full VACUUM runs on a
|
|
388
|
+
request. Older page files without a verifiable reader lease are retained.
|
|
389
|
+
|
|
390
|
+
Builds and dev sessions also journal their owned temporary directories. After a
|
|
391
|
+
crash, later starts reclaim eligible entries only after a grace period and proof
|
|
392
|
+
that their local owner/workers have exited. Unknown directories, foreign-host or
|
|
393
|
+
unverifiable owners and uncommitted build backups are kept for recovery. Do not
|
|
394
|
+
remove a recovery backup until the selected build has been checked.
|
|
395
|
+
|
|
396
|
+
### Runtime observations
|
|
397
|
+
|
|
257
398
|
Production rendering, API and regeneration admission use bounded queues grouped
|
|
258
399
|
by resolved route pattern. Different parameter values share one pattern's
|
|
259
400
|
waiting budget. Waiting patterns take turns as capacity becomes available;
|
package/docs/ai/quickstart.md
CHANGED
|
@@ -13,6 +13,11 @@ react-server-dom-webpack 19.3.1**. Its Node engine range is
|
|
|
13
13
|
`^22.23.4 || ^24.22.1 || >=26.12.1`. Check the installed package manifest when
|
|
14
14
|
upgrading; these requirements may change between releases.
|
|
15
15
|
|
|
16
|
+
Optional peers are installed only for the features that use them:
|
|
17
|
+
`@redis/client` for [shared Redis caching](data.md), and `oxc-transform-react`
|
|
18
|
+
for the [experimental Oxc React Compiler](compatibility.md#react-compiler).
|
|
19
|
+
The default Babel compiler needs no additional compiler installation.
|
|
20
|
+
|
|
16
21
|
Published releases include a matching native component through optional
|
|
17
22
|
dependencies for Windows, macOS and glibc Linux, on x64 and ARM64. Keep optional
|
|
18
23
|
dependencies enabled. Consumers do not need Rust; building PRNext from its source
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thomas.f/prnext",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20-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",
|
|
@@ -146,7 +146,6 @@
|
|
|
146
146
|
"dotenv-expand": "11.0.7",
|
|
147
147
|
"esbuild": "^0.28.2",
|
|
148
148
|
"estree-walker": "2.0.2",
|
|
149
|
-
"fontkit": "2.0.4",
|
|
150
149
|
"get-tsconfig": "4.14.3",
|
|
151
150
|
"glob": "14.0.2",
|
|
152
151
|
"graceful-fs": "4.2.11",
|
|
@@ -173,20 +172,24 @@
|
|
|
173
172
|
"@redis/client": "^6.3.0",
|
|
174
173
|
"react": "19.3.1",
|
|
175
174
|
"react-dom": "19.3.1",
|
|
176
|
-
"react-server-dom-webpack": "19.3.1"
|
|
175
|
+
"react-server-dom-webpack": "19.3.1",
|
|
176
|
+
"oxc-transform-react": "0.157.0"
|
|
177
177
|
},
|
|
178
178
|
"peerDependenciesMeta": {
|
|
179
179
|
"@redis/client": {
|
|
180
180
|
"optional": true
|
|
181
|
+
},
|
|
182
|
+
"oxc-transform-react": {
|
|
183
|
+
"optional": true
|
|
181
184
|
}
|
|
182
185
|
},
|
|
183
186
|
"optionalDependencies": {
|
|
184
|
-
"prnext-darwin-arm64": "0.1.
|
|
185
|
-
"prnext-darwin-x64": "0.1.
|
|
186
|
-
"prnext-linux-arm64-gnu": "0.1.
|
|
187
|
-
"prnext-linux-x64-gnu": "0.1.
|
|
188
|
-
"@thomas.f/prnext-win32-x64-msvc": "0.1.
|
|
189
|
-
"@thomas.f/prnext-win32-arm64-msvc": "0.1.
|
|
187
|
+
"prnext-darwin-arm64": "0.1.20-alpha",
|
|
188
|
+
"prnext-darwin-x64": "0.1.20-alpha",
|
|
189
|
+
"prnext-linux-arm64-gnu": "0.1.20-alpha",
|
|
190
|
+
"prnext-linux-x64-gnu": "0.1.20-alpha",
|
|
191
|
+
"@thomas.f/prnext-win32-x64-msvc": "0.1.20-alpha",
|
|
192
|
+
"@thomas.f/prnext-win32-arm64-msvc": "0.1.20-alpha"
|
|
190
193
|
},
|
|
191
194
|
"engines": {
|
|
192
195
|
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {readFile, readdir} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const destination = fileURLToPath(new URL('../vendor/fontkit/', import.meta.url));
|
|
7
|
+
const digest = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
8
|
+
|
|
9
|
+
/** Validate the shipped inventory without loading fontkit or its build dependencies. */
|
|
10
|
+
export async function verifyVendoredFontkit(directory = destination) {
|
|
11
|
+
const provenance = JSON.parse(await readFile(path.join(directory, 'upstream.json'), 'utf8'));
|
|
12
|
+
if (provenance.schemaVersion !== 1 || provenance.name !== 'fontkit' || provenance.version !== '2.0.4'
|
|
13
|
+
|| provenance.tool?.name !== 'esbuild' || !provenance.files?.['index.mjs']
|
|
14
|
+
|| !provenance.files?.['NOTICE.txt'] || !provenance.files?.['SOURCE-NOTICES.txt']
|
|
15
|
+
|| !provenance.packages?.fontkit || !provenance.packages?.brotli) throw new Error('Incomplete fontkit provenance.');
|
|
16
|
+
const actual = [];
|
|
17
|
+
async function visit(directory, prefix = '') {
|
|
18
|
+
for (const entry of await readdir(directory, {withFileTypes: true})) {
|
|
19
|
+
const name = prefix + entry.name;
|
|
20
|
+
if (entry.isDirectory()) await visit(path.join(directory, entry.name), name + '/');
|
|
21
|
+
else if (!entry.isFile()) throw new Error('Vendored fontkit files must be regular files.');
|
|
22
|
+
else if (name !== 'upstream.json') actual.push(name);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
await visit(directory);
|
|
26
|
+
if (JSON.stringify(actual.sort()) !== JSON.stringify(Object.keys(provenance.files).sort())) throw new Error('Vendored fontkit inventory changed.');
|
|
27
|
+
for (const name of actual) {
|
|
28
|
+
if (digest(await readFile(path.join(directory, name))) !== provenance.files[name]) throw new Error(`Vendored fontkit differs from its reviewed build: ${name}`);
|
|
29
|
+
}
|
|
30
|
+
return {version: provenance.version, files: actual.length, bytes: (await readFile(path.join(directory, 'index.mjs'))).length};
|
|
31
|
+
}
|
package/release/verify.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { packageRoot, packageManifest, platforms, sourceCheckout } from '../native/resolve.mjs';
|
|
5
5
|
import { verifyAiDocumentation } from './ai-docs.mjs';
|
|
6
|
+
import { verifyVendoredFontkit } from './fontkit.mjs';
|
|
6
7
|
|
|
7
8
|
export async function verifyPackage() {
|
|
8
9
|
const pkg = packageManifest;
|
|
@@ -27,6 +28,8 @@ export async function verifyPackage() {
|
|
|
27
28
|
}
|
|
28
29
|
await exported(pkg.exports);
|
|
29
30
|
await verifyAiDocumentation(packageRoot, pkg);
|
|
31
|
+
await verifyVendoredFontkit();
|
|
32
|
+
if (pkg.dependencies.fontkit || pkg.dependencies['oxc-transform-react'] || pkg.peerDependenciesMeta?.['oxc-transform-react']?.optional !== true) throw new Error('Fontkit must be bundled and the Oxc React compiler must remain an optional peer.');
|
|
30
33
|
await access(path.join(packageRoot, 'runtime/profiles.json'));
|
|
31
34
|
for (const name of ['index.node.mjs', 'resvg.wasm', 'hb.wasm', 'Geist-Regular.ttf', 'LICENSE', 'LICENSE.fflate', 'LICENSE.Geist', 'LICENSE.harfbuzzjs', 'LICENSE.satori', 'NOTICE.txt']) await access(path.join(packageRoot, 'runtime/og', name));
|
|
32
35
|
if (pkg.dependencies['@vercel/og'] || (await readFile(path.join(packageRoot, 'runtime/og/index.node.mjs'), 'utf8')).includes('fflate@0.7.3')) throw new Error('OG renderer still depends on vulnerable upstream fflate. Run scripts/prepare-og.mjs.');
|
package/runtime/api.mjs
CHANGED
|
@@ -195,7 +195,7 @@ async function runApiInner({ modulePath, route = {}, manifest = {}, production =
|
|
|
195
195
|
// IncomingMessage here duplicates URL, header and cookie work and allocates
|
|
196
196
|
// an unused Duplex. Keep the existing Pages request path unchanged.
|
|
197
197
|
const { request, bytes } = appHandler
|
|
198
|
-
? { bytes: Buffer.isBuffer(body) ? body : Buffer.from(body || '', 'base64') }
|
|
198
|
+
? { bytes: body instanceof ReadableStream ? body : Buffer.isBuffer(body) ? body : Buffer.from(body || '', 'base64') }
|
|
199
199
|
: createRequest({ url, originalUrl: requestOriginal?.href, params, method, headers, body });
|
|
200
200
|
if (!appHandler) {
|
|
201
201
|
request.draftMode = cacheContext.draftMode;
|
|
@@ -226,7 +226,10 @@ async function runApiInner({ modulePath, route = {}, manifest = {}, production =
|
|
|
226
226
|
throw abortError(lifecycle.controller.signal.reason);
|
|
227
227
|
}
|
|
228
228
|
const init = { method, headers, ...(lifecycle ? { signal: lifecycle.controller.signal } : {}) };
|
|
229
|
-
if (!['GET', 'HEAD'].includes(method) && bytes.length)
|
|
229
|
+
if (!['GET', 'HEAD'].includes(method) && (bytes instanceof ReadableStream || bytes.length)) {
|
|
230
|
+
init.body = bytes;
|
|
231
|
+
if (bytes instanceof ReadableStream) init.duplex = 'half';
|
|
232
|
+
}
|
|
230
233
|
const timeout = lifecycle && setTimeout(() => lifecycle.controller.abort(apiTimeoutError('response headers', timeoutMs)), timeoutMs);
|
|
231
234
|
timeout?.unref();
|
|
232
235
|
const pendingResponse = runRequestContext({ url, method, headers, params, production, cacheComponents: manifest.config?.cacheComponents, cacheLife: manifest.config?.cacheLife, cacheHandlers: manifest.config?.cacheHandlers, cacheHandler: manifest.config?.cacheHandler, cacheMaxMemorySize: manifest.config?.cacheMaxMemorySize, distDir: path.dirname(path.dirname(modulePath)), signal: lifecycle?.controller.signal || signal, previewModeId: manifest.previewModeId, basePath: manifest.config?.basePath || '', routePattern: route.pattern, cacheConfig: route.cacheConfig, phase: 'route', cacheState: cacheContext.cacheState, staticState: cacheContext.staticState, mutableCookies: true }, async () => {
|