@thomas.f/prnext 0.1.20-alpha → 0.1.21-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/build/app.mjs +4 -4
  2. package/build/build-timings.mjs +38 -0
  3. package/build/bundle-runtime.mjs +8 -8
  4. package/build/compiler.mjs +2 -1
  5. package/build/flight-summary.mjs +64 -0
  6. package/build/index.mjs +32 -28
  7. package/build/inspect.mjs +3 -0
  8. package/build/inspection-summary.mjs +7 -2
  9. package/build/native-discovery.mjs +18 -6
  10. package/build/npm-packages.mjs +2 -2
  11. package/build/source-analysis.mjs +54 -1
  12. package/build/standalone-copy.mjs +48 -0
  13. package/build/standalone.mjs +2 -22
  14. package/build/transform-cache.mjs +17 -3
  15. package/cli.mjs +13 -0
  16. package/compat/diagnostics.cjs +5 -4
  17. package/compat/runtime-sample.cjs +56 -0
  18. package/compat/use-cache.cjs +5 -2
  19. package/docs/ai/compatibility.md +3 -3
  20. package/docs/ai/deployment.md +8 -5
  21. package/docs/ai/troubleshooting.md +22 -0
  22. package/package.json +20 -20
  23. package/runtime/app-client.mjs +106 -38
  24. package/runtime/app-render.mjs +31 -6
  25. package/runtime/app-stream-channel.mjs +17 -4
  26. package/runtime/app-stream-html.mjs +30 -11
  27. package/runtime/cache-components-rsc.mjs +26 -6
  28. package/runtime/cache-result.mjs +58 -0
  29. package/runtime/dev-client.mjs +26 -5
  30. package/runtime/dev-compiler-broker.mjs +20 -23
  31. package/runtime/dev-gateway.mjs +22 -6
  32. package/runtime/development-flight.mjs +59 -0
  33. package/runtime/development-queue.mjs +33 -0
  34. package/runtime/development.mjs +11 -9
  35. package/runtime/flight-diagnostics.mjs +48 -0
  36. package/runtime/flight-tap.mjs +10 -5
  37. package/runtime/html-envelope.mjs +34 -0
  38. package/runtime/loopback-rpc.mjs +92 -0
  39. package/runtime/native-ppr.mjs +57 -4
  40. package/runtime/persistent/client.mjs +1 -1
  41. package/runtime/persistent/common.mjs +7 -18
  42. package/runtime/persistent/daemon.mjs +1 -1
  43. package/runtime/persistent/native-proxy.mjs +1 -1
  44. package/runtime/persistent/watchdog.mjs +12 -27
  45. package/runtime/prewarm.mjs +35 -0
  46. package/runtime/rsc-worker.mjs +58 -8
  47. package/runtime/worker.mjs +29 -14
@@ -0,0 +1,48 @@
1
+ import {lstat, realpath, mkdir, copyFile, stat} from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import {mapConcurrent} from './concurrent.mjs';
4
+
5
+ const within = (root, file) => file === root || file.startsWith(root + path.sep);
6
+ const version = info => [info.dev, info.ino, info.size, info.mtimeMs, info.ctimeMs];
7
+ const unchanged = (left, right) => version(left).every((value, index) => value === version(right)[index]);
8
+
9
+ /** Materialize a finite trace, keeping links deferred until all file writes settle. */
10
+ export async function copyStandaloneTrace(files, {tracingRoot, relocate, maxFiles, maxBytes, concurrency = 8}, io = {lstat, realpath, mkdir, copyFile, stat}) {
11
+ const sources = Array.from(files);
12
+ if (sources.length > maxFiles) throw new Error('Standalone output exceeds 100000 files / 2 GiB. Narrow tracing includes.');
13
+ // Metadata can overlap, but admission and destination ordering remain exactly
14
+ // the trace's order. No bytes are copied before the whole budget is admitted.
15
+ const metadata = await mapConcurrent(sources, async source => {
16
+ const info = await io.lstat(source);
17
+ if (info.isSymbolicLink()) return {source, info, destination: await io.realpath(source)};
18
+ return {source, info};
19
+ }, concurrency);
20
+ let copiedFiles = 0, copiedBytes = 0;
21
+ const pendingLinks = [], groups = new Map();
22
+ for (const {source, info, destination} of metadata) {
23
+ if (info.isSymbolicLink() && !within(tracingRoot, source) && within(destination, tracingRoot)) continue;
24
+ const target = relocate(source);
25
+ if (++copiedFiles > maxFiles || (copiedBytes += info.size) > maxBytes) throw new Error('Standalone output exceeds 100000 files / 2 GiB. Narrow tracing includes.');
26
+ if (info.isSymbolicLink()) { pendingLinks.push([target, relocate(destination)]); continue; }
27
+ if (!info.isFile()) continue;
28
+ const key = process.platform === 'win32' ? target.toLowerCase() : target;
29
+ if (!groups.has(key)) groups.set(key, []);
30
+ groups.get(key).push({source, target, info});
31
+ }
32
+ const directories = new Map();
33
+ await mapConcurrent(groups.values(), async entries => {
34
+ // Alias collisions retain the old serial last-writer order. Unrelated files
35
+ // share a small I/O worker set and one mkdir promise per destination parent.
36
+ for (const {source, target, info} of entries) {
37
+ const before = await io.lstat(source);
38
+ if (!before.isFile() || !unchanged(info, before)) throw new Error('Standalone input changed before copying.');
39
+ const directory = path.dirname(target);
40
+ if (!directories.has(directory)) directories.set(directory, io.mkdir(directory, {recursive: true}));
41
+ await directories.get(directory);
42
+ await io.copyFile(source, target);
43
+ const [after, copied] = await Promise.all([io.lstat(source), io.stat(target)]);
44
+ if (!after.isFile() || !unchanged(before, after) || !copied.isFile() || copied.size !== before.size) throw new Error('Standalone input changed while copying.');
45
+ }
46
+ }, concurrency);
47
+ return {copiedFiles, copiedBytes, pendingLinks};
48
+ }
@@ -7,6 +7,7 @@ import path from 'node:path';
7
7
  import { resolveNativeBinary } from '../native/resolve.mjs';
8
8
  import { createHash } from 'node:crypto';
9
9
  import { createRequire } from 'node:module';
10
+ import {copyStandaloneTrace} from './standalone-copy.mjs';
10
11
 
11
12
  const slash = value => value.split(path.sep).join('/');
12
13
  const within = (root, file) => file === root || file.startsWith(root + path.sep);
@@ -219,28 +220,7 @@ export async function createStandalone({ projectRoot, stage, manifest, config })
219
220
  } else traceFiles.add(target);
220
221
  }
221
222
  await mkdir(output, { recursive: true });
222
- let copiedBytes = 0;
223
- let copiedFiles = 0;
224
- const pendingLinks = [];
225
- for (const source of traceFiles) {
226
- const info = await lstat(source);
227
- // NFT also emits ancestor aliases used by absolute workspace links, such as
228
- // macOS /var -> /private/var. Their canonical children are already traced;
229
- // these host directory aliases are not dependencies of the artifact.
230
- // Real files and links to unrelated external workspaces still go through
231
- // relocated(), which enforces the configured tracing boundary.
232
- if (info.isSymbolicLink() && !within(tracingRoot, source) && within(await realpath(source), tracingRoot)) continue;
233
- const target = relocated(source);
234
- if (++copiedFiles > maxFiles || (copiedBytes += info.size) > maxBytes) throw new Error('Standalone output exceeds 100000 files / 2 GiB. Narrow tracing includes.');
235
- if (info.isSymbolicLink()) {
236
- const destination = await realpath(source);
237
- pendingLinks.push([target, relocated(destination)]);
238
- continue;
239
- }
240
- if (!info.isFile()) continue;
241
- await mkdir(path.dirname(target), { recursive: true });
242
- await copyFile(source, target);
243
- }
223
+ let {copiedBytes, copiedFiles, pendingLinks} = await copyStandaloneTrace(traceFiles, {tracingRoot, relocate: relocated, maxFiles, maxBytes});
244
224
  const owners = new Set(bridges.values());
245
225
  const includedOwners = new Set();
246
226
  for (const file of traceFiles) for (let dir = path.dirname(file); dir !== path.dirname(dir); dir = path.dirname(dir)) if (owners.has(dir)) includedOwners.add(dir);
@@ -25,7 +25,14 @@ export async function withPersistentTransforms(root,{enabled=true,dev=false}={},
25
25
  return result;
26
26
  }
27
27
  function identity(kind,source,settings){return createHash('sha256').update(persistent.getStore()?.key||'memory').update(JSON.stringify([kind,settings,sourceMapsEnabled()])).update('\0').update(source).digest('hex');}
28
- function diskValue(key){const scope=persistent.getStore();if(!scope?.values.has(key))return;const value=scope.values.get(key);scope.values.delete(key);scope.values.set(key,value);diskHits++;hits++;return value;}
28
+ function discard(key){const value=entries.get(key);if(value){entries.delete(key);bytes-=value.size;}}
29
+ function validValue(value,validate){if(!validate)return true;try{return validate(value)===true;}catch{return false;}}
30
+ function diskValue(key,validate){
31
+ const scope=persistent.getStore();if(!scope?.values.has(key))return;
32
+ const value=scope.values.get(key);scope.values.delete(key);
33
+ if(!validValue(value,validate)){scope.bytes-=Buffer.byteLength(value);scope.dirty=true;return;}
34
+ scope.values.set(key,value);diskHits++;hits++;return value;
35
+ }
29
36
  function remember(key,output){
30
37
  const scope=persistent.getStore();if(!scope||typeof output!=='string')return;
31
38
  const size=Buffer.byteLength(output);if(size>128*1024)return;
@@ -38,16 +45,23 @@ function remember(key,output){
38
45
  // closure or mutable build metadata survives in the cache. Include all options
39
46
  // affecting output in settings, including build identity and target mode.
40
47
  export function cachedTransform(kind, source, settings, transform) {
48
+ return cachedValidatedTransform(kind,source,settings,undefined,transform);
49
+ }
50
+
51
+ /** Typed compiler facts may replay only structurally validated string records. */
52
+ export function cachedValidatedTransform(kind, source, settings, validate, transform) {
41
53
  const key = identity(kind,source,settings);
42
54
  const found = entries.get(key);
43
- if (found) {
55
+ if (found && validValue(found.output,validate)) {
44
56
  hits++; entries.delete(key); entries.set(key,found);
45
57
  if(persistent.getStore()&&!persistent.getStore().values.has(key))remember(key,found.output);
46
58
  return found.output;
47
59
  }
48
- const saved=diskValue(key);if(saved!==undefined)return saved;
60
+ if(found)discard(key);
61
+ const saved=diskValue(key,validate);if(saved!==undefined)return saved;
49
62
  misses++;
50
63
  const output = transform();
64
+ if(!validValue(output,validate))throw new TypeError('Invalid typed compiler cache result');
51
65
  remember(key,output);
52
66
  // UTF-16 is an upper bound for the retained string payload. Metadata adds
53
67
  // engine overhead; the entry count also bounds that overhead.
package/cli.mjs CHANGED
@@ -22,6 +22,7 @@ Use prnext for portable scripts; prn is a shortcut on compatible shells.
22
22
  Detect a completed browser build
23
23
  prnext build [directory] --adapter static --from .
24
24
  Build a plain HTML/CSS/JS site
25
+ prnext build [directory] --timings Show time spent in React build phases
25
26
  prnext output [directory] [--json] Read the versioned build output contract
26
27
  prnext start [directory] [options] Start native Rust production server
27
28
  prnext pstart [directory] [options] Start production persistently in the background
@@ -338,11 +339,16 @@ async function main() {
338
339
  }
339
340
  if (command === 'build') {
340
341
  const options = {};
342
+ let showTimings = false;
341
343
  for (let i = 0; i < args.length; i++) {
342
344
  const flag = args[i];
343
345
  if (flag === '--dev') options.dev = true;
344
346
  else if (flag === '--watch') options.watch = true;
345
347
  else if (flag === '--spa') options.spa = true;
348
+ else if (flag === '--timings') {
349
+ if (showTimings) throw new Error('Duplicate build option: --timings');
350
+ showTimings = true;
351
+ }
346
352
  else if (flag === '--adapter' || flag === '--from') {
347
353
  const value = args[++i];
348
354
  if (!value || value.startsWith('-')) throw new Error(`${flag} requires a value`);
@@ -356,6 +362,7 @@ async function main() {
356
362
  process.env.NODE_ENV = dev ? 'development' : 'production';
357
363
  const started = performance.now();
358
364
  if(options.watch){
365
+ if(showTimings)throw new Error('--timings reports one build. Run prnext build --timings without --watch.');
359
366
  if(dev)throw new Error('--watch builds production; use prnext dev for development.');
360
367
  const {watchProduction}=await import('./build/production-builder.mjs');
361
368
  delete options.watch;await watchProduction(root,options);return;
@@ -367,11 +374,17 @@ async function main() {
367
374
  else if (options.adapter === 'auto') options.adapter = 'react';
368
375
  }
369
376
  if (options.adapter === 'static') {
377
+ if(showTimings)throw new Error('--timings requires the React build adapter.');
370
378
  const { buildStatic } = await import('./build/static-adapter.mjs');
371
379
  await buildStatic(root, options);
372
380
  } else {
373
381
  const { build } = await import('./build/index.mjs');
374
382
  const result=await build(root, { ...options, dev, envMode });
383
+ if(showTimings&&result.buildTimings){
384
+ console.log('Build timings (milliseconds; nested phases are not additive):');
385
+ for(const[name,phase]of Object.entries(result.buildTimings.phases))console.log(` ${name}: ${phase.milliseconds.toFixed(2)} (${phase.calls} ${phase.calls===1?'call':'calls'})`);
386
+ console.log(` total: ${result.buildTimings.totalMilliseconds.toFixed(2)} (excludes CLI module startup)`);
387
+ }
375
388
  if(!dev&&result.compilationCache?.hits){
376
389
  const {hits,reusedBytes}=result.compilationCache;
377
390
  console.log(`Compilation cache: ${hits} ${hits===1?'graph':'graphs'} reused (${Math.ceil(reusedBytes/1024)} KiB).`);
@@ -4,10 +4,11 @@ const directory = process.env.PRNEXT_INSPECT_DIR;
4
4
  const { threadId } = require('node:worker_threads');
5
5
  let queue = [], scheduled = false, writing = false, size;
6
6
  const limit = 1024 * 1024;
7
- if (directory) setInterval(() => {
8
- const {rss, heapUsed} = process.memoryUsage();
9
- record('process', {rssBytes:rss, heapUsedBytes:heapUsed});
10
- }, 5000).unref();
7
+ if (directory) {
8
+ // No GC observer or periodic sampler exists when diagnostics are disabled.
9
+ const sampler = require('./runtime-sample.cjs').createRuntimeSampler();
10
+ setInterval(() => record('process', sampler.sample()), 5000).unref();
11
+ }
11
12
  function record(type, fields = {}) {
12
13
  if (!directory || queue.length >= 256) return;
13
14
  const line = JSON.stringify({ time: Date.now(), pid: process.pid, type, ...fields }) + '\n';
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ // Constructed only for opt-in diagnostics. Samples contain numeric runtime
4
+ // counters, never application values, request context or allocation stacks.
5
+ function createRuntimeSampler() {
6
+ const { PerformanceObserver, performance } = require('node:perf_hooks');
7
+ const { threadId, resourceLimits } = require('node:worker_threads');
8
+ const { getHeapSpaceStatistics } = require('node:v8');
9
+ let count = 0, durationMs = 0, maxPauseMs = 0;
10
+ let previousTime = performance.now();
11
+ let previousLoop = performance.eventLoopUtilization();
12
+ let previousCpu = threadId === 0 ? process.cpuUsage() : undefined;
13
+ const consume = entries => {
14
+ for (const entry of entries) {
15
+ count++;
16
+ durationMs += entry.duration;
17
+ maxPauseMs = Math.max(maxPauseMs, entry.duration);
18
+ }
19
+ };
20
+ const observer = new PerformanceObserver(list => consume(list.getEntries()));
21
+ observer.observe({ entryTypes: ['gc'] });
22
+ return {
23
+ sample() {
24
+ consume(observer.takeRecords());
25
+ const now = performance.now();
26
+ const loop = performance.eventLoopUtilization();
27
+ const eventLoop = performance.eventLoopUtilization(loop, previousLoop);
28
+ const { rss, heapUsed, heapTotal, external, arrayBuffers } = process.memoryUsage();
29
+ const young = getHeapSpaceStatistics().find(space => space.space_name === 'new_space');
30
+ const value = {
31
+ node: process.version, v8: process.versions.v8, threadId,
32
+ intervalMs: now - previousTime,
33
+ rssBytes: rss, heapUsedBytes: heapUsed, heapTotalBytes: heapTotal,
34
+ externalBytes: external, arrayBufferBytes: arrayBuffers,
35
+ youngSpaceBytes: young?.space_size ?? 0,
36
+ workerLimits: { ...resourceLimits },
37
+ gc: { count, durationMs, maxPauseMs },
38
+ eventLoop,
39
+ };
40
+ // CPU usage is process-wide, including all worker threads. Emit it only
41
+ // in the main isolate so aggregators cannot count the RSC CPU twice.
42
+ if (previousCpu) {
43
+ const cpu = process.cpuUsage();
44
+ value.cpu = { scope: 'process', userMicros: cpu.user - previousCpu.user,
45
+ systemMicros: cpu.system - previousCpu.system };
46
+ previousCpu = cpu;
47
+ }
48
+ previousLoop = loop; previousTime = now;
49
+ count = durationMs = maxPauseMs = 0;
50
+ return value;
51
+ },
52
+ close() { observer.disconnect(); },
53
+ };
54
+ }
55
+
56
+ module.exports = { createRuntimeSampler };
@@ -93,14 +93,17 @@ async function invokeCache(id, kind, captures, args, callback, segment = '') {
93
93
  then(_resolve, reject) { reject(new Error("searchParams cannot be read inside 'use cache'; read them outside and pass their values as arguments")); },
94
94
  };
95
95
  if (encoded.key.length > MAX_BYTES) throw new Error("'use cache' arguments exceed 2 MiB");
96
- const key = createHash('sha256').update(id).update(kind).update(codec === plainCodec ? ':v8:' : ':flight:').update(encoded.key).digest('hex');
96
+ const format=codec===plainCodec?':v8:':codec.cacheKeyVersion==='flight-primitives-v2'?':flight-primitives-v2:':':flight:';
97
+ const key = createHash('sha256').update(id).update(kind).update(format).update(encoded.key).digest('hex');
97
98
  const profiles = context.cacheLife || context.manifest?.config?.cacheLife || defaults;
98
99
  const scope = { kind, profiles, life: { ...profiles.default }, explicit: new Set(), tags: new Set() };
99
100
  let producedMetadata;
100
101
  const producer = async () => runCacheScope(async () => {
101
102
  const value = await codec.render(() => callback(...encoded.values), encoded, context);
102
103
  producedMetadata = metadata(scope, context);
103
- return envelope(producedMetadata, Buffer.from(value));
104
+ // envelope owns its result immediately; an already owned codec Buffer need
105
+ // not be copied into another temporary Buffer before this synchronous copy.
106
+ return envelope(producedMetadata, Buffer.isBuffer(value)?value:Buffer.from(value));
104
107
  }, scope);
105
108
  let bytes, handlerMetadata;
106
109
  const custom = kind !== 'private' && !context.draftMode && Object.hasOwn(configured(context), kind);
@@ -104,7 +104,7 @@ implemented through PRNext's generated route registry. Do not copy a full
104
104
  Next.js configuration template into a new application.
105
105
 
106
106
  The repository's reference tests build and run matching applications against
107
- Next.js 16.4.3, including mounted App navigation, form Actions and cookies,
107
+ Next.js 16.4.4, including mounted App navigation, form Actions and cookies,
108
108
  metadata, Pages locale routing, redirects, APIs and selected webpack loader
109
109
  contracts. These executable examples cover specific behavior; they do not
110
110
  certify every Next.js application or plugin. `NextRequest.nextUrl` understands
@@ -112,10 +112,10 @@ data-route build IDs and locales, and request-cookie mutations synchronize the
112
112
  request header. Request cookies have different mutation results from response
113
113
  cookies; do not use them interchangeably.
114
114
 
115
- A locked real-application fixture additionally exercises Better Auth 1.7.14
115
+ A locked real-application fixture additionally exercises Better Auth 1.7.15
116
116
  (email/password, session cookies and logout), Drizzle ORM 0.45.4 over SQLite
117
117
  through its proxy driver, next-intl 4.14.9 (server and client translations),
118
- and the MDX 3.1.1 webpack loader against Next.js 16.4.3. It covers authenticated
118
+ and the MDX 3.1.1 webpack loader against Next.js 16.4.4. It covers authenticated
119
119
  Server Actions, user-owned rows, cookie/locale isolation, client navigation and
120
120
  Server Component `after()` callbacks. This is evidence for those workflows,
121
121
  not a guarantee for every plugin, provider or deployment configuration.
@@ -15,7 +15,7 @@ prnext output --json
15
15
  prnext start --hostname 127.0.0.1 --port 3000
16
16
  ```
17
17
 
18
- ### Upgrading from 0.1.19-alpha
18
+ ### Upgrading from an earlier alpha
19
19
 
20
20
  Install matching framework/native versions, then rebuild and restart the complete
21
21
  application or standalone output. Keep Node and the React protocol peers aligned
@@ -24,14 +24,17 @@ their identities match; never delete `data.sqlite3` or invalidation history to
24
24
  perform an upgrade. Read the storage section below before pruning compiler caches.
25
25
 
26
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
27
+ whole `PRNEXT_PM_HOME`, run the old installation's `prnext pdown` with that same
28
+ home before replacing packages. The 0.1.21 local protocol rejects an old
29
+ supervisor rather than silently taking it over. Shutdown stops all managed apps
30
+ and preserves saved settings and enabled
29
31
  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
32
+ rebuild the installations that must return with the new version, then launch `pstart` or
31
33
  `prestart` through the new CLI. It also restores all enabled entries, with their
32
34
  saved binary paths and health routes. Check `pstatus` and real health responses;
33
35
  a global CLI update does not replace each app's package or binary. Keep independent
34
- lifecycles in separate homes.
36
+ lifecycles in separate homes. Do not stop unrelated Node processes or external
37
+ databases, Redis, proxies and OS services as an automatic upgrade fallback.
35
38
 
36
39
  Set `server.publicOrigin` to the external HTTPS origin and `server.trustedProxies`
37
40
  to the immediate proxy's IP/CIDR addresses. The configured origin wins; forwarded
@@ -185,6 +185,28 @@ exclude the full streaming body, and missing/dropped samples are not zero.
185
185
  `prnext analyze`, after building, rather than interpreting request time as
186
186
  compiler performance.
187
187
 
188
+ The `flight` section of `prnext inspect --json` groups observed App Router
189
+ payload sizes by route pattern and render phase. `flightP95Bytes` is the
190
+ uncompressed Flight size for completed samples; `responseP95Bytes` is the
191
+ uncompressed application output yielded by the HTML renderer. Cancelled, failed
192
+ and timed-out samples remain visible separately and do not count as complete
193
+ payloads. RSC and SSR can observe the same Flight stream: never add their sizes
194
+ or request counts. `modelReadyP95Ms`, `shellReadyP95Ms` and `durationP95Ms`
195
+ include rendering, application work and asynchronous waits, not isolated
196
+ encoder/decoder CPU. Missing data stays `null`. The OTLP/JSON view includes
197
+ `prnext.flight.response.size` in bytes with separate render-phase attributes.
198
+
199
+ Large server-generated presentation trees can produce much more Flight data
200
+ than compact public props. For a pure table, consider a small explicit
201
+ `'use client'` presentation component receiving only the public fields it needs.
202
+ Its initial document can still render on the server, while its implementation
203
+ also joins the browser bundle. Measure the resulting Flight/HTML sizes,
204
+ JavaScript transfer, browser execution and hydration together. Keep database
205
+ access, credentials and private server logic outside that boundary. A count-only
206
+ synthetic table does not predict savings for real row data; converting arbitrary
207
+ Server Components automatically is not a safe optimization. Inspecting a route
208
+ does not automatically rewrite it, enable caching or change its privacy policy.
209
+
188
210
  Initial-JS analysis includes required hydration imports and SSR-used dynamic
189
211
  chunks. Schema 2 reports per-prerendered-path samples and the maximum size for
190
212
  each compression metric; a route's file list can be the union of its samples.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thomas.f/prnext",
3
- "version": "0.1.20-alpha",
3
+ "version": "0.1.21-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",
@@ -135,38 +135,38 @@
135
135
  "@edge-runtime/vm": "5.0.0",
136
136
  "@jridgewell/trace-mapping": "0.3.31",
137
137
  "@mapbox/node-pre-gyp": "2.0.3",
138
- "@rollup/pluginutils": "5.4.0",
139
- "acorn": "8.18.0",
138
+ "@rollup/pluginutils": "5.5.1",
139
+ "acorn": "8.19.0",
140
140
  "acorn-import-attributes": "1.9.5",
141
141
  "async-sema": "3.1.1",
142
142
  "babel-plugin-react-compiler": "1.0.0",
143
143
  "bindings": "1.5.0",
144
- "browserslist": "4.29.0",
145
- "dotenv": "16.6.1",
144
+ "browserslist": "4.29.5",
145
+ "dotenv": "18.0.9",
146
146
  "dotenv-expand": "11.0.7",
147
147
  "esbuild": "^0.28.2",
148
148
  "estree-walker": "2.0.2",
149
149
  "get-tsconfig": "4.14.3",
150
- "glob": "14.0.2",
150
+ "glob": "14.0.3",
151
151
  "graceful-fs": "4.2.11",
152
152
  "html-to-image": "1.11.13",
153
153
  "jsonc-parser": "3.3.1",
154
- "lightningcss": "1.31.0",
154
+ "lightningcss": "1.33.0",
155
155
  "loader-runner": "4.3.2",
156
- "minimatch": "11.0.1",
156
+ "minimatch": "11.0.4",
157
157
  "node-gyp-build": "4.8.4",
158
- "oxc-parser": "0.155.0",
158
+ "oxc-parser": "0.156.0",
159
159
  "path-to-regexp": "6.3.0",
160
160
  "picomatch": "4.0.7",
161
- "postcss": "8.5.28",
161
+ "postcss": "8.5.32",
162
162
  "postcss-flexbugs-fixes": "5.0.2",
163
- "postcss-preset-env": "11.5.3",
164
- "postcss-selector-parser": "7.1.6",
163
+ "postcss-preset-env": "11.6.2",
164
+ "postcss-selector-parser": "7.1.7",
165
165
  "postcss-value-parser": "4.2.0",
166
166
  "react-refresh": "0.19.0",
167
167
  "resolve-from": "5.0.0",
168
- "schema-utils": "4.3.3",
169
- "webpack": "5.111.1"
168
+ "schema-utils": "4.8.2",
169
+ "webpack": "5.112.2"
170
170
  },
171
171
  "peerDependencies": {
172
172
  "@redis/client": "^6.3.0",
@@ -184,12 +184,12 @@
184
184
  }
185
185
  },
186
186
  "optionalDependencies": {
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"
187
+ "prnext-darwin-arm64": "0.1.21-alpha",
188
+ "prnext-darwin-x64": "0.1.21-alpha",
189
+ "prnext-linux-arm64-gnu": "0.1.21-alpha",
190
+ "prnext-linux-x64-gnu": "0.1.21-alpha",
191
+ "@thomas.f/prnext-win32-x64-msvc": "0.1.21-alpha",
192
+ "@thomas.f/prnext-win32-arm64-msvc": "0.1.21-alpha"
193
193
  },
194
194
  "engines": {
195
195
  "node": "^22.23.4 || ^24.22.1 || >=26.12.1"