@thomas.f/prnext 0.1.19-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 (113) hide show
  1. package/build/app.mjs +46 -78
  2. package/build/build-lock.mjs +2 -71
  3. package/build/build-retention.mjs +119 -0
  4. package/build/build-timings.mjs +38 -0
  5. package/build/bundle-runtime.mjs +17 -16
  6. package/build/cache-command.mjs +31 -0
  7. package/build/check.mjs +41 -3
  8. package/build/compilation-cache.mjs +32 -12
  9. package/build/compilation-storage.mjs +190 -0
  10. package/build/compiler.mjs +5 -4
  11. package/build/config.mjs +23 -2
  12. package/build/create.mjs +1 -1
  13. package/build/css-modules.mjs +4 -0
  14. package/build/dev-action-graph.mjs +21 -0
  15. package/build/flight-summary.mjs +64 -0
  16. package/build/font-cache.mjs +9 -6
  17. package/build/font-files.mjs +1 -1
  18. package/build/index.mjs +57 -36
  19. package/build/inspect.mjs +3 -0
  20. package/build/inspection-summary.mjs +7 -2
  21. package/build/native-discovery.mjs +18 -6
  22. package/build/native-ppr.mjs +32 -0
  23. package/build/npm-packages.mjs +2 -2
  24. package/build/og.mjs +121 -1
  25. package/build/parallel-plan.mjs +55 -0
  26. package/build/persistent-cache.mjs +29 -24
  27. package/build/production-check.mjs +34 -0
  28. package/build/react-compiler-report.mjs +31 -8
  29. package/build/react-compiler.mjs +101 -10
  30. package/build/server-config.mjs +35 -0
  31. package/build/source-analysis.mjs +108 -5
  32. package/build/standalone-copy.mjs +48 -0
  33. package/build/standalone.mjs +2 -22
  34. package/build/static-adapter.mjs +7 -3
  35. package/build/transform-cache.mjs +17 -3
  36. package/build/transform.mjs +38 -6
  37. package/build/typescript-dev.mjs +3 -1
  38. package/cli.mjs +27 -5
  39. package/compat/app-layout-context.cjs +7 -3
  40. package/compat/diagnostics.cjs +5 -4
  41. package/compat/flight-delta.cjs +47 -12
  42. package/compat/index.d.ts +24 -1
  43. package/compat/navigation-authorization.cjs +8 -1
  44. package/compat/runtime-sample.cjs +56 -0
  45. package/compat/use-cache.cjs +5 -2
  46. package/docs/ai/compatibility.md +113 -10
  47. package/docs/ai/deployment.md +144 -0
  48. package/docs/ai/quickstart.md +5 -0
  49. package/docs/ai/troubleshooting.md +22 -0
  50. package/package.json +25 -22
  51. package/release/fontkit.mjs +31 -0
  52. package/release/verify.mjs +3 -0
  53. package/runtime/api.mjs +5 -2
  54. package/runtime/app-client.mjs +246 -54
  55. package/runtime/app-partial.mjs +2 -10
  56. package/runtime/app-prefetch.mjs +2 -2
  57. package/runtime/app-render.mjs +42 -13
  58. package/runtime/app-stream-channel.mjs +20 -6
  59. package/runtime/app-stream-html.mjs +30 -11
  60. package/runtime/app-styles.mjs +22 -4
  61. package/runtime/cache-components-rsc.mjs +26 -6
  62. package/runtime/cache-result.mjs +58 -0
  63. package/runtime/compile-cache-retention.mjs +53 -0
  64. package/runtime/compile-cache.mjs +28 -1
  65. package/runtime/dev-client.mjs +32 -8
  66. package/runtime/dev-compiler-broker.mjs +20 -23
  67. package/runtime/dev-gateway.mjs +23 -6
  68. package/runtime/dev-route-selection.mjs +64 -0
  69. package/runtime/dev-runtime-pool.mjs +3 -2
  70. package/runtime/dev-runtime-spare.mjs +1 -1
  71. package/runtime/dev-session-retention.mjs +154 -0
  72. package/runtime/development-flight.mjs +59 -0
  73. package/runtime/development-queue.mjs +33 -0
  74. package/runtime/development.mjs +37 -16
  75. package/runtime/env.mjs +9 -0
  76. package/runtime/flight-delta.mjs +31 -5
  77. package/runtime/flight-diagnostics.mjs +48 -0
  78. package/runtime/flight-tap.mjs +10 -5
  79. package/runtime/html-envelope.mjs +34 -0
  80. package/runtime/loopback-rpc.mjs +92 -0
  81. package/runtime/native-ppr.mjs +88 -0
  82. package/runtime/og/NOTICE.txt +1 -1
  83. package/runtime/og/index.node.mjs +76 -31216
  84. package/runtime/og/upstream.json +115 -0
  85. package/runtime/persistent/client.mjs +5 -3
  86. package/runtime/persistent/common.mjs +7 -18
  87. package/runtime/persistent/daemon.mjs +4 -12
  88. package/runtime/persistent/health.mjs +58 -0
  89. package/runtime/persistent/native-proxy.mjs +1 -1
  90. package/runtime/persistent/watchdog.mjs +12 -27
  91. package/runtime/prewarm.mjs +35 -0
  92. package/runtime/project-lock.mjs +71 -0
  93. package/runtime/request-transport.mjs +112 -32
  94. package/runtime/rsc-worker.mjs +58 -8
  95. package/runtime/storage-path.mjs +36 -0
  96. package/runtime/trace.mjs +44 -4
  97. package/runtime/worker.mjs +37 -17
  98. package/vendor/fontkit/NOTICE.txt +5 -0
  99. package/vendor/fontkit/SOURCE-NOTICES.txt +209 -0
  100. package/vendor/fontkit/index.mjs +3 -0
  101. package/vendor/fontkit/licenses/@swc__helpers/LICENSE +201 -0
  102. package/vendor/fontkit/licenses/base64-js/LICENSE +21 -0
  103. package/vendor/fontkit/licenses/brotli/readme.md +61 -0
  104. package/vendor/fontkit/licenses/clone/LICENSE +18 -0
  105. package/vendor/fontkit/licenses/dfa/README.md +83 -0
  106. package/vendor/fontkit/licenses/fast-deep-equal/LICENSE +21 -0
  107. package/vendor/fontkit/licenses/fontkit/README.md +266 -0
  108. package/vendor/fontkit/licenses/restructure/LICENSE +21 -0
  109. package/vendor/fontkit/licenses/tiny-inflate/LICENSE +21 -0
  110. package/vendor/fontkit/licenses/tslib/LICENSE.txt +12 -0
  111. package/vendor/fontkit/licenses/unicode-properties/LICENSE +8 -0
  112. package/vendor/fontkit/licenses/unicode-trie/LICENSE +8 -0
  113. package/vendor/fontkit/upstream.json +215 -0
@@ -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.19-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,58 +135,61 @@
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
- "fontkit": "2.0.4",
150
149
  "get-tsconfig": "4.14.3",
151
- "glob": "14.0.2",
150
+ "glob": "14.0.3",
152
151
  "graceful-fs": "4.2.11",
153
152
  "html-to-image": "1.11.13",
154
153
  "jsonc-parser": "3.3.1",
155
- "lightningcss": "1.31.0",
154
+ "lightningcss": "1.33.0",
156
155
  "loader-runner": "4.3.2",
157
- "minimatch": "11.0.1",
156
+ "minimatch": "11.0.4",
158
157
  "node-gyp-build": "4.8.4",
159
- "oxc-parser": "0.155.0",
158
+ "oxc-parser": "0.156.0",
160
159
  "path-to-regexp": "6.3.0",
161
160
  "picomatch": "4.0.7",
162
- "postcss": "8.5.28",
161
+ "postcss": "8.5.32",
163
162
  "postcss-flexbugs-fixes": "5.0.2",
164
- "postcss-preset-env": "11.5.3",
165
- "postcss-selector-parser": "7.1.6",
163
+ "postcss-preset-env": "11.6.2",
164
+ "postcss-selector-parser": "7.1.7",
166
165
  "postcss-value-parser": "4.2.0",
167
166
  "react-refresh": "0.19.0",
168
167
  "resolve-from": "5.0.0",
169
- "schema-utils": "4.3.3",
170
- "webpack": "5.111.1"
168
+ "schema-utils": "4.8.2",
169
+ "webpack": "5.112.2"
171
170
  },
172
171
  "peerDependencies": {
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.19-alpha",
185
- "prnext-darwin-x64": "0.1.19-alpha",
186
- "prnext-linux-arm64-gnu": "0.1.19-alpha",
187
- "prnext-linux-x64-gnu": "0.1.19-alpha",
188
- "@thomas.f/prnext-win32-x64-msvc": "0.1.19-alpha",
189
- "@thomas.f/prnext-win32-arm64-msvc": "0.1.19-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"
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
+ }
@@ -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) init.body = bytes;
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 () => {