@thomas.f/prnext 0.1.12-alpha → 0.1.15-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 +305 -65
- package/build/ADAPTERS.md +121 -0
- package/build/actions.mjs +4 -0
- package/build/app-config.mjs +2 -0
- package/build/app.mjs +23 -5
- package/build/build-lock.mjs +71 -0
- package/build/bundle-runtime.mjs +82 -0
- package/build/compiler.mjs +3 -1
- package/build/config.mjs +9 -2
- package/build/font-cache.mjs +50 -0
- package/build/font-files.mjs +5 -3
- package/build/fonts.mjs +1 -1
- package/build/index.mjs +12 -1
- package/build/inspect.mjs +20 -2
- package/build/inspection-summary.mjs +37 -0
- package/build/metadata-conflicts.mjs +13 -0
- package/build/metadata-images.mjs +3 -3
- package/build/metadata-routes.mjs +3 -3
- package/build/native-packages.mjs +5 -1
- package/build/npm-packages.mjs +2 -1
- package/build/og.mjs +3 -9
- package/build/optimize-imports.mjs +112 -0
- package/build/output-contract.mjs +41 -0
- package/build/precompress.mjs +13 -0
- package/build/proxy-body-config.mjs +10 -0
- package/build/scan.mjs +10 -8
- package/build/static-adapter.mjs +100 -0
- package/cli.mjs +64 -11
- package/compat/after.cjs +51 -0
- package/compat/diagnostics.cjs +4 -0
- package/compat/index.d.ts +32 -1
- package/compat/server.cjs +2 -1
- package/compat/server.d.cts +2 -0
- package/index.mjs +2 -0
- package/native/launch.mjs +19 -0
- package/package.json +8 -9
- package/release/verify.mjs +2 -0
- package/runtime/api.mjs +20 -7
- package/runtime/app-render.mjs +78 -33
- package/runtime/app-route-plan.mjs +45 -0
- package/runtime/app-routing-state.mjs +101 -21
- package/runtime/app-routing.mjs +37 -18
- package/runtime/app-segments.mjs +25 -11
- package/runtime/app-static.mjs +1 -1
- package/runtime/app-stream-html.mjs +2 -1
- package/runtime/cache-request.mjs +10 -2
- package/runtime/cloud/archive.mjs +317 -0
- package/runtime/cloud/client.mjs +641 -0
- package/runtime/cloud/environment.mjs +60 -0
- package/runtime/cloud/state.mjs +156 -0
- package/runtime/cloud/upload.mjs +48 -0
- package/runtime/html-envelope.mjs +70 -7
- package/runtime/http.mjs +7 -4
- package/runtime/middleware.mjs +3 -3
- package/runtime/og/Geist-Regular.ttf +0 -0
- package/runtime/og/LICENSE +373 -0
- package/runtime/og/LICENSE.Geist +93 -0
- package/runtime/og/LICENSE.fflate +21 -0
- package/runtime/og/LICENSE.harfbuzzjs +22 -0
- package/runtime/og/LICENSE.satori +373 -0
- package/runtime/og/NOTICE.txt +4 -0
- package/runtime/og/hb.wasm +0 -0
- package/runtime/og/index.node.mjs +31260 -0
- package/runtime/og/resvg.wasm +0 -0
- package/runtime/og-backend.mjs +8 -0
- package/runtime/og-loader.mjs +2 -15
- package/runtime/request-transport.mjs +1 -1
- package/runtime/route-static.mjs +1 -1
- package/runtime/rsc-worker.mjs +27 -4
- package/runtime/stream-utils.mjs +22 -3
- package/runtime/transport.mjs +16 -0
- package/runtime/worker-metadata.mjs +23 -0
- package/runtime/worker.mjs +36 -9
package/README.md
CHANGED
|
@@ -1,43 +1,21 @@
|
|
|
1
|
-
<
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://prnext.dev">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/Lorkydey/PRNext/main/.github/assets/prnext-logo.svg" alt="PRNext logo" width="112" height="112">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
<h1 align="center">PRNext</h1>
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/@thomas.f/prnext"><img src="https://img.shields.io/npm/v/%40thomas.f%2Fprnext/alpha?style=for-the-badge&label=npm&labelColor=161b22&color=007ec6" alt="Current npm alpha version" height="28" align="absmiddle"></a>
|
|
11
|
+
<a href="https://github.com/Lorkydey/PRNext/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Lorkydey/PRNext?style=for-the-badge&label=license&labelColor=161b22&color=98d6ac" alt="MIT License" height="28" align="absmiddle"></a>
|
|
12
|
+
<a href="https://x.com/PRNextdev"><img src="https://img.shields.io/badge/-%40PRNextdev-171717?style=for-the-badge&logo=x&logoColor=white" alt="Follow @PRNextdev on X" height="28" align="absmiddle"></a>
|
|
13
|
+
</p>
|
|
6
14
|
|
|
7
|
-
A Rust-powered runtime
|
|
15
|
+
A Rust-powered web runtime with React rendering on Node.js and a static adapter for completed browser builds.
|
|
8
16
|
|
|
9
17
|
> **Very early alpha.** Built for experimentation and feedback. Next.js compatibility is partial; expect bugs, missing features and breaking changes.
|
|
10
18
|
|
|
11
|
-
## Latest update
|
|
12
|
-
|
|
13
|
-
**0.1.12-alpha — stability and bounded resource usage.** Client cancellations retain healthy Node hosts; worker retirement protects tracked finalization; filesystem and proxy overload return explicit, noncacheable 503 responses. Clean installs and runtime/native/build consistency checks are part of the release path. Rebuild dynamic applications with `prnext build` after upgrading, then restart them.
|
|
14
|
-
|
|
15
|
-
The experimental eight-app `balanced`/1 GiB Windows commit configuration with `NODE_OPTIONS=--max-semi-space-size=1` still has a reproduced allocation-failure regression relative to V5. It is not a supported density target and is not fixed. Defaults remain `start=balanced` and `host=memory`. Compatibility remains partial, including the compiler and TypeScript limits below.
|
|
16
|
-
|
|
17
|
-
<details>
|
|
18
|
-
<summary><strong>0.1.1-alpha · October 8, 2026 · Features & fixes</strong></summary>
|
|
19
|
-
|
|
20
|
-
**README update:** `0.1.11-alpha` updates the README only; runtime features and fixes remain those of `0.1.1-alpha`.
|
|
21
|
-
|
|
22
|
-
### Added
|
|
23
|
-
|
|
24
|
-
- **Windows x64 & ARM64:** native packages alongside macOS and Linux glibc support.
|
|
25
|
-
- **Persistent apps:** background processes, crash recovery, rotating logs, health-checked replacements and optional restoration at login.
|
|
26
|
-
- **Multi-app hosting:** hostname routing, on-demand startup, idle sleep and sampled memory budgets.
|
|
27
|
-
- **Migration checks:** source audits and optional GET response comparisons with `migrate --check`.
|
|
28
|
-
- **Inspector:** opt-in route, cache and request timing diagnostics.
|
|
29
|
-
|
|
30
|
-
### Fixed
|
|
31
|
-
|
|
32
|
-
- Standalone dependency tracing for Windows drives, scoped packages and workspace aliases.
|
|
33
|
-
- Development process shutdown and file replacement on Windows.
|
|
34
|
-
- Cold-worker connection bursts on macOS.
|
|
35
|
-
- Windows package publishing and portable `prnext` migration scripts.
|
|
36
|
-
|
|
37
|
-
</details>
|
|
38
|
-
|
|
39
|
-
[Full changelog](https://github.com/Lorkydey/PRNext/blob/main/CHANGELOG.md)
|
|
40
|
-
|
|
41
19
|
## Install & upgrade
|
|
42
20
|
|
|
43
21
|
Node.js **22+** · Windows, macOS and Linux glibc · x64 and ARM64
|
|
@@ -61,6 +39,44 @@ Install only `@thomas.f/prnext`. Keep optional dependencies enabled: npm install
|
|
|
61
39
|
|
|
62
40
|
`reactCompiler: false` and `typedRoutes: false` are supported. Enabling either option fails with an explicit diagnostic: PRNext does not yet run React Compiler or generate and validate typed route declarations. Keep builds that require these features on Next.js.
|
|
63
41
|
|
|
42
|
+
The current source candidate also supports `devIndicators: false`, configured
|
|
43
|
+
`experimental.optimizePackageImports` for pure ESM barrels with `sideEffects: false`,
|
|
44
|
+
and App Router `maxDuration` values from 1 to 300 seconds. Import optimization
|
|
45
|
+
preserves client/server boundaries and falls back to normal bundling when a barrel
|
|
46
|
+
cannot be safely expanded. Duplicate App convention files follow `pageExtensions`
|
|
47
|
+
priority and emit a warning. Identical public/App metadata files are accepted;
|
|
48
|
+
conflicting content still fails explicitly. Route Handlers can call shared
|
|
49
|
+
`use server` functions directly; pages retain their Flight/Server Action
|
|
50
|
+
registrations. Mixed npm component exports preserve the real React server
|
|
51
|
+
instance without requiring unused client hooks to exist in that environment.
|
|
52
|
+
|
|
53
|
+
Node Route Handlers and Server Actions can use `after()` from `next/server`. Callbacks run after the
|
|
54
|
+
response finishes or disconnects, including error responses, and retain their
|
|
55
|
+
request context. Tracked work prevents idle worker retirement without blocking the
|
|
56
|
+
next HTTP request. There are at most 128 tasks per request, within `maxDuration`
|
|
57
|
+
(25 seconds by default, including the response's running time). Cookie reads are supported;
|
|
58
|
+
post-response cookie writes are rejected. Server Actions wait for the HTTP response
|
|
59
|
+
to finish, not just their intermediate Flight render. Server Component and Edge
|
|
60
|
+
`after()` lifecycles are not implemented and fail explicitly. `revalidateTag`,
|
|
61
|
+
`revalidatePath` and `updateTag` are also rejected inside an `after()` callback;
|
|
62
|
+
perform supported invalidation in the Route Handler or Server Action before
|
|
63
|
+
returning. This differs from the full Next.js lifecycle. During service shutdown,
|
|
64
|
+
the 30-second standalone drain (35 seconds for global Host shutdown) can shorten a route's larger `maxDuration`. Ordinary sleep/eviction instead allows a 300-second drain. Forced
|
|
65
|
+
termination, a memory-budget violation or a machine failure can still interrupt
|
|
66
|
+
work; use durable external job storage for tasks that must survive these events.
|
|
67
|
+
|
|
68
|
+
`experimental.proxyClientMaxBodySize` accepts a byte count or a size such as
|
|
69
|
+
`'25mb'`, up to 32 MiB (default 10 MiB). Like Next's body clone, overflow retains
|
|
70
|
+
only complete chunks before the limit and warns; a chunk crossing the limit is
|
|
71
|
+
discarded. General request ingress remains capped at 32 MiB and Server Actions at
|
|
72
|
+
8 MiB. PostgreSQL's `pg` stays external by default so its optional native driver
|
|
73
|
+
remains lazy; explicitly putting `pg` in `transpilePackages` opts out.
|
|
74
|
+
|
|
75
|
+
Google font build inputs are cached under `.prnext-cache/fonts-v1` (64 MiB,
|
|
76
|
+
256 entries, verified content hashes). A warm cache permits offline rebuilding;
|
|
77
|
+
a cold build still needs access to Google's font endpoints. Remove this cache to
|
|
78
|
+
refresh downloaded font inputs. This does not cache dynamic page responses.
|
|
79
|
+
|
|
64
80
|
Set `typescript: {}` to check an existing `tsconfig.json` during production builds using the application's installed `typescript` package. `typescript.tsconfigPath` selects a custom configuration for type checking and compilation, including its inherited path aliases. `typescript.ignoreBuildErrors: true` explicitly skips the production check; development builds also skip it. Checks run in a temporary child process without emitting files or editing the configuration. Unlike Next.js, omitting `typescript` preserves PRNext's existing transpile-only behavior. PRNext does not install TypeScript, create `next-env.d.ts`, generate Next.js route types, or implement the Next.js TypeScript language-service plugin; retain the project's type declarations and run separate editor checks as needed. See the [Next.js option definitions](https://nextjs.org/docs/app/api-reference/config/next-config-js/typescript).
|
|
65
81
|
|
|
66
82
|
</details>
|
|
@@ -68,7 +84,7 @@ Set `typescript: {}` to check an existing `tsconfig.json` during production buil
|
|
|
68
84
|
<details>
|
|
69
85
|
<summary><strong>Upgrade · update an existing PRNext project</strong></summary>
|
|
70
86
|
|
|
71
|
-
Stop your development or production process before changing dependencies. If you use `pstart`, follow the [persistent app upgrade guide](
|
|
87
|
+
Stop your development or production process before changing dependencies. If you use `pstart`, follow the [persistent app upgrade guide](#upgrade-persistent-apps) first.
|
|
72
88
|
|
|
73
89
|
```sh
|
|
74
90
|
npm install @thomas.f/prnext@alpha react@19.3.0 react-dom@19.3.0 react-server-dom-webpack@19.3.0
|
|
@@ -223,9 +239,18 @@ Client disconnections release response permits and close incomplete transport la
|
|
|
223
239
|
|
|
224
240
|
Explicit disposal of a worker pool starts a separate graceful shutdown: tracked work has up to 30 seconds to drain before Rust terminates and reaps the process, including a blocked JavaScript event loop. This shutdown grace does not limit normal idle cache finalization. Arbitrary detached tasks are outside the tracked lifecycle; applications that rely on them or long-lived in-process state should use `speed` or `classic` to keep their primary host warm. Such tasks are still not preserved across shutdown or worker failure.
|
|
225
241
|
|
|
226
|
-
|
|
242
|
+
Each profile keeps its own admission, buffering and idle policy. Limits below are per worker host; render starts and live responses are separate budgets.
|
|
243
|
+
|
|
244
|
+
| Profile | Render starts | Live responses | API concurrency | Response buffer | Primary idle deadline |
|
|
245
|
+
|---|---:|---:|---:|---:|---|
|
|
246
|
+
| `balanced` | 16 | 32 | 256 | 4 MiB | 300 seconds |
|
|
247
|
+
| `speed` | 32 | 128 | 512 | 16 MiB | Keep warm |
|
|
248
|
+
| `memory` | 8 | 28 | 64 | 1 MiB | 30 seconds |
|
|
249
|
+
| `classic` | 16 | 32 | 512 | 8 MiB | Keep warm |
|
|
227
250
|
|
|
228
|
-
`
|
|
251
|
+
On Linux with Node's `process.execve` available (Node 22.15+/23.11+), production `prnext start` replaces its CLI process with the native server. This removes a resident Node launcher from every profile without shrinking render or response budgets. Rust handles SIGINT, SIGTERM and SIGHUP gracefully; Linux also kills Node workers if their native parent dies, including when JavaScript is blocked. Explicit `--shutdown-on-stdin-eof` remains supported. Windows, macOS, older Node versions, IPC callers and Node instrumentation keep the supervised launch path. Only heap-sizing Node flags are eligible for replacement; other flags and `NODE_V8_COVERAGE` conservatively retain supervision. Node still labels `process.execve` experimental. This change does not remove the supervisors used by `pstart`, or change `host` and development mode.
|
|
252
|
+
|
|
253
|
+
`balanced` keeps its 4 MiB V8 semi-space without `--optimize-for-size`. `speed` keeps its 16 MiB semi-space, larger concurrency and buffers, and warm hosts. `memory` keeps V8's size policy and early retirement; `classic` preserves its original V8 settings. Node heap flags can take precedence over the RSC isolate's young-generation resource limit: these settings are not total RSS caps. The earlier Windows V6 validation includes three 30-second measurements per scenario and a one-hour mixed endurance run; it predates this launcher change and is not an endurance result for this candidate. Workload and platform affect the CPU/RSS trade-off; a comparable Linux Nginx benchmark remains pending.
|
|
229
254
|
|
|
230
255
|
An explicit `NODE_OPTIONS="--max-semi-space-size=4"` (including Node's underscore spelling) overrides the profile's nursery policy. PRNext also omits `--optimize-for-size` in that case: on Node 24.19.0, V8 otherwise replaces the explicit value with 1 MiB. This opts out of V8's broader size policy as well, so CPU and memory consumption can change; measure the chosen value for your workload. It does not add an old-generation heap limit or change the profile's admission, response buffers or idle retirement. Without an explicit override, PRNext uses the defaults of the selected profile.
|
|
231
256
|
|
|
@@ -247,74 +272,289 @@ improved small-file throughput by about 5% in two short configuration trials,
|
|
|
247
272
|
but allows four times as many submitted helper tasks. It has not received the
|
|
248
273
|
same endurance validation and is not a universal profile improvement.
|
|
249
274
|
|
|
250
|
-
Results depend on your workload; see the [benchmark and its methodology](
|
|
275
|
+
Results depend on your workload; see the [benchmark and its methodology](#early-benchmarks-against-nextjs).
|
|
276
|
+
|
|
277
|
+
</details>
|
|
278
|
+
|
|
279
|
+
## Early benchmarks against Next.js
|
|
280
|
+
|
|
281
|
+
Dynamic streaming with 128 concurrent clients and a deterministic local backend delayed by 40 ms:
|
|
282
|
+
|
|
283
|
+
| Configuration | RAM | CPU load¹ | CPU/response | Throughput |
|
|
284
|
+
|---|---:|---:|---:|---:|
|
|
285
|
+
| Next.js | 605 MiB | 164% | 2.442 ms | 673 req/s |
|
|
286
|
+
| **Balanced** | **207 MiB** | 124% | 1.704 ms | 726 req/s |
|
|
287
|
+
| **Speed** | 496 MiB | 250% | **0.926 ms** | **2,698 req/s** |
|
|
288
|
+
| **Memory** | **179 MiB** | **75%** | 2.067 ms | 362 req/s |
|
|
289
|
+
| **Classic** | 303 MiB | 90% | 1.241 ms | 723 req/s |
|
|
290
|
+
|
|
291
|
+
<details>
|
|
292
|
+
<summary><strong>Benchmark methodology & trade-offs</strong></summary>
|
|
293
|
+
|
|
294
|
+
¹ 100% CPU = one fully utilized CPU core. RAM is measured under load.
|
|
295
|
+
|
|
296
|
+
Measured September 27, 2026, on Apple M4 against Next.js 15.5.12. Values are medians of three six-second runs using identical application sources. This table covers one streaming scenario, not an overall average or guaranteed gains. Functional parity was checked before benchmarking.
|
|
297
|
+
|
|
298
|
+
Under concurrent streaming, `memory` used 179 MiB versus 207 MiB for `balanced`, but served 362 versus 726 responses/s. On concurrent SSR, it used slightly more memory than `balanced`. The lowest-memory profile is workload-dependent.
|
|
251
299
|
|
|
252
300
|
</details>
|
|
253
301
|
|
|
254
302
|
## Guides
|
|
255
303
|
|
|
304
|
+
### Build adapters
|
|
305
|
+
|
|
306
|
+
PRNext keeps its React/Next-convention build as the default. The source candidate
|
|
307
|
+
also imports static browser builds produced by Vite or another framework:
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
npm run build
|
|
311
|
+
npx --no-install prnext build --adapter static --from dist --spa
|
|
312
|
+
npx --no-install prnext start
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Use `--spa` only for client-side routing. Omit it for a multi-page site. PRNext
|
|
316
|
+
imports the completed public files; it does not compile Vue/Svelte source or run
|
|
317
|
+
another framework's SSR server. Static requests require no Node render worker.
|
|
318
|
+
This adapter needs the updated native binary built from the same source candidate;
|
|
319
|
+
it is not part of the previously published alpha package.
|
|
320
|
+
|
|
321
|
+
Every new build writes a versioned `build-output.json`. Inspect it with
|
|
322
|
+
`prnext output --json`. See the [adapter contract](packages/prnext/build/ADAPTERS.md)
|
|
323
|
+
and [compatibility scope](COMPATIBILITY.md) for supported inputs and boundaries.
|
|
324
|
+
|
|
256
325
|
<details>
|
|
257
|
-
<summary><strong>
|
|
326
|
+
<summary><strong>Deploy to PRNext Cloud (local development)</strong></summary>
|
|
327
|
+
|
|
328
|
+
### Deploy to PRNext Cloud
|
|
258
329
|
|
|
259
|
-
|
|
330
|
+
The local CLI now supports browser login and source deployments. This feature is
|
|
331
|
+
not yet included in a published npm release or deployed to the public Cloud app.
|
|
260
332
|
|
|
261
333
|
```sh
|
|
334
|
+
prn login
|
|
335
|
+
prn deploy ./my-app
|
|
336
|
+
prn update ./my-app
|
|
337
|
+
prn whoami
|
|
338
|
+
prn logout
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
`prnext` accepts the same commands. Login opens the browser and reuses its session,
|
|
342
|
+
or returns to terminal approval after web sign-in. Confirm the code shown in your
|
|
343
|
+
terminal. `--no-browser` prints the URL for manual authorization.
|
|
344
|
+
|
|
345
|
+
`deploy` creates an upload project and saves its link in the ignored
|
|
346
|
+
`.prnext-cloud/project.json`; `update` sends changes to that same project.
|
|
347
|
+
`deploy --project ID` links an existing upload project. `--dry-run` lists source
|
|
348
|
+
files without connecting or uploading; `--no-wait` returns after queueing.
|
|
349
|
+
`--timeout SECONDS` controls waiting (default 600). After interruption or
|
|
350
|
+
`--no-wait`, `update` resumes the recorded request first; run it again to deploy
|
|
351
|
+
further changes. Server-side request IDs prevent duplicate queued builds.
|
|
352
|
+
|
|
353
|
+
Archives exclude `.env*`, credentials, `node_modules`, `.git`, generated
|
|
354
|
+
`.next`/`.prnext*` output and `.local`. Root `.gitignore` and `.prnextignore`
|
|
355
|
+
support additional glob exclusions and `!` rules; nested/global Git ignore files
|
|
356
|
+
are not read. Mandatory exclusions cannot be re-included. Nonexcluded symbolic
|
|
357
|
+
links are refused. On a compatible Host server: 100 MiB ZIP, 500 MiB expanded,
|
|
358
|
+
15,000 files and 64 MiB per file. Transfers use resumable 4 MiB chunks on the VPS;
|
|
359
|
+
legacy servers retain their 7 MiB upload limit. No object storage is required.
|
|
360
|
+
|
|
361
|
+
When production `.env` files are detected, `deploy` and `update` ask before
|
|
362
|
+
importing variables (default: no). `--with-env` explicitly opts in;
|
|
363
|
+
`--without-env` skips the prompt. Noninteractive commands skip import unless
|
|
364
|
+
`--with-env` is set. Variables travel through the encrypted environment API,
|
|
365
|
+
never inside the source ZIP or project link. Matching remote keys are replaced;
|
|
366
|
+
other keys remain. `NEXT_PUBLIC_` values may be exposed in the browser bundle.
|
|
367
|
+
This feature requires the updated Host API and remains unpublished.
|
|
368
|
+
Use project environment settings for secrets; inspect `--dry-run` for other
|
|
369
|
+
sensitive files in your own source tree.
|
|
370
|
+
|
|
371
|
+
Credentials stay under `~/.prnext/cloud/` (or private `PRNEXT_CLOUD_HOME`, outside
|
|
372
|
+
the source directory). Tokens are scoped to project reads/uploads/deployments,
|
|
373
|
+
expire after 30 days, and can be revoked with `logout` or in workspace settings.
|
|
374
|
+
Browser sign-out does not revoke authorized terminals.
|
|
375
|
+
|
|
376
|
+
For a local Cloud server, use `--server http://127.0.0.1:3003` or
|
|
377
|
+
`PRNEXT_CLOUD_URL`. From this checkout, run
|
|
378
|
+
`node packages/prnext/cli.mjs login --server http://127.0.0.1:3003`.
|
|
379
|
+
Production defaults to `https://app.prnext.dev`; credentials are isolated by origin.
|
|
380
|
+
|
|
381
|
+
A configured isolated Linux worker is required to deploy. A local Windows panel
|
|
382
|
+
supports login and dry runs, but reports unavailable hosting explicitly.
|
|
383
|
+
Approved offline dependencies, runtime network isolation and current Cloud quotas
|
|
384
|
+
still apply; these commands do not bypass the hosting worker or configure DNS/TLS.
|
|
385
|
+
|
|
386
|
+
</details>
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
<details>
|
|
390
|
+
<summary><strong>Check a migration before switching</strong></summary>
|
|
391
|
+
|
|
392
|
+
### Check a migration before switching
|
|
393
|
+
|
|
394
|
+
```sh
|
|
395
|
+
prnext migrate ./my-app --check
|
|
262
396
|
prnext migrate ./my-app --check --json
|
|
263
397
|
prnext migrate ./my-app --check --against http://localhost:3000 --candidate http://localhost:3001 --routes paths.json
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The source audit reports unsupported Next imports with file and line numbers, client/server import conflicts, dependency versions and scripts requiring manual changes. It leaves sources, package files and builds untouched. Configuration files are evaluated by the existing preflight; installed dependencies and custom loaders still require a build. Warnings are distinct from blocking errors. A failed audit or response comparison exits with code 1.
|
|
401
|
+
|
|
402
|
+
For comparison, start the existing Next application and the PRNext candidate separately. `paths.json` is a JSON array such as `["/", "/products/123", "/api/catalog"]`; without it, the command selects non-parameterized page routes from the audit. It sends GET requests without authentication, follows no redirects, and compares status, content type, redirect destinations and bodies. JSON object key order is ignored; HTML compares text with scripts/styles removed. This verifies response samples, not hydration, styling, interactive behavior or every dynamic path. Timing observations are not controlled performance benchmarks. `--timeout 10000` sets each response's deadline in milliseconds.
|
|
403
|
+
|
|
404
|
+
</details>
|
|
405
|
+
|
|
406
|
+
<details>
|
|
407
|
+
<summary><strong>Inspect routes, cache and timings</strong></summary>
|
|
408
|
+
|
|
409
|
+
### Inspect routes, cache and timings
|
|
410
|
+
|
|
411
|
+
```sh
|
|
412
|
+
prnext build ./my-app
|
|
264
413
|
prnext start ./my-app --inspect
|
|
414
|
+
# In another terminal, after visiting the application:
|
|
415
|
+
prnext inspect ./my-app
|
|
265
416
|
prnext inspect ./my-app --json
|
|
266
|
-
prnext
|
|
267
|
-
prnext host prnext.host.json
|
|
268
|
-
prnext host prnext.host.json --status --json
|
|
417
|
+
prnext inspect ./my-app --otlp > metrics.json
|
|
269
418
|
```
|
|
270
419
|
|
|
271
|
-
The
|
|
420
|
+
The inspector explains static, dynamic and partially prerendered routes using build metadata, including observed App prerendering bailouts and Pages data hooks. Opt-in measurements show native HTTP timings, fetch destinations and timings, native data cache hits/misses/stale reads, bypass reasons and successful tag/path invalidations. A custom cache handler is identified as a delegation; its internal hit rate is not inferred. Native page-file cache decisions and client router caching are not counted as data-cache hits.
|
|
421
|
+
|
|
422
|
+
The summary adds sampled HTTP p50/p95, server errors, worker startup p95, current
|
|
423
|
+
worker queue/activity and recent Node worker RSS. Missing or stale resource samples
|
|
424
|
+
are shown as unavailable, never as zero. RSS excludes the native server; worker
|
|
425
|
+
threads sharing a PID are counted once. `start --inspect` also enables bounded native
|
|
426
|
+
counter snapshots. These opt-in diagnostics do not change normal serving defaults.
|
|
272
427
|
|
|
273
|
-
|
|
428
|
+
`inspect --otlp` writes an OTLP/JSON Metrics payload for a bounded local sample.
|
|
429
|
+
The custom `prnext.http.response.headers.duration` histogram uses seconds and stops
|
|
430
|
+
at response headers, excluding streaming body time. It is deliberately not named
|
|
431
|
+
`http.server.request.duration`. Samples can span sessions and overlap repeated
|
|
432
|
+
exports; this is a diagnostic snapshot, not a continuous traffic or billing counter.
|
|
433
|
+
No collector connection or public diagnostics endpoint is enabled automatically.
|
|
274
434
|
|
|
275
|
-
|
|
435
|
+
Timings end when headers are ready, so streaming body time is excluded. Samples are bounded, can span process sessions, and are written under the app's `.prnext-cache/inspect` directory. Request bodies, headers, cookies, URL queries and credentials are omitted; runtime tag/path identifiers are hashed consistently. Logs retain at most 1 MiB per segment plus a previous segment, with old process sessions pruned. Under load, diagnostics may drop samples. `--inspect` also works with `prnext dev`; standalone/native launches can set `PRNEXT_INSPECT_DIR` explicitly. No public diagnostic endpoint is added.
|
|
276
436
|
|
|
277
|
-
|
|
437
|
+
</details>
|
|
278
438
|
|
|
279
|
-
|
|
439
|
+
<details>
|
|
440
|
+
<summary><strong>Host several applications</strong></summary>
|
|
280
441
|
|
|
281
|
-
|
|
442
|
+
### Host several applications
|
|
443
|
+
|
|
444
|
+
```sh
|
|
445
|
+
npm run build -- examples/basic
|
|
446
|
+
npm run build -- examples/app
|
|
447
|
+
node packages/prnext/cli.mjs host examples/hosting/prnext.host.json --check
|
|
448
|
+
node packages/prnext/cli.mjs host examples/hosting/prnext.host.json
|
|
449
|
+
# In another terminal:
|
|
450
|
+
node packages/prnext/cli.mjs host examples/hosting/prnext.host.json --status
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
The example routes `basic.localhost:8080` and `app.localhost:8080` to separate applications. Resolve those hostnames to the listener address, or supply their exact `Host` header when testing. Installed packages use `prnext host config.json`. Configuration roots are relative to the JSON file. Each app has a unique name, root and exact hostname list, with optional `memoryMb`, `idleSeconds`, `workers`, `profile`, `env` and `inspect` settings. `--status --json` reads the local status snapshot beside the configuration, including its age; it does not connect to a live admin endpoint. Restart the host to apply configuration or build changes.
|
|
454
|
+
|
|
455
|
+
The native gateway keeps each application asleep until requested. A waking app reserves its configured memory budget; if capacity is full, inactive apps can release theirs, while busy requests receive 503 rather than starting an unbudgeted app. Requests and streaming responses hold their reservation until completion or cancellation. Idle apps begin stopping after `idleSeconds` and wake on the next request. Sleep and capacity eviction retain the memory reservation while the child drains supported `after()` and ISR work, allowing up to 300 seconds plus five seconds for process cleanup. A wake waiting for that capacity can take longer. Status shows `draining` with the process still present; monitoring and memory-budget enforcement continue. Global Host shutdown shortens outstanding retirement to its own 35-second deadline. Persistent caches remain on disk; in-process state and untracked background jobs do not survive sleep. Apps receive their configured environment and a small platform environment allowlist, then load their own `.env` files.
|
|
456
|
+
|
|
457
|
+
The watchdog samples the RSS of each app's native server and descendants about once per second. Exceeding `memoryMb` stops that process tree, can interrupt its in-flight requests, and imposes a 30-second cooldown. V8 also receives an initial heap guard unless the app config supplies `NODE_OPTIONS`. These are sampled operating budgets, not kernel-enforced hard limits; allow extra RAM for the gateway, the OS and sampling overshoot. Process separation isolates caches, environment and ordinary failures, but apps run as the same OS user: host only trusted applications. Use separate users or containers for untrusted tenants.
|
|
458
|
+
|
|
459
|
+
The 0.1.12-alpha Windows host check used four applications sharing one CPU and a 1 GiB Job commit budget. Two 30-second mixed SSR/stream/API trials measured about 333 requests/s through `host` versus 361 directly with the same V8 heap guard (about 8% gateway cost in this fixture). The matching direct controls followed the host trials; these short runs are not a general capacity guarantee. Three sleep/resume cycles per trial used `idleSeconds: 3` to exercise retirement: 32 simultaneous arrivals completed in 1.92–2.35 seconds, with one startup per application and no failures. The default whole-app deadline remains 300 seconds. The gateway keeps its one-second status/RSS monitor running while apps sleep; idle CPU is not zero. No host endurance or Linux performance result is claimed.
|
|
460
|
+
|
|
461
|
+
Hosting supports HTTP streaming and preserves the original application Host. Protocol upgrades such as WebSockets return 501. Put a TLS reverse proxy in front for public hosting; this command does not configure DNS, TLS, OS services or deploy to a VPS. Windows, macOS and Linux use the same JSON format and CLI.
|
|
282
462
|
|
|
283
463
|
</details>
|
|
284
464
|
|
|
285
465
|
<details>
|
|
286
|
-
<summary><strong>
|
|
466
|
+
<summary><strong>Keep production apps running</strong></summary>
|
|
467
|
+
|
|
468
|
+
### Keep production apps running
|
|
469
|
+
|
|
470
|
+
`prnext start` keeps its normal foreground behavior. Use the separate persistent commands when an app should survive closing the terminal:
|
|
471
|
+
|
|
472
|
+
```sh
|
|
473
|
+
prnext build ./my-app
|
|
474
|
+
prnext pstart ./my-app --name web --port 3000
|
|
475
|
+
prnext pstatus
|
|
476
|
+
prnext plogs web --follow
|
|
477
|
+
# After rebuilding the app:
|
|
478
|
+
prnext prestart web
|
|
479
|
+
prnext pstop web
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
`pstart` starts a detached supervisor and returns once the app passes its health check. Applications restart after a crash with increasing delays; ten failures within five minutes stop retries until `prestart`. A separate watchdog restarts the supervisor after a crash or an unresponsive event loop. Each app has a stable HTTP listener and its own native server and workers. This mode adds a Node HTTP proxy and two supervisor processes; it does not enable the idle sleep or memory reservations of `prnext host`.
|
|
483
|
+
|
|
484
|
+
`prestart` copies the current production build, starts a replacement on a private port, and sends a GET to `--health-path` (default `/`). A 2xx or 3xx response must arrive within `--health-timeout 15000` milliseconds. Only then does new traffic switch to the replacement. If startup or the health check fails, the existing healthy instance keeps serving. Existing HTTP streams can finish for up to `--drain-timeout 30000` milliseconds, after which remaining requests are closed. Choose a health route that checks the dependencies your application needs. Two generations temporarily use memory and disk space during replacement.
|
|
485
|
+
|
|
486
|
+
Build copies live under the project's ignored `.prnext-persistent` directory and are removed after their processes stop. Rebuilding does not replace the active generation's compiled files. Project dependencies, `public` files and persistent caches remain shared; avoid changing dependencies or deleting public assets while old requests still need them. HTTP streaming, original Host headers and multiple cookies are preserved. WebSocket/protocol upgrades return 501. A supervisor crash can interrupt requests; health-checked replacement is not a guarantee against machine failures or incompatible application changes. A TLS reverse proxy can sit in front of the persistent listener.
|
|
487
|
+
|
|
488
|
+
Available commands:
|
|
489
|
+
|
|
490
|
+
| Command | Behavior |
|
|
491
|
+
| --- | --- |
|
|
492
|
+
| `prnext pstart [directory] --name NAME` | Start or restore an app; identical repeated starts are idempotent |
|
|
493
|
+
| `prnext prestart [name] [--all]` | Load the current build through a health-checked replacement |
|
|
494
|
+
| `prnext pstop [name] [--all]` | Stop apps and keep them stopped across supervisor restarts |
|
|
495
|
+
| `prnext pdelete [name] [--all]` | Stop apps and remove their saved configuration; retain logs |
|
|
496
|
+
| `prnext pstatus [name] [--json]` / `prnext plist` | Show status, PIDs, build IDs, starts and active requests |
|
|
497
|
+
| `prnext plogs [name] --lines 100 --follow` | Read/tail app output; `--supervisor` selects supervisor logs |
|
|
498
|
+
| `prnext pdown` | Stop the supervisor and apps, retaining enabled flags for their next launch |
|
|
499
|
+
| `prnext pstartup [--remove]` | Enable/remove automatic restoration at user login |
|
|
287
500
|
|
|
288
|
-
|
|
501
|
+
Without a name, `prestart`, `pstop`, `pdelete` and `plogs` select the app registered for the current directory. `pstart` accepts `--hostname`, `--port`, `--workers`, `--profile`, `--inspect`, `--health-path`, `--health-timeout` and `--drain-timeout`; see `prnext pstart --help`. Change saved listener/settings with `pdelete`, then `pstart`. Applications load their project `.env` files; arbitrary caller-shell variables and secrets are not copied into the saved registry. The state directory defaults to `~/.prnext/processes`; set `PRNEXT_PM_HOME` consistently for a separate supervisor. Keep that directory private: it contains local control credentials and application logs. Logs rotate at 10 MiB with three retained segments per app and for the supervisor. App output can contain whatever the application logs.
|
|
289
502
|
|
|
290
|
-
`
|
|
503
|
+
`pstartup` is opt-in and enables restoration on the **next user login**. On Windows it registers a hidden scheduled task for the current account; on macOS it writes a user LaunchAgent; on Linux it enables a systemd user service. These definitions restart a failed watchdog once managed by the OS. Windows and macOS user startup do not run before login. A Linux administrator can enable user lingering separately for boot-time operation without an interactive login. No OS startup settings change just by running `pstart`, and the command does not install a Windows system service or alter other accounts. Run `pstartup` again after moving or upgrading Node or the PRNext package. Platform references: [Windows scheduled tasks](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtasksettingsset), [Apple launch agents](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html), [systemd user lingering](https://www.freedesktop.org/software/systemd/man/252/loginctl.html).
|
|
291
504
|
|
|
292
|
-
|
|
505
|
+
### Upgrade persistent apps
|
|
293
506
|
|
|
294
|
-
|
|
507
|
+
The supervisor may be shared by several projects. Use the same user and `PRNEXT_PM_HOME` throughout, and record the applications and their settings first:
|
|
295
508
|
|
|
296
|
-
|
|
509
|
+
```sh
|
|
510
|
+
npx --no-install prnext pstatus --json
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Before changing package files, stop the apps and supervisor using the **old installation**. These commands affect **all apps managed by that supervisor**, so schedule the interruption accordingly:
|
|
514
|
+
|
|
515
|
+
```sh
|
|
516
|
+
npx --no-install prnext pstop --all
|
|
517
|
+
npx --no-install prnext pdown
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Install the update and rebuild each affected project as described under [Install & upgrade](#install--upgrade). Using the new CLI, restart each app with its original name, port and other options. For example:
|
|
521
|
+
|
|
522
|
+
```sh
|
|
523
|
+
npx --no-install prnext pstart --name web --port 3000
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
If the saved Node or native executable path changed, remove that app's saved entry with `prnext pdelete NAME`, then register it again with `prnext pstart` and its recorded options. This removes saved settings but retains logs. If login startup was enabled, re-run `prnext pstartup` to refresh its paths.
|
|
527
|
+
|
|
528
|
+
`pdown` alone keeps apps enabled for restoration; stop them first to prevent an old configuration from restarting during the upgrade.
|
|
297
529
|
|
|
298
530
|
</details>
|
|
299
531
|
|
|
300
532
|
<details>
|
|
301
|
-
<summary><strong>
|
|
533
|
+
<summary><strong>Develop from this repository</strong></summary>
|
|
534
|
+
|
|
535
|
+
### Develop from this repository
|
|
536
|
+
|
|
537
|
+
Install Node.js 22+ and Rust with rustup, then run:
|
|
538
|
+
|
|
539
|
+
```sh
|
|
540
|
+
npm ci
|
|
541
|
+
npm run build:native
|
|
542
|
+
npm run dev -- examples/app
|
|
543
|
+
```
|
|
302
544
|
|
|
303
|
-
|
|
545
|
+
These commands also work in PowerShell and Command Prompt. On Windows, source builds require Visual Studio Build Tools with the Desktop development with C++ workload and a Windows SDK, plus NASM on x64. Rustup selects the repository's Rust 1.98 toolchain. Linux source builds need a C/C++ toolchain, NASM and pkg-config; macOS needs Xcode command line tools and NASM.
|
|
304
546
|
|
|
305
|
-
|
|
547
|
+
For production locally, run `npm run build -- examples/app`, then `npm run start -- examples/app`. Standalone output runs with `node server.js` on its build OS and architecture, including Windows. Published native packages remove the Rust/C++ build requirement for consumers. Keep the framework and platform components on matching release versions.
|
|
306
548
|
|
|
307
|
-
|
|
549
|
+
For source builds, `cargo build --release --locked --features gzip-zlib-rs` enables an experimental alternative gzip/deflate backend. Ubuntu x64 trials reduced JSON API CPU cost but increased cumulative streaming RSS, so this option is disabled by default. It also changes the Rust PNG/TIFF codecs; runtime profiles and Node heap settings remain unchanged. Compare CPU, response size and memory on your workload before choosing it.
|
|
308
550
|
|
|
309
551
|
</details>
|
|
310
552
|
|
|
311
|
-
##
|
|
553
|
+
## Contributing
|
|
312
554
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
- Bug reports: [GitHub Issues](https://github.com/Lorkydey/PRNext/issues)
|
|
317
|
-
- Questions and suggestions: [contact@heythomas.dev](mailto:contact@heythomas.dev)
|
|
555
|
+
Read the [contribution guide](https://github.com/Lorkydey/PRNext/blob/main/CONTRIBUTING.md),
|
|
556
|
+
[Code of Conduct](https://github.com/Lorkydey/PRNext/blob/main/CODE_OF_CONDUCT.md)
|
|
557
|
+
and [security policy](https://github.com/Lorkydey/PRNext/blob/main/SECURITY.md).
|
|
318
558
|
|
|
319
559
|
<details>
|
|
320
560
|
<summary><strong>License & credits</strong></summary>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Build output contract v1
|
|
2
|
+
|
|
3
|
+
This contract describes the adapter and entrypoints of a completed PRNext build.
|
|
4
|
+
It is distinct from `runtime-contract.json`, which fingerprints the React worker
|
|
5
|
+
implementation and enforces lifecycle guarantees. It is not a signature or an
|
|
6
|
+
integrity check for arbitrary files, and build artifacts must remain trusted.
|
|
7
|
+
|
|
8
|
+
## Output selection
|
|
9
|
+
|
|
10
|
+
The project-level `.prnext-output.json` selects a relative build directory, usually
|
|
11
|
+
`.prnext`. Paths in `build-output.json` are relative to that directory. The runtime
|
|
12
|
+
does not evaluate project configuration to start a completed build.
|
|
13
|
+
|
|
14
|
+
Every new React and static build emits all seven fields below. Unknown fields,
|
|
15
|
+
versions, adapters, entrypoints and inconsistent build identities are rejected by
|
|
16
|
+
the native server before listening. Existing React builds without this metadata
|
|
17
|
+
retain their original runtime-contract checks. Static builds require it.
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"version": 1,
|
|
22
|
+
"adapter": "react",
|
|
23
|
+
"buildId": "a-build-identifier",
|
|
24
|
+
"manifest": "manifest.json",
|
|
25
|
+
"assets": "assets",
|
|
26
|
+
"server": {
|
|
27
|
+
"entry": "runtime/worker.mjs",
|
|
28
|
+
"runtimeContract": "runtime-contract.json"
|
|
29
|
+
},
|
|
30
|
+
"fallback": null
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`buildId` must match the manifest and contain 1–128 ASCII letters, digits,
|
|
35
|
+
underscores or hyphens. Metadata is limited to 64 KiB. Entry paths are fixed in
|
|
36
|
+
v1; they cannot select an arbitrary executable or escape the build directory.
|
|
37
|
+
The React manifest continues to describe routing, caching and rendering details;
|
|
38
|
+
its implementation-specific fields are not made stable by this public envelope.
|
|
39
|
+
|
|
40
|
+
## Static adapter
|
|
41
|
+
|
|
42
|
+
Build the application with its own tool, then import **only** its public browser
|
|
43
|
+
output. Vite's default output is `dist`; see its [deployment guide](https://vite.dev/guide/static-deploy.html).
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npm run build
|
|
47
|
+
prnext build --adapter static --from dist
|
|
48
|
+
prnext output --json
|
|
49
|
+
prnext start --hostname 127.0.0.1 --port 3000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Programmatic equivalent:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
import { build, buildStatic, readBuildOutput } from '@thomas.f/prnext';
|
|
56
|
+
await build(projectRoot, { adapter: 'static', from: 'dist', spa: true });
|
|
57
|
+
// Or: await buildStatic(projectRoot, { from: 'dist', spa: true });
|
|
58
|
+
const output = await readBuildOutput(buildDirectory);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The static envelope has `adapter: "static"`, `assets: "static"`, `server: null`
|
|
62
|
+
and `fallback: null`. With `--spa`, fallback is `"index.html"`. Its manifest has:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"version": 1,
|
|
67
|
+
"buildId": "a-build-identifier",
|
|
68
|
+
"config": { "staticOnly": true, "staticFallback": false, "compress": true, "poweredByHeader": true },
|
|
69
|
+
"routes": [],
|
|
70
|
+
"prerendered": [
|
|
71
|
+
{ "path": "/", "file": "static/index.html", "revalidate": false }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Every imported file has an exact, URL-encoded path in `prerendered`; `file` stays
|
|
77
|
+
relative to the build. `/index.html` also maps to `/`, `/docs/index.html` to `/docs`,
|
|
78
|
+
and `/about.html` to `/about`. Conflicting aliases fail the import. Normal native
|
|
79
|
+
trailing-slash redirects apply. No server routes, middleware, ISR or cache handler
|
|
80
|
+
can be declared. The project's separate `public/` directory is not served.
|
|
81
|
+
|
|
82
|
+
With SPA fallback enabled, unmatched GET/HEAD requests that explicitly accept
|
|
83
|
+
`text/html` can receive the root index. Existing files and pages take precedence.
|
|
84
|
+
Missing file extensions, `/api/*`, hidden paths and framework transport paths do
|
|
85
|
+
not fall back. JSON requests and POSTs never receive an SPA fallback. Apps using
|
|
86
|
+
dotted client route names need explicit HTML output for those URLs. Configure the
|
|
87
|
+
source builder's asset base for its deployed URL; the adapter does not rewrite JS
|
|
88
|
+
or HTML assets or infer subpath hosting configuration.
|
|
89
|
+
|
|
90
|
+
The source directory must be inside the project, separate from the destination,
|
|
91
|
+
with real directories and files. Symlinks, dotfiles other than a root `.well-known`
|
|
92
|
+
tree, `node_modules`, reserved `_next`/`_prnext` paths and a root `server` directory
|
|
93
|
+
are rejected. Limits are 20,000 files, 512 MiB total and 64 directory levels.
|
|
94
|
+
Environment files and server outputs must never be placed in public input.
|
|
95
|
+
|
|
96
|
+
Imports use a staging directory and publish the manifest, files and output pointer
|
|
97
|
+
together. A validation/copy failure leaves the previous build intact. Source files
|
|
98
|
+
and configuration are untouched. A project lock excludes concurrent React builds
|
|
99
|
+
and static imports, including separate processes and canonical path aliases.
|
|
100
|
+
Another active build fails clearly; a dead local owner is recovered. A lock from
|
|
101
|
+
another host, an unverifiable owner or an interrupted lock recovery fails closed;
|
|
102
|
+
check `.prnext-build.lock` manually only after confirming no build is active.
|
|
103
|
+
Finish the source framework build before importing its files.
|
|
104
|
+
|
|
105
|
+
Compressible files are precompressed with the same bounded gzip pipeline as React
|
|
106
|
+
builds. Source files are unchanged. Existing gzip sidecars must decode exactly to
|
|
107
|
+
their source; a mismatched sidecar fails the import and preserves the last build.
|
|
108
|
+
|
|
109
|
+
Serve with the updated native binary from this source candidate. Node can run the
|
|
110
|
+
CLI to build/start, but no Node worker or React installation is required to serve
|
|
111
|
+
static requests. `prnext host` can validate and host these completed artifacts.
|
|
112
|
+
Cloud source-build auto-detection is a separate integration; this change does not
|
|
113
|
+
change existing PRNext Host projects or enable generic SSR hosting.
|
|
114
|
+
|
|
115
|
+
## Extending adapters
|
|
116
|
+
|
|
117
|
+
The current adapter names are `react` and `static`. Unknown adapters fail rather
|
|
118
|
+
than being silently interpreted as React. Add a new adapter only with a documented
|
|
119
|
+
runtime model and integration tests. A future generic Node SSR adapter needs its
|
|
120
|
+
own request/response, streaming, cancellation, crash and shutdown contract; a
|
|
121
|
+
`server` path alone does not implement those guarantees.
|
package/build/actions.mjs
CHANGED
|
@@ -205,6 +205,10 @@ export function createServerActions({ projectRoot }) {
|
|
|
205
205
|
transform(source,file,mode) {
|
|
206
206
|
if(!source.includes('use server')) return source;
|
|
207
207
|
const result=analyze(source,file);
|
|
208
|
+
// A Route Handler invokes the implementation as ordinary server code.
|
|
209
|
+
// It does not serialize functions through Flight or manufacture proxies.
|
|
210
|
+
// Analyze first to preserve validation and the shared action registry.
|
|
211
|
+
if(mode==='route') return source;
|
|
208
212
|
if(mode==='rsc') return result.server;
|
|
209
213
|
if(result.moduleServer) return `import {createServerReference} from 'prnext-internal:action-${mode==='browser'?'client':'ssr'}';\n`+result.declared.map((item,index)=>`const action${index}=createServerReference(${JSON.stringify(item.id)});export {action${index} as ${JSON.stringify(item.name)}};`).join('\n');
|
|
210
214
|
if(result.inline) throw new Error(`Inline Server Actions cannot be imported into the client graph (${file}); move the action to a module marked 'use server'.`);
|
package/build/app-config.mjs
CHANGED
|
@@ -79,6 +79,7 @@ export async function validateAppConfig(file) {
|
|
|
79
79
|
config.generateStaticParams = true;
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
82
|
+
if (item.name === 'maxDuration' && Number.isSafeInteger(value) && value >= 1 && value <= 300) { config.maxDuration = value; continue; }
|
|
82
83
|
if (allowed.includes(value) || (item.name === 'revalidate' && Number.isSafeInteger(value) && value >= 0)) { config[item.name] = value; continue; }
|
|
83
84
|
throw new Error(`App Router export ${item.name} in ${file} is not supported with this value.${allowed.length ? ` Supported literal values: ${allowed.map(value => JSON.stringify(value)).join(', ')}${item.name === 'revalidate' ? ', or a nonnegative integer' : ''}.` : ''}`);
|
|
84
85
|
}
|
|
@@ -105,6 +106,7 @@ export function mergeAppConfig(configs) {
|
|
|
105
106
|
}
|
|
106
107
|
let forceDynamic = false;
|
|
107
108
|
for (const config of configs) {
|
|
109
|
+
if (config.maxDuration !== undefined) merged.maxDuration = config.maxDuration;
|
|
108
110
|
if (config.runtime === 'edge') merged.runtime = 'edge';
|
|
109
111
|
else if (config.runtime === 'nodejs') delete merged.runtime;
|
|
110
112
|
// The first explicit ancestor decides whether a blocking route is allowed.
|